[gpstk] 02/09: Imported Upstream version 1.3

Bas Couwenberg sebastic at debian.org
Sat Jul 9 21:51:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository gpstk.

commit 1b4eaa969cd03e30656a6cc10a94b9ffdb714779
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Jun 4 19:28:08 2016 +0200

    Imported Upstream version 1.3
---
 dev/AUTHORS                                        |    32 +
 {trunk => dev}/COPYING                             |     0
 dev/ChangeLog                                      |   442 +
 dev/Doxyfile                                       |  1177 +
 dev/INSTALL                                        |   386 +
 dev/Jamfile                                        |    11 +
 dev/Jamrules                                       |   315 +
 dev/Makefile.am                                    |     3 +
 dev/NEWS                                           |    20 +
 dev/README                                         |    98 +
 .../DataAvailability/DataAvailabilityAnalyzer.cpp  |   760 +
 .../DataAvailability/DataAvailabilityAnalyzer.hpp  |   200 +
 dev/apps/DataAvailability/Jamfile                  |     7 +
 dev/apps/DataAvailability/Makefile.am              |     7 +
 dev/apps/DataAvailability/daa.cpp                  |    64 +
 dev/apps/Jamfile                                   |    23 +
 dev/apps/MDPtools/BELogEntry.cpp                   |    64 +
 dev/apps/MDPtools/BELogEntry.hpp                   |    51 +
 dev/apps/MDPtools/Histogram.hpp                    |    74 +
 dev/apps/MDPtools/Jamfile                          |    23 +
 dev/apps/MDPtools/MDPProcessors.cpp                |   337 +
 dev/apps/MDPtools/MDPProcessors.hpp                |   130 +
 dev/apps/MDPtools/Makefile.am                      |    13 +
 dev/apps/MDPtools/NavProc.cpp                      |   278 +
 dev/apps/MDPtools/NavProc.hpp                      |    72 +
 dev/apps/MDPtools/SummaryProc.cpp                  |   559 +
 dev/apps/MDPtools/SummaryProc.hpp                  |    70 +
 dev/apps/MDPtools/TrackProc.cpp                    |   142 +
 dev/apps/MDPtools/TrackProc.hpp                    |    37 +
 dev/apps/MDPtools/UniqueAlmStore.cpp               |   406 +
 dev/apps/MDPtools/UniqueAlmStore.hpp               |    93 +
 dev/apps/MDPtools/mdp2fic.cpp                      |   450 +
 dev/apps/MDPtools/mdp2rinex.cpp                    |   355 +
 dev/apps/MDPtools/mdpscreen/Jamfile                |    11 +
 dev/apps/MDPtools/mdpscreen/ScreenProc.cpp         |   436 +
 dev/apps/MDPtools/mdpscreen/ScreenProc.hpp         |    75 +
 dev/apps/MDPtools/mdpscreen/mdpscreen.cpp          |   115 +
 dev/apps/MDPtools/mdptool.cpp                      |   267 +
 dev/apps/MDPtools/tcptest.cpp                      |    86 +
 dev/apps/Makefile.am                               |     4 +
 dev/apps/RinexPlot/README                          |    31 +
 {trunk => dev}/apps/RinexPlot/RSW214B.obs          |     0
 {trunk => dev}/apps/RinexPlot/RinexPlot.bat        |     0
 dev/apps/RinexPlot/RinexPlot.pl                    |  3487 ++
 {trunk => dev}/apps/RinexPlot/alic0320.04o         |     0
 {trunk => dev}/apps/RinexPlot/goRP                 |     0
 {trunk => dev}/apps/RinexPlot/goRP.bat             |     0
 {trunk => dev}/apps/RinexPlot/goRP1                |     0
 {trunk => dev}/apps/RinexPlot/goRP1.bat            |     0
 {trunk => dev}/apps/RinexPlot/positions.txt        |     0
 dev/apps/Rinextools/EditRinex.cpp                  |   371 +
 dev/apps/Rinextools/Jamfile                        |    13 +
 dev/apps/Rinextools/Makefile.am                    |    14 +
 dev/apps/Rinextools/NavMerge.cpp                   |   276 +
 dev/apps/Rinextools/README                         |    45 +
 dev/apps/Rinextools/ResCor.cpp                     |  2230 +
 dev/apps/Rinextools/RinSum.cpp                     |   852 +
 dev/apps/Rinextools/RinexDump.cpp                  |   369 +
 dev/apps/Rinextools/RinexEditor.cpp                |  1467 +
 dev/apps/Rinextools/RinexEditor.hpp                |   317 +
 {trunk => dev}/apps/bindings/DayTime.i             |     0
 {trunk => dev}/apps/bindings/Exception.i           |     0
 {trunk => dev}/apps/bindings/FFTextStream.i        |     0
 {trunk => dev}/apps/bindings/GPSZcount.i           |     0
 {trunk => dev}/apps/bindings/README                |     0
 {trunk => dev}/apps/bindings/RinexObsStream.i      |     0
 {trunk => dev}/apps/bindings/common.i              |     0
 {trunk => dev}/apps/bindings/gpstk.i               |     0
 dev/apps/bindings/java/Makefile                    |    51 +
 .../apps/bindings/java/examples/example1.java      |     0
 dev/apps/bindings/octave/Makefile                  |    29 +
 .../apps/bindings/octave/calculatePosition.cpp     |     0
 .../apps/bindings/octave/readRinexObsFast.cpp      |     0
 .../apps/bindings/octave/readRinexObsGeom.cpp      |     0
 dev/apps/bindings/perl/Makefile                    |    50 +
 .../apps/bindings/python/DayTimeException.i        |     0
 {trunk => dev}/apps/bindings/python/Developer.txt  |     0
 .../apps/bindings/python/ExtraWaveFact.i           |     0
 {trunk => dev}/apps/bindings/python/Makefile       |     0
 {trunk => dev}/apps/bindings/python/RinexDatum.i   |     0
 {trunk => dev}/apps/bindings/python/RinexObsType.i |     0
 {trunk => dev}/apps/bindings/python/common.i       |     0
 .../apps/bindings/python/examples/example1.html    |     0
 .../apps/bindings/python/examples/example1.py      |     0
 .../apps/bindings/python/examples/example2.html    |     0
 .../apps/bindings/python/examples/example2.py      |     0
 .../apps/bindings/python/examples/example3.html    |     0
 .../apps/bindings/python/examples/example3.py      |     0
 .../apps/bindings/python/gpstkPython.html          |     0
 {trunk => dev}/apps/bindings/python/gpstkPython.i  |     0
 .../apps/bindings/python/gpstkPythonUtils.cpp      |     0
 .../apps/bindings/python/gpstkPythonUtils.i        |     0
 .../apps/bindings/python/gpstkPython_wrap.cxx      |     0
 {trunk => dev}/apps/bindings/python/pydoc.py       |     0
 {trunk => dev}/apps/bindings/python/sensorType.i   |     0
 {trunk => dev}/apps/bindings/python/streamRead.cpp |     0
 {trunk => dev}/apps/bindings/python/streamRead.i   |     0
 dev/apps/bindings/tcl/Makefile                     |    49 +
 .../apps/bindings/tcl/examples/example1.tcl        |     0
 .../apps/bindings/tcl/gpstk_wrap_mod.cxx           |     0
 dev/apps/checktools/CheckFrame.hpp                 |   121 +
 dev/apps/checktools/Jamfile                        |    12 +
 dev/apps/checktools/Makefile.am                    |    11 +
 dev/apps/checktools/ficacheck.cpp                  |    39 +
 dev/apps/checktools/ficcheck.cpp                   |    39 +
 dev/apps/checktools/rmwcheck.cpp                   |    40 +
 dev/apps/checktools/rnwcheck.cpp                   |    39 +
 dev/apps/checktools/rowcheck.cpp                   |    39 +
 dev/apps/converters/Jamfile                        |     9 +
 dev/apps/converters/Makefile.am                    |     7 +
 dev/apps/converters/NovatelData.cpp                |  1123 +
 dev/apps/converters/NovatelData.hpp                |   184 +
 dev/apps/converters/NovatelStream.hpp              |    97 +
 dev/apps/converters/novaRinex.cpp                  |  1151 +
 {trunk => dev}/apps/differential/Jamfile           |     0
 dev/apps/differential/Makefile.am                  |     7 +
 dev/apps/differential/vecsol.1                     |   118 +
 dev/apps/differential/vecsol.conf                  |    11 +
 dev/apps/differential/vecsol.cpp                   |  1303 +
 dev/apps/differential/vecsol.eph                   |    15 +
 dev/apps/differential/vecsol.nav                   |    15 +
 {trunk => dev}/apps/differential/vecsol.pdf        |   Bin
 dev/apps/difftools/DiffFrame.hpp                   |    85 +
 dev/apps/difftools/Jamfile                         |    12 +
 dev/apps/difftools/Makefile.am                     |    11 +
 dev/apps/difftools/ephdiff.cpp                     |   360 +
 dev/apps/difftools/ficdiff.cpp                     |   104 +
 dev/apps/difftools/rmwdiff.cpp                     |   222 +
 dev/apps/difftools/rnwdiff.cpp                     |   206 +
 dev/apps/difftools/rowdiff.cpp                     |   204 +
 dev/apps/filetools/Jamfile                         |    17 +
 dev/apps/filetools/Makefile.am                     |    13 +
 {trunk => dev}/apps/filetools/RinexThin.cpp        |     0
 dev/apps/filetools/bc2sp3.cpp                      |   323 +
 dev/apps/filetools/fic2rin.cpp                     |   126 +
 dev/apps/filetools/ficafic.cpp                     |    90 +
 dev/apps/filetools/ficfica.cpp                     |    90 +
 dev/apps/filetools/navdmp.cpp                      |   430 +
 dev/apps/filetools/sp32bc.cpp                      |    83 +
 dev/apps/filetools/sp3version.cpp                  |   239 +
 dev/apps/geomatics/Jamfile                         |    12 +
 dev/apps/geomatics/Makefile.am                     |     2 +
 dev/apps/geomatics/cycleslips/DiscFix.cpp          |  1511 +
 dev/apps/geomatics/cycleslips/Jamfile              |    16 +
 dev/apps/geomatics/cycleslips/Makefile.am          |     7 +
 .../apps/geomatics}/cycleslips/examples/GFP2.gp    |     0
 .../geomatics}/cycleslips/examples/GFP2_Win.gp     |     0
 .../apps/geomatics}/cycleslips/examples/GFP3.gp    |     0
 .../geomatics}/cycleslips/examples/GFP3_Win.gp     |     0
 .../apps/geomatics}/cycleslips/examples/GFP6.gp    |     0
 .../geomatics}/cycleslips/examples/GFP6_Win.gp     |     0
 .../geomatics}/cycleslips/examples/GFRF2G17.jpg    |   Bin
 .../geomatics}/cycleslips/examples/GFRF3G17.jpg    |   Bin
 .../geomatics}/cycleslips/examples/GFRF6G31.jpg    |   Bin
 .../apps/geomatics}/cycleslips/examples/WLSS2.gp   |     0
 .../geomatics}/cycleslips/examples/WLSS2G17.jpg    |   Bin
 .../geomatics}/cycleslips/examples/WLSS2_Win.gp    |     0
 .../apps/geomatics}/cycleslips/examples/WLSS3.gp   |     0
 .../geomatics}/cycleslips/examples/WLSS3G17.jpg    |   Bin
 .../geomatics}/cycleslips/examples/WLSS3_Win.gp    |     0
 .../apps/geomatics}/cycleslips/examples/WLSS6.gp   |     0
 .../geomatics}/cycleslips/examples/WLSS6G31.jpg    |   Bin
 .../geomatics}/cycleslips/examples/WLSS6_Win.gp    |     0
 .../cycleslips/examples/chmp0110.02o.G31           |     0
 .../apps/geomatics}/cycleslips/examples/df.inp     |     0
 .../geomatics}/cycleslips/examples/df_occult.inp   |     0
 .../cycleslips/examples/eusk2950.01o.G17           |     0
 .../cycleslips/examples/euskCorrected.jpg          |   Bin
 .../apps/geomatics}/cycleslips/examples/goDF1      |     0
 .../apps/geomatics}/cycleslips/examples/goDF1.bat  |     0
 .../apps/geomatics}/cycleslips/examples/goDF2      |     0
 .../apps/geomatics}/cycleslips/examples/goDF2.bat  |     0
 .../geomatics}/cycleslips/examples/mjd2gps.awk     |     0
 dev/apps/geomatics/kalman/Jamfile                  |    20 +
 dev/apps/geomatics/kalman/Makefile.am              |    12 +
 dev/apps/geomatics/kalman/mergeSRI.cpp             |   180 +
 dev/apps/geomatics/kalman/tnl.cpp                  |   105 +
 dev/apps/geomatics/kalman/tsrif.cpp                |  1430 +
 dev/apps/geomatics/kalman/tsrifsu.cpp              |   441 +
 dev/apps/geomatics/kalman/tsriftu.cpp              |   247 +
 dev/apps/geomatics/lib/DCinternals.hpp             |   320 +
 dev/apps/geomatics/lib/DDid.cpp                    |   264 +
 dev/apps/geomatics/lib/DDid.hpp                    |   143 +
 dev/apps/geomatics/lib/DiscCorr.cpp                |  2559 +
 dev/apps/geomatics/lib/DiscCorr.hpp                |    82 +
 dev/apps/geomatics/lib/EarthOrientation.cpp        |   535 +
 dev/apps/geomatics/lib/EarthOrientation.hpp        |   286 +
 dev/apps/geomatics/lib/GDCconfiguration.cpp        |   204 +
 dev/apps/geomatics/lib/GDCconfiguration.hpp        |    94 +
 dev/apps/geomatics/lib/GSatID.cpp                  |    51 +
 dev/apps/geomatics/lib/GSatID.hpp                  |   197 +
 dev/apps/geomatics/lib/GeodeticFrames.cpp          |  2157 +
 dev/apps/geomatics/lib/GeodeticFrames.hpp          |   483 +
 dev/apps/geomatics/lib/Jamfile                     |    21 +
 dev/apps/geomatics/lib/Makefile.am                 |     7 +
 dev/apps/geomatics/lib/Namelist.cpp                |   472 +
 dev/apps/geomatics/lib/Namelist.hpp                |   223 +
 dev/apps/geomatics/lib/PhaseWindup.cpp             |   407 +
 dev/apps/geomatics/lib/PhaseWindup.hpp             |    59 +
 dev/apps/geomatics/lib/PreciseRange.cpp            |   201 +
 dev/apps/geomatics/lib/PreciseRange.hpp            |   124 +
 dev/apps/geomatics/lib/RobustStats.cpp             |   328 +
 dev/apps/geomatics/lib/RobustStats.hpp             |   477 +
 dev/apps/geomatics/lib/SRI.cpp                     |   951 +
 dev/apps/geomatics/lib/SRI.hpp                     |   425 +
 dev/apps/geomatics/lib/SRIFilter.cpp               |  1200 +
 dev/apps/geomatics/lib/SRIFilter.hpp               |   519 +
 dev/apps/geomatics/lib/SatPass.cpp                 |   292 +
 dev/apps/geomatics/lib/SatPass.hpp                 |   214 +
 dev/apps/geomatics/lib/format.cpp                  |    57 +
 dev/apps/geomatics/lib/format.hpp                  |    73 +
 dev/apps/geomatics/lib/index.hpp                   |    87 +
 dev/apps/geomatics/lib/random.cpp                  |   170 +
 dev/apps/geomatics/lib/random.hpp                  |    52 +
 dev/apps/geomatics/relposition/ClockModel.cpp      |    77 +
 dev/apps/geomatics/relposition/CommandInput.cpp    |  1451 +
 dev/apps/geomatics/relposition/CommandInput.hpp    |   137 +
 .../geomatics/relposition/ComputeRAIMSolution.cpp  |   212 +
 dev/apps/geomatics/relposition/Configure.cpp       |   241 +
 dev/apps/geomatics/relposition/DDBase.cpp          |   278 +
 dev/apps/geomatics/relposition/DDBase.hpp          |   126 +
 dev/apps/geomatics/relposition/DataOutput.cpp      |   338 +
 dev/apps/geomatics/relposition/DataStructures.cpp  |   134 +
 dev/apps/geomatics/relposition/DataStructures.hpp  |   176 +
 .../geomatics/relposition/DoubleDifference.cpp     |   426 +
 dev/apps/geomatics/relposition/EditDDs.cpp         |   788 +
 .../geomatics/relposition/EditRawDataBuffers.cpp   |   216 +
 dev/apps/geomatics/relposition/ElevationMask.cpp   |   135 +
 .../geomatics/relposition/EphemerisImprovement.cpp |   154 +
 dev/apps/geomatics/relposition/Estimation.cpp      |  1611 +
 dev/apps/geomatics/relposition/Jamfile             |    27 +
 dev/apps/geomatics/relposition/Makefile.am         |    17 +
 dev/apps/geomatics/relposition/ProcessRawData.cpp  |   371 +
 dev/apps/geomatics/relposition/ReadObsFiles.cpp    |   229 +
 dev/apps/geomatics/relposition/ReadRawData.cpp     |   367 +
 dev/apps/geomatics/relposition/Synchronization.cpp |   409 +
 dev/apps/geomatics/relposition/Timetable.cpp       |   845 +
 dev/apps/geomatics/relposition/constants.hpp       |    88 +
 dev/apps/geomatics/relposition/ddmerge.cpp         |   299 +
 dev/apps/geomatics/relposition/doc/CodePlan.pdf    |   Bin 0 -> 248552 bytes
 .../geomatics/relposition/doc/DDBaseCmdLineRef.pdf |   Bin 0 -> 93250 bytes
 dev/apps/geomatics/relposition/doc/DDBaseDoc.pdf   |   Bin 0 -> 301018 bytes
 .../geomatics/relposition/doc/SimpleBasePlan.pdf   |   Bin 0 -> 104247 bytes
 dev/apps/geomatics/robust/Jamfile                  |    15 +
 dev/apps/geomatics/robust/Makefile.am              |     7 +
 dev/apps/geomatics/robust/rstats.cpp               |   344 +
 dev/apps/ionosphere/IonoBias.cpp                   |  1666 +
 dev/apps/ionosphere/Jamfile                        |    10 +
 dev/apps/ionosphere/Makefile.am                    |     8 +
 dev/apps/ionosphere/README                         |    15 +
 dev/apps/ionosphere/TECMaps.cpp                    |  1479 +
 dev/apps/ionosphere/VTECMap.cpp                    |   479 +
 dev/apps/ionosphere/VTECMap.hpp                    |   249 +
 {trunk => dev}/apps/ionosphere/example/README      |     0
 {trunk => dev}/apps/ionosphere/example/bias.inp    |     0
 {trunk => dev}/apps/ionosphere/example/getigs      |     0
 {trunk => dev}/apps/ionosphere/example/gobias      |     0
 {trunk => dev}/apps/ionosphere/example/gomaps      |     0
 {trunk => dev}/apps/ionosphere/example/gopre       |     0
 {trunk => dev}/apps/ionosphere/example/list.files  |     0
 {trunk => dev}/apps/ionosphere/example/maps.inp    |     0
 {trunk => dev}/apps/ionosphere/example/plotall.pl  |     0
 .../apps/ionosphere/example/preprocess.pl          |     0
 {trunk => dev}/apps/ionosphere/example/see         |     0
 {trunk => dev}/apps/mergetools/Jamfile             |     0
 dev/apps/mergetools/Makefile.am                    |    10 +
 dev/apps/mergetools/MergeFrame.hpp                 |    94 +
 dev/apps/mergetools/mergeFIC.cpp                   |   110 +
 dev/apps/mergetools/mergeRinMet.cpp                |   124 +
 dev/apps/mergetools/mergeRinNav.cpp                |   127 +
 dev/apps/mergetools/mergeRinObs.cpp                |   127 +
 dev/apps/multipath/Jamfile                         |     6 +
 dev/apps/multipath/Makefile.am                     |     7 +
 dev/apps/multipath/ObsArray.cpp                    |   390 +
 dev/apps/multipath/ObsArray.hpp                    |   146 +
 dev/apps/multipath/SparseBinnedStats.hpp           |   106 +
 dev/apps/multipath/ValarrayUtils.cpp               |    15 +
 dev/apps/multipath/ValarrayUtils.hpp               |   117 +
 dev/apps/multipath/mpsolve.cpp                     |   343 +
 dev/apps/multipath/testObsArray.cpp                |    80 +
 dev/apps/multipath/testSparseBinnedStats.cpp       |    59 +
 dev/apps/multipath/testValarrayUtils.cpp           |    25 +
 dev/apps/positioning/Jamfile                       |     9 +
 dev/apps/positioning/Makefile.am                   |    10 +
 dev/apps/positioning/PRSolve.cpp                   |  2107 +
 {trunk => dev}/apps/positioning/doc/brdc0200.05n   |     0
 {trunk => dev}/apps/positioning/doc/nga13063.apc   |     0
 {trunk => dev}/apps/positioning/doc/nga13064.apc   |     0
 {trunk => dev}/apps/positioning/doc/nga13065.apc   |     0
 .../apps/positioning/doc/rinexpvt-manual.pdf       |   Bin
 .../apps/positioning/doc/rinexpvt-manual.tm        |     0
 {trunk => dev}/apps/positioning/doc/usno0200.05m   |     0
 {trunk => dev}/apps/positioning/doc/usno0200.05o   |     0
 dev/apps/positioning/examples/brdc3040.03n         |  2992 ++
 dev/apps/positioning/examples/grca304a.03o         |  6522 +++
 .../positioning/examples/positions.PRSolve.jpg     |   Bin 0 -> 48872 bytes
 .../positioning/examples/positions.posInterp.jpg   |   Bin 0 -> 49133 bytes
 dev/apps/positioning/examples/rd.pi.gp             |     7 +
 dev/apps/positioning/examples/rd.prs.gp            |     7 +
 dev/apps/positioning/examples/testpi               |    18 +
 dev/apps/positioning/posInterp.cpp                 |  1119 +
 dev/apps/positioning/poscvt.cpp                    |   216 +
 dev/apps/positioning/rinexpvt.cpp                  |   563 +
 dev/apps/positioning/rinexpvt.hpp                  |   118 +
 dev/apps/qa/Expression.cpp                         |   459 +
 dev/apps/qa/Expression.hpp                         |   245 +
 dev/apps/qa/Jamfile                                |     8 +
 dev/apps/qa/LinearCombination.cpp                  |    94 +
 dev/apps/qa/LinearCombination.hpp                  |   100 +
 dev/apps/qa/Makefile.am                            |     8 +
 dev/apps/qa/obsrip.cpp                             |    45 +
 dev/apps/receiver/AshtechMessage.cpp               |   434 +
 dev/apps/receiver/AshtechMessage.hpp               |   193 +
 dev/apps/receiver/Jamfile                          |    13 +
 dev/apps/receiver/Makefile.am                      |     9 +
 dev/apps/receiver/ScreenControl.cpp                |    19 +
 dev/apps/receiver/ScreenControl.hpp                |    20 +
 dev/apps/receiver/TODO.txt                         |    23 +
 dev/apps/receiver/ash2mdp.cpp                      |   273 +
 dev/apps/receiver/getUnixSerialInfo.cpp            |    24 +
 dev/apps/receiver/rfw.cpp                          |   156 +
 dev/apps/receiver/rinex.nav.template               |     3 +
 dev/apps/receiver/rinex.obs.template               |    14 +
 dev/apps/receiver/rtAshtech.cpp                    |   502 +
 dev/apps/reszilla/CycleSlipList.cpp                |    87 +
 dev/apps/reszilla/CycleSlipList.hpp                |    64 +
 dev/apps/reszilla/DDEpoch.cpp                      |   830 +
 dev/apps/reszilla/DDEpoch.hpp                      |   126 +
 dev/apps/reszilla/ElevationRange.hpp               |    72 +
 dev/apps/reszilla/Jamfile                          |    36 +
 dev/apps/reszilla/Makefile.am                      |     7 +
 dev/apps/reszilla/OrdApp.cpp                       |   267 +
 dev/apps/reszilla/OrdApp.hpp                       |    69 +
 dev/apps/reszilla/OrdEngine.cpp                    |   268 +
 dev/apps/reszilla/OrdEngine.hpp                    |    83 +
 dev/apps/reszilla/PhaseCleaner.cpp                 |   806 +
 dev/apps/reszilla/PhaseCleaner.hpp                 |   159 +
 dev/apps/reszilla/PhaseResidual.cpp                |   341 +
 dev/apps/reszilla/PhaseResidual.hpp                |   134 +
 dev/apps/reszilla/RobustLinearEstimator.cpp        |   218 +
 dev/apps/reszilla/RobustLinearEstimator.hpp        |    85 +
 dev/apps/reszilla/SvElevationMap.cpp               |    72 +
 dev/apps/reszilla/SvElevationMap.hpp               |    57 +
 dev/apps/reszilla/ddGen.cpp                        |   510 +
 dev/apps/reszilla/ddPlot                           |   400 +
 dev/apps/reszilla/ordClock.cpp                     |   176 +
 dev/apps/reszilla/ordEdit.cpp                      |   379 +
 dev/apps/reszilla/ordGen.cpp                       |   263 +
 dev/apps/reszilla/ordLinEst.cpp                    |   228 +
 dev/apps/reszilla/ordPlot                          |   329 +
 dev/apps/reszilla/ordStats.cpp                     |   329 +
 dev/apps/swrx/CACodeGenerator.hpp                  |    74 +
 dev/apps/swrx/CCReplica.cpp                        |   174 +
 dev/apps/swrx/CCReplica.hpp                        |   121 +
 dev/apps/swrx/CodeGenerator.hpp                    |    64 +
 dev/apps/swrx/ConstLinearRecurrentSequence.cpp     |    50 +
 dev/apps/swrx/ConstLinearRecurrentSequence.hpp     |    71 +
 dev/apps/swrx/EMLTracker.cpp                       |   247 +
 dev/apps/swrx/EMLTracker.hpp                       |   139 +
 dev/apps/swrx/IQStream.cpp                         |   324 +
 dev/apps/swrx/IQStream.hpp                         |   219 +
 dev/apps/swrx/Jamfile                              |    23 +
 dev/apps/swrx/NAVCodeGenerator.hpp                 |    42 +
 dev/apps/swrx/NavFramer.cpp                        |   190 +
 dev/apps/swrx/NavFramer.hpp                        |    94 +
 dev/apps/swrx/P0CodeGenerator.hpp                  |    70 +
 dev/apps/swrx/PCodeGenerator.hpp                   |   108 +
 dev/apps/swrx/SVSource.hpp                         |   183 +
 dev/apps/swrx/SimpleCorrelator.hpp                 |    67 +
 dev/apps/swrx/codeDump.cpp                         |   163 +
 dev/apps/swrx/complex_math.h                       |    73 +
 dev/apps/swrx/corltr.cpp                           |   338 +
 dev/apps/swrx/gpsSim.cpp                           |   387 +
 dev/apps/swrx/iqdump.cpp                           |   192 +
 dev/apps/swrx/normal.cpp                           |    67 +
 dev/apps/swrx/normal.hpp                           |    28 +
 dev/apps/swrx/plot                                 |   161 +
 dev/apps/swrx/simpleNav.cpp                        |   179 +
 dev/apps/swrx/tracker.cpp                          |   309 +
 dev/apps/time/Jamfile                              |     7 +
 dev/apps/time/Makefile.am                          |     8 +
 dev/apps/time/README.txt                           |    94 +
 dev/apps/time/calgps.cpp                           |   150 +
 dev/apps/time/timeconvert.cpp                      |   229 +
 dev/apps/visibility/Jamfile                        |    13 +
 dev/apps/visibility/Makefile.am                    |     8 +
 {trunk => dev}/apps/visibility/UsingWhereSat.doc   |   Bin
 dev/apps/visibility/WhereSat.cpp                   |   286 +
 dev/apps/visibility/findMoreThan12.cpp             |   189 +
 dev/autogen.sh                                     |     7 +
 dev/config.guess                                   |  1459 +
 dev/config.sub                                     |  1566 +
 dev/configure.ac                                   |    69 +
 dev/depcomp                                        |   529 +
 dev/examples/Jamfile                               |    21 +
 dev/examples/Makefile.am                           |    14 +
 {trunk => dev}/examples/bahr1620.04m               |     0
 {trunk => dev}/examples/bahr1620.04n               |     0
 {trunk => dev}/examples/bahr1620.04o               |     0
 dev/examples/bell030a.02o                          |  3185 ++
 dev/examples/brdc0300.02n                          |  2960 ++
 dev/examples/ebre030a.02o                          |  3071 ++
 {trunk => dev}/examples/example1.cpp               |     0
 {trunk => dev}/examples/example2.cpp               |     0
 dev/examples/example3.cpp                          |   122 +
 dev/examples/example4.cpp                          |   276 +
 dev/examples/example5.cpp                          |   295 +
 dev/examples/example6.cpp                          |   124 +
 dev/examples/example7.cpp                          |   741 +
 dev/install-sh                                     |   323 +
 dev/lib/Jamfile                                    |     5 +
 dev/lib/Makefile.am                                |     3 +
 dev/lib/rxio/AshtechALB.cpp                        |   115 +
 dev/lib/rxio/AshtechALB.hpp                        |    79 +
 dev/lib/rxio/AshtechData.cpp                       |   169 +
 dev/lib/rxio/AshtechData.hpp                       |   134 +
 dev/lib/rxio/AshtechEPB.cpp                        |   115 +
 dev/lib/rxio/AshtechEPB.hpp                        |    80 +
 dev/lib/rxio/AshtechMBEN.cpp                       |   252 +
 dev/lib/rxio/AshtechMBEN.hpp                       |   113 +
 dev/lib/rxio/AshtechPBEN.cpp                       |   157 +
 dev/lib/rxio/AshtechPBEN.hpp                       |    92 +
 dev/lib/rxio/AshtechStream.hpp                     |    82 +
 dev/lib/rxio/DataStatus.hpp                        |    69 +
 dev/lib/rxio/DeviceStream.hpp                      |   230 +
 dev/lib/rxio/EphReader.cpp                         |   170 +
 dev/lib/rxio/EphReader.hpp                         |    75 +
 dev/lib/rxio/FDStreamBuff.cpp                      |   181 +
 dev/lib/rxio/FDStreamBuff.hpp                      |    78 +
 dev/lib/rxio/FFIdentifier.cpp                      |   223 +
 dev/lib/rxio/FFIdentifier.hpp                      |    79 +
 dev/lib/rxio/Jamfile                               |    27 +
 dev/lib/rxio/MDPHeader.cpp                         |   405 +
 dev/lib/rxio/MDPHeader.hpp                         |   142 +
 dev/lib/rxio/MDPNavSubframe.cpp                    |   312 +
 dev/lib/rxio/MDPNavSubframe.hpp                    |   135 +
 dev/lib/rxio/MDPObsEpoch.cpp                       |   320 +
 dev/lib/rxio/MDPObsEpoch.hpp                       |   125 +
 dev/lib/rxio/MDPPVTSolution.cpp                    |   152 +
 dev/lib/rxio/MDPPVTSolution.hpp                    |    91 +
 dev/lib/rxio/MDPSelftestStatus.cpp                 |   136 +
 dev/lib/rxio/MDPSelftestStatus.hpp                 |    90 +
 dev/lib/rxio/MDPStream.hpp                         |    93 +
 dev/lib/rxio/Makefile.am                           |    17 +
 dev/lib/rxio/MetReader.cpp                         |    73 +
 dev/lib/rxio/MetReader.hpp                         |    68 +
 dev/lib/rxio/ObsReader.cpp                         |   169 +
 dev/lib/rxio/ObsReader.hpp                         |    81 +
 dev/lib/rxio/ObsUtils.cpp                          |   301 +
 dev/lib/rxio/ObsUtils.hpp                          |    96 +
 dev/lib/rxio/RinexConverters.cpp                   |   283 +
 dev/lib/rxio/RinexConverters.hpp                   |    70 +
 dev/lib/rxio/TCPStream.hpp                         |    81 +
 dev/lib/rxio/TCPStreamBuff.cpp                     |   145 +
 dev/lib/rxio/TCPStreamBuff.hpp                     |    71 +
 dev/lib/rxio/miscdefs.hpp                          |    22 +
 dev/lib/rxio/miscenum.hpp                          |   134 +
 dev/ltmain.sh                                      |  6496 +++
 dev/missing                                        |   357 +
 dev/oldtests/AnotherFileFilterTest.cpp             |   151 +
 dev/oldtests/DayTimeConversionTest.cpp             |   260 +
 dev/oldtests/DayTimeIncrementTest.cpp              |   131 +
 dev/oldtests/DayTimeIncrementTest2.cpp             |   104 +
 dev/oldtests/DayTimeToleranceTest.cpp              |   103 +
 dev/oldtests/EphComp.cpp                           |   107 +
 {trunk/tests => dev/oldtests}/EphComp.gp           |     0
 {trunk/tests => dev/oldtests}/EphCompWin.gp        |     0
 dev/oldtests/FileSpecTest.cpp                      |   116 +
 dev/oldtests/FileSpecTest.pl                       |    15 +
 dev/oldtests/Jamfile                               |    64 +
 dev/oldtests/Makefile.am                           |    37 +
 {trunk/tests => dev/oldtests}/MatrixTest.cpp       |     0
 dev/oldtests/MinSfTest.cpp                         |   309 +
 dev/oldtests/Rinex_dl.pl                           |   111 +
 {trunk/tests => dev/oldtests}/RungeKuttaTest.cpp   |     0
 dev/oldtests/TimeTest.cpp                          |   446 +
 dev/oldtests/Xbegweek.cpp                          |   135 +
 dev/oldtests/Xendweek.cpp                          |   158 +
 dev/oldtests/configfile.txt                        |    52 +
 .../tests => dev/oldtests}/configfile_readme.txt   |     0
 dev/oldtests/data/405_077A.02M                     |   108 +
 {trunk/tests => dev/oldtests}/data/MatrixTest.ref  |     0
 .../oldtests}/data/MatrixTest.ref.Win32            |     0
 {trunk/tests => dev/oldtests}/data/Xbegweek.can    |     0
 {trunk/tests => dev/oldtests}/data/Xendweek.can    |     0
 {trunk/tests => dev/oldtests}/data/anotsym.dat     |     0
 {trunk/tests => dev/oldtests}/data/cov.dat         |     0
 {trunk/tests => dev/oldtests}/data/dia.dat         |     0
 {trunk/tests => dev/oldtests}/data/lt.dat          |     0
 dev/oldtests/data/nga12600.apc                     |  5303 ++
 dev/oldtests/data/nga12601.apc                     |  5303 ++
 {trunk/tests => dev/oldtests}/data/partials.dat    |     0
 .../tests => dev/oldtests}/data/positiontest.ref   |     0
 {trunk/tests => dev/oldtests}/data/squ.dat         |     0
 .../tests => dev/oldtests}/data/stringutiltest.ref |     0
 {trunk/tests => dev/oldtests}/data/sym.dat         |     0
 {trunk/tests => dev/oldtests}/data/tmatrix.dat     |     0
 {trunk/tests => dev/oldtests}/data/ut.dat          |     0
 dev/oldtests/daytimetest.cpp                       |    80 +
 dev/oldtests/delFileSpecTestDirs.pl                |    25 +
 dev/oldtests/exceptiontest.cpp                     |    52 +
 dev/oldtests/genFileSpecTestDirs.pl                |    32 +
 dev/oldtests/gpszcounttest.cpp                     |   184 +
 {trunk/tests => dev/oldtests}/petest.cpp           |     0
 dev/oldtests/positiontest.cpp                      |   234 +
 .../tests => dev/oldtests}/rinex_met_livetest.pl   |     0
 dev/oldtests/rinex_met_read_write.cpp              |    63 +
 dev/oldtests/rinex_met_test.cpp                    |    59 +
 dev/oldtests/rinex_nav_read_write.cpp              |    61 +
 dev/oldtests/rinex_nav_test.cpp                    |    60 +
 dev/oldtests/rinex_obs_read_write.cpp              |    65 +
 dev/oldtests/rinex_obs_test.cpp                    |    61 +
 {trunk/tests => dev/oldtests}/runAllTests          |     0
 dev/oldtests/runAllTests.bat                       |    72 +
 dev/oldtests/stringutiltest.cpp                    |   470 +
 dev/oldtests/svnKeyWordTest.txt                    |     8 +
 dev/oldtests/testExpression.cpp                    |   117 +
 dev/oldtests/testscript.pl                         |   184 +
 .../tests => dev/oldtests}/testscript_readme.txt   |     0
 dev/src/ANSITime.cpp                               |   148 +
 dev/src/ANSITime.hpp                               |   174 +
 dev/src/AlmOrbit.cpp                               |   284 +
 dev/src/AlmOrbit.hpp                               |   142 +
 dev/src/AlmanacStore.cpp                           |   186 +
 dev/src/AlmanacStore.hpp                           |   135 +
 dev/src/BCEphemerisStore.cpp                       |   489 +
 dev/src/BCEphemerisStore.hpp                       |   285 +
 dev/src/Bancroft.cpp                               |   169 +
 dev/src/Bancroft.hpp                               |   135 +
 dev/src/BasicFramework.cpp                         |   132 +
 dev/src/BasicFramework.hpp                         |   184 +
 dev/src/BinUtils.cpp                               |    61 +
 dev/src/BinUtils.hpp                               |   417 +
 dev/src/BinexData.cpp                              |  1689 +
 dev/src/BinexData.hpp                              |   933 +
 dev/src/BinexFilterOperators.hpp                   |    41 +
 dev/src/BinexStream.hpp                            |    55 +
 dev/src/BivarStats.hpp                             |   435 +
 dev/src/CheckPRData.hpp                            |   100 +
 dev/src/CivilTime.cpp                              |   322 +
 dev/src/CivilTime.hpp                              |   191 +
 dev/src/ClockModel.hpp                             |    77 +
 dev/src/CodeBuffer.cpp                             |    70 +
 dev/src/CodeBuffer.hpp                             |   175 +
 dev/src/CodeSmoother.hpp                           |   415 +
 dev/src/CommandOption.cpp                          |   396 +
 dev/src/CommandOption.hpp                          |   631 +
 dev/src/CommandOptionParser.cpp                    |   360 +
 dev/src/CommandOptionParser.hpp                    |   156 +
 dev/src/CommandOptionWithCommonTimeArg.cpp         |    82 +
 dev/src/CommandOptionWithCommonTimeArg.hpp         |   113 +
 dev/src/CommandOptionWithPositionArg.cpp           |    91 +
 dev/src/CommandOptionWithPositionArg.hpp           |   131 +
 dev/src/CommandOptionWithTimeArg.cpp               |   109 +
 dev/src/CommandOptionWithTimeArg.hpp               |   162 +
 dev/src/CommonTime.cpp                             |   338 +
 dev/src/CommonTime.hpp                             |   356 +
 dev/src/ComputeCombination.hpp                     |   154 +
 dev/src/ComputeIURAWeights.hpp                     |   304 +
 dev/src/ComputeLC.hpp                              |   120 +
 dev/src/ComputeLI.hpp                              |   116 +
 dev/src/ComputeLdelta.hpp                          |   120 +
 dev/src/ComputeMOPSWeights.hpp                     |   317 +
 dev/src/ComputeMelbourneWubbena.hpp                |   165 +
 dev/src/ComputePC.hpp                              |   129 +
 dev/src/ComputePI.hpp                              |   121 +
 dev/src/ComputePdelta.hpp                          |   125 +
 dev/src/DOP.cpp                                    |    71 +
 dev/src/DOP.hpp                                    |    97 +
 dev/src/DataHeaders.cpp                            |   152 +
 dev/src/DataHeaders.hpp                            |   542 +
 dev/src/DataStructures.cpp                         |   399 +
 dev/src/DataStructures.hpp                         |  1608 +
 dev/src/DayTime.cpp                                |  1922 +
 dev/src/DayTime.hpp                                |  1217 +
 dev/src/DeltaOp.cpp                                |    96 +
 dev/src/DeltaOp.hpp                                |   375 +
 dev/src/ECEF.cpp                                   |   104 +
 dev/src/ECEF.hpp                                   |   110 +
 dev/src/EngAlmanac.cpp                             |   540 +
 dev/src/EngAlmanac.hpp                             |   321 +
 dev/src/EngEphemeris.cpp                           |  1386 +
 dev/src/EngEphemeris.hpp                           |   496 +
 dev/src/EngNav.cpp                                 |   686 +
 dev/src/EngNav.hpp                                 |   284 +
 dev/src/EphemerisRange.cpp                         |   227 +
 dev/src/EphemerisRange.hpp                         |   114 +
 dev/src/EphemerisStore.hpp                         |   129 +
 dev/src/EpochClockModel.hpp                        |   110 +
 dev/src/Exception.cpp                              |   222 +
 dev/src/Exception.hpp                              |   479 +
 dev/src/Expression.cpp                             |   606 +
 dev/src/Expression.hpp                             |   329 +
 dev/src/ExtractC1.hpp                              |    82 +
 dev/src/ExtractCombinationData.hpp                 |   149 +
 dev/src/ExtractD1.hpp                              |    82 +
 dev/src/ExtractD2.hpp                              |    82 +
 dev/src/ExtractData.hpp                            |   175 +
 dev/src/ExtractL1.hpp                              |    82 +
 dev/src/ExtractL2.hpp                              |    82 +
 dev/src/ExtractLC.hpp                              |    94 +
 dev/src/ExtractP1.hpp                              |    82 +
 dev/src/ExtractP2.hpp                              |    82 +
 dev/src/ExtractPC.hpp                              |    97 +
 dev/src/FFBinaryStream.hpp                         |   174 +
 dev/src/FFData.cpp                                 |   101 +
 dev/src/FFData.hpp                                 |   191 +
 dev/src/FFStream.cpp                               |   278 +
 dev/src/FFStream.hpp                               |   211 +
 dev/src/FFStreamError.hpp                          |    65 +
 dev/src/FFTextStream.hpp                           |   222 +
 dev/src/FICAStream.hpp                             |    92 +
 dev/src/FICBase.hpp                                |    69 +
 dev/src/FICData.cpp                                |  1168 +
 dev/src/FICData.hpp                                |   189 +
 dev/src/FICData109.cpp                             |    88 +
 dev/src/FICData109.hpp                             |    77 +
 dev/src/FICData162.cpp                             |    82 +
 dev/src/FICData162.hpp                             |    78 +
 dev/src/FICData62.cpp                              |   111 +
 dev/src/FICData62.hpp                              |    82 +
 dev/src/FICData9.cpp                               |   133 +
 dev/src/FICData9.hpp                               |    85 +
 dev/src/FICFilterOperators.hpp                     |   303 +
 dev/src/FICHeader.cpp                              |   142 +
 dev/src/FICHeader.hpp                              |   113 +
 dev/src/FICStream.hpp                              |    96 +
 dev/src/FICStreamBase.hpp                          |    87 +
 dev/src/FileFilter.hpp                             |   411 +
 dev/src/FileFilterFrame.hpp                        |   378 +
 dev/src/FileFilterFrameWithHeader.hpp              |   433 +
 dev/src/FileHunter.cpp                             |   698 +
 dev/src/FileHunter.hpp                             |   229 +
 dev/src/FileSpec.cpp                               |   537 +
 dev/src/FileSpec.hpp                               |   319 +
 dev/src/FileStore.hpp                              |   121 +
 dev/src/FileUtils.hpp                              |   156 +
 dev/src/GPSEpochWeekSecond.cpp                     |   214 +
 dev/src/GPSEpochWeekSecond.hpp                     |   175 +
 dev/src/GPSGeoid.hpp                               |    89 +
 dev/src/GPSWeekSecond.cpp                          |   196 +
 dev/src/GPSWeekSecond.hpp                          |   176 +
 dev/src/GPSWeekZcount.cpp                          |   194 +
 dev/src/GPSWeekZcount.hpp                          |   173 +
 dev/src/GPSZcount.cpp                              |   405 +
 dev/src/GPSZcount.hpp                              |   376 +
 dev/src/GPSZcount29.cpp                            |   186 +
 dev/src/GPSZcount29.hpp                            |   173 +
 dev/src/GPSZcount32.cpp                            |   168 +
 dev/src/GPSZcount32.hpp                            |   170 +
 dev/src/GenXSequence.cpp                           |   215 +
 dev/src/GenXSequence.hpp                           |   161 +
 dev/src/Geodetic.cpp                               |   201 +
 dev/src/Geodetic.hpp                               |   134 +
 dev/src/GeoidModel.hpp                             |   106 +
 dev/src/IonoModel.cpp                              |   182 +
 dev/src/IonoModel.hpp                              |   161 +
 dev/src/IonoModelStore.cpp                         |    95 +
 dev/src/IonoModelStore.hpp                         |   117 +
 dev/src/Jamfile                                    |   140 +
 dev/src/JulianDate.cpp                             |   158 +
 dev/src/JulianDate.hpp                             |   170 +
 dev/src/LICSDetector.hpp                           |   422 +
 dev/src/LinearClockModel.cpp                       |   173 +
 dev/src/LinearClockModel.hpp                       |   104 +
 dev/src/LoopedFramework.cpp                        |    68 +
 dev/src/LoopedFramework.hpp                        |   109 +
 dev/src/MJD.cpp                                    |   161 +
 dev/src/MJD.hpp                                    |   170 +
 dev/src/MOPSWeight.cpp                             |   240 +
 dev/src/MOPSWeight.hpp                             |   148 +
 dev/src/MSCData.cpp                                |   144 +
 dev/src/MSCData.hpp                                |   114 +
 dev/src/MSCStream.hpp                              |    87 +
 dev/src/MWCSDetector.hpp                           |   421 +
 dev/src/Makefile.am                                |    82 +
 dev/src/MathBase.hpp                               |    58 +
 dev/src/Matrix.hpp                                 |   712 +
 dev/src/MatrixBase.hpp                             |   422 +
 dev/src/MatrixBaseOperators.hpp                    |   171 +
 dev/src/MatrixFunctors.hpp                         |   762 +
 dev/src/MatrixImplementation.hpp                   |   139 +
 dev/src/MatrixOperators.hpp                        |   774 +
 dev/src/MiscMath.hpp                               |   222 +
 dev/src/ModeledPR.cpp                              |   122 +
 dev/src/ModeledPR.hpp                              |   442 +
 dev/src/ModeledPseudorangeBase.hpp                 |   130 +
 dev/src/ModeledReferencePR.cpp                     |   410 +
 dev/src/ModeledReferencePR.hpp                     |   615 +
 dev/src/NablaOp.cpp                                |   106 +
 dev/src/NablaOp.hpp                                |   284 +
 dev/src/ORDEpoch.hpp                               |   113 +
 dev/src/ObsClockModel.cpp                          |   183 +
 dev/src/ObsClockModel.hpp                          |   224 +
 dev/src/ObsEpochMap.cpp                            |    76 +
 dev/src/ObsEpochMap.hpp                            |    80 +
 dev/src/ObsID.cpp                                  |   170 +
 dev/src/ObsID.hpp                                  |   193 +
 dev/src/ObsRngDev.cpp                              |   330 +
 dev/src/ObsRngDev.hpp                              |   326 +
 dev/src/OneFreqCSDetector.hpp                      |   513 +
 dev/src/PCSmoother.hpp                             |   342 +
 dev/src/PCodeConst.hpp                             |   100 +
 dev/src/PRSolution.cpp                             |   711 +
 dev/src/PRSolution.hpp                             |   282 +
 dev/src/PolyFit.hpp                                |   266 +
 dev/src/Position.cpp                               |  1540 +
 dev/src/Position.hpp                               |   922 +
 dev/src/RACRotation.cpp                            |   142 +
 dev/src/RACRotation.hpp                            |    62 +
 dev/src/README                                     |   216 +
 dev/src/RTFileFrame.hpp                            |   601 +
 dev/src/RinexEphemerisStore.cpp                    |   104 +
 dev/src/RinexEphemerisStore.hpp                    |    98 +
 dev/src/RinexMetBase.hpp                           |    75 +
 dev/src/RinexMetData.cpp                           |   284 +
 dev/src/RinexMetData.hpp                           |   149 +
 dev/src/RinexMetFilterOperators.hpp                |   249 +
 dev/src/RinexMetHeader.cpp                         |   511 +
 dev/src/RinexMetHeader.hpp                         |   268 +
 dev/src/RinexMetStream.hpp                         |   113 +
 dev/src/RinexNavBase.hpp                           |    75 +
 dev/src/RinexNavData.cpp                           |   575 +
 dev/src/RinexNavData.hpp                           |   254 +
 dev/src/RinexNavFilterOperators.hpp                |   240 +
 dev/src/RinexNavHeader.cpp                         |   343 +
 dev/src/RinexNavHeader.hpp                         |   165 +
 dev/src/RinexNavStream.hpp                         |   104 +
 dev/src/RinexObsBase.hpp                           |    75 +
 dev/src/RinexObsData.cpp                           |   407 +
 dev/src/RinexObsData.hpp                           |   172 +
 dev/src/RinexObsFilterOperators.hpp                |   270 +
 dev/src/RinexObsHeader.cpp                         |  1027 +
 dev/src/RinexObsHeader.hpp                         |   373 +
 dev/src/RinexObsID.cpp                             |    64 +
 dev/src/RinexObsID.hpp                             |    70 +
 dev/src/RinexObsStream.hpp                         |   110 +
 dev/src/RinexSatID.cpp                             |    51 +
 dev/src/RinexSatID.hpp                             |   205 +
 dev/src/RinexUtilities.cpp                         |   309 +
 dev/src/RinexUtilities.hpp                         |   110 +
 dev/src/RungeKutta4.cpp                            |   120 +
 dev/src/RungeKutta4.hpp                            |   144 +
 dev/src/SEMAlmanacStore.cpp                        |   107 +
 dev/src/SEMAlmanacStore.hpp                        |    74 +
 dev/src/SEMBase.hpp                                |    78 +
 dev/src/SEMData.cpp                                |   198 +
 dev/src/SEMData.hpp                                |   135 +
 dev/src/SEMHeader.cpp                              |    94 +
 dev/src/SEMHeader.hpp                              |   112 +
 dev/src/SEMStream.hpp                              |   114 +
 dev/src/SMODFData.cpp                              |   328 +
 dev/src/SMODFData.hpp                              |   125 +
 dev/src/SMODFStream.hpp                            |   116 +
 dev/src/SP3Base.hpp                                |    73 +
 dev/src/SP3Data.cpp                                |   317 +
 dev/src/SP3Data.hpp                                |   129 +
 dev/src/SP3EphemerisStore.cpp                      |   140 +
 dev/src/SP3EphemerisStore.hpp                      |    91 +
 dev/src/SP3Header.cpp                              |   330 +
 dev/src/SP3Header.hpp                              |   153 +
 dev/src/SP3SatID.cpp                               |    51 +
 dev/src/SP3SatID.hpp                               |   200 +
 dev/src/SP3Stream.hpp                              |    83 +
 dev/src/SVExclusionList.cpp                        |   346 +
 dev/src/SVExclusionList.hpp                        |   154 +
 dev/src/SVPCodeGen.cpp                             |   112 +
 dev/src/SVPCodeGen.hpp                             |   149 +
 dev/src/SatID.hpp                                  |   174 +
 dev/src/SimpleFilter.hpp                           |   320 +
 dev/src/SimpleIURAWeight.cpp                       |   158 +
 dev/src/SimpleIURAWeight.hpp                       |   116 +
 dev/src/SimpleKalmanFilter.cpp                     |   325 +
 dev/src/SimpleKalmanFilter.hpp                     |   379 +
 dev/src/SolverBase.hpp                             |    87 +
 dev/src/SolverLMS.cpp                              |   118 +
 dev/src/SolverLMS.hpp                              |   217 +
 dev/src/SolverWMS.cpp                              |   146 +
 dev/src/SolverWMS.hpp                              |   207 +
 dev/src/SourceID.cpp                               |    97 +
 dev/src/SourceID.hpp                               |   146 +
 dev/src/Stats.hpp                                  |   513 +
 dev/src/StringUtils.hpp                            |  2603 +
 dev/src/SystemTime.cpp                             |    59 +
 dev/src/SystemTime.hpp                             |   115 +
 dev/src/TabularEphemerisStore.cpp                  |   265 +
 dev/src/TabularEphemerisStore.hpp                  |   144 +
 dev/src/TimeConstants.hpp                          |    78 +
 dev/src/TimeConverters.cpp                         |   174 +
 dev/src/TimeConverters.hpp                         |    94 +
 dev/src/TimeNamedFileStream.hpp                    |   157 +
 dev/src/TimeString.cpp                             |   724 +
 dev/src/TimeString.hpp                             |    75 +
 dev/src/TimeTag.cpp                                |   171 +
 dev/src/TimeTag.hpp                                |   152 +
 dev/src/Triple.cpp                                 |   279 +
 dev/src/Triple.hpp                                 |   224 +
 dev/src/TropModel.cpp                              |  2171 +
 dev/src/TropModel.hpp                              |  1435 +
 dev/src/TypeID.cpp                                 |   202 +
 dev/src/TypeID.hpp                                 |   332 +
 dev/src/UnixTime.cpp                               |   182 +
 dev/src/UnixTime.hpp                               |   193 +
 dev/src/ValidType.hpp                              |   123 +
 dev/src/Vector.hpp                                 |   431 +
 dev/src/VectorBase.cpp                             |    38 +
 dev/src/VectorBase.hpp                             |   301 +
 dev/src/VectorBaseOperators.hpp                    |   272 +
 dev/src/VectorOperators.hpp                        |   189 +
 dev/src/WGS84Geoid.hpp                             |   131 +
 dev/src/WeightBase.hpp                             |    70 +
 dev/src/WxObsMap.cpp                               |   243 +
 dev/src/WxObsMap.hpp                               |   174 +
 dev/src/X1Sequence.cpp                             |   161 +
 dev/src/X1Sequence.hpp                             |   134 +
 dev/src/X2Sequence.cpp                             |   239 +
 dev/src/X2Sequence.hpp                             |   296 +
 dev/src/XYZ2NED.cpp                                |    90 +
 dev/src/XYZ2NED.hpp                                |   269 +
 dev/src/XYZ2NEU.cpp                                |    90 +
 dev/src/XYZ2NEU.hpp                                |   270 +
 dev/src/Xvt.cpp                                    |   125 +
 dev/src/Xvt.hpp                                    |   103 +
 dev/src/YDSTime.cpp                                |   214 +
 dev/src/YDSTime.hpp                                |   174 +
 dev/src/YumaAlmanacStore.cpp                       |   104 +
 dev/src/YumaAlmanacStore.hpp                       |    72 +
 dev/src/YumaBase.hpp                               |    77 +
 dev/src/YumaData.cpp                               |   273 +
 dev/src/YumaData.hpp                               |   141 +
 dev/src/YumaHeader.cpp                             |    66 +
 dev/src/YumaHeader.hpp                             |   106 +
 dev/src/YumaStream.hpp                             |   104 +
 dev/src/convhelp.hpp                               |   135 +
 dev/src/geometry.hpp                               |    71 +
 {trunk => dev}/src/getopt.c                        |     0
 {trunk => dev}/src/getopt.h                        |     0
 {trunk => dev}/src/getopt1.c                       |     0
 dev/src/gps_constants.hpp                          |    67 +
 dev/src/gpstkplatform.h                            |    57 +
 dev/src/icd_200_constants.hpp                      |   183 +
 dev/src/mergePCodeWords.h                          |    52 +
 dev/src/regex.c                                    |  4949 ++
 {trunk => dev}/src/regex.h                         |     0
 dev/src/stl_helpers.hpp                            |   129 +
 dev/tests/ANSITime/ANSITime.cpp                    |     1 +
 dev/tests/ANSITime/ANSITime.hpp                    |     1 +
 dev/tests/ANSITime/Jamfile                         |     2 +
 dev/tests/ANSITime/Logs/printfOutput               |     2 +
 dev/tests/ANSITime/xANSITime.cpp                   |    58 +
 dev/tests/ANSITime/xANSITime.hpp                   |    28 +
 dev/tests/ANSITime/xANSITimeM.cpp                  |    30 +
 dev/tests/BCEphemerisStore/BCEphemerisStore.cpp    |     1 +
 dev/tests/BCEphemerisStore/BCEphemerisStore.hpp    |     1 +
 dev/tests/BCEphemerisStore/Checks/DumpData0.chk    |     4 +
 dev/tests/BCEphemerisStore/Checks/DumpData1.chk    |    49 +
 .../tests/BCEphemerisStore/Checks/DumpData2.chk    |     0
 .../BCEphemerisStore/Checks/TestRinexDump.dmp      |     0
 .../BCEphemerisStore/Checks/addEphemerisTest.chk   |     5 +
 .../BCEphemerisStore/Checks/addToListTest.chk      |  1886 +
 dev/tests/BCEphemerisStore/Checks/clearTest.chk    |     5 +
 dev/tests/BCEphemerisStore/Checks/editTest.chk     |    33 +
 dev/tests/BCEphemerisStore/Checks/findEph1.chk     |    46 +
 dev/tests/BCEphemerisStore/Checks/findEph15.chk    |    46 +
 dev/tests/BCEphemerisStore/Checks/findEph32.chk    |    46 +
 dev/tests/BCEphemerisStore/Checks/findNearTest.chk |    11 +
 dev/tests/BCEphemerisStore/Checks/findUserTest.chk |    11 +
 dev/tests/BCEphemerisStore/Checks/getPrnXvt1.chk   |     1 +
 dev/tests/BCEphemerisStore/Checks/getPrnXvt15.chk  |     1 +
 dev/tests/BCEphemerisStore/Checks/getPrnXvt32.chk  |     1 +
 dev/tests/BCEphemerisStore/Checks/wiperTest.chk    |    31 +
 dev/tests/BCEphemerisStore/Checks/wiperTest2.chk   |     8 +
 dev/tests/BCEphemerisStore/Jamfile                 |     4 +
 dev/tests/BCEphemerisStore/Logs/DumpData.txt       |    49 +
 dev/tests/BCEphemerisStore/Logs/DumpData0.txt      |     4 +
 dev/tests/BCEphemerisStore/Logs/DumpData1.txt      |    49 +
 .../tests/BCEphemerisStore/Logs/DumpData2.txt      |     0
 dev/tests/BCEphemerisStore/Logs/TestRinex06.031NEW |   347 +
 .../BCEphemerisStore/Logs/addEphemerisTest.txt     |     5 +
 dev/tests/BCEphemerisStore/Logs/addToListTest.txt  |  1886 +
 dev/tests/BCEphemerisStore/Logs/clearTest.txt      |     5 +
 dev/tests/BCEphemerisStore/Logs/editTest.txt       |    33 +
 dev/tests/BCEphemerisStore/Logs/findEph1.txt       |    46 +
 dev/tests/BCEphemerisStore/Logs/findEph15.txt      |    46 +
 dev/tests/BCEphemerisStore/Logs/findEph32.txt      |    46 +
 dev/tests/BCEphemerisStore/Logs/findNearTest.txt   |    11 +
 dev/tests/BCEphemerisStore/Logs/findUserTest.txt   |    11 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt1.txt     |     1 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt15.txt    |     1 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt2_1.txt   |     1 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt2_15.txt  |     1 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt2_32.txt  |     1 +
 dev/tests/BCEphemerisStore/Logs/getPrnXvt32.txt    |     1 +
 dev/tests/BCEphemerisStore/Logs/s011100a.00nEdit   |   233 +
 dev/tests/BCEphemerisStore/Logs/wiperTest.txt      |    31 +
 dev/tests/BCEphemerisStore/Logs/wiperTest2.txt     |     8 +
 dev/tests/BCEphemerisStore/Makefile                |    24 +
 dev/tests/BCEphemerisStore/RinexEphemerisStore.cpp |     1 +
 dev/tests/BCEphemerisStore/RinexEphemerisStore.hpp |     1 +
 dev/tests/BCEphemerisStore/TestRinex06.031         |   339 +
 .../BCEphemerisStore/xRinexEphemerisStore.cpp      |   658 +
 .../BCEphemerisStore/xRinexEphemerisStore.hpp      |    57 +
 .../BCEphemerisStore/xRinexEphemerisStoreM.cpp     |    30 +
 dev/tests/BinUtils/BinUtils.cpp                    |     1 +
 dev/tests/BinUtils/BinUtils.hpp                    |     1 +
 dev/tests/BinUtils/Jamfile                         |     3 +
 dev/tests/BinUtils/makefile                        |    20 +
 dev/tests/BinUtils/xBinUtils.cpp                   |   146 +
 dev/tests/BinUtils/xBinUtils.hpp                   |    30 +
 dev/tests/BinUtils/xBinUtilsM.cpp                  |    30 +
 dev/tests/Binex/binex_read_write.cpp               |   539 +
 dev/tests/Binex/binex_types_test.cpp               |   813 +
 dev/tests/CivilTime/CivilTime.cpp                  |     1 +
 dev/tests/CivilTime/CivilTime.hpp                  |     1 +
 dev/tests/CivilTime/Jamfile                        |     2 +
 dev/tests/CivilTime/Logs/printfOutput              |     4 +
 dev/tests/CivilTime/xCivilTime.cpp                 |    94 +
 dev/tests/CivilTime/xCivilTime.hpp                 |    28 +
 dev/tests/CivilTime/xCivilTimeM.cpp                |    30 +
 dev/tests/CommonTime/CommonTime.cpp                |     1 +
 dev/tests/CommonTime/CommonTime.hpp                |     1 +
 dev/tests/CommonTime/Jamfile                       |     2 +
 dev/tests/CommonTime/xCommonTime.cpp               |   128 +
 dev/tests/CommonTime/xCommonTime.hpp               |    28 +
 dev/tests/CommonTime/xCommonTimeM.cpp              |    30 +
 dev/tests/DayTime/DayTime.cpp                      |     1 +
 dev/tests/DayTime/DayTime.hpp                      |     1 +
 dev/tests/DayTime/DayTimeConvTest.hpp              |    56 +
 dev/tests/DayTime/Jamfile                          |     5 +
 dev/tests/DayTime/Logs/DayTimeDump                 |    58 +
 dev/tests/DayTime/makefile                         |    34 +
 dev/tests/DayTime/xDayTime.cpp                     |   299 +
 dev/tests/DayTime/xDayTime.hpp                     |    36 +
 dev/tests/DayTime/xDayTimeConv.cpp                 |   196 +
 dev/tests/DayTime/xDayTimeInc.cpp                  |    98 +
 dev/tests/DayTime/xDayTimeInc.hpp                  |    77 +
 dev/tests/DayTime/xDayTimeM.cpp                    |    30 +
 .../tests/Example_Classname/Example_ClassName.cpp  |     0
 .../tests/Example_Classname/Example_ClassName.hpp  |     0
 .../tests/Example_Classname/Example_Jamfile        |     0
 .../tests/Example_Classname/Example_xClassName.cpp |     0
 .../tests/Example_Classname/Example_xClassName.hpp |     0
 .../Example_Classname/Example_xClassNameM.cpp      |     0
 .../GPSEpochWeekSecond/GPSEpochWeekSecond.cpp      |     1 +
 .../GPSEpochWeekSecond/GPSEpochWeekSecond.hpp      |     1 +
 dev/tests/GPSEpochWeekSecond/Jamfile               |     2 +
 dev/tests/GPSEpochWeekSecond/Logs/printfOutput     |     2 +
 .../GPSEpochWeekSecond/xGPSEpochWeekSecond.cpp     |    82 +
 .../GPSEpochWeekSecond/xGPSEpochWeekSecond.hpp     |    28 +
 .../GPSEpochWeekSecond/xGPSEpochWeekSecondM.cpp    |    30 +
 dev/tests/GPSWeekSecond/GPSWeekSecond.cpp          |     1 +
 dev/tests/GPSWeekSecond/GPSWeekSecond.hpp          |     1 +
 dev/tests/GPSWeekSecond/Jamfile                    |     2 +
 dev/tests/GPSWeekSecond/Logs/printfOutput          |     2 +
 dev/tests/GPSWeekSecond/xGPSWeekSecond.cpp         |    62 +
 dev/tests/GPSWeekSecond/xGPSWeekSecond.hpp         |    28 +
 dev/tests/GPSWeekSecond/xGPSWeekSecondM.cpp        |    30 +
 dev/tests/GPSWeekZcount/GPSWeekZcount.cpp          |     1 +
 dev/tests/GPSWeekZcount/GPSWeekZcount.hpp          |     1 +
 dev/tests/GPSWeekZcount/Jamfile                    |     2 +
 dev/tests/GPSWeekZcount/Logs/printfOutput          |     2 +
 dev/tests/GPSWeekZcount/xGPSWeekZcount.cpp         |    63 +
 dev/tests/GPSWeekZcount/xGPSWeekZcount.hpp         |    28 +
 dev/tests/GPSWeekZcount/xGPSWeekZcountM.cpp        |    30 +
 dev/tests/IonoModel/IonoModel.cpp                  |     1 +
 dev/tests/IonoModel/IonoModel.hpp                  |     1 +
 dev/tests/IonoModel/Jamfile                        |     3 +
 dev/tests/IonoModel/makefile                       |    22 +
 dev/tests/IonoModel/xIonoModel.cpp                 |   113 +
 dev/tests/IonoModel/xIonoModel.hpp                 |    36 +
 dev/tests/IonoModel/xIonoModelM.cpp                |    30 +
 dev/tests/Jamfile                                  |    27 +
 dev/tests/Jamrules                                 |    22 +
 dev/tests/JulianDate/Jamfile                       |     2 +
 dev/tests/JulianDate/JulianDate.cpp                |     1 +
 dev/tests/JulianDate/JulianDate.hpp                |     1 +
 dev/tests/JulianDate/Logs/printfOutput             |     2 +
 dev/tests/JulianDate/xJulianDate.cpp               |    56 +
 dev/tests/JulianDate/xJulianDate.hpp               |    28 +
 dev/tests/JulianDate/xJulianDateM.cpp              |    30 +
 dev/tests/MJD/Jamfile                              |     2 +
 dev/tests/MJD/Logs/printfOutput                    |     2 +
 dev/tests/MJD/MJD.cpp                              |     1 +
 dev/tests/MJD/MJD.hpp                              |     1 +
 dev/tests/MJD/xMJD.cpp                             |    57 +
 dev/tests/MJD/xMJD.hpp                             |    27 +
 dev/tests/MJD/xMJDM.cpp                            |    30 +
 dev/tests/MSC/Jamfile                              |     2 +
 dev/tests/MSC/Logs/CoordFile                       |     5 +
 dev/tests/MSC/Logs/Output                          |     5 +
 dev/tests/MSC/Logs/Output2                         |     5 +
 dev/tests/MSC/MSCData.cpp                          |     1 +
 dev/tests/MSC/MSCData.hpp                          |     1 +
 .../src/Makefile.am => dev/tests/MSC/MSCStream.cpp |     0
 dev/tests/MSC/MSCStream.hpp                        |     1 +
 dev/tests/MSC/xMSC.cpp                             |    67 +
 dev/tests/MSC/xMSC.hpp                             |    28 +
 dev/tests/MSC/xMSCM.cpp                            |    30 +
 dev/tests/Makefile.am                              |     3 +
 dev/tests/PolyFit/Jamfile                          |     4 +
 .../Makefile.am => dev/tests/PolyFit/PolyFit.cpp   |     0
 dev/tests/PolyFit/PolyFit.hpp                      |     1 +
 dev/tests/PolyFit/makefile                         |    22 +
 dev/tests/PolyFit/xPolyFit.cpp                     |   280 +
 dev/tests/PolyFit/xPolyFit.hpp                     |    41 +
 dev/tests/PolyFit/xPolyFitM.cpp                    |    30 +
 dev/tests/RACRotation/Jamfile                      |     2 +
 dev/tests/RACRotation/RACRotation.cpp              |     1 +
 dev/tests/RACRotation/RACRotation.hpp              |     1 +
 dev/tests/RACRotation/xRACRotation.cpp             |   224 +
 dev/tests/RACRotation/xRACRotation.hpp             |    35 +
 dev/tests/RACRotation/xRACRotationM.cpp            |    30 +
 dev/tests/RinexEphemerisStore/Checks/DumpData0.chk |     4 +
 dev/tests/RinexEphemerisStore/Checks/DumpData1.chk |    49 +
 .../tests/RinexEphemerisStore/Checks/DumpData2.chk |     0
 .../RinexEphemerisStore/Checks/TestRinexDump.dmp   |     0
 .../Checks/addEphemerisTest.chk                    |     5 +
 .../RinexEphemerisStore/Checks/addToListTest.chk   |  1886 +
 dev/tests/RinexEphemerisStore/Checks/clearTest.chk |     5 +
 dev/tests/RinexEphemerisStore/Checks/editTest.chk  |    33 +
 dev/tests/RinexEphemerisStore/Checks/findEph1.chk  |    46 +
 dev/tests/RinexEphemerisStore/Checks/findEph15.chk |    46 +
 dev/tests/RinexEphemerisStore/Checks/findEph32.chk |    46 +
 .../RinexEphemerisStore/Checks/findNearTest.chk    |    11 +
 .../RinexEphemerisStore/Checks/findUserTest.chk    |    11 +
 .../RinexEphemerisStore/Checks/getPrnXvt1.chk      |     1 +
 .../RinexEphemerisStore/Checks/getPrnXvt15.chk     |     1 +
 .../RinexEphemerisStore/Checks/getPrnXvt32.chk     |     1 +
 dev/tests/RinexEphemerisStore/Checks/wiperTest.chk |    31 +
 .../RinexEphemerisStore/Checks/wiperTest2.chk      |     8 +
 dev/tests/RinexEphemerisStore/DumpData.txt         |    49 +
 dev/tests/RinexEphemerisStore/Jamfile              |     5 +
 dev/tests/RinexEphemerisStore/Logs/DumpData0.txt   |     4 +
 dev/tests/RinexEphemerisStore/Logs/DumpData1.txt   |    49 +
 .../tests/RinexEphemerisStore/Logs/DumpData2.txt   |     0
 .../RinexEphemerisStore/Logs/TestRinex06.031NEW    |   347 +
 .../RinexEphemerisStore/Logs/addEphemerisTest.txt  |     5 +
 .../RinexEphemerisStore/Logs/addToListTest.txt     |  1886 +
 dev/tests/RinexEphemerisStore/Logs/clearTest.txt   |     5 +
 dev/tests/RinexEphemerisStore/Logs/editTest.txt    |    33 +
 dev/tests/RinexEphemerisStore/Logs/findEph1.txt    |    46 +
 dev/tests/RinexEphemerisStore/Logs/findEph15.txt   |    46 +
 dev/tests/RinexEphemerisStore/Logs/findEph32.txt   |    46 +
 .../RinexEphemerisStore/Logs/findNearTest.txt      |    11 +
 .../RinexEphemerisStore/Logs/findUserTest.txt      |    11 +
 dev/tests/RinexEphemerisStore/Logs/getPrnXvt1.txt  |     1 +
 dev/tests/RinexEphemerisStore/Logs/getPrnXvt15.txt |     1 +
 .../RinexEphemerisStore/Logs/getPrnXvt2_1.txt      |     1 +
 .../RinexEphemerisStore/Logs/getPrnXvt2_15.txt     |     1 +
 .../RinexEphemerisStore/Logs/getPrnXvt2_32.txt     |     1 +
 dev/tests/RinexEphemerisStore/Logs/getPrnXvt32.txt |     1 +
 .../RinexEphemerisStore/Logs/s011100a.00nEdit      |   233 +
 dev/tests/RinexEphemerisStore/Logs/wiperTest.txt   |    31 +
 dev/tests/RinexEphemerisStore/Logs/wiperTest2.txt  |     8 +
 .../RinexEphemerisStore/RinexEphemerisStore.cpp    |     1 +
 .../RinexEphemerisStore/RinexEphemerisStore.hpp    |     1 +
 dev/tests/RinexEphemerisStore/TestRinex06.031      |   339 +
 dev/tests/RinexEphemerisStore/makefile             |    24 +
 .../RinexEphemerisStore/xRinexEphemerisStore.cpp   |   658 +
 .../RinexEphemerisStore/xRinexEphemerisStore.hpp   |    57 +
 .../RinexEphemerisStore/xRinexEphemerisStoreM.cpp  |    30 +
 dev/tests/RinexMet/Jamfile                         |     2 +
 dev/tests/RinexMet/Logs/408_110a.04m               |   108 +
 dev/tests/RinexMet/Logs/BLL.04m                    |    15 +
 dev/tests/RinexMet/Logs/BOL.04m                    |    14 +
 dev/tests/RinexMet/Logs/ContLines10.04m            |   212 +
 dev/tests/RinexMet/Logs/ContinuationLines.04m      |   210 +
 dev/tests/RinexMet/Logs/ContinuationLines.04mBKUP  |   205 +
 dev/tests/RinexMet/Logs/ExtraH.04m                 |    17 +
 .../tests/RinexMet/Logs/ExtraOutput.txt            |     0
 dev/tests/RinexMet/Logs/FER.04m                    |    14 +
 .../tests/RinexMet/Logs/FilterOutput.txt           |     0
 dev/tests/RinexMet/Logs/FilterTest1.04m            |    18 +
 dev/tests/RinexMet/Logs/FilterTest2.04m            |    18 +
 dev/tests/RinexMet/Logs/FilterTest3.04m            |    14 +
 dev/tests/RinexMet/Logs/InvalidTimeFormat.04m      |    14 +
 dev/tests/RinexMet/Logs/MetDumps                   |    13 +
 dev/tests/RinexMet/Logs/MissingEOH.04m             |    13 +
 dev/tests/RinexMet/Logs/MissingMarkerName          |    16 +
 dev/tests/RinexMet/Logs/NoObsData.04m              |    13 +
 dev/tests/RinexMet/Logs/NoObsDataCont.04m          |    27 +
 dev/tests/RinexMet/Logs/NotMetFile.04m             |    14 +
 dev/tests/RinexMet/Logs/ObsHeaderStringError.04m   |    27 +
 dev/tests/RinexMet/Logs/Output.txt                 |   108 +
 dev/tests/RinexMet/Logs/OutputCont.txt             |   212 +
 dev/tests/RinexMet/Logs/OutputDataExceptions.txt   |    14 +
 dev/tests/RinexMet/Logs/SensorTypeError.04m        |    14 +
 dev/tests/RinexMet/Logs/UnSupRinex.04m             |    14 +
 .../tests/RinexMet/RinexMetBase.cpp                |     0
 dev/tests/RinexMet/RinexMetBase.hpp                |     1 +
 dev/tests/RinexMet/RinexMetData.cpp                |     1 +
 dev/tests/RinexMet/RinexMetData.hpp                |     1 +
 .../tests/RinexMet/RinexMetFilterOperators.cpp     |     0
 dev/tests/RinexMet/RinexMetFilterOperators.hpp     |     1 +
 dev/tests/RinexMet/RinexMetHeader.cpp              |     1 +
 dev/tests/RinexMet/RinexMetHeader.hpp              |     1 +
 .../tests/RinexMet/RinexMetStream.cpp              |     0
 dev/tests/RinexMet/RinexMetStream.hpp              |     1 +
 dev/tests/RinexMet/xRinexMet.cpp                   |   516 +
 dev/tests/RinexMet/xRinexMet.hpp                   |    54 +
 dev/tests/RinexMet/xRinexMetM.cpp                  |    28 +
 dev/tests/RinexNav/Jamfile                         |     2 +
 dev/tests/RinexNav/Logs/BACKUP                     |    24 +
 dev/tests/RinexNav/Logs/BadHeader.99n              |     8 +
 dev/tests/RinexNav/Logs/FilterOutput.txt           |     9 +
 dev/tests/RinexNav/Logs/FilterTest1.99n            |    24 +
 dev/tests/RinexNav/Logs/FilterTest2.99n            |    24 +
 dev/tests/RinexNav/Logs/FilterTest3.99n            |    24 +
 dev/tests/RinexNav/Logs/IncompleteHeader.99n       |     7 +
 dev/tests/RinexNav/Logs/InvalidLineLength.99n      |     8 +
 dev/tests/RinexNav/Logs/NotaNavFile.99n            |     8 +
 dev/tests/RinexNav/Logs/RinexDump                  |    13 +
 dev/tests/RinexNav/Logs/RinexNavExample.99n        |    24 +
 dev/tests/RinexNav/Logs/RinexNavHeaderNorm.99n     |     8 +
 dev/tests/RinexNav/Logs/TestOutput.99n             |    24 +
 dev/tests/RinexNav/Logs/TestOutput2.99n            |    24 +
 dev/tests/RinexNav/Logs/TestOutput3.99n            |    24 +
 dev/tests/RinexNav/Logs/TestOutputEric.99n         |    24 +
 .../tests/RinexNav/Logs/TestOutputHeader.99n       |     0
 .../tests/RinexNav/Logs/TestOutputHeader2.99n      |     0
 .../tests/RinexNav/Logs/TestOutputHeader3.99n      |     0
 dev/tests/RinexNav/Logs/UnknownHeaderLabel.99n     |     9 +
 dev/tests/RinexNav/Logs/UnsupportedRinex.99n       |     8 +
 .../tests/RinexNav/RinexNavBase.cpp                |     0
 dev/tests/RinexNav/RinexNavBase.hpp                |     1 +
 dev/tests/RinexNav/RinexNavData.cpp                |     1 +
 dev/tests/RinexNav/RinexNavData.hpp                |     1 +
 .../tests/RinexNav/RinexNavFilterOperators.cpp     |     0
 dev/tests/RinexNav/RinexNavFilterOperators.hpp     |     1 +
 dev/tests/RinexNav/RinexNavHeader.cpp              |     1 +
 dev/tests/RinexNav/RinexNavHeader.hpp              |     1 +
 .../tests/RinexNav/RinexNavStream.cpp              |     0
 dev/tests/RinexNav/RinexNavStream.hpp              |     1 +
 dev/tests/RinexNav/xRinexNav.cpp                   |   271 +
 dev/tests/RinexNav/xRinexNav.hpp                   |    37 +
 dev/tests/RinexNav/xRinexNavM.cpp                  |    28 +
 dev/tests/RinexObs/Jamfile                         |     2 +
 dev/tests/RinexObs/Logs/BACKUP.06o                 |   155 +
 dev/tests/RinexObs/Logs/BadEpochFlag.06o           |   154 +
 dev/tests/RinexObs/Logs/BadEpochLine.06o           |   154 +
 dev/tests/RinexObs/Logs/BadLineSize.06o            |   154 +
 .../tests/RinexObs/Logs/DataExceptionOutput.06o    |     0
 dev/tests/RinexObs/Logs/FilterOutput.txt           |    41 +
 dev/tests/RinexObs/Logs/FilterTest1.06o            |   154 +
 dev/tests/RinexObs/Logs/FilterTest2.06o            |   154 +
 dev/tests/RinexObs/Logs/FilterTest3.06o            |   154 +
 dev/tests/RinexObs/Logs/FilterTest4.06o            |   154 +
 dev/tests/RinexObs/Logs/IncompleteHeader.06o       |   154 +
 dev/tests/RinexObs/Logs/InvalidLineLength.06o      |   155 +
 dev/tests/RinexObs/Logs/InvalidNumPRNWaveFact.06o  |   155 +
 dev/tests/RinexObs/Logs/InvalidTimeFormat.06o      |   154 +
 dev/tests/RinexObs/Logs/NotObs.06o                 |   155 +
 dev/tests/RinexObs/Logs/ObsDump                    |    45 +
 dev/tests/RinexObs/Logs/RinexContData.06o          |   187 +
 dev/tests/RinexObs/Logs/RinexObsFile.06o           |   154 +
 dev/tests/RinexObs/Logs/SystemGeosync.06o          |   155 +
 dev/tests/RinexObs/Logs/SystemGlonass.06o          |   155 +
 dev/tests/RinexObs/Logs/SystemMixed.06o            |   155 +
 dev/tests/RinexObs/Logs/SystemTransit.06o          |   155 +
 dev/tests/RinexObs/Logs/TestOutput.06o             |   215 +
 dev/tests/RinexObs/Logs/TestOutput2.06o            |   154 +
 dev/tests/RinexObs/Logs/TestOutput3.06o            |   165 +
 dev/tests/RinexObs/Logs/UnSupVersion.06o           |   155 +
 .../tests/RinexObs/RinexObsBase.cpp                |     0
 dev/tests/RinexObs/RinexObsBase.hpp                |     1 +
 dev/tests/RinexObs/RinexObsData.cpp                |     1 +
 dev/tests/RinexObs/RinexObsData.hpp                |     1 +
 .../tests/RinexObs/RinexObsFilterOperators.cpp     |     0
 dev/tests/RinexObs/RinexObsFilterOperators.hpp     |     1 +
 dev/tests/RinexObs/RinexObsHeader.cpp              |     1 +
 dev/tests/RinexObs/RinexObsHeader.hpp              |     1 +
 .../tests/RinexObs/RinexObsStream.cpp              |     0
 dev/tests/RinexObs/RinexObsStream.hpp              |     1 +
 dev/tests/RinexObs/xRinexObs.cpp                   |   277 +
 dev/tests/RinexObs/xRinexObs.hpp                   |    37 +
 dev/tests/RinexObs/xRinexObsM.cpp                  |    28 +
 dev/tests/RungeKutta4/Jamfile                      |     3 +
 dev/tests/RungeKutta4/RungeKutta4.cpp              |     1 +
 dev/tests/RungeKutta4/RungeKutta4.hpp              |     1 +
 dev/tests/RungeKutta4/makefile                     |    22 +
 dev/tests/RungeKutta4/pendulum.hpp                 |    43 +
 dev/tests/RungeKutta4/xRungeKutta4.cpp             |   110 +
 dev/tests/RungeKutta4/xRungeKutta4.hpp             |    35 +
 dev/tests/RungeKutta4/xRungeKutta4M.cpp            |    30 +
 dev/tests/SEM/Jamfile                              |     2 +
 trunk/src/Makefile.am => dev/tests/SEM/SEMBase.cpp |     0
 dev/tests/SEM/SEMBase.hpp                          |    78 +
 dev/tests/SEM/SEMData.cpp                          |   198 +
 dev/tests/SEM/SEMData.hpp                          |   135 +
 dev/tests/SEM/SEMHeader.cpp                        |    94 +
 dev/tests/SEM/SEMHeader.hpp                        |   112 +
 .../src/Makefile.am => dev/tests/SEM/SEMStream.cpp |     0
 dev/tests/SEM/SEMStream.hpp                        |   114 +
 .../Makefile.am => dev/tests/SEM/StringUtils.cpp   |     0
 dev/tests/SEM/StringUtils.hpp                      |  2603 +
 dev/tests/SEM/sem.txt                              |   272 +
 dev/tests/SEM/sem387.txt                           |   274 +
 dev/tests/SEM/xSEM.cpp                             |    49 +
 dev/tests/SetVariables                             |    27 +
 dev/tests/Stats/Jamfile                            |     3 +
 trunk/src/Makefile.am => dev/tests/Stats/Stats.cpp |     0
 dev/tests/Stats/Stats.hpp                          |     1 +
 dev/tests/Stats/makefile                           |    18 +
 dev/tests/Stats/xStats.cpp                         |   454 +
 dev/tests/Stats/xStats.hpp                         |    64 +
 dev/tests/Stats/xStatsM.cpp                        |    30 +
 dev/tests/TimeConverters/Jamfile                   |     2 +
 dev/tests/TimeConverters/TimeConverters.cpp        |     1 +
 dev/tests/TimeConverters/TimeConverters.hpp        |     1 +
 dev/tests/TimeConverters/xTimeConverters.cpp       |    89 +
 dev/tests/TimeConverters/xTimeConverters.hpp       |    26 +
 dev/tests/TimeConverters/xTimeConvertersM.cpp      |    30 +
 dev/tests/TotalTest.pl                             |    72 +
 dev/tests/UnixTime/Jamfile                         |     2 +
 dev/tests/UnixTime/Logs/printfOutput               |     2 +
 dev/tests/UnixTime/UnixTime.cpp                    |     1 +
 dev/tests/UnixTime/UnixTime.hpp                    |     1 +
 dev/tests/UnixTime/xUnixTime.cpp                   |    64 +
 dev/tests/UnixTime/xUnixTime.hpp                   |    28 +
 dev/tests/UnixTime/xUnixTimeM.cpp                  |    30 +
 dev/tests/YDSTime/Jamfile                          |     2 +
 dev/tests/YDSTime/Logs/printfOutput                |     4 +
 dev/tests/YDSTime/YDSTime.cpp                      |     1 +
 dev/tests/YDSTime/YDSTime.hpp                      |     1 +
 dev/tests/YDSTime/xYDSTime.cpp                     |    82 +
 dev/tests/YDSTime/xYDSTime.hpp                     |    28 +
 dev/tests/YDSTime/xYDSTimeM.cpp                    |    30 +
 dev/tests/Yuma/Jamfile                             |     2 +
 .../Makefile.am => dev/tests/Yuma/StringUtils.cpp  |     0
 dev/tests/Yuma/StringUtils.hpp                     |  2603 +
 .../src/Makefile.am => dev/tests/Yuma/YumaBase.cpp |     0
 dev/tests/Yuma/YumaBase.hpp                        |    77 +
 dev/tests/Yuma/YumaData.cpp                        |   273 +
 dev/tests/Yuma/YumaData.hpp                        |   141 +
 .../Makefile.am => dev/tests/Yuma/YumaStream.cpp   |     0
 dev/tests/Yuma/YumaStream.hpp                      |   104 +
 dev/tests/Yuma/xYuma.cpp                           |    53 +
 dev/tests/Yuma/yuma377.txt                         |   450 +
 dev/tests/a.txt                                    |     3 +
 ref/build/gpstk.aip                                |   431 +
 ref/glance/colortbl.dtx                            |  1296 +
 ref/glance/colortbl.ins                            |     5 +
 ref/glance/colortbl.sty                            |   339 +
 ref/glance/svgnam.def                              |   186 +
 ref/glance/toolkitlandscape.tex                    |   118 +
 ref/glance/toolkitportrait.tex                     |   158 +
 ref/glance/x11nam.def                              |   352 +
 ref/glance/xcolor.dtx                              |  8231 +++
 ref/glance/xcolor.ins                              |    28 +
 ref/glance/xcolor.lox                              |    57 +
 ref/glance/xcolor.pro                              |    56 +
 ref/glance/xcolor.sty                              |  1451 +
 ref/glance/xcolor1.tex                             |   182 +
 ref/glance/xcolor2.tex                             |   157 +
 ref/glance/xcolor3.tex                             |   241 +
 ref/glance/xcolor4.tex                             |   209 +
 ref/usersguide/DiscFix.tex                         |    67 +
 ref/usersguide/FICcheck.tex                        |    45 +
 ref/usersguide/IonoBias.tex                        |   110 +
 ref/usersguide/NavMerge.tex                        |    39 +
 ref/usersguide/PRSolve.tex                         |    82 +
 ref/usersguide/RINcheck.tex                        |    44 +
 ref/usersguide/RINdiff.tex                         |    44 +
 ref/usersguide/ResCor.tex                          |   189 +
 ref/usersguide/RinSum.tex                          |   104 +
 ref/usersguide/RinexDump.tex                       |    56 +
 ref/usersguide/TECMaps.tex                         |    92 +
 ref/usersguide/WhereSat.tex                        |    99 +
 ref/usersguide/arl280-10.06m                       |   299 +
 ref/usersguide/arl280-10.06n                       |  1285 +
 ref/usersguide/arl280-10.06o                       | 48828 ++++++++++++++++++
 ref/usersguide/arl2800.06m                         |   299 +
 ref/usersguide/arl2800.06n                         |  1261 +
 ref/usersguide/arl2800.06o                         | 48700 ++++++++++++++++++
 ref/usersguide/arl2800thin.06o                     | 24356 +++++++++
 ref/usersguide/arl2810.06m                         |   299 +
 ref/usersguide/arl2810.06n                         |  1285 +
 ref/usersguide/arl2810.06o                         | 48828 ++++++++++++++++++
 ref/usersguide/brokenfica                          |  1902 +
 ref/usersguide/calgps.tex                          |    40 +
 ref/usersguide/convcoordtime.tex                   |   177 +
 ref/usersguide/data_set/408_213a.99m               |    16 +
 ref/usersguide/data_set/408_214a.99m               |    11 +
 ref/usersguide/data_set/408_215a.99m               |    11 +
 ref/usersguide/data_set/s081213a.99n               |  1603 +
 ref/usersguide/data_set/s081213a.99o               | 49413 ++++++++++++++++++
 ref/usersguide/data_set/s081214a.99n               |  1579 +
 ref/usersguide/data_set/s081214a.99o               | 49847 +++++++++++++++++++
 ref/usersguide/data_set/s081215a.99n               |  1595 +
 ref/usersguide/data_set/s081215a.99o               | 42971 ++++++++++++++++
 ref/usersguide/ephdiff.tex                         |    39 +
 ref/usersguide/fic06.187                           |   Bin 0 -> 51064 bytes
 ref/usersguide/fic1                                |   Bin 0 -> 291064 bytes
 ref/usersguide/fic2                                |   Bin 0 -> 288472 bytes
 ref/usersguide/fic2rin.tex                         |    63 +
 ref/usersguide/fica06.187                          |  1902 +
 ref/usersguide/ficconv.tex                         |    66 +
 ref/usersguide/ficdiff.tex                         |    52 +
 ref/usersguide/ficm                                |   Bin 0 -> 579496 bytes
 ref/usersguide/firstprototype/appsoverview.tex     |   138 +
 ref/usersguide/firstprototype/faq.tex              |    53 +
 ref/usersguide/firstprototype/firstprototype.tex   |    50 +
 ref/usersguide/firstprototype/gpsoverview.tex      |    57 +
 ref/usersguide/firstprototype/gpstklogo.eps        | 32692 ++++++++++++
 ref/usersguide/firstprototype/introduction.tex     |     9 +
 ref/usersguide/firstprototype/makefile             |    20 +
 ref/usersguide/firstprototype/notices.tex          |     1 +
 ref/usersguide/firstprototype/rinexpvt.tex         |   371 +
 ref/usersguide/firstprototype/rtashtech.tex        |   292 +
 ref/usersguide/firstprototype/titlepg.tex          |    39 +
 ref/usersguide/firstprototype/vecsol.tex           |    49 +
 ref/usersguide/firstprototype/wheresat.tex         |   161 +
 ref/usersguide/gpsfileforms.tex                    |    42 +
 ref/usersguide/gpsnutshell.tex                     |    58 +
 ref/usersguide/gpstk-user-reference.pdf            |   Bin 0 -> 352330 bytes
 ref/usersguide/gpstk-user-reference.tex            |    39 +
 ref/usersguide/gpstk.bib                           |   392 +
 ref/usersguide/gpstklogo.eps                       | 32692 ++++++++++++
 ref/usersguide/introduction.tex                    |     9 +
 ref/usersguide/makefile                            |    29 +
 ref/usersguide/makescr                             |     6 +
 ref/usersguide/mdpconv.tex                         |    74 +
 ref/usersguide/mdptool.tex                         |    59 +
 ref/usersguide/mergeFIC.tex                        |    41 +
 ref/usersguide/mergeRin.tex                        |    48 +
 ref/usersguide/navdmp.tex                          |   117 +
 ref/usersguide/navsum.tex                          |    88 +
 ref/usersguide/new.06o                             | 49274 ++++++++++++++++++
 ref/usersguide/notices.tex                         |     1 +
 ref/usersguide/novaRinex.tex                       |    75 +
 ref/usersguide/poscvt.tex                          |    47 +
 ref/usersguide/reszilla.tex                        |   229 +
 ref/usersguide/rin1870.06                          |   339 +
 ref/usersguide/rinexpvt.tex                        |    53 +
 ref/usersguide/rinexthin.tex                       |    33 +
 ref/usersguide/rtAshtech.tex                       |    53 +
 .../Makefile.am => ref/usersguide/s081213-214.99n  |     0
 ref/usersguide/s081213a.99n                        |  1603 +
 ref/usersguide/s081214a.99n                        |  1579 +
 ref/usersguide/sec2.tex                            |    44 +
 ref/usersguide/src/bibtexall                       |    17 +
 ref/usersguide/src/bibunits.sty                    |   402 +
 ref/usersguide/src/cmd.tex                         |    60 +
 ref/usersguide/src/colortbl.dtx                    |  1296 +
 ref/usersguide/src/colortbl.ins                    |     5 +
 ref/usersguide/src/colortbl.sty                    |   339 +
 ref/usersguide/src/fancyvrb.dtx                    |  4474 ++
 ref/usersguide/src/fancyvrb.ins                    |    35 +
 ref/usersguide/src/fancyvrb.log                    |    91 +
 ref/usersguide/src/fancyvrb.sty                    |  1421 +
 ref/usersguide/src/manual.cls                      |   754 +
 ref/usersguide/src/perltex                         |   375 +
 ref/usersguide/src/perltex.sty                     |   229 +
 ref/usersguide/src/svgnam.def                      |   186 +
 ref/usersguide/src/x11nam.def                      |   352 +
 ref/usersguide/src/xcolor.dtx                      |  8231 +++
 ref/usersguide/src/xcolor.ins                      |    28 +
 ref/usersguide/src/xcolor.log                      |   541 +
 ref/usersguide/src/xcolor.lox                      |    57 +
 ref/usersguide/src/xcolor.pro                      |    56 +
 ref/usersguide/src/xcolor.sty                      |  1451 +
 ref/usersguide/src/xcolor1.tex                     |   182 +
 ref/usersguide/src/xcolor2.tex                     |   157 +
 ref/usersguide/src/xcolor3.tex                     |   241 +
 ref/usersguide/src/xcolor4.tex                     |   209 +
 ref/usersguide/summary                             |   169 +
 ref/usersguide/test.tex                            |    23 +
 ref/usersguide/timeconvert.tex                     |    56 +
 ref/usersguide/titlepg.tex                         |    43 +
 ref/usersguide/toolkitportrait.tex                 |   148 +
 ref/usersguide/vecsol.tex                          |    75 +
 trunk/AUTHORS                                      |    32 -
 trunk/ChangeLog                                    |   131 -
 trunk/Doxyfile                                     |  1168 -
 trunk/INSTALL                                      |   236 -
 trunk/Jamfile                                      |    10 -
 trunk/Jamrules                                     |   276 -
 trunk/Makefile.am                                  |     2 -
 trunk/NEWS                                         |    17 -
 trunk/README                                       |    91 -
 trunk/apps/Jamfile                                 |    21 -
 trunk/apps/MDPtools/DataStatus.hpp                 |    85 -
 trunk/apps/MDPtools/FormatConversionFunctions.cpp  |   234 -
 trunk/apps/MDPtools/FormatConversionFunctions.hpp  |    60 -
 trunk/apps/MDPtools/Jamfile                        |    30 -
 trunk/apps/MDPtools/MDPHeader.cpp                  |   350 -
 trunk/apps/MDPtools/MDPHeader.hpp                  |   118 -
 trunk/apps/MDPtools/MDPNavSubframe.cpp             |   136 -
 trunk/apps/MDPtools/MDPNavSubframe.hpp             |    76 -
 trunk/apps/MDPtools/MDPObsEpoch.cpp                |   219 -
 trunk/apps/MDPtools/MDPObsEpoch.hpp                |    85 -
 trunk/apps/MDPtools/MDPPVTSolution.cpp             |   116 -
 trunk/apps/MDPtools/MDPPVTSolution.hpp             |    54 -
 trunk/apps/MDPtools/MDPProcessors.cpp              |   314 -
 trunk/apps/MDPtools/MDPProcessors.hpp              |   136 -
 trunk/apps/MDPtools/MDPSelftestStatus.cpp          |    98 -
 trunk/apps/MDPtools/MDPSelftestStatus.hpp          |    52 -
 trunk/apps/MDPtools/MDPStream.hpp                  |   101 -
 trunk/apps/MDPtools/NavProc.cpp                    |   159 -
 trunk/apps/MDPtools/NavProc.hpp                    |    55 -
 trunk/apps/MDPtools/ScreenProc.cpp                 |   354 -
 trunk/apps/MDPtools/ScreenProc.hpp                 |    54 -
 trunk/apps/MDPtools/SummaryProc.cpp                |   402 -
 trunk/apps/MDPtools/SummaryProc.hpp                |    62 -
 trunk/apps/MDPtools/TCPStream.cpp                  |   317 -
 trunk/apps/MDPtools/TCPStream.hpp                  |   154 -
 trunk/apps/MDPtools/TrackProc.cpp                  |   141 -
 trunk/apps/MDPtools/TrackProc.hpp                  |    36 -
 trunk/apps/MDPtools/mdp2rinex.cpp                  |   301 -
 trunk/apps/MDPtools/mdptool.cpp                    |   296 -
 trunk/apps/MDPtools/miscenum.hpp                   |   134 -
 trunk/apps/MDPtools/tcptest.cpp                    |    83 -
 trunk/apps/Makefile.am                             |     1 -
 trunk/apps/Makefile.in                             |   467 -
 trunk/apps/RinexPlot/README                        |    31 -
 trunk/apps/RinexPlot/RinexPlot.pl                  |  3458 --
 trunk/apps/Rinextools/EditRinex                    |   Bin 842076 -> 0 bytes
 trunk/apps/Rinextools/EditRinex.cpp                |   330 -
 trunk/apps/Rinextools/Jamfile                      |    18 -
 trunk/apps/Rinextools/Makefile.am                  |    13 -
 trunk/apps/Rinextools/Makefile.in                  |   482 -
 trunk/apps/Rinextools/NavMerge                     |   Bin 489118 -> 0 bytes
 trunk/apps/Rinextools/NavMerge.cpp                 |   251 -
 trunk/apps/Rinextools/README                       |    45 -
 trunk/apps/Rinextools/ResCor                       |   Bin 1331833 -> 0 bytes
 trunk/apps/Rinextools/ResCor.cpp                   |  2063 -
 trunk/apps/Rinextools/RinSum                       |   Bin 814962 -> 0 bytes
 trunk/apps/Rinextools/RinSum.cpp                   |   662 -
 trunk/apps/Rinextools/RinexDump                    |   Bin 690710 -> 0 bytes
 trunk/apps/Rinextools/RinexDump.cpp                |   242 -
 trunk/apps/Rinextools/RinexEditor.cpp              |  1290 -
 trunk/apps/Rinextools/RinexEditor.hpp              |   287 -
 trunk/apps/Rinextools/RinexUtilities.cpp           |   237 -
 trunk/apps/Rinextools/RinexUtilities.hpp           |    90 -
 trunk/apps/Rinextools/toolslib.a                   |   Bin 560266 -> 0 bytes
 trunk/apps/bindings/java/Makefile                  |    51 -
 trunk/apps/bindings/octave/Makefile                |    29 -
 trunk/apps/bindings/perl/Makefile                  |    50 -
 trunk/apps/bindings/tcl/Makefile                   |    49 -
 trunk/apps/checktools/CheckFrame.hpp               |   120 -
 trunk/apps/checktools/Jamfile                      |    12 -
 trunk/apps/checktools/Makefile.am                  |    10 -
 trunk/apps/checktools/Makefile.in                  |   460 -
 trunk/apps/checktools/ficacheck                    |   Bin 529884 -> 0 bytes
 trunk/apps/checktools/ficacheck.cpp                |    38 -
 trunk/apps/checktools/ficcheck                     |   Bin 528156 -> 0 bytes
 trunk/apps/checktools/ficcheck.cpp                 |    38 -
 trunk/apps/checktools/rmwcheck                     |   Bin 417111 -> 0 bytes
 trunk/apps/checktools/rmwcheck.cpp                 |    39 -
 trunk/apps/checktools/rnwcheck                     |   Bin 508042 -> 0 bytes
 trunk/apps/checktools/rnwcheck.cpp                 |    38 -
 trunk/apps/checktools/rowcheck                     |   Bin 495613 -> 0 bytes
 trunk/apps/checktools/rowcheck.cpp                 |    38 -
 trunk/apps/converters/Jamfile                      |     9 -
 trunk/apps/converters/Makefile.am                  |     6 -
 trunk/apps/converters/NovatelData.cpp              |  1065 -
 trunk/apps/converters/NovatelData.hpp              |   183 -
 trunk/apps/converters/NovatelStream.hpp            |    96 -
 trunk/apps/converters/novaRinex                    |   Bin 761182 -> 0 bytes
 trunk/apps/converters/novaRinex.cpp                |   981 -
 trunk/apps/cycleslips/DiscCorr.cpp                 |  2902 --
 trunk/apps/cycleslips/DiscCorr.hpp                 |   246 -
 trunk/apps/cycleslips/DiscFix                      |   Bin 748753 -> 0 bytes
 trunk/apps/cycleslips/DiscFix.cpp                  |  1411 -
 trunk/apps/cycleslips/Jamfile                      |     9 -
 trunk/apps/cycleslips/Makefile.am                  |     6 -
 trunk/apps/cycleslips/Makefile.in                  |   422 -
 trunk/apps/cycleslips/README                       |    67 -
 trunk/apps/differential/Makefile.am                |     6 -
 trunk/apps/differential/Makefile.in                |   421 -
 trunk/apps/differential/vecsol                     |   Bin 1075487 -> 0 bytes
 trunk/apps/differential/vecsol.1                   |   111 -
 trunk/apps/differential/vecsol.conf                |    10 -
 trunk/apps/differential/vecsol.cpp                 |   883 -
 trunk/apps/differential/vecsol.eph                 |     7 -
 trunk/apps/differential/vecsol.nav                 |     3 -
 trunk/apps/difftools/DiffFrame.hpp                 |    84 -
 trunk/apps/difftools/Jamfile                       |    12 -
 trunk/apps/difftools/Makefile.am                   |    10 -
 trunk/apps/difftools/Makefile.in                   |   460 -
 trunk/apps/difftools/ephdiff                       |   Bin 746819 -> 0 bytes
 trunk/apps/difftools/ephdiff.cpp                   |   359 -
 trunk/apps/difftools/ficdiff                       |   Bin 659848 -> 0 bytes
 trunk/apps/difftools/ficdiff.cpp                   |   103 -
 trunk/apps/difftools/rmwdiff                       |   Bin 576219 -> 0 bytes
 trunk/apps/difftools/rmwdiff.cpp                   |   221 -
 trunk/apps/difftools/rnwdiff                       |   Bin 644514 -> 0 bytes
 trunk/apps/difftools/rnwdiff.cpp                   |   205 -
 trunk/apps/difftools/rowdiff                       |   Bin 658399 -> 0 bytes
 trunk/apps/difftools/rowdiff.cpp                   |   203 -
 trunk/apps/filetools/Jamfile                       |    14 -
 trunk/apps/filetools/Makefile.am                   |    10 -
 trunk/apps/filetools/Makefile.in                   |   460 -
 trunk/apps/filetools/fic2rin                       |   Bin 690752 -> 0 bytes
 trunk/apps/filetools/fic2rin.cpp                   |   125 -
 trunk/apps/filetools/ficafic                       |   Bin 568592 -> 0 bytes
 trunk/apps/filetools/ficafic.cpp                   |    89 -
 trunk/apps/filetools/ficfica                       |   Bin 568624 -> 0 bytes
 trunk/apps/filetools/ficfica.cpp                   |    89 -
 trunk/apps/filetools/navdmp                        |   Bin 743486 -> 0 bytes
 trunk/apps/filetools/navdmp.cpp                    |   429 -
 trunk/apps/filetools/rinexthin                     |   Bin 469813 -> 0 bytes
 trunk/apps/ionosphere/IonoBias                     |   Bin 978140 -> 0 bytes
 trunk/apps/ionosphere/IonoBias.cpp                 |  1612 -
 trunk/apps/ionosphere/Jamfile                      |    10 -
 trunk/apps/ionosphere/Makefile.am                  |     7 -
 trunk/apps/ionosphere/Makefile.in                  |   433 -
 trunk/apps/ionosphere/README                       |    15 -
 trunk/apps/ionosphere/RinexUtilities.cpp           |   237 -
 trunk/apps/ionosphere/RinexUtilities.hpp           |    90 -
 trunk/apps/ionosphere/TECMaps                      |   Bin 1027794 -> 0 bytes
 trunk/apps/ionosphere/TECMaps.cpp                  |  1482 -
 trunk/apps/ionosphere/VTECMap.cpp                  |   484 -
 trunk/apps/ionosphere/VTECMap.hpp                  |   251 -
 trunk/apps/mergetools/Makefile.am                  |     9 -
 trunk/apps/mergetools/Makefile.in                  |   451 -
 trunk/apps/mergetools/MergeFrame.hpp               |    93 -
 trunk/apps/mergetools/mergeFIC                     |   Bin 638182 -> 0 bytes
 trunk/apps/mergetools/mergeFIC.cpp                 |   109 -
 trunk/apps/mergetools/mergeRinMet                  |   Bin 572860 -> 0 bytes
 trunk/apps/mergetools/mergeRinMet.cpp              |   123 -
 trunk/apps/mergetools/mergeRinNav                  |   Bin 644262 -> 0 bytes
 trunk/apps/mergetools/mergeRinNav.cpp              |   126 -
 trunk/apps/mergetools/mergeRinObs                  |   Bin 657707 -> 0 bytes
 trunk/apps/mergetools/mergeRinObs.cpp              |   126 -
 trunk/apps/positioning/Jamfile                     |     8 -
 trunk/apps/positioning/Makefile.am                 |     8 -
 trunk/apps/positioning/Makefile.in                 |   440 -
 trunk/apps/positioning/PRSolve                     |   Bin 1207021 -> 0 bytes
 trunk/apps/positioning/PRSolve.cpp                 |  1723 -
 trunk/apps/positioning/poscvt                      |   Bin 225733 -> 0 bytes
 trunk/apps/positioning/poscvt.cpp                  |   215 -
 trunk/apps/positioning/rinexpvt                    |   Bin 1231371 -> 0 bytes
 trunk/apps/positioning/rinexpvt.cpp                |   467 -
 trunk/apps/positioning/rinexpvt.hpp                |   112 -
 trunk/apps/reszilla/DDEpoch.cpp                    |   402 -
 trunk/apps/reszilla/DDEpoch.hpp                    |    61 -
 trunk/apps/reszilla/ElevationRange.hpp             |    36 -
 trunk/apps/reszilla/Jamfile                        |    22 -
 trunk/apps/reszilla/Makefile.am                    |    10 -
 trunk/apps/reszilla/Makefile.in                    |   451 -
 trunk/apps/reszilla/PhaseCleaner.cpp               |   180 -
 trunk/apps/reszilla/PhaseCleaner.hpp               |    38 -
 trunk/apps/reszilla/PhaseResidual.cpp              |   115 -
 trunk/apps/reszilla/PhaseResidual.hpp              |    71 -
 trunk/apps/reszilla/ordUtils.cpp                   |   268 -
 trunk/apps/reszilla/ordUtils.hpp                   |    28 -
 trunk/apps/reszilla/readers.cpp                    |   499 -
 trunk/apps/reszilla/readers.hpp                    |    41 -
 trunk/apps/reszilla/reszilla                       |   Bin 1517142 -> 0 bytes
 trunk/apps/reszilla/reszilla.cpp                   |   318 -
 trunk/apps/reszilla/rlib.a                         |   Bin 805776 -> 0 bytes
 trunk/apps/reszilla/util.cpp                       |   360 -
 trunk/apps/reszilla/util.hpp                       |   126 -
 trunk/apps/time/Jamfile                            |     7 -
 trunk/apps/time/Makefile.am                        |     7 -
 trunk/apps/time/Makefile.in                        |   430 -
 trunk/apps/time/README.txt                         |    94 -
 trunk/apps/time/calgps                             |   Bin 238633 -> 0 bytes
 trunk/apps/time/calgps.cpp                         |   142 -
 trunk/apps/time/timcvt                             |   Bin 282937 -> 0 bytes
 trunk/apps/time/timcvt.cpp                         |   217 -
 trunk/apps/visibility/Jamfile                      |     7 -
 trunk/apps/visibility/Makefile.am                  |     6 -
 trunk/apps/visibility/WhereSat.cpp                 |   261 -
 trunk/apps/visibility/wheresat                     |   Bin 555858 -> 0 bytes
 trunk/configure.ac                                 |    54 -
 trunk/examples/Jamfile                             |     7 -
 trunk/examples/Makefile.am                         |    10 -
 trunk/examples/Makefile.in                         |   452 -
 trunk/examples/example3.cpp                        |   112 -
 trunk/examples/example4.cpp                        |   160 -
 trunk/src/ANSITime.cpp                             |   126 -
 trunk/src/ANSITime.hpp                             |   150 -
 trunk/src/AlmOrbit.cpp                             |   282 -
 trunk/src/AlmOrbit.hpp                             |   118 -
 trunk/src/AlmanacStore.cpp                         |   164 -
 trunk/src/AlmanacStore.hpp                         |   114 -
 trunk/src/BCEphemerisStore.cpp                     |   459 -
 trunk/src/BCEphemerisStore.hpp                     |   243 -
 trunk/src/BasicFramework.cpp                       |   130 -
 trunk/src/BasicFramework.hpp                       |   182 -
 trunk/src/BinUtils.cpp                             |    59 -
 trunk/src/BinUtils.hpp                             |   388 -
 trunk/src/CivilTime.cpp                            |   316 -
 trunk/src/CivilTime.hpp                            |   168 -
 trunk/src/ClockModel.hpp                           |    75 -
 trunk/src/CodeBuffer.cpp                           |    81 -
 trunk/src/CodeBuffer.hpp                           |   173 -
 trunk/src/CommandOption.cpp                        |   394 -
 trunk/src/CommandOption.hpp                        |   629 -
 trunk/src/CommandOptionParser.cpp                  |   358 -
 trunk/src/CommandOptionParser.hpp                  |   153 -
 trunk/src/CommandOptionWithPositionArg.cpp         |    89 -
 trunk/src/CommandOptionWithPositionArg.hpp         |   129 -
 trunk/src/CommandOptionWithTimeArg.cpp             |   107 -
 trunk/src/CommandOptionWithTimeArg.hpp             |   160 -
 trunk/src/CommonTime.cpp                           |   314 -
 trunk/src/CommonTime.hpp                           |   328 -
 trunk/src/DayTime.cpp                              |  1863 -
 trunk/src/DayTime.hpp                              |  1185 -
 trunk/src/ECEF.cpp                                 |   102 -
 trunk/src/ECEF.hpp                                 |   108 -
 trunk/src/EngAlmanac.cpp                           |   495 -
 trunk/src/EngAlmanac.hpp                           |   320 -
 trunk/src/EngEphemeris.cpp                         |  1383 -
 trunk/src/EngEphemeris.hpp                         |   494 -
 trunk/src/EngNav.cpp                               |   677 -
 trunk/src/EngNav.hpp                               |   176 -
 trunk/src/EphemerisRange.cpp                       |   230 -
 trunk/src/EphemerisRange.hpp                       |   115 -
 trunk/src/EphemerisStore.hpp                       |   115 -
 trunk/src/EpochClockModel.hpp                      |   115 -
 trunk/src/Exception.cpp                            |   213 -
 trunk/src/Exception.hpp                            |   479 -
 trunk/src/FFBinaryStream.hpp                       |   219 -
 trunk/src/FFData.cpp                               |    99 -
 trunk/src/FFData.hpp                               |   189 -
 trunk/src/FFStream.cpp                             |   276 -
 trunk/src/FFStream.hpp                             |   209 -
 trunk/src/FFStreamError.hpp                        |    63 -
 trunk/src/FFTextStream.hpp                         |   220 -
 trunk/src/FICAStream.hpp                           |    90 -
 trunk/src/FICBase.hpp                              |    67 -
 trunk/src/FICData.cpp                              |  1182 -
 trunk/src/FICData.hpp                              |   187 -
 trunk/src/FICFilterOperators.hpp                   |   301 -
 trunk/src/FICHeader.cpp                            |   140 -
 trunk/src/FICHeader.hpp                            |   111 -
 trunk/src/FICStream.hpp                            |    94 -
 trunk/src/FICStreamBase.hpp                        |    85 -
 trunk/src/FileFilter.hpp                           |   409 -
 trunk/src/FileFilterFrame.hpp                      |   376 -
 trunk/src/FileFilterFrameWithHeader.hpp            |   431 -
 trunk/src/FileHunter.cpp                           |   696 -
 trunk/src/FileHunter.hpp                           |   227 -
 trunk/src/FileSpec.cpp                             |   535 -
 trunk/src/FileSpec.hpp                             |   317 -
 trunk/src/FileStore.hpp                            |   119 -
 trunk/src/FileUtils.hpp                            |   132 -
 trunk/src/GPSEpochWeekSecond.cpp                   |   197 -
 trunk/src/GPSEpochWeekSecond.hpp                   |   151 -
 trunk/src/GPSGeoid.hpp                             |    87 -
 trunk/src/GPSWeekSecond.cpp                        |   186 -
 trunk/src/GPSWeekSecond.hpp                        |   149 -
 trunk/src/GPSWeekZcount.cpp                        |   173 -
 trunk/src/GPSWeekZcount.hpp                        |   149 -
 trunk/src/GPSZcount.cpp                            |   375 -
 trunk/src/GPSZcount.hpp                            |   331 -
 trunk/src/GPSZcount29.cpp                          |   171 -
 trunk/src/GPSZcount29.hpp                          |   149 -
 trunk/src/GPSZcount32.cpp                          |   142 -
 trunk/src/GPSZcount32.hpp                          |   146 -
 trunk/src/GenXSequence.cpp                         |   215 -
 trunk/src/GenXSequence.hpp                         |   159 -
 trunk/src/Geodetic.cpp                             |   199 -
 trunk/src/Geodetic.hpp                             |   132 -
 trunk/src/GeoidModel.hpp                           |   101 -
 trunk/src/IonoModel.cpp                            |   176 -
 trunk/src/IonoModel.hpp                            |   152 -
 trunk/src/IonoModelStore.cpp                       |    93 -
 trunk/src/IonoModelStore.hpp                       |   115 -
 trunk/src/Jamfile                                  |   104 -
 trunk/src/JulianDate.cpp                           |   132 -
 trunk/src/JulianDate.hpp                           |   146 -
 trunk/src/LinearClockModel.cpp                     |   176 -
 trunk/src/LinearClockModel.hpp                     |   109 -
 trunk/src/LoopedFramework.cpp                      |    66 -
 trunk/src/LoopedFramework.hpp                      |   107 -
 trunk/src/MJD.cpp                                  |   135 -
 trunk/src/MJD.hpp                                  |   146 -
 trunk/src/MSCData.cpp                              |   133 -
 trunk/src/MSCData.hpp                              |   112 -
 trunk/src/MSCStream.hpp                            |    85 -
 trunk/src/Makefile.in                              |   309 -
 trunk/src/MathBase.hpp                             |    56 -
 trunk/src/Matrix.hpp                               |   710 -
 trunk/src/MatrixBase.hpp                           |   420 -
 trunk/src/MatrixBaseOperators.hpp                  |   169 -
 trunk/src/MatrixFunctors.hpp                       |   712 -
 trunk/src/MatrixImplementation.hpp                 |   137 -
 trunk/src/MatrixOperators.hpp                      |   737 -
 trunk/src/MiscMath.hpp                             |   201 -
 trunk/src/ORDEpoch.hpp                             |   106 -
 trunk/src/ObsClockModel.cpp                        |   180 -
 trunk/src/ObsClockModel.hpp                        |   211 -
 trunk/src/ObsRngDev.cpp                            |   332 -
 trunk/src/ObsRngDev.hpp                            |   327 -
 trunk/src/ObservationStore.cpp                     |   338 -
 trunk/src/ObservationStore.hpp                     |   338 -
 trunk/src/PCodeConst.hpp                           |   104 -
 trunk/src/PRSolution.cpp                           |   713 -
 trunk/src/PRSolution.hpp                           |   276 -
 trunk/src/PolyFit.hpp                              |   262 -
 trunk/src/Position.cpp                             |  1432 -
 trunk/src/Position.hpp                             |   893 -
 trunk/src/RACRotation.cpp                          |   113 -
 trunk/src/RACRotation.hpp                          |    34 -
 trunk/src/RAIM.cpp                                 |   592 -
 trunk/src/RAIMSolution.hpp                         |   240 -
 trunk/src/README                                   |   201 -
 trunk/src/RTFileFrame.hpp                          |   599 -
 trunk/src/RinexEphemerisStore.cpp                  |   102 -
 trunk/src/RinexEphemerisStore.hpp                  |    96 -
 trunk/src/RinexMetBase.hpp                         |    73 -
 trunk/src/RinexMetData.cpp                         |   281 -
 trunk/src/RinexMetData.hpp                         |   157 -
 trunk/src/RinexMetFilterOperators.hpp              |   247 -
 trunk/src/RinexMetHeader.cpp                       |   494 -
 trunk/src/RinexMetHeader.hpp                       |   262 -
 trunk/src/RinexMetStream.hpp                       |   111 -
 trunk/src/RinexNavBase.hpp                         |    73 -
 trunk/src/RinexNavData.cpp                         |   573 -
 trunk/src/RinexNavData.hpp                         |   253 -
 trunk/src/RinexNavFilterOperators.hpp              |   238 -
 trunk/src/RinexNavHeader.cpp                       |   337 -
 trunk/src/RinexNavHeader.hpp                       |   160 -
 trunk/src/RinexNavStream.hpp                       |   102 -
 trunk/src/RinexObsBase.hpp                         |    73 -
 trunk/src/RinexObsData.cpp                         |   405 -
 trunk/src/RinexObsData.hpp                         |   182 -
 trunk/src/RinexObsFilterOperators.hpp              |   268 -
 trunk/src/RinexObsHeader.cpp                       |   958 -
 trunk/src/RinexObsHeader.hpp                       |   442 -
 trunk/src/RinexObsStream.hpp                       |   108 -
 trunk/src/RungeKutta4.cpp                          |   118 -
 trunk/src/RungeKutta4.hpp                          |   142 -
 trunk/src/SMODFData.cpp                            |   369 -
 trunk/src/SMODFData.hpp                            |   140 -
 trunk/src/SMODFStream.hpp                          |   114 -
 trunk/src/SP3Base.hpp                              |    71 -
 trunk/src/SP3Data.cpp                              |   146 -
 trunk/src/SP3Data.hpp                              |   117 -
 trunk/src/SP3EphemerisStore.cpp                    |   136 -
 trunk/src/SP3EphemerisStore.hpp                    |    97 -
 trunk/src/SP3Header.cpp                            |   215 -
 trunk/src/SP3Header.hpp                            |   128 -
 trunk/src/SP3Stream.hpp                            |    87 -
 trunk/src/SVExclusionList.cpp                      |   312 -
 trunk/src/SVExclusionList.hpp                      |   126 -
 trunk/src/SVPCodeGen.cpp                           |   118 -
 trunk/src/SVPCodeGen.hpp                           |   151 -
 trunk/src/SatID.cpp                                |    52 -
 trunk/src/SatID.hpp                                |   174 -
 trunk/src/Stats.hpp                                |   512 -
 trunk/src/StringUtils.hpp                          |  2566 -
 trunk/src/TabularEphemerisStore.cpp                |   265 -
 trunk/src/TabularEphemerisStore.hpp                |   140 -
 trunk/src/TimeConstants.hpp                        |    49 -
 trunk/src/TimeConverters.cpp                       |   150 -
 trunk/src/TimeConverters.hpp                       |    65 -
 trunk/src/TimeString.cpp                           |   405 -
 trunk/src/TimeString.hpp                           |    35 -
 trunk/src/TimeTag.cpp                              |   168 -
 trunk/src/TimeTag.hpp                              |   121 -
 trunk/src/Triple.cpp                               |   255 -
 trunk/src/Triple.hpp                               |   206 -
 trunk/src/TropModel.cpp                            |  1700 -
 trunk/src/TropModel.hpp                            |   900 -
 trunk/src/UnixTime.cpp                             |   158 -
 trunk/src/UnixTime.hpp                             |   169 -
 trunk/src/ValidType.hpp                            |   125 -
 trunk/src/Vector.hpp                               |   382 -
 trunk/src/VectorBase.cpp                           |    36 -
 trunk/src/VectorBase.hpp                           |   299 -
 trunk/src/VectorBaseOperators.hpp                  |   270 -
 trunk/src/VectorOperators.hpp                      |   187 -
 trunk/src/WGS84Geoid.hpp                           |   126 -
 trunk/src/X1Sequence.cpp                           |   159 -
 trunk/src/X1Sequence.hpp                           |   132 -
 trunk/src/X2Sequence.cpp                           |   237 -
 trunk/src/X2Sequence.hpp                           |   294 -
 trunk/src/Xvt.cpp                                  |   123 -
 trunk/src/Xvt.hpp                                  |   101 -
 trunk/src/YDSTime.cpp                              |   202 -
 trunk/src/YDSTime.hpp                              |   151 -
 trunk/src/convhelp.hpp                             |   133 -
 trunk/src/geometry.hpp                             |    69 -
 trunk/src/gps_constants.hpp                        |    65 -
 trunk/src/gpstkplatform.h                          |    23 -
 trunk/src/icd_200_constants.hpp                    |   120 -
 trunk/src/mergePCodeWords.h                        |    25 -
 trunk/src/regex.c                                  |  4948 --
 trunk/src/stl_helpers.hpp                          |   127 -
 trunk/tests/AnotherFileFilterTest.cpp              |   149 -
 trunk/tests/DayTimeConversionTest.cpp              |   258 -
 trunk/tests/DayTimeIncrementTest.cpp               |   129 -
 trunk/tests/DayTimeIncrementTest2.cpp              |   102 -
 trunk/tests/DayTimeToleranceTest.cpp               |   101 -
 trunk/tests/EphComp.cpp                            |   108 -
 trunk/tests/FileSpecTest.cpp                       |   114 -
 trunk/tests/FileSpecTest.pl                        |    15 -
 trunk/tests/Jamfile                                |    63 -
 trunk/tests/Makefile.am                            |    34 -
 trunk/tests/MinSfTest.cpp                          |   307 -
 trunk/tests/Rinex_dl.pl                            |   111 -
 trunk/tests/TimeTest.cpp                           |   388 -
 trunk/tests/Xbegweek.cpp                           |   135 -
 trunk/tests/Xendweek.cpp                           |   158 -
 trunk/tests/configfile.txt                         |    52 -
 trunk/tests/data/405_077A.02M                      |   108 -
 trunk/tests/data/nga12600.apc                      |  5303 --
 trunk/tests/data/nga12601.apc                      |  5303 --
 trunk/tests/daytimetest.cpp                        |    78 -
 trunk/tests/delFileSpecTestDirs.pl                 |    25 -
 trunk/tests/exceptiontest.cpp                      |    50 -
 trunk/tests/genFileSpecTestDirs.pl                 |    30 -
 trunk/tests/gpszcounttest.cpp                      |   182 -
 trunk/tests/positiontest.cpp                       |   232 -
 trunk/tests/rinex_met_read_write.cpp               |    61 -
 trunk/tests/rinex_met_test.cpp                     |    57 -
 trunk/tests/rinex_nav_read_write.cpp               |    59 -
 trunk/tests/rinex_nav_test.cpp                     |    58 -
 trunk/tests/rinex_obs_read_write.cpp               |    63 -
 trunk/tests/rinex_obs_test.cpp                     |    59 -
 trunk/tests/runAllTests.bat                        |    72 -
 trunk/tests/stringutiltest.cpp                     |   468 -
 trunk/tests/testscript.pl                          |   184 -
 1818 files changed, 731717 insertions(+), 119371 deletions(-)

diff --git a/dev/AUTHORS b/dev/AUTHORS
new file mode 100644
index 0000000..dc7180d
--- /dev/null
+++ b/dev/AUTHORS
@@ -0,0 +1,32 @@
+$Id$
+
+GPS Toolkit Authorship
+------------------------------------------------------------------------------
+    The GPSTk is sponsored by the Space and Geophysics Laboratory, within the
+    Applied Research Laboratories at the University of Texas at Austin (ARL:UT).
+    GPSTk is the by-product of GPS research conducted at ARL:UT since before
+    the first GPS satellite launched in 1978; it is the combined effort
+    of many software engineers and scientists. In 2003 the research staff
+    at ARL:UT decided to open source much of their basic GPS processing
+    software as the GPSTk.
+
+Contact Information
+------------------------------------------------------------------------------
+   GPSTk Homepage
+   ----------------------------
+   http://gpstk.sourceforge.net
+
+   SGL:ARL:UT Homepage
+   ----------------------------
+   http://sgl.arlut.utexas.edu
+
+   Mailing Lists                         Subscribe/Unsubscribe/Archive URL
+   ------------------------------------  -------------------------------------
+   gpstk-users at lists.sourceforge.net     http://lists.sourceforge.net/lists/listinfo/gpstk-users
+   gpstk-devel at lists.sourceforge.net     http://lists.sourceforge.net/lists/listinfo/gpstk-devel
+   gpstk-announce at lists.sourceforge.net  http://lists.sourceforge.net/lists/listinfo/gpstk-announce
+   gpstk-admin at lists.sourceforge.net     http://lists.sourceforge.net/lists/listinfo/gpstk-admin
+  
+   Email
+   ----------------------------
+   Please send general GPSTk comments to gpstk at arlut.utexas.edu .
diff --git a/trunk/COPYING b/dev/COPYING
similarity index 100%
rename from trunk/COPYING
rename to dev/COPYING
diff --git a/dev/ChangeLog b/dev/ChangeLog
new file mode 100644
index 0000000..19da576
--- /dev/null
+++ b/dev/ChangeLog
@@ -0,0 +1,442 @@
+Version 1.3   Monday, July 16, 2006
+
+	General modifications
+	---------------------
+
+	- Added examples/example7.cpp & associated RINEX data files
+	        This program shows 10 different ways to process GPS data using
+		"GNSS data structures". The "GNSS Data Structures" paradigm can   
+	        process GPS code-based data based on Voctors, Matrices 
+		and other objects.
+	- Added examples/example6.cpp
+	        Shows a rather minimilist way to process GPS data using GNSS Data
+		Strutures
+
+	- Improved src/icd_200_constants.hpp
+	        Fixed RSVCLK value	
+
+	- GPSTk is now able to build under the MS Visual Studio 2005 (Express
+	        Edition)
+
+	Library modifications
+	---------------------
+
+	- Added src/SimpleKalmanFilter.[h|c]pp
+	        Implements a version of the Kalman filter, based in Vectors
+		and Matrices - not yet GNSS data stuctrures-enabled
+
+	- Added src/NablaOp.[h|c]pp
+	        Applies differences in satellite-related data to the GNSS
+		data structures (a.k.a. GDS)
+
+	- Added src/TypeID.[h|c]pp
+	        Index able to represent any type of observation, correction,
+		model paratmeter or other data value of interest 
+
+	- Added src/DataHeaders.[h|c]pp
+	        Adds several headers to be used with the GNSS data structures
+		classes
+
+	- Added DataStructures.[h|c]pp
+	        Set of data structures to be used by other GPSTk classes
+
+	- Added src/DeltaOp.[h|c]pp
+	        Applies differences on ground-related data to the GNSS data
+		structures
+
+	- Added src/ComputeCompination.hpp
+	        Base class to ease computing of data for the GNSS data structures
+
+	- Added src/ComputePC.hpp
+	        Eases computing the PC combination for GNSS data structures
+
+	- Added src/ComputeLC.hpp
+	        Eases computing the LC combination for GNSS data structures
+
+	- Added src/ComputeLI.hpp
+	        Eases computing the LI combination for GNSS data structures
+
+	- Added src/ComputeLdelta.hpp
+	        Eases computing the Ldelta combination for GNSS data structures
+
+	- Added src/ComputePdelta.hpponly
+	        Eases computing the Pdelta combination for GNSS data structures
+
+	- Added src/ComputePI.hpp
+	        Eases computing the PI combination for GNSS data structures
+
+	- Added src/ComputeMelbourneWubbena.hpp
+	        Eases computing the ComputeMelbourneWubbena combination for 
+		GNSS data structures
+
+	- Added src/ComputeIURAWeights.hpp
+	        Computes satellites weights based on URA Index for GNSS data
+		structures
+
+	- Added src/ComputeMOPSWeights.hpp
+	        Computes satellites weights based on Appendix J of MOPS C
+		(RTCA/DO-229C), for GNSS data structures
+
+	- Added src/SimpleFilter.hpp
+	        Filters satellites with observables grossly out of bounds, 
+		intended for GNSS data structures
+
+	- Added src/OneFreqCSDetector.hpp
+	        Detects cycle slips using observables in just one frequency
+
+	- Added src/LICSDetector.hpp
+	        Detects cycle slips using LI observables
+
+	- Added src/MWCSDetector.hpp
+	        Detects cycle slips using the Melbourne-Wubbena combination
+
+	- Added src/CodeSmoother.hpp
+	        Smoothes a given code observable using the corresponding phase 
+		observable
+
+	- Added src/PCsmoother.hpp
+	        Smoothes PC code obserrvables using the corresponding LC phase
+		observables
+
+	- Added src/XYZ2NEU.[h|c]pp
+	        Refrence frame change from ECEF XYZ to topcentric North-East-Up
+
+	- Added src/XYZ2NED.[h|c]pp
+	        Refrence frame change from ECEF XYZ to topcentric North-East-Down
+
+	- Added src/TimeNamedFileStream.hpp
+	        Simple time-based file name stream
+
+	- Improved src/ModeledReferencePR.[c|h]pp
+	        Adapted to work with GNSS data structures
+
+	- Improved src/ModeledPR.[h|c]pp
+	        Adapted to work with GNSS data structures
+
+	- Improved src/TropModel.hpp
+	        Added method MOPSTropModel::setAllParameters()
+
+	- Improved src/SolverLMS.[h|c]pp
+	        Adapted to work with GNSS data structures
+
+	- Improved src/SolverWMS.[h|c]pp
+	        Adapted to work with GNSS data structures
+
+	- Improved src/icd_200_constants.hpp
+	        Added data regarding L5, L6, L7 and L8
+
+	- Improved src/RinexHeader.[h|c]pp
+	        Can now process RINEX2.11 navigation files
+
+	- Improved src/icd_200_constants.hpp
+	        Fixed RSVCLK
+
+	- Improved src/SP3Data.cpp
+	        Modified the reader to be tolerant of short PG lines
+
+	- Improved src/RinexUtilitties.[h|c]pp
+	        Added a sort routine 
+
+	- Improved src/EngAlmanac.[h|c]pp
+	        Routing added that performs a simple sanity check on the almanac
+	
+	Application modifications
+	-------------------------
+
+	- Improved apps/differential/vecsol.cpp
+	        Along with other submissions allows the toolkit to build under
+		the MS Visual Studio 2005
+
+	- Improved apps/Rinextools.[h|c]pp
+	        Increased robustness and added multiple input file capabilities
+
+	- Improved all tools to process input files in time order:
+	        apps/Rinextools/ResCor.cpp
+		apps/Rinextools/RinSum.cpp
+		apps/Rinextools/RinexDump.cpp
+		apps/positioning/PRSolve.cpp
+
+Version 1.2   Monday, November 6, 2006
+	
+	General modifications
+	---------------------
+	
+	- Moved from Perfoce to a Subversion repository located at 
+		https://svn.sourceforge.net/svnroot/gpstk
+	
+	- GPSTk library testing is now being implemented in dev/tests
+		These unit tests for the library currently cover over
+		40% of the code with an average of 95% coverage for 
+		tested classes.  Tests use CppUnit and Perl scripts.
+		See test documentation for more information.
+	
+	- Added examples/example5.cpp
+		An example that shows how to use some very useful high level
+		GPSTk classes for positioning
+			
+	- Added Python support to the GPSTk
+	
+	- Added capability for FileHunter to work under Window and Cigwin
+	
+	Library modifications
+	---------------------
+	
+	- Added RINEX version 2.11 support for GPS Navigation files
+	
+	- Added the following prototype time classes:
+		src/CommonTime.[h|c]pp
+		src/YDSTime.[h|c]pp
+		src/CivilTime.[h|c]pp
+		src/GPSEpochWeekSecond.[h|c]pp
+		src/GPSWeekSecond.[h|c]pp
+		src/GPSWeekZcount.[h|c]pp
+		src/JulianDate.[h|c]pp
+		src/MJD.[h|c]pp
+		src/UnixTime.[h|c]pp
+		
+	- Added src/TimeConstants.hpp
+		Time constants
+	
+	- Added src/TimeConverters.[c|h]pp
+		Time converstion routines
+	
+	- Added src/Bancroft.[h|c]pp 
+		Class gets an initial guess of GPS receiver's position
+		
+	- Added src/SVExclusionList.[h|c]pp
+	
+	- Added and implemented src/SatID.hpp
+		Class which encapsulates Satellite Identification information
+	
+	- Added src/ModeledPR.[h|c]pp
+		Encapsulates computation of modeled psuedoranges of a mobile
+		receiver
+		
+	- Added src/SimpleIURAWeight.[h|c]pp
+		Assigns weights to satellites based on their URA Index
+	 
+	- Added src/ModeledPseudorangeBase.hpp
+		An abstract base class for modeled pseudoranges
+	
+	- Added src/WeightBase.hpp
+		An abstract base class for algorithms assigning weights to satellites
+			
+	- Improved src/Matrix.hpp and src/Vector.hpp
+		Added unary minus operator, CholeskyCrout class and inverseChol method
+	
+	- Improved src/Vector.hpp
+		Added add() method
+		
+	- Improved src/Stats.hpp 
+		Better handling of weighted input
+		
+	- Improved src/Tropmodel.[h|c]pp
+		Added GCAT tropospheric model
+		Added MOPSTropModel
+		
+	- Improved src/Position.[h|c]pp 
+		Added methods elevationGeodetic() and azimuthGeodetic()
+	
+	- Improved src/TabularEphemerisStore.[h|c]pp
+		Added method hasTGD()
+		
+	- Improved src/BCEphemerisStore.[h|c]pp
+		Added method getTGD() to return the Total Group Delay of the SV
+	
+	- Improved src/EphemerisRange.[h|c]pp
+		Added methods elevationGeodeitc() and azimuthGeodetic().
+		Changed RelativityCorrection() to improved speed and precision
+	
+	- Added src/BinexData.[c|h]pp BinexFilterOperators.hpp BinexStream.hpp
+		Added BINEX code
+	
+	Application modifications
+	-------------------------
+	
+	- Added apps/positioning/poscvt
+		A position converter modeled after timecvt
+		
+	- Added apps/geomatics/relposition/DDBase
+		A network positioning application that uses double
+		differenced carrier phases
+	
+	- Added apps/geomatics/kalman
+		Tools for Kalman filtering
+		
+	- Added apps/geomatics/robust
+		Tool for robust statistics
+	
+	- Added apps/geomatics/????
+		Added geodetic reference frames
+		
+	- Complete rewrite and move of apps/cycleslips/DiscFix
+	  Located now in apps/geomatics/cycleslips/DiscFix
+	  	DiscFix is now much more object-oriented, faster, more robust
+		and more accurate.  Significant changes have been made to the
+		algorithm.
+	
+	- Added apps/visibility/wheresat
+		A tool for caculating SV position, azimut and
+		elevation from a navigation file
+	
+	- Added apps/converters/novaRinex
+		A Novatel binary to Rinex converter
+	
+	- Improved apps/filetools/navdmp
+		Added -r (RINEX) input capability
+	
+	- Added apps/qa/Expression
+		A fully functional expression interpreter
+	
+	- Added apps/qa/obsrip
+		A utility which transforms RINEX files into linear combos, ect.
+	
+	- Added functionality to apps/multipath/ObsArray
+		Can track observations by PRN and by epoch.  Also can insert
+		GPS constants into Expressions.
+		
+	- Added apps/positioning/posInterp
+		This application interpolates positions in a RINEX file, increasing
+		the data rate by an integer factor.  An example that uses both
+		posInterp and PRSolve is included.
+	
+	- Added apps/visibility/findMoreThan12
+		A new tools which given an ephemeris source, finds times when there are
+		more than 12 SVs simultaneously above a specified elevation angle.
+	
+	- Improved apps/positioning/rinexpvt
+		Added optional log file to output
+		Added option to set observation rate to other than 30s
+		
+	
+Version 1.1   Friday, January 7, 2005
+
+	General modifications
+        ---------------------
+
+	- Added patches to support more compilers:
+	     GNU compiler version 3.4
+	     GNU compiler under AIX
+	     Sun ONE Studio 8
+
+	- GNU make/configure capabilities have been added. Since this
+	  is a new feature, your mileage may vary. The makefiles
+	  work on systems with newer autoconf toolchain versions.
+
+	- More examples were added to gpstk/examples.
+	  These examples are described in the website.
+
+	- Major revamp of the website.
+	
+	Library modifications
+	---------------------
+
+	- P code generator was allocating over 20 megabytes in
+	  processes that use the shared library. Now the 20 megabytes
+	  are not allocated until the P code generator is initialized
+	  by the user.
+	
+	- Bug fix: FileSpec::extractDayTime() now initializes
+	  DayTimes generated from file name to have zero hours,
+	  zero minutes, zero seconds, etc.
+	
+	- Bug fix: DayTime::setGPS now more robust when accepting
+	  a year number as a hint to disambiguate which full GPS
+	  week to use.
+	
+	- Added more DayTime tests.
+	
+	- Bug fix: DayTime user-defined tolerance for comparisons now is
+	  working.
+
+	- Bug fix: DayTime::setGPS(short week, long zcount, short year)
+	  now works across a year rollover.
+	
+        - Renamed WxObsData::find(...) to getWxObservation(...) to remove
+          any confusion that might develop over having the same method
+          name as the STL.
+	
+	- Bug fix: satellite combinations in RAIM sometimes incorrect when 
+	  a user-marked satellite was present.
+
+	- Some Vector routines were throwing Exception instead of 
+	  VectorException.
+
+	- Added Vector-Matrix concatentation operators.
+
+	- Added a sub-Vector constructor.
+
+	- Modification to Householder decomposition routine.
+	
+	- Various fixes to MatrixOperators.
+
+	- Updates to FIC routines.
+
+	- Implemented azimuth and elevation functions in Position, making 
+	  use of the functions inherited from Triple. Modified EphemerisRange
+	  to use Position, as well as TropModel and ResCor; this eliminated 
+	  a bug in the azimuth() in EphemerisRange and means that now there 
+	  is only one implementation of az and el. Added a test of the new 
+	  routines in positiontest. Also added a Position constructor from Xvt.
+
+	- Bugs in DayTime::setToString() - %f was missing (!) and %S 
+	  was acting as %f should; date was trashed when time was set, 
+	  and vice versa, and year input was not properly handled.
+
+        - Implemented ionospheric model types other than linear.
+
+	- Cleaned up weather data handling (e.g., error checks).
+
+	
+	Application modifications
+        -------------------------
+	
+	apps/difftools
+
+	- Unified code using a common class.
+
+
+	apps/filetools
+
+	- Improvments to navdmp.
+
+	- New utility called rinexthin for subsampling RINEX observation
+	  files.
+
+	
+	apps/binding
+	
+	- This  new project provides GPSTk bindings in other languages
+	  has been added. This capability is in an alpha state.
+	  Currently there are a limited set of bindings for
+	  Tcl/Tk, Python and Octave.
+
+	
+	apps/RinexEditor
+
+	-  Bug fix: 'delete all' command was being deleted prematurely.
+
+
+	apps/RinexPlot
+
+	- Improved documentation.
+        
+	- Various tweaks.
+
+	- Plotting improvements.
+
+
+	apps/ionosphere
+
+	- Various improvements.
+
+
+
+
+
+
+
+
+
+
+	
\ No newline at end of file
diff --git a/dev/Doxyfile b/dev/Doxyfile
new file mode 100644
index 0000000..33e14a2
--- /dev/null
+++ b/dev/Doxyfile
@@ -0,0 +1,1177 @@
+#
+#  The version in Subversion is optimized for the GPSTk website.  Note that 
+#  any changes to this file will effect the documentation display at
+#  http://www.gpstk.org/doxygen/.
+#
+#  This file should work locally without modification however it will have 
+#  a 'search' entry that will be nonfunctional unless hosted by a web server.
+#
+
+# Doxyfile 1.3.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "GPS ToolKit Software Library"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of source 
+# files, where putting all generated files in the same directory would otherwise 
+# cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is used 
+# as the annotated text. Otherwise, the brief description is used as-is. If left 
+# blank, the following values are used ("$name" is automatically replaced with the 
+# name of the entity): "The $name class" "The $name widget" "The $name file" 
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = . \
+                         ./src/README
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.hpp \
+                         *.h \
+                         *.cpp
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = ./tests \
+                         ./examples \
+                         ./apps
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = getopt.h \
+                         regex.h
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = ./tests \
+                         ./examples
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = *.cpp
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = ./src
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = /usr/bin
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = .
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = YES
diff --git a/dev/INSTALL b/dev/INSTALL
new file mode 100644
index 0000000..c8d81be
--- /dev/null
+++ b/dev/INSTALL
@@ -0,0 +1,386 @@
+Installation of the GPSTk library and applications
+--------------------------------------------------
+
+This INSTALL file has these sections
+
+Introduction
+Building and Installing in a UNIX-like Environment using Jam
+Building and Installing in a UNIX-like Environment using GNU make
+Building under Microsoft Visual Studio .NET 2003
+ADDENDUM: Standard notes on using configure
+
+More background information can be found at the GPSTk website, 
+http://www.gpstk.org/ .
+
+
+Introduction
+------------
+   This project uses Jam for all of its configuration, build & install tasks.
+   See http://perforce.com/jam/jam.html for more details on this tool.
+   There are two sections below, excepted from the GPSTk website at 
+   http://www.gpstk.org/, that describe how UNIX and Windows users
+   can build and install the GPSTk.
+
+   This project uses doxygen to dynamically generate API documentation.
+   See http://www.doxygen.org/ for more details.
+
+   As of ver. 1.1 we have introduced autoconf generated Makefiles into the 
+   GPSTk. Since this is a new feature, your mileage may vary. If you don't
+   know how use these makefile, the attached info at the bottom of this file
+   from the Free Software Foundation will help.
+
+   For more information about building the GPSTk, see the website at
+   http://www.gpstk.org/
+
+
+Building and Installing in a UNIX-like Environment using Jam
+------------------------------------------------------------
+
+This section describes build and installation under the following environments.
+
+   - Linux, AIX and Windows/Cygwin using gcc versions 3.3 and 3.4
+   - Solaris using Sun Workshop 6 or Sun ONE Studio 8
+
+The following procedure will build and install the GPSTk.
+
+   1. Ensure that prerequisites such as jam have been installed.
+
+   2. Download the GPSTk distribution.
+
+   3. Extract the GPSTk tarball. For example, using GNU tar
+
+         $ tar xvzf gpstk.tar.gz
+
+   4. Change into the gpstk directory and type
+
+         $ jam
+
+   5. To build the source documentation using doxygen:
+
+         $ doxygen 
+
+   6. To install GPSTk as a system library in /usr/local, assume root 
+      privileges then execute
+
+         $ jam install 
+
+
+Building and Installing in a UNIX-like Environment using GNU make
+-----------------------------------------------------------------
+
+The following procedure will build and install the GPSTk using recent, 
+standard GNU build tools. The build has been successfully tested using the 
+following tools and versions:
+
+   autoconf (GNU Autoconf) 2.59
+   GNU m4 1.4.2
+   automake (GNU automake) 1.8.5
+
+To check the version of the tool you are using, use the GNU standard version option. For example
+
+   autoconf --version
+
+If you successfully build the GPSTk using other versions of the tools, or 
+if you successfully build in a new environment, please let us know.
+
+The following procedure will build and install the GPSTk.
+
+   1. Ensure that prerequisites, except jam , have been installed.
+
+   2. Download the GPSTk distribution.
+
+   3. Extract the GPSTk tarball. For example, using GNU tar
+
+         tar xvzf gpstk.tar.gz
+
+   4. Change into the gpstk directory and type
+
+         ./configure
+         make
+
+   5. To build the source documentation using doxygen:
+ 
+         doxygen 
+
+   6. To install GPSTk as a system library in /usr/local, assume root 
+      privileges then execute
+
+         make install
+
+
+Building under Microsoft Visual Studio .NET 2003
+------------------------------------------------
+
+If you are building GPSTk under the Cygwin environment, then use the build
+instructions for UNIX-like environments (above).
+
+The GPSTk is not supported under Microsoft Visual C++ 6.0, or earlier, mainly 
+because templates in classes are not supported. Currently there are no 
+Makefiles for MSVS, so you must use jam to build the GPSTk on the command line.
+
+Here are the steps we have used to build under MSVS.NET 2003.
+
+   1. Ensure that prerequisites such as jam have been installed.
+
+   2. You may have to reinstall the compiler to build with GPSTk. In order f
+      or Jam to work under Windows, the MSVS command line tools 
+      (compiler and linker) must be installed in a directory without
+      whitespace in it. Thus the default installation directory 
+      (\Program Files\...), for example, will cause Jam to fail. Sorry, 
+      you'll have to re-install in another directory; for example we used 
+      C:\MSVS2003. Also, if the VCVARS32.BAT batch file was not installed, 
+      this option must be chosen during a reinstallation.
+
+   3. Open a command window and run the VCVARS32.BAT batch file before 
+      running Jam.
+
+   4. Jam requires that two other environment variables be set. Using the 
+      command line:
+
+         set MSVCNT=C:\MSVS2003\VC7
+         set NEED_REGEX=yes
+
+      The directory on the right of the '=' is the directory where the command
+      line tools are installed (it will have subdirectories bin,lib,include, 
+      etc.); this tells Jam where the compiler and linker are. NEED_REGEX 
+      tells Jam to compile regex.c into the library, as it is not part of the 
+      MSVS libraries.
+
+   5. Ensure the Jam executable for Windows is in the path. Install the GPSTk 
+      tarball and change to the /dev directory of the GPSTk -- the file 
+      'Jamrules' should be here -- and type
+
+         jam
+
+
+ADDENDUM: Standard notes on using configure
+-------------------------------------------
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Foundation, Inc.
+
+   This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
\ No newline at end of file
diff --git a/dev/Jamfile b/dev/Jamfile
new file mode 100644
index 0000000..c23f3b3
--- /dev/null
+++ b/dev/Jamfile
@@ -0,0 +1,11 @@
+#
+# $Id$
+#
+
+SubDir TOP ;
+
+SubInclude TOP src ;
+SubInclude TOP lib ;
+SubInclude TOP apps ;
+SubInclude TOP examples ;
+# SubInclude TOP tests ;
diff --git a/dev/Jamrules b/dev/Jamrules
new file mode 100644
index 0000000..ad15458
--- /dev/null
+++ b/dev/Jamrules
@@ -0,0 +1,315 @@
+#
+# $Id$
+#
+
+# not much here... just use defaults as much as possible
+
+NotFile install ;
+
+KEEPOBJS = true ;
+
+# where to install the .hpp files
+INCDIR = /usr/local/include ;
+
+# override the built-in EXEMODE
+EXEMODE = 755 ;
+
+# Used to prevent mixing static and dynamic library objects.
+# This was, once upon a time, .lo, but forte's C compiler prevents, stupidly,
+# any attempt to use any extension other than .o
+SUFOBJ2      ?= -l.o ;
+
+# some OS specific stuff
+if $(UNIX)
+{
+   LIBPREFIX     = lib ;
+   LDSHARE_FLAGS = -shared ;
+   LINKLIBS += -lm ;
+   switch $(OS)
+   {
+      case CYGWIN :
+         LINKLIBS += -lstdc++ ;
+
+      case LINUX :
+         LINKLIBS += -lstdc++ ;
+         CCSHARE_FLAGS = -fPIC ;
+
+      case OPENBSD :
+         LINKLIBS += -lstdc++ ;
+         CCSHARE_FLAGS = -fPIC ;
+
+      case SOLARIS :
+         LINKLIBS += -lgen ;
+         # for solaris forte
+         if ( $(C++) = CC )
+         {
+            LDSHARE_FLAGS = -G -mt ;
+            CCSHARE_FLAGS = -KPIC ;
+            CCFLAGS += -Xc ;
+            C++FLAGS += -compat=5 ;
+            AR = "CC -xar -o" ;
+            NOARSCAN = true ;
+            RM = "rm -rf" ;
+            # Required for TCP things to compile.
+            LINKLIBS += -lnsl -lsocket ;
+         }
+	 else # for solaris gcc
+         {
+            LINKLIBS += -lstdc++ ;
+            CCSHARE_FLAGS = -fPIC ;  
+         }
+
+      case AIX :
+         LINKLIBS += -lstdc++ ;
+   }
+   SUFLIBSHR      ?= .so ;
+}
+else if $(NT)
+{
+   LIBPREFIX       = "" ;
+   NEED_REGEX      = true ;
+   # do NOT define __STDC__ here
+
+   # use these to create a dll
+   #LDSHARE_FLAGS  ?= /MAP /DLL ;
+   #SUFLIBSHR       = .dll ;
+
+   # Microsoft compilers define macro _MSC_VER = MMmm.mm MM=Major ver mm.mm=minor ver.
+   # ------ MS VC++ 2005 : _MSC_VER >= 1400
+   if $(MSCVER) >= 1400 {
+      MSVCVersion = "VC++2005(VC8)" ;
+      LINKLIBS   =  ;
+      # -wd turns off warnings; warning 4274 says its ignoring #ident
+      # _CRT_SECURE... stops (many!) warnings
+      # /Ox is max optimize for speed, /O2 opt for speed,
+      #    /GL whole prgm opt (add /LTCG to LINKFLAGS)
+      # /O2 seems to be the best
+      CCFLAGS    += -D_CRT_SECURE_NO_DEPRECATE -wd4274 ;
+      C++FLAGS   += /O2 -DWIN32 -D_CRT_SECURE_NO_DEPRECATE /EHsc /GR -wd4274 ;
+      #LINKFLAGS  += /LTCG ;  # use with /GL
+   }
+   # ------ MS VC++.NET 2003 : _MSC_VER >= 1300
+   else if $(MSCVER) >= 1300 {
+      MSVCVersion = "VC++.NET2003(VC7)" ;
+      LINKLIBS   =  ;
+      CCFLAGS    += /ML -wd4274 ;
+      # use /O2 optimization
+      C++FLAGS   += /O2 /ML -DWIN32 /GX /GR -wd4274 -wd4675 ;
+   }
+   # ------ MS VC++ 6 : _MSC_VER >= 1200
+   # VC++ ver 6 and earlier do not support templates in classes or the toolkit
+   else {
+      Echo "If MSCVER is set, compiler is not supported in Jamrules; otherwise" ;
+      Echo "  for MSVC++.NET2003, set MSCVER=1300" ;
+      Echo "  for MSVC++2005,     set MSCVER=1400" ;
+   }
+   Echo "  Version" $(MSVCVersion) "( MSCVER ==" $(MSCVER) ")" ;
+}
+
+# compiler specific stuff - this is probably actually pretty clumsy
+if ( $(CC) != gcc )
+{
+   NEED_GETOPT = true ;
+}
+
+if $(PREFIX)
+{
+# fix does this need to be forward/backslash independent? darn windows.
+   BINDIR = [ FDirName $(PREFIX) bin ] ;
+   LIBDIR = [ FDirName $(PREFIX) lib ] ;
+   INCDIR = [ FDirName $(PREFIX) include ] ;
+}
+
+# debug - edit this as needed.
+if $(DEBUG)
+{
+      # forte
+   if ( $(C++) = CC ) && ( $(OS) = SOLARIS )
+   {
+      OPTIM = -g -xs ;
+   }
+      # gcc-ish rules
+   else if ( $(C++) = cc ) || ( $(C++) = g++ ) || ( $(C++) = c++ )
+   {
+      OPTIM = -ggdb ;
+   }
+   else if $(MSVC) || $(MSVCNT)
+   {
+      OPTIM = /DEBUG ;
+   }
+}
+
+rule BonkForte
+{
+   # forte for solaris...
+   if ( $(C++) = CC ) && ( $(OS) = SOLARIS )
+   {
+      # we need the explicit /usr/include to make sure the regex.h there
+      # is included instead of the one in the gpstk/dev/src directory
+      CCFLAGS += -I/usr/include ;
+      C++FLAGS += -I/usr/include ;
+   }
+}
+
+rule LibObjects
+{
+   local _i ;
+
+   for _i in [ FGristFiles $(<) ]
+   {
+      Object $(_i:S=$(SUFOBJ2)) : $(_i) ;
+      Depends obj : $(_i:S=$(SUFOBJ2)) ;
+   }
+}
+
+
+# Useage :
+# GPSSetupLibrary target : depenancies ;
+rule GPSSetupLibrary
+{
+   local lib = [ FGristFiles $(LIBPREFIX)$(<:S=$(SUFLIB)) ] ;
+   MakeLocate $(lib) : $(LOCATE_SOURCE) ;
+   $(1)_DIR = $(SUBDIR) ;
+   $(1)_GRIST = $(lib) ;
+   $(1)_DEPENDENCIES = $(2) ;
+}
+
+rule GPSBuildLibrary
+{
+   # $(<) is short name of lib to build.
+   # $(>) is the list of sources that build lib.
+
+   local shlib arlib ;
+   local statobjs shrobjs ;
+   local dependencies = [ Uniqueify [ GetDependencies $(<) ] ] ;
+
+   if $(dependencies)
+   {
+      local thisDependency ;
+      for thisDependency in $(dependencies)
+      {
+         SubDirHdrs $($(thisDependency)_DIR) ;
+      }
+   }
+
+   arlib = $(LIBPREFIX)$(<:S=$(SUFLIB)) ;
+   statobjs = $(>:S=$(SUFOBJ)) ;
+
+   Depends lib : $(arlib) ;
+   Objects $(>) ;
+   LibraryFromObjects $(arlib) : $(statobjs) ;
+   InstallLib $(LIBDIR) : $(arlib) ;
+
+   # Windows is limited to 1024 character command lines.  This goes over that.
+   # Until we can figure out how to properly split our library, we just can't
+   # build DLLs for Windows.
+   if $(UNIX)
+   {
+      shlib = $(LIBPREFIX)$(<:S=$(SUFLIBSHR)) ;
+      shrobjs = $(>:S=$(SUFOBJ2)) ;
+      ObjectCcFlags $(shrobjs) : $(CCSHARE_FLAGS) ;
+      ObjectC++Flags $(shrobjs) : $(CCSHARE_FLAGS) ;
+      LINKFLAGS on $(shlib) += $(CCSHARE_FLAGS) $(LDSHARE_FLAGS) $(OPTIM) ;
+      Depends lib : $(shlib) ;
+      LibObjects $(>) ;
+      MainFromObjects $(shlib) : $(shrobjs) ;
+      InstallLib $(LIBDIR) : $(shlib) ;
+   }
+}
+
+rule GPSLinkLibraries
+{
+   local dependencies = [ Uniqueify [ GetDependencies $(>) ] ] ;
+   dependencies = [ Uniqueify $(>) $(dependencies) ] ;
+
+   if $(dependencies)
+   {
+      local thisDependency ;
+      local thisDir thisLib ;
+      for thisDependency in $(dependencies)
+      {
+         thisDir = $($(thisDependency)_DIR) ;
+         SubDirHdrs $(thisDir) ;
+         thisLib = $($(thisDependency)_GRIST) ;
+         LinkLibraries $(<) : $(thisLib) ;
+      }
+   }
+}
+
+# a rule to replace Main that includes the functionality of GPSFile.
+rule GPSMain
+{
+   Main $(<) : $(>) ;
+   InstallBin $(BINDIR) : $(<:S=$(SUFEXE)) ;
+}
+
+# a rule to replace File that also cleans it when "jam clean" is run.
+# $(1) is the target, $(2) is the source, $(3) is the mode.
+# $(3) is optional - it will default to 644.
+#
+#  GPSFile $(TARGETETCDIR)/moo : moo : $(EXEMODE) ;
+#  GPSFile $(TARGETETCDIR)/moo2 : moo2 ;
+#
+rule GPSFile
+{
+   File $(1) : $(2) ;
+   if $(3)
+   {
+      MODE on $(1) = $(3) ;
+   }
+   Clean clean : $(1) ;
+}
+
+PREPROCESSING = yes ;
+SubInclude TOP src ;
+SubInclude TOP lib ;
+PREPROCESSING = ;
+
+rule TestFiles
+{
+   Depends test : $(<) ;
+   if $(TESTCODE)
+   {
+      ReallyTestFiles $(<) : $(>) ;
+   }
+}
+
+actions ReallyTestFiles
+{
+   $(<) $(>)
+}
+
+
+rule GetDependencies
+{
+   local thisDepends itr ;
+
+   if $($(1)_DEPENDENCIES)
+   {
+      thisDepends += $($(1)_DEPENDENCIES) ;
+      thisDepends += [ GetDependencies $($(1)_DEPENDENCIES) ] ;
+   }
+   return $(thisDepends) ;
+}
+
+rule Uniqueify
+{
+   local thisList thisItem thatItem found newList ;
+
+   for thatItem in $(1)
+   {
+      found = 0 ;
+      newList = ;
+      for thisItem in $(thisList)
+      {
+         if $(thatItem) != $(thisItem)
+         {
+             newList += $(thisItem) ;
+         }
+      }
+      newList += $(thatItem) ;
+      thisList = $(newList) ;
+   }
+   return $(thisList) ;
+}
+
diff --git a/dev/Makefile.am b/dev/Makefile.am
new file mode 100644
index 0000000..a38c911
--- /dev/null
+++ b/dev/Makefile.am
@@ -0,0 +1,3 @@
+# $Id$
+EXTRA_DIST = README
+SUBDIRS = src lib apps examples
diff --git a/dev/NEWS b/dev/NEWS
new file mode 100644
index 0000000..ed21f53
--- /dev/null
+++ b/dev/NEWS
@@ -0,0 +1,20 @@
+$Id$
+
+GPS Toolkit News
+------------------------------------------------------------------------------
+   July 16, 2007 . . . .  Release Cadidate for Version 1.3 is frozen in the 
+			  repository
+
+   January 10, 2005 . . . Version 1.1 is released.
+
+   September 20, 2004 . . Version 1.0.2 is released to coincide with
+                          presentation at the ION GNSS 2002 in
+                          Portland, Oregon.
+
+   August 9, 2004 . . . . Version 1.0.1 released quickly to address
+                          minor issues.
+
+   August 8, 2004 . . . . The first version of the GPSTk is released to 
+                          coincide with an article in LinuxJournal.
+
+   February 25, 2004  . . The GPSTk project is registered at SourceForge.net.
diff --git a/dev/README b/dev/README
new file mode 100644
index 0000000..6e1889c
--- /dev/null
+++ b/dev/README
@@ -0,0 +1,98 @@
+$Id$
+
+The following is excerpted from the GPSTk web site at http://www.gpstk.org/. 
+
+What is GPSTk?
+--------------
+
+The goal of the GPSTk project is to provide a world class, open source 
+computing suite to the satellite navigation community. It is our hope that the
+GPSTk will empower its users to perform new research and create new 
+applications.
+
+GPS users employ practically every computational architecture and operating 
+system. Therefore the design of the GPSTk suite is as platform-independent as 
+possible. Platform independence is achieved through use of the ANSI-standard 
+C++ programming language. The principles of object-oriented programming are 
+used throughout the GPSTk code base in order to ensure that the code is 
+modular, extensible and maintainable.
+
+The GPSTk suite consists of a core ibrary and a set of applications. The 
+library provides a wide array of functions that solve processing problems 
+associated with GPS such as processing or using RINEX. The library is the 
+basis for the more advanced applications distributed as part of the GPSTk 
+suite.
+
+The GPSTk is sponsored by Space and Geophysics Laboratory, within the Applied 
+Research Laboratories at the University of Texas at Austin (ARL:UT). GPSTk is
+the by-product of GPS research conducted at ARL:UT since before the first 
+satellite launched in 1978; it is the combined effort of many software 
+engineers and scientists. In 2003 the research staff at ARL:UT decided to 
+open source much of their basic GPS processing software as the GPSTk.
+
+
+GPSTk Library
+-------------
+
+The GPSTk library provides a number of models and algorithms found in GPS 
+textbooks and classic papers, such as solving for the user position, or 
+estimating atmospheric refraction. Common formats are supported as well, such 
+as RINEX or SP-3. There are several categories of functions in the GPSTk 
+library:
+
+   1. GPS time. Conversion among time representations such as MJD, GPS week 
+      and seconds of week, and many others.
+
+   2. Ephemeris calculations. Position and clock interpolation for both
+      broadcast and precise ephemerides.
+
+   3. Atmospheric delay models. Includes ionosphere and troposphere models.
+
+   4. Position solution. Includes an implementation of a Receiver Autonomous 
+      Integrity Monitoring algorithm.
+
+   5. Mathematics. Includes Matrix and Vector implementations, as well as 
+      interpolation and numerical integration.
+
+   6. Application framework. Includes processing command lines options, 
+      providing interactive help and working with file systems.
+
+A more detailed description of the functionality provided by the GPSTk library can be found in the Doxygen documentation.
+
+
+GPSTk Applications
+------------------
+
+The library is the foundation for applications within the GPSTk suite. Many 
+such applications are utilities necessary for advanced research and 
+development. For example the GPSTk utility DiscFix fixes cycle slips. Some 
+applications implement algorithms described in research papers. The TECMap 
+application is an example of an application that creates models of the 
+ionosphere, based on models published by a number of researchers. To date 
+GPSTk applications fall into the following categories.
+
+   1. Ionosphere modeling. TEC calculation, receiver bias estimation.
+
+   2. Residual analysis. Computation of pseudorange observations residuals, 
+      and comparison between the precise and broadcast ephemerides.
+
+   3. RINEX file manipulation. Differencing, mergins and clipping RINEX files.
+
+   4. Interactive analysis. Bindings to Octave, an open source alternative to 
+      MATLAB.
+
+
+License
+-------
+
+The source code provided by the GPSTk is distributed under the GNU LGPL 
+(http://www.gnu.org/copyleft/lesser.html). This license gives all users the 
+right to use and redistribute the code. Users of the GPSTk are not required 
+to open their source, according to the LGPL. This makes the GPSTk a practical 
+choice for commercial projects.
+
+Contact Info
+------------
+The GPSTk is a collaborative effort. However, you can email to 
+gpstk at arlut.utexas.edu or contact one of the CoreTeam members
+(http://www.gpstk.org/bin/view/Development/CoreTeam).
\ No newline at end of file
diff --git a/dev/apps/DataAvailability/DataAvailabilityAnalyzer.cpp b/dev/apps/DataAvailability/DataAvailabilityAnalyzer.cpp
new file mode 100644
index 0000000..94fe389
--- /dev/null
+++ b/dev/apps/DataAvailability/DataAvailabilityAnalyzer.cpp
@@ -0,0 +1,760 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/** @file Performs a data availability analysis of the input data. In general,
+    availability is determined by station and satellite position.
+
+    This program refers to the items that can be specified as the (in)dependant
+    variables in the analysis as ObservationIDs or oid. The complete list of
+    these are:
+    el      Elevation, degrees (0-90)
+    az      Azimuth, degrees (0-360, 0=north)
+    time    Time in mjd (or as specified)
+    prn     SV ID (1-31)
+    ccid    Three character Rinex 3.0 observation specification: tna
+              t: observation type (C, L, D, S)
+              n: carrier band (1, 2, 5)
+              a: code tracked (C, P, W, Y, M, I, Q, ...)
+    ch      Channel (1-99)
+    snr     Signal to noise ratio (C/C0) in dB-Hz
+    health  SV health bits
+    tcnt    Continuous tracking count
+    iod     Issue of data
+    intrk   Number of SVs in track
+    tama    Time above mask angle
+*/
+
+#include <map>
+#include <algorithm>
+
+#include "StringUtils.hpp"
+#include "ObsID.hpp"
+
+#include "DataAvailabilityAnalyzer.hpp"
+
+#include "EphReader.hpp"
+#include "ObsReader.hpp"
+
+#include "MSCData.hpp"
+#include "MSCStream.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+
+//------------------------------------------------------------------------------
+// The constructor basically just sets up all the command line options
+//------------------------------------------------------------------------------
+DataAvailabilityAnalyzer::DataAvailabilityAnalyzer(const std::string& applName)
+   throw()
+   : timeFormat("%Y %j %02H:%02M:%04.1f"),
+     BasicFramework(
+        applName,
+        "Performs a data availability analysis of the input data. In general,"
+        "availability is determined by station and satellite position."),
+     
+     inputOpt('o', "obs", 
+              "Where to get the data to analyze.", true),
+     
+     outputOpt('\0', "output",
+               "Where to send the output. The default is stdout."),
+     
+     independantOpt('x', "indepndant",
+                    "The independant variable in the analysis. The default "
+                    "is time."),
+     
+     ephFileOpt('e', "eph",  "Where to get the ephemeris data. Can be "
+                   " rinex, fic, or sp3", true),
+     
+     mscFileOpt('c', "msc", "Station coordinate file"),
+     
+     msidOpt('m', "msid", "Station to process data for. Used to select "
+                "a station position from the msc file."),
+     
+     timeFmtOpt('t', "time-format", "Daytime format specifier used for "
+                   "times in the output. The default is \"" 
+                   + timeFormat + "\"."),
+     
+     startTimeOpt('\0', "start-time", "%4Y/%03j/%02H:%02M:%05.2f",
+                  "Ignore data before this time. (%4Y/%03j/%02H:%02M:%05.2f)"),
+     
+     stopTimeOpt('\0',  "stop-time", "%4Y/%03j/%02H:%02M:%05.2f",
+                 "Ignore any data after this time"),
+     
+     timeSpanOpt('l', "time-span", "How much data to process, in seconds"),
+
+     maskAngleOpt('\0', "mask-angle",
+                  "Ignore anomalies on SVs below this elevation. The default"
+                  " is 10 degrees."),
+     
+     timeMaskOpt('\0', "time-mask",
+                 "Ignore anomalies on SVs that haven't been above the mask"
+                 " angle for this number of seconds. The default is 0 "
+                 "seconds."),
+
+     badHealthMaskOpt('b', "bad-health",
+                      "Ignore anomalies associated with SVs that are marked "
+                      "unhealthy."),
+
+     smashAdjacentOpt('s', "smash-adjacent",
+                      "Combine adjacent lines from the same PRN."),
+
+     maskAngle(10), badHealthMask(false), timeMask(0), smashAdjacent(false),
+     epochRate(0), epochCounter(0), allMissingCounter(0), 
+     anyMissingCounter(0), pointsMissedCounter(0)
+{
+   // Set up a couple of helper arrays to map from enum <-> string
+   obsItemName[oiUnknown] = "unk";
+   obsItemName[oiElevation] = "el";
+   obsItemName[oiAzimuth] = "az";
+   obsItemName[oiTime] = "time";
+   obsItemName[oiPRN] = "prn";
+   obsItemName[oiCCID] = "ccid";
+   obsItemName[oiSNR] = "snr";
+   obsItemName[oiHealth] = "health";
+   obsItemName[oiTrackCount] = "tcnt";
+   obsItemName[oiIOD] = "iod";
+
+   for (ObsItemName::const_iterator i=obsItemName.begin(); 
+        i!=obsItemName.end(); i++)
+      obsItemId[i->second] = i->first;
+}
+
+
+//------------------------------------------------------------------------------
+// Here the command line options parsed and used to configure the program
+//------------------------------------------------------------------------------
+bool DataAvailabilityAnalyzer::initialize(int argc, char *argv[]) throw()
+{
+   if (!BasicFramework::initialize(argc,argv)) return false;
+
+   if (debugLevel)
+      cout << "debugLevel: " << debugLevel << endl
+           << "verboseLevel: " << verboseLevel << endl;
+
+   if (outputOpt.getCount())
+   {
+      output.open(outputOpt.getValue()[0].c_str(), std::ios::out);
+      if (debugLevel)
+         cout << "Sending output to" 
+              << outputOpt.getValue()[0]
+              << endl;
+   }
+   else
+   {
+      if (debugLevel)
+         cout << "Sending output to stdout" << endl;
+      output.copyfmt(std::cout);
+      output.clear(std::cout.rdstate());
+      output.basic_ios<char>::rdbuf(std::cout.rdbuf());
+   }
+
+   if (timeFmtOpt.getCount())
+      timeFormat = timeFmtOpt.getValue()[0];
+
+   if (startTimeOpt.getCount())
+      startTime = startTimeOpt.getTime()[0];
+   else
+      startTime = DayTime::BEGINNING_OF_TIME;
+
+   if (stopTimeOpt.getCount())
+      stopTime = stopTimeOpt.getTime()[0];
+   else
+      stopTime = DayTime::END_OF_TIME;
+
+   if (timeSpanOpt.getCount())
+      timeSpan = StringUtils::asDouble(timeSpanOpt.getValue()[0]);
+   else
+      timeSpan = 1e99;
+
+   if (maskAngleOpt.getCount())
+      maskAngle = StringUtils::asDouble(maskAngleOpt.getValue()[0]);
+
+   if (timeMaskOpt.getCount())
+      timeMask = StringUtils::asDouble(timeMaskOpt.getValue()[0]);
+
+   if (badHealthMaskOpt.getCount())
+      badHealthMask = true;
+
+   if (smashAdjacentOpt.getCount())
+      smashAdjacent = true;
+
+   oiX = oiTime;
+   if (independantOpt.getCount())
+   {
+      ObsItemId::const_iterator i;
+      i = obsItemId.find(independantOpt.getValue()[0]);
+      if (i == obsItemId.end())
+      {
+         cout << "Cound not find obs item. Valid items are:" << endl;
+         for (i=obsItemId.begin(); i!=obsItemId.end(); i++)
+            cout << i->first << " ";
+         cout << endl;
+         exit(-1);
+      }
+      else
+      {
+         oiX = i->second;
+      }
+   }
+
+   if (verboseLevel)
+   {
+      cout << "Using " << obsItemName[oiX] << " as the independant variable." 
+           << endl
+           << "Using a mask angle of " << maskAngle << " degrees" << endl;
+      if (badHealthMask)
+         cout << "Ignore anomalies associated with SVs marked unhealthy." 
+              << endl;
+      else
+         cout << "Including anomalies associated with SVs marked unhealthy."
+              << endl;
+              
+      MDPHeader::debugLevel = debugLevel;
+   }
+
+   return true;
+}
+
+
+//------------------------------------------------------------------------------
+// Load all the data to analyze.
+//------------------------------------------------------------------------------
+void DataAvailabilityAnalyzer::spinUp()
+{      
+   EphReader ephData;
+   ephData.verboseLevel = verboseLevel;
+   for (int i=0; i < ephFileOpt.getCount(); i++)
+      ephData.read(ephFileOpt.getValue()[i]);
+
+   if (ephData.eph == NULL)
+   {
+      cout << "Didn't get any ephemeris data from the eph files. "
+           << "Exiting." << endl;
+      exit(-1);
+   }
+   eph = ephData.eph;
+
+   msid = 0;
+   bool haveAntennaPos=false;
+   if (msidOpt.getCount())
+      msid = StringUtils::asUnsigned(msidOpt.getValue()[0]);
+
+   if (msid && mscFileOpt.getCount())
+   {
+      string fn = mscFileOpt.getValue()[0];
+      if (verboseLevel)
+         cout << "Reading " << fn << " as MSC data." << endl;
+      MSCStream mscs(fn.c_str(), ios::in);
+      MSCData mscd;
+      while (mscs >> mscd)
+      {
+         if (mscd.station == msid)
+         {
+            antennaPos = mscd.coordinates;
+            if (verboseLevel>1)
+               cout << "Antenna position read from MSC file:"
+                    << antennaPos << " (msid: "
+                    << msid << ")" << endl;
+            haveAntennaPos=true;
+            break;
+         }
+      }
+      if (!haveAntennaPos)
+         cout << "Did not find station " << msid << " in " << fn 
+              << "." << endl;
+   }
+
+   const string fn=inputOpt.getValue()[0];
+   ObsReader obsReader(fn);
+
+   if (obsReader.inputType == FFIdentifier::tRinexObs && !haveAntennaPos)
+   {
+      antennaPos = obsReader.roh.antennaPosition;
+      if (verboseLevel>1)
+         cout << "Antenna position read from RINEX obs file:"
+              << antennaPos << endl;
+   }
+
+   if (obsReader.inputType == FFIdentifier::tSMODF)
+      obsReader.msid = msid;
+
+   DayTime t0;
+   ObsEpoch oe;
+   int i,j;
+   
+   for (i=j=0; i<100 && j<10 && obsReader(); i++)
+   {
+      oe = obsReader.getObsEpoch();
+      double dt = oe.time - t0;
+      if (std::abs(dt - epochRate) > 0.1)
+      {
+         epochRate = dt;
+         j = 0;
+      }
+      else
+         j++;
+      t0 = oe.time;
+   }
+      
+   if (j<10)
+   {
+      cout << "Could not determine data rate after " << i << " epochs. Sorry."
+           << " This program is really\nwritten to just work with data that "
+           << "is being collected at a fixed data rate.\nI guess it could be"
+           << " re-written to work for changing data rates but I am too\n"
+           << "lazy to do that right now. I'm not, however, too lazy to "
+           << "write needlessly long\ndiagnostic messages." 
+           << endl;
+      exit(-1);
+   }
+
+   if (verboseLevel)
+      cout << "Data rate is " << epochRate << " seconds after " << i 
+           << " epochs." << endl;
+}
+
+
+std::string secAsHMS(double seconds, bool frac=false)
+{
+   std::ostringstream oss;
+   oss << setfill('0');
+   
+   if (seconds<0)
+      oss << "-";
+
+   seconds = std::abs(seconds);
+   long d=0,h=0,m=0,s=0;
+   s = static_cast<long>(std::floor(seconds));
+   seconds -= static_cast<double>(s);
+
+   if (s > 86400) { d = s/86400; s %= 86400; }
+   if (s > 3600)  { h = s/3600;  s %= 3600;  }
+   if (s > 60)    { m = s/60;    s %= 60;    }
+
+   if (d) oss << d << " d ";
+   if (h) oss << setw(2) << h << ":";
+   if (m) oss << setw(2) << m << ":";
+
+   if (h || m)
+      oss << setfill('0');
+   else
+      oss << setfill(' ');
+
+   oss << setw(2) << s;
+
+   if (seconds>=0.1 && frac)
+      oss << "." << static_cast<int>(seconds*10);
+   else
+      oss << "  ";
+
+   return oss.str();
+}
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+DataAvailabilityAnalyzer::MissingList DataAvailabilityAnalyzer::processList(
+   const MissingList& ml, const gpstk::EphemerisStore& eph)
+{
+   MissingList sml;
+   for (MissingList::const_iterator i = ml.begin(); i != ml.end(); i++)
+   {
+      InView curr = *i;
+      
+      // calculate SV visibility info
+      short prnTemp = 1;
+      short numSVsInView = 0;
+      ECEF rxpos(antennaPos);
+      
+      while (prnTemp <= gpstk::MAX_PRN)
+      {
+         Xvt svXVT;
+         bool NoEph = false;
+   
+         try { svXVT = eph.getPrnXvt(prnTemp, curr.time); }
+         catch(gpstk::Exception& e) 
+         {
+            if (verboseLevel> 1) {cout << e << endl;}
+            NoEph = true;
+         }
+         double elvAngle = 0;
+         if (!NoEph)
+         {
+            try {elvAngle = rxpos.elvAngle(svXVT.x);}
+            catch(gpstk::Exception& e) { if (verboseLevel > 1) 
+            {cout << e << endl;}}
+            if (elvAngle > maskAngle)  { numSVsInView++; }
+         } 
+         prnTemp++;
+      }     
+      
+      curr.numSVsVisible = numSVsInView;
+      InView& prev = *sml.rbegin();
+      
+       // increment counter if there isn't data from any SVs
+      if (curr.prn == 0)
+      {
+         allMissingCounter++;     
+         pointsMissedCounter += numSVsInView;
+      }
+      else
+         pointsMissedCounter++;
+           
+      
+      if (i == ml.begin())
+      {
+         sml.push_back(curr);
+         anyMissingCounter++;
+         continue;
+      }
+      else if (prev.time != curr.time)
+         anyMissingCounter++;
+      
+      // smash together epochs if necessary
+      if (curr.prn == prev.prn && smashAdjacent)
+      {
+         prev.smashCount++;
+         prev.time = curr.time;
+         prev.elevation = curr.elevation;
+         prev.azimuth = curr.azimuth;
+         prev.snr = curr.snr;
+         prev.epochCount = curr.epochCount;
+      }
+      else
+      {
+         sml.push_back(curr);
+      }    
+   }
+   return sml;
+}
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+void DataAvailabilityAnalyzer::process()
+{
+   const string fn=inputOpt.getValue()[0];
+   ObsReader obsReader(fn);
+   
+   if (msidOpt.getCount() && obsReader.inputType == FFIdentifier::tSMODF)
+   {
+      msid = StringUtils::asUnsigned(msidOpt.getValue()[0]);
+      obsReader.msid = msid;
+   }
+      
+   double x=RSS(antennaPos[0], antennaPos[1], antennaPos[2]);
+   
+   if (x<1)
+   {
+      cout << "Warning! The antenna appears to be within one meter of the" 
+           << endl << "center of the geoid. Please go check it." << endl;
+      return;
+   }
+
+   ObsEpoch oe, prev_oe;
+
+   DayTime firstEpochTime, lastEpochTime;
+   while (obsReader())
+   {
+      oe = obsReader.getObsEpoch();
+      if (startTime > oe.time)
+         continue;
+      if (stopTime < oe.time)
+         break;
+      
+      epochCounter++;
+      
+      if (obsReader.epochCount==1)
+      {
+         firstEpochTime = oe.time;
+         if (verboseLevel)
+            cout << "First observation is at " 
+                 << firstEpochTime.printf(timeFormat) << endl;
+      }
+      else
+      {
+         lastEpochTime = oe.time;
+         if (lastEpochTime - firstEpochTime > timeSpan)
+            break;
+         
+         processEpoch(antennaPos, oe, prev_oe);
+      }
+      prev_oe = oe;
+   }
+
+   if (verboseLevel)
+      cout << "Last observation is at " << lastEpochTime.printf(timeFormat) 
+           << endl;
+}
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+void DataAvailabilityAnalyzer::processEpoch(
+   const Triple& ap, 
+   const ObsEpoch& oe,
+   const ObsEpoch& prev_oe)
+{
+   ECEF rxpos(ap);
+   InView::dump ivdumper(cout, timeFormat);
+   
+   for (DayTime t = prev_oe.time + epochRate; t <= oe.time; t += epochRate)
+   {
+
+      for (int prn=1; prn<=32; prn++)
+         inView[prn].update(prn, t, rxpos, *eph, gm, maskAngle, verboseLevel);
+      
+      if (verboseLevel>2)
+      {
+         cout << t.printf(timeFormat) << "  SVs in view: ";
+         for (int prn=1; prn<=32; prn++)
+            if (inView[prn].up)
+               cout << prn << "(" << setprecision(3)
+                    << inView[prn].elevation << ") ";
+         cout << endl;
+      }
+
+      if (t != oe.time)
+      {
+         InView iv;
+         iv.prn = 0;
+         iv.time = t;
+         missingList.push_back(iv);
+         continue;
+      }
+
+      for (int prn=1; prn<=32; prn++)
+      {
+         ObsEpoch::const_iterator oei;
+         SatID svid(prn, SatID::systemGPS);
+         
+         oei = oe.find(svid);
+         InView& iv=inView[prn];
+         iv.inTrack = oe.size();
+
+         if (oei == oe.end())  // No data from this SV
+         {
+            if (oe.size()<12 && iv.elevation>maskAngle && 
+                (!iv.health || !badHealthMask))
+            {
+               missingList.push_back(iv);
+            }
+         }
+         else // There is data from this SV
+         {
+            if (verboseLevel>3)
+               cout << oei->first << " " << oei->second << endl;
+            if (verboseLevel>3)
+               ivdumper(iv);
+            if (!iv.up)
+            {
+               missingList.push_back(iv);
+            }
+            else
+            {
+               iv.epochCount++;
+               const SvObsEpoch& soe = oei->second;
+               SvObsEpoch::const_iterator q;
+               
+               q = soe.find(ObsID(ObsID::otSNR, ObsID::cbL1, ObsID::tcCA));
+               if (q != soe.end())
+                  iv.snr = q->second;
+            } // else
+         }    // else      
+      }       // for (int prn=1; prn<=32; prn++)
+   }          // for (DayTime t=prev_oe.time+epochRate;t<=oe.time;t+= epochRate)
+}             // void DataAvailabilityAnalyzer::processEpoch()
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+void DataAvailabilityAnalyzer::shutDown()
+{
+   MissingList sml = processList(missingList, *eph);
+   
+   cout << "\n Availability Raw Results:\n\n";
+   cout << "      Time         Smashes PRN  CCID  Elv    Az  Hlth  SNR    SVs"
+        << " Above Mask       tama\n"
+        << "=================================================================="
+        << "======================\n";
+   
+   for_each(sml.begin(), sml.end(), InView::dump(cout, timeFormat));
+
+   outputSummary();
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+void DataAvailabilityAnalyzer::InView::update(
+   short prn,
+   const DayTime& time,
+   const ECEF& rxpos,
+   const EphemerisStore& eph,
+   GeoidModel& gm,
+   float maskAngle,
+   const int verbosityLevel)
+{
+   verbosity = verbosityLevel;
+      
+   try
+   {
+      this->prn = prn;
+      this->time = time;
+      // We really don't care about the observed range deviation, the
+      // ObsRngDev class is just a convient way to get the azimuth, 
+      // elevation, health, iodc, 
+
+      ObsRngDev ord(0, SatID(prn, SatID::systemGPS), time, rxpos, eph, gm);
+      vfloat el=ord.getElevation();
+
+      if (el.is_valid() && el > 0)
+      {
+         if (!up)
+         {
+            firstEpoch = time;
+            up = true;
+            aboveMask = false;
+            epochCount = 0;
+            snr = 0;
+            inTrack = 0;
+         }
+         else
+         {
+            rising = el > elevation;
+         }
+         if (el > maskAngle && !aboveMask)
+         {
+            aboveMask = true;
+            firstEpochAboveMask = time;
+         }
+      }
+      else
+      {
+         up = false;
+         aboveMask = false;
+      }
+      elevation = ord.getElevation();
+      azimuth = ord.getAzimuth();
+      iodc = ord.getIODC();
+      health = ord.getHealth();
+   }
+   catch (EphemerisStore::NoEphemerisFound& e)
+   {
+      up = false;
+      aboveMask = false;
+      elevation = 0;
+      azimuth = 0;
+      iodc = 0;
+      health = 0;
+   }
+} // end of InView::update()
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+bool DataAvailabilityAnalyzer::InView::dump::operator()(const InView& iv)
+{
+   double timeUp     = iv.time - iv.firstEpoch;
+   double timeUpMask = iv.time - iv.firstEpochAboveMask;
+   char dir;
+   if (iv.elevation > 0)
+      dir = iv.rising ? '^' : 'v';
+   else
+      dir = ' ';
+
+   string ccid="all";
+   
+   cout << left << iv.time.printf(timeFormat)
+        << "   " << setw(4) << iv.smashCount << "  ";
+
+   if (iv.prn>0)
+   {
+      cout << setw(3) << iv.prn << " "
+           << ccid << " "
+           << fixed << right
+           << setprecision(2) << setw(6) << iv.elevation
+           << dir << "  "
+           << setprecision(0) << setw(3) << iv.azimuth << "  "
+           << hex << setw(2) << iv.health << "   " << dec;
+   
+      if (iv.up)
+         cout << setprecision(1) << setw(4) << iv.snr;
+      else
+         cout << "-El ";
+      
+      if (iv.smashCount == 0)
+         cout << right << setw(14) << iv.numSVsVisible;
+      
+      else
+         cout << right << setw(14) << "n/a-smashed";
+      
+      if (iv.up)
+         cout << right << setw(16) <<  secAsHMS(timeUpMask);
+      else
+         cout << right << setw(16) << "-El";
+   }
+   else
+   {
+      cout << "All";
+      if (iv.smashCount == 0)
+        cout << right << setw(42) << iv.numSVsVisible;
+      else
+        cout << right << setw(42) << "n/a-smashed";
+   }
+      
+   cout << endl;
+
+   return true;
+}
+
+void DataAvailabilityAnalyzer::outputSummary()
+{
+   cout << "\n\n Summary:\n\n";
+   
+   cout << right << setw(40) << "Total number of epochs with data: " 
+        << left  << setw(10) << epochCounter << endl;
+   cout << right << setw(40) << "Epochs with any # of missed points: "
+        << left  << setw(10) << anyMissingCounter << endl;
+   cout << right << setw(40) << "Epochs without data from any SV: "
+        << left  << setw(10) << allMissingCounter << endl;
+   cout << right << setw(40) << "Total number of points missed: "
+        << left  << setw(10) << pointsMissedCounter << endl << endl;
+        
+
+}
diff --git a/dev/apps/DataAvailability/DataAvailabilityAnalyzer.hpp b/dev/apps/DataAvailability/DataAvailabilityAnalyzer.hpp
new file mode 100644
index 0000000..5da7fb5
--- /dev/null
+++ b/dev/apps/DataAvailability/DataAvailabilityAnalyzer.hpp
@@ -0,0 +1,200 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/** @file Performs a data availability analysis of the input data. In general,
+    availability is determined by station and satellite position.
+*/
+
+#ifndef DATAAVAILABILITYANALYZER_HPP
+#define DATAAVAILABILITYANALYZER_HPP
+
+#include <fstream>
+#include <list>
+
+#include "BasicFramework.hpp"
+#include "CommandOptionWithTimeArg.hpp"
+#include "GPSGeoid.hpp"
+#include "MiscMath.hpp"
+#include "ObsRngDev.hpp"
+#include "EphemerisStore.hpp"
+
+class DataAvailabilityAnalyzer : public gpstk::BasicFramework
+{
+public:
+   DataAvailabilityAnalyzer(const std::string& applName) throw();
+   bool initialize(int argc, char *argv[]) throw();
+   
+protected:
+   virtual void spinUp();
+   virtual void process();
+   virtual void shutDown();
+
+private:
+   void processEpoch(
+      const gpstk::Triple& antPos, 
+      const gpstk::ObsEpoch& oe,
+      const gpstk::ObsEpoch& prev_oe);
+      
+   void outputSummary();
+   
+   std::ifstream input;
+   std::ofstream output;
+   std::string timeFormat;
+   gpstk::CommandOptionWithAnyArg inputOpt, outputOpt, independantOpt,
+      mscFileOpt, msidOpt, timeFmtOpt, ephFileOpt, maskAngleOpt, timeMaskOpt;
+
+   gpstk::CommandOptionNoArg badHealthMaskOpt, smashAdjacentOpt;
+
+   gpstk::CommandOptionWithNumberArg timeSpanOpt;
+   gpstk::CommandOptionWithTimeArg startTimeOpt, stopTimeOpt;
+
+   gpstk::DayTime startTime, stopTime;
+   double timeSpan, timeMask;
+   double epochRate;
+   
+   // these are counters used in the summary
+   unsigned long epochCounter;
+   unsigned long allMissingCounter;
+   unsigned long anyMissingCounter;
+   unsigned long pointsMissedCounter;
+
+   enum ObsItemEnum {oiUnknown, oiElevation, oiAzimuth, oiTime, oiPRN, oiCCID,
+                     oiSNR, oiHealth, oiTrackCount, oiIOD};
+
+   typedef std::map<ObsItemEnum, std::string> ObsItemName;
+   typedef std::map<std::string, ObsItemEnum> ObsItemId;
+   typedef std::map<gpstk::DayTime, int> SVsInView;
+
+   ObsItemName obsItemName;
+   ObsItemId obsItemId;
+   ObsItemEnum oiX;    
+
+   bool badHealthMask, smashAdjacent;
+
+   gpstk::EphemerisStore* eph;
+   gpstk::GPSGeoid gm;
+   gpstk::Triple antennaPos;
+   long msid;
+
+   float maskAngle;
+
+   // This is used to keep track of SV info for both what SVs are in view
+   // and when there is a obs that is missing. 
+   struct InView
+   {
+      InView() : up(false), aboveMask(false), smashCount(0){};
+
+      void update(
+         short prn,
+         const gpstk::DayTime& time,
+         const gpstk::ECEF& rxpos,
+         const gpstk::EphemerisStore& eph,
+         gpstk::GeoidModel& gm,
+         float maskAngle,
+         const int verbosityLevel);
+
+      short prn;
+      gpstk::DayTime time;
+      
+      int verbosity;
+
+      // Set true when this SV has an elevation greater than 0
+      // If this is false, no other fields are valid.
+      bool up;
+      
+      // true when the sv is rising
+      bool rising;
+
+      // First epoch when this SV had an elevation greater than 0 
+      gpstk::DayTime firstEpoch;
+
+      // Set true when this SV has risen above the 'mask angle'
+      // It is not cleared when the SV goes back below the mask angle.
+      bool aboveMask;
+
+      // First epoch when this SV had an elevation greater than the
+      // 'mask angle'. Not valid unles aboveMask is true.
+      gpstk::DayTime firstEpochAboveMask;
+
+      // Number of epochs received from this SV during this pass
+      unsigned epochCount;
+      float elevation,azimuth;
+      short iodc, health;
+
+      // This following items are only useful when this class is used to record
+      // information associated with missing data.
+
+      // Set only when the smash function merges this record with others.
+      // Indicates the number of records merged.
+      unsigned smashCount;
+
+      // The number of SVs in track at this point in time.
+      short inTrack;
+      
+      // The number of SVs physically above the mask angle at this time
+      short numSVsVisible;
+
+      // The SNR of the CA signal. Note that this will be the SNR of the 
+      // most recently received observation when an outage is detected.
+      float snr;
+
+      // A function object to allow printing of a list of these with a 
+      // for_each loop
+      class dump
+      {
+      public:
+         dump(std::ostream& s, const std::string fmt)
+            : stream(s), timeFormat(fmt) {};
+         std::ostream& stream;
+         std::string timeFormat;
+         bool operator()(const InView& iv);
+      };
+   };
+
+   typedef std::list<InView> MissingList;
+   MissingList missingList;
+
+   // This combines adjecent items from the same SV
+   MissingList processList(const MissingList& ml, 
+                           const gpstk::EphemerisStore& eph);
+   
+   std::map<int, InView> inView;                         
+   
+   
+};
+#endif
diff --git a/dev/apps/DataAvailability/Jamfile b/dev/apps/DataAvailability/Jamfile
new file mode 100644
index 0000000..f370255
--- /dev/null
+++ b/dev/apps/DataAvailability/Jamfile
@@ -0,0 +1,7 @@
+# $Id:$
+
+SubDir TOP apps DataAvailability ;
+
+GPSLinkLibraries daa : rxio gpstk ;
+
+GPSMain daa : daa.cpp DataAvailabilityAnalyzer.cpp ;
diff --git a/dev/apps/DataAvailability/Makefile.am b/dev/apps/DataAvailability/Makefile.am
new file mode 100644
index 0000000..4b0af9f
--- /dev/null
+++ b/dev/apps/DataAvailability/Makefile.am
@@ -0,0 +1,7 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../lib/rxio -I$(srcdir)/../../src
+LDADD = ../../lib/rxio/librxio.la ../../src/libgpstk.la
+
+bin_PROGRAMS = daa
+
+daa_SOURCES = daa.cpp DataAvailabilityAnalyzer.cpp
diff --git a/dev/apps/DataAvailability/daa.cpp b/dev/apps/DataAvailability/daa.cpp
new file mode 100644
index 0000000..c054bb3
--- /dev/null
+++ b/dev/apps/DataAvailability/daa.cpp
@@ -0,0 +1,64 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/** @file Performs a data availability analysis of the input data. In general,
+    availability is determined by station and satellite position.
+*/
+
+#include "DataAvailabilityAnalyzer.hpp"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+   try
+   {
+      DataAvailabilityAnalyzer crap(argv[0]);
+
+      if (!crap.initialize(argc, argv))
+         exit(0);
+
+      crap.run();
+   }
+   catch (gpstk::Exception &exc)
+   { cout << exc << endl; }
+   catch (std::exception &exc)
+   { cout << "Caught std::exception " << exc.what() << endl; }
+   catch (...)
+   { cout << "Caught unknown exception" << endl; }
+}
diff --git a/dev/apps/Jamfile b/dev/apps/Jamfile
new file mode 100644
index 0000000..eeb7830
--- /dev/null
+++ b/dev/apps/Jamfile
@@ -0,0 +1,23 @@
+#
+# $Id$
+#
+
+SubDir TOP apps ;
+
+SubInclude TOP apps checktools ;
+SubInclude TOP apps converters ;
+SubInclude TOP apps DataAvailability ;
+SubInclude TOP apps differential ;
+SubInclude TOP apps difftools ;
+SubInclude TOP apps filetools ;
+SubInclude TOP apps geomatics ;
+SubInclude TOP apps ionosphere ;
+SubInclude TOP apps mergetools ;
+SubInclude TOP apps MDPtools ; 
+SubInclude TOP apps multipath ;
+SubInclude TOP apps positioning ;
+SubInclude TOP apps receiver ;
+SubInclude TOP apps reszilla ;
+SubInclude TOP apps Rinextools ;
+SubInclude TOP apps time ;
+SubInclude TOP apps visibility ;
diff --git a/dev/apps/MDPtools/BELogEntry.cpp b/dev/apps/MDPtools/BELogEntry.cpp
new file mode 100644
index 0000000..49acf79
--- /dev/null
+++ b/dev/apps/MDPtools/BELogEntry.cpp
@@ -0,0 +1,64 @@
+#pragma ident "$Id$"
+
+
+//lgpl-license START
+//lgpl-license END
+
+//dod-release-statement START
+//dod-release-statement END
+
+/**
+ * @file BELogEntry.cpp
+ */
+#include "DayTime.hpp"
+#include "BELogEntry.hpp"
+
+namespace gpstk
+{
+   using namespace std;
+   using namespace gpstk;
+
+      // Initialize the static const.
+   const std::string BELogEntry::header = "PRN Earliest SF 1 HOW !                 Toe                      IODC #Collected";
+   
+   BELogEntry::BELogEntry( const gpstk::EngEphemeris ee )
+   {
+      HOWSF1 = ee.getTransmitTime();
+      Toe = ee.getEpochTime();
+      PRN_ID = ee.getPRNID();
+      IODC = ee.getIODC();
+      count = 1;
+      
+      long shortweek = (long) ee.getFullWeek();
+      shortweek &= 0x0000001F;
+      long sixteenSecCount = long (Toe.GPSsow() / 16.0);
+      
+         // The purpose of the key is to enable placing these objects
+         // into SV-specific maps ordered by 
+         //    Primary :week
+         //    Secondary :SOW
+         //    Tertiary: IODC
+         // The latter (IODC) is actually only a part of the key so as to
+         // enforce uniqueness
+      key = shortweek << 26 |
+            sixteenSecCount << 10 | IODC;
+   }
+   
+   unsigned long BELogEntry::getKey() const { return(key); }
+   DayTime BELogEntry::getHOW() const { return(HOWSF1); }
+   void BELogEntry::increment()    { count++; }
+   
+   std::string BELogEntry::getStr() const
+   {
+      std::string timeFmt1 = "%02m/%02d/%02y %02H:%02M:%02S";
+      std::string timeFmt2 = "%02m/%02d/%02y %02H:%02M:%02S %03j %5.0s %04F %6.0g";
+      char line[100];
+      sprintf( line, " %02d %s ! %s 0x%03X %4d",
+         PRN_ID, HOWSF1.printf( timeFmt1 ).c_str(),
+                 Toe.printf(timeFmt2).c_str(),
+                 IODC,count);
+      std::string retStr(line);
+      return(retStr);
+   }
+   
+}   // namespace
diff --git a/dev/apps/MDPtools/BELogEntry.hpp b/dev/apps/MDPtools/BELogEntry.hpp
new file mode 100644
index 0000000..ed459d7
--- /dev/null
+++ b/dev/apps/MDPtools/BELogEntry.hpp
@@ -0,0 +1,51 @@
+#pragma ident "$Id$"
+
+
+/**
+ * @file BELogEntry.hpp
+ * Record the unique identifying information associated
+ * with a Broadcast Ephemeris, allow it to be ordered (i.e. map support)
+ * and provide a string output capability. 
+ */
+
+#ifndef GPSTK_BELOGENTRY_HPP
+#define GPSTK_BELOGENTRY_HPP
+
+//lgpl-license START
+//lgpl-license END
+
+//dod-release-statement START
+//dod-release-statement END
+
+#include "EngEphemeris.hpp"
+
+namespace gpstk
+{
+   class BELogEntry
+   {
+   public:
+         /// Default constructor
+      BELogEntry( const gpstk::EngEphemeris ee );
+      
+         /// Destructor
+      virtual ~BELogEntry() {}
+
+      std::string getStr() const;
+      gpstk::DayTime getHOW() const;
+      void increment();
+      unsigned long getKey() const;
+      static const std::string header;
+      
+      protected:
+         DayTime HOWSF1;
+         DayTime Toe;
+         int PRN_ID;
+         int IODC;
+         int count;
+         unsigned long key;
+         
+   }; // class BELogEntry
+
+} // namespace
+
+#endif
diff --git a/dev/apps/MDPtools/Histogram.hpp b/dev/apps/MDPtools/Histogram.hpp
new file mode 100644
index 0000000..39e9dd8
--- /dev/null
+++ b/dev/apps/MDPtools/Histogram.hpp
@@ -0,0 +1,74 @@
+#pragma ident "$Id: Histogram.hpp 71 2006-08-01 18:46:39Z ehagen $"
+
+#ifndef HISTOGRAM_HPP
+#define HISTOGRAM_HPP
+
+#include <map>
+#include <list>
+#include <ostream>
+
+namespace gpstk
+{
+
+   //-----------------------------------------------------------------------------
+   class Histogram
+   {
+   public:
+      typedef std::pair<double, double> BinRange;
+      typedef std::list<BinRange> BinRangeList;
+      typedef std::map<BinRange, unsigned> BinMap;
+
+      BinMap bins;
+      unsigned total;
+
+      void resetBins(const BinRangeList& brl)
+      {
+         bins.clear();
+         total=0;
+         for (BinRangeList::const_iterator i=brl.begin(); i != brl.end(); i++)
+            bins[*i] = 0;
+      }
+
+      inline void addValue(double v)
+      {
+         BinMap::iterator bm_itr;
+         for (bm_itr=bins.begin(); bm_itr != bins.end(); bm_itr++)
+         {
+            const BinRange& range = bm_itr->first;
+            if (range.first < v && v <= range.second)
+            {
+               bm_itr->second++;
+               total++;
+               break;
+            }
+         }
+      }
+
+      inline void dump(std::ostream& s) const
+      {
+         BinMap::const_iterator bmi;
+         for (bmi = bins.begin(); bmi != bins.end(); bmi++)
+         {
+            const BinRange& br = bmi->first;
+            s << std::right << std::setw(3) << br.first
+              << "-" << std::left  << std::setw(3) << br.second
+              << ":   " << std::right <<  bmi->second
+              << std::endl;
+         }
+
+         s << std::right << std::setw(3) << bins.begin()->first.first
+           << "-" << std::left  << std::setw(3) << bins.rbegin()->first.second
+           << ":   " << std::right <<  total
+           << std::endl;
+      };
+
+   };
+
+   inline std::ostream& operator<<(std::ostream& s, const Histogram& hist)
+   {
+      hist.dump(s);
+      return s;
+   };
+
+}
+#endif
diff --git a/dev/apps/MDPtools/Jamfile b/dev/apps/MDPtools/Jamfile
new file mode 100644
index 0000000..18ebea3
--- /dev/null
+++ b/dev/apps/MDPtools/Jamfile
@@ -0,0 +1,23 @@
+# $Id$
+
+SubDir TOP apps MDPtools ;
+
+
+# Note that the local library needs to be declaired and built prior to anything
+# else is done.
+GPSLinkLibraries mdplib : rxio gpstk ;
+Library mdplib : MDPProcessors.cpp SummaryProc.cpp TrackProc.cpp NavProc.cpp ;
+
+
+# Now we can set up the things that depend upon the local library. Note that
+# the LinkLibraries rule for mdplib must preceede the GPSLinkLibraries rule
+# for things to build properly
+LinkLibraries mdptool mdp2rinex tcptest : mdplib ;
+GPSLinkLibraries mdptool mdp2rinex tcptest : rxio gpstk ;
+
+
+GPSMain mdptool : mdptool.cpp ;
+
+GPSMain mdp2rinex : mdp2rinex.cpp ;
+
+GPSMain tcptest : tcptest.cpp ;
diff --git a/dev/apps/MDPtools/MDPProcessors.cpp b/dev/apps/MDPtools/MDPProcessors.cpp
new file mode 100644
index 0000000..b5bb860
--- /dev/null
+++ b/dev/apps/MDPtools/MDPProcessors.cpp
@@ -0,0 +1,337 @@
+#pragma ident "$Id$"
+
+
+/** @file Various presentations/analysis on MDP streams */
+
+//lgpl-license START
+//lgpl-license END
+
+#include "MDPProcessors.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+gpstk::MDPStream d1;
+std::ofstream d2;
+
+
+MDPProcessor::MDPProcessor() :
+      timeFormat("%4Y/%03j/%02H:%02M:%04.1f"),
+      stopTime(gpstk::DayTime::END_OF_TIME),
+      startTime(gpstk::DayTime::BEGINNING_OF_TIME),
+      timeSpan(-1), processBad(false), bugMask(0),
+      debugLevel(0), verboseLevel(0), in(d1), out(d2), die(false),
+      pvtOut(false), obsOut(false), navOut(false), tstOut(false)
+{}
+
+MDPProcessor::MDPProcessor(gpstk::MDPStream& in, std::ofstream& out) :
+      timeFormat("%4Y/%03j/%02H:%02M:%04.1f"),
+      stopTime(gpstk::DayTime::END_OF_TIME),
+      startTime(gpstk::DayTime::BEGINNING_OF_TIME),
+      timeSpan(-1), processBad(false), bugMask(0),
+      debugLevel(0), verboseLevel(0), in(in), out(out), die(false),
+      pvtOut(false), obsOut(false), navOut(false), tstOut(false)
+{}
+
+void MDPProcessor::process()
+{
+   MDPHeader header;
+
+   msgCount=0;
+   firstFC=0;
+   lastFC=0;
+   fcErrorCount=0;
+
+   while (!die && in >> header)
+   {
+      if (startTime == DayTime(DayTime::BEGINNING_OF_TIME) && timeSpan>0)
+      {
+         startTime = header.time;
+         if (debugLevel)
+            cout << "startTime: " << startTime << endl;
+      }
+      
+      if (stopTime == DayTime(DayTime::END_OF_TIME) && timeSpan>0)
+      {
+         stopTime = startTime + timeSpan;
+         if (debugLevel)
+            cout << "stopTime: " << stopTime << endl;
+      }
+
+      if (header.time > stopTime)
+         return;
+            
+      if (header.time < startTime)
+         continue;
+
+      msgCount++;
+
+      if (verboseLevel>5 || debugLevel>2)
+         out << "Record: " << in.recordNumber
+             << ", message: " << msgCount << endl;
+
+      if (msgCount == 1)
+         firstFC = lastFC = in.header.freshnessCount;
+      else
+      {
+         if (in.header.freshnessCount != static_cast<unsigned short>(lastFC+1))
+         {
+            fcErrorCount++;
+            if (verboseLevel)
+               cout << header.time.printf(timeFormat)
+                    <<"  Freshness count error.  Previous was " << lastFC
+                    << " current is " << in.header.freshnessCount << endl;
+         }
+         lastFC = in.header.freshnessCount;
+      }
+
+      switch (in.header.id)
+      {
+         case gpstk::MDPObsEpoch::myId:
+            if (obsOut)
+            {
+               gpstk::MDPObsEpoch obs;
+               in >> obs;
+               if (obs || processBad)
+                  process(obs);
+            }
+            break;
+
+         case gpstk::MDPPVTSolution::myId:
+            if (pvtOut)
+            {
+               gpstk::MDPPVTSolution pvt;
+               in >> pvt;
+               if (pvt || processBad)
+                  process(pvt);
+            }
+            break;
+
+         case gpstk::MDPNavSubframe::myId:
+            if (navOut)
+            {
+               gpstk::MDPNavSubframe nav;
+               in >> nav;
+               if (nav || processBad)
+                  process(nav);
+            }
+            break;
+
+         case gpstk::MDPSelftestStatus::myId:
+            if (tstOut) 
+            {
+               gpstk::MDPSelftestStatus sts;
+               in >> sts;
+               if (sts || processBad)
+                  process(sts);
+            }
+            break;
+      } // end of switch()
+   } // end of while()
+}
+
+
+//-----------------------------------------------------------------------------
+MDPTableProcessor::MDPTableProcessor(gpstk::MDPStream& in, std::ofstream& out) :
+   MDPProcessor(in, out), headerDone(false)
+{}
+
+
+//-----------------------------------------------------------------------------
+void MDPTableProcessor::outputHeader()
+{
+   if (headerDone)
+      return;
+
+   if (obsOut)
+      out << "# time, 300, prn, chan, hlth, #SVs, ele, az, code, carrier, LC, SNR, range, phase, doppler" << endl;
+   if (pvtOut)
+      out << "# time, 301, #SV, dtime, ddtime, x, y, z, vx, vy, vz" << endl;
+   if (navOut)
+      out << "# time, 310, prn, carrier_code, range_code, nav_code, word1, word2, ..." << endl;
+   if (tstOut)
+      out << "# time, 400, tstTime, startTime, Tant, Trx, status, cpu, freq, ssw" << endl;
+
+   headerDone=true;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPTableProcessor::process(const gpstk::MDPObsEpoch& oe)
+{
+   outputHeader();
+   MDPObsEpoch::ObsMap::const_iterator i;
+   for (i = oe.obs.begin(); i != oe.obs.end(); i++)
+   {
+      const MDPObsEpoch::Observation& obs=i->second;
+      out << oe.time.printf(timeFormat)
+          << fixed
+          << ", " << setw(3) << oe.id
+          << ", " << setw(2) << (int) oe.prn
+          << ", " << setw(2) << (int) oe.channel
+          << ", " << setw(2) << hex << (int) oe.status << dec
+          << ", " << setw(2) << (int) oe.numSVs
+          << setprecision(1)
+          << ", " << setw(2) << (int) oe.elevation
+          << ", " << setw(3) << (int) oe.azimuth
+          << ", " << setw(1) << obs.range
+          << ", " << setw(1) << obs.carrier
+          << ", " << setw(7) << obs.lockCount
+          << setprecision(2)
+          << ", " << setw(5) << obs.snr
+          << setprecision(4)
+          << ", " << setw(13) << obs.pseudorange
+          << ", " << setw(14) << obs.phase
+          << ", " << setw(10) << obs.doppler
+          << endl;
+   }
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPTableProcessor::process(const gpstk::MDPPVTSolution& pvt)
+{
+   outputHeader();
+   out << pvt.time.printf(timeFormat)
+       << fixed
+       << ", " << setw(3) << pvt.id
+       << ", " << setw(2) << (int)pvt.numSVs
+       << setprecision(3)
+       << ", " << setw(12) << pvt.dtime*1e9
+       << setprecision(6)
+       << ", " << setw(9)  << pvt.ddtime*1e9
+       << setprecision(3)
+       << ", " << setw(12) << pvt.x[0]
+       << ", " << setw(12) << pvt.x[1]
+       << ", " << setw(12) << pvt.x[2]
+       << setprecision(3)
+       << ", " << setw(8) << pvt.v[0]
+       << ", " << setw(8) << pvt.v[1]
+       << ", " << setw(8) << pvt.v[2]
+       << endl;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPTableProcessor::process(const gpstk::MDPNavSubframe& sf)
+{
+   outputHeader();
+   out << sf.time.printf(timeFormat)
+       << fixed
+       << ", " << setw(3) << sf.id
+       << ", " << setw(2) << sf.prn
+       << ", " << int(sf.carrier)
+       << ", " << int(sf.range)
+       << ", " << int(sf.nav);
+
+   if (verboseLevel)
+   {
+      out <<  setfill('0') << hex;
+      for(int i = 1; i < sf.subframe.size(); i++)
+         out << ", " << setw(8) << uppercase << sf.subframe[i];
+      out << dec << setfill(' ');
+   }
+   out << endl;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPTableProcessor::process(const gpstk::MDPSelftestStatus& sts)
+{
+   outputHeader();
+   out << sts.time.printf(timeFormat)
+       << fixed
+       << ", " << setw(3) << sts.id
+       << ", " << sts.selfTestTime.printf("%4F/%9.2g")
+       << ", " << sts.firstPVTTime.printf("%4F/%9.2g")
+       << ", " << setprecision(1) << sts.antennaTemp
+       << ", " << setprecision(1) << sts.receiverTemp
+       << ", " << hex << sts.status << dec
+       << ", " << setprecision(1) << sts.cpuLoad
+       << ", " << hex << sts.extFreqStatus << dec
+       << ", " << hex << sts.saasmStatusWord << dec
+       << endl;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPVerboseProcessor::process(const gpstk::MDPObsEpoch& oe)
+{
+   if (verboseLevel)
+   {
+      oe.dump(out);
+      out << endl;
+   }
+   else
+   {
+      out << oe.getName() << "-:"
+          << " T:" << oe.time.printf(timeFormat)
+          << left
+          << " #SV:" << setw(2) << (int)oe.numSVs
+          << " Ch:" << setw(2) << (int)oe.channel
+          << " PRN:" << setw(2) << (int)oe.prn
+          << " El:" << setw(2) << (int)oe.elevation;
+      
+      MDPObsEpoch::ObsMap::const_iterator i;
+      for (i = oe.obs.begin(); i != oe.obs.end(); i++)
+      {
+         const MDPObsEpoch::Observation& obs=i->second;
+         out << " " << StringUtils::asString(obs.carrier)
+             << "-" << StringUtils::asString(obs.range);
+      }
+      out << endl;
+   }
+
+
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPVerboseProcessor::process(const gpstk::MDPPVTSolution& pvt)
+{
+   if (verboseLevel)
+   {
+      pvt.dump(out);
+      out << endl;
+   }
+   else
+   {
+      out << pvt.getName() << "-:"
+          << " T:" << pvt.time.printf(timeFormat)
+          << left
+          << " #SV:" << setw(2) << (int)pvt.numSVs
+          << " X:" << StringUtils::asString(pvt.x[0], 3)
+          << " Y:" << StringUtils::asString(pvt.x[1], 3)
+          << " Z:" << StringUtils::asString(pvt.x[2], 3)
+          << endl;
+   }
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPVerboseProcessor::process(const gpstk::MDPNavSubframe& sf)
+{
+   if (verboseLevel)
+   {
+      sf.dump(out);
+      out << endl;
+   }
+   else
+   {
+      out << sf.getName() << "-:"
+          << " T:" << sf.time.printf(timeFormat)
+          << " PRN:" << sf.prn
+          << " " << StringUtils::asString(sf.carrier)
+          << "-" << StringUtils::asString(sf.range)
+          << " " << static_cast<int>(sf.nav)
+          << endl;
+   }
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPVerboseProcessor::process(const gpstk::MDPSelftestStatus& sts)
+{
+   sts.dump(out);
+   out << endl;
+}
diff --git a/dev/apps/MDPtools/MDPProcessors.hpp b/dev/apps/MDPtools/MDPProcessors.hpp
new file mode 100644
index 0000000..2826213
--- /dev/null
+++ b/dev/apps/MDPtools/MDPProcessors.hpp
@@ -0,0 +1,130 @@
+#pragma ident "$Id$"
+
+
+/** @file Various presentations/analysis on MDP streams */
+
+//lgpl-license START
+//lgpl-license END
+
+#ifndef MDPPROCESSORS_HPP
+#define MDPPROCESSORS_HPP
+
+#include <vector>
+#include <map>
+#include <set>
+
+#include <StringUtils.hpp>
+#include <Stats.hpp>
+#include <DayTime.hpp>
+
+#include "MDPStream.hpp"
+#include "MDPNavSubframe.hpp"
+#include "MDPObsEpoch.hpp"
+#include "MDPPVTSolution.hpp"
+#include "MDPSelftestStatus.hpp"
+
+//-----------------------------------------------------------------------------
+class MDPProcessor
+{
+public:
+
+   MDPProcessor();
+   MDPProcessor(gpstk::MDPStream& in, std::ofstream& out);
+   virtual ~MDPProcessor() {};
+
+   void process();
+
+   std::string timeFormat;
+   int debugLevel;
+   int verboseLevel;
+   bool die;
+
+   bool processBad;
+   bool pvtOut, obsOut, navOut, tstOut;
+
+   std::ofstream& out;
+   gpstk::MDPStream& in;
+
+   gpstk::DayTime startTime, stopTime;
+   double timeSpan;
+
+   unsigned long msgCount, fcErrorCount;
+   unsigned short firstFC, lastFC;
+
+   // A bitmask of bugs to not report
+   // bit 0: SV count mismatch
+   // 
+   unsigned long bugMask;
+
+private:
+   /// All processors are defined so that subclasses don't have to
+   /// define process methods for messages they don't care about.
+   virtual void process(const gpstk::MDPObsEpoch& oe)        {};
+   virtual void process(const gpstk::MDPPVTSolution& pvt)    {};
+   virtual void process(const gpstk::MDPNavSubframe& sf)     {};
+   virtual void process(const gpstk::MDPSelftestStatus& sts) {};
+};
+
+
+//-----------------------------------------------------------------------------
+class MDPTableProcessor : public MDPProcessor
+{
+   void process(const gpstk::MDPObsEpoch& oe);
+   void process(const gpstk::MDPPVTSolution& pvt);
+   void process(const gpstk::MDPNavSubframe& sts);
+   void process(const gpstk::MDPSelftestStatus& sts);
+
+   bool headerDone;
+   void outputHeader();
+
+public:
+   MDPTableProcessor(gpstk::MDPStream& in, std::ofstream& out);
+};
+
+
+//-----------------------------------------------------------------------------
+class MDPBriefProcessor : public MDPProcessor
+{
+   void process(const gpstk::MDPObsEpoch& oe)
+   {out << "o " << std::flush; };
+
+   void process(const gpstk::MDPPVTSolution& pvt)
+   {out << "p " << std::flush; };
+
+   void process(const gpstk::MDPNavSubframe& sf)
+   {out << "n " << std::flush; };
+
+   void process(const gpstk::MDPSelftestStatus& sts)
+   {out << "s " << std::flush; };
+
+public:
+   MDPBriefProcessor(gpstk::MDPStream& in, std::ofstream& out) :
+      MDPProcessor(in, out)
+   {};
+};
+
+
+//-----------------------------------------------------------------------------
+class MDPVerboseProcessor : public MDPProcessor
+{
+   void process(const gpstk::MDPObsEpoch& oe);
+   void process(const gpstk::MDPPVTSolution& pvt);
+   void process(const gpstk::MDPNavSubframe& sf);
+   void process(const gpstk::MDPSelftestStatus& sts);
+
+public:
+   MDPVerboseProcessor(gpstk::MDPStream& in, std::ofstream& out) :
+      MDPProcessor(in, out)
+   {}
+};
+
+
+//-----------------------------------------------------------------------------
+class MDPNullProcessor : public MDPProcessor
+{
+public:
+   MDPNullProcessor(gpstk::MDPStream& in, std::ofstream& out) :
+      MDPProcessor(in, out)
+   {}
+};
+#endif
diff --git a/dev/apps/MDPtools/Makefile.am b/dev/apps/MDPtools/Makefile.am
new file mode 100644
index 0000000..4874ee7
--- /dev/null
+++ b/dev/apps/MDPtools/Makefile.am
@@ -0,0 +1,13 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../lib/rxio -I$(srcdir)/../../src
+LDADD = ./libmdplib.la ../../lib/rxio/librxio.la ../../src/libgpstk.la
+
+lib_LTLIBRARIES = libmdplib.la
+libmdplib_la_SOURCES = MDPProcessors.cpp SummaryProc.cpp TrackProc.cpp \
+NavProc.cpp
+
+bin_PROGRAMS = mdp2rinex mdptool tcptest
+
+mdptool_SOURCES = mdptool.cpp
+mdp2rinex_SOURCES = mdp2rinex.cpp
+tcptest_SOURCES = tcptest.cpp
diff --git a/dev/apps/MDPtools/NavProc.cpp b/dev/apps/MDPtools/NavProc.cpp
new file mode 100644
index 0000000..cf34a4d
--- /dev/null
+++ b/dev/apps/MDPtools/NavProc.cpp
@@ -0,0 +1,278 @@
+#pragma ident "$Id$"
+
+
+/*
+  Think, navdmp for mdp, with bonus output that you get data from all code/carrier
+  combos.
+*/
+
+#include "Geodetic.hpp"
+#include "NavProc.hpp"
+
+#include "RinexConverters.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace gpstk::StringUtils;
+
+
+//-----------------------------------------------------------------------------
+MDPNavProcessor::MDPNavProcessor(gpstk::MDPStream& in, std::ofstream& out)
+   : MDPProcessor(in, out),
+     firstNav(true), almOut(false), ephOut(false), minimalAlm(false),
+     badNavSubframeCount(0), navSubframeCount(0)
+{
+   timeFormat = "%4Y/%03j/%02H:%02M:%02S";
+
+   binByElevation = true;
+   if (binByElevation)
+   {
+      double binSize=5;
+      for (double x=0; x<90; x+=binSize)
+         bins.push_back(Histogram::BinRange(x, x+binSize));
+   }
+   else
+   {
+      bins.push_back(Histogram::BinRange(0, 30));
+      double binSize=3;
+      for (double x=30; x<60; x+=binSize)
+         bins.push_back(Histogram::BinRange(x, x+binSize));
+      bins.push_back(Histogram::BinRange(60, 99));
+   }
+}
+
+
+//-----------------------------------------------------------------------------
+MDPNavProcessor::~MDPNavProcessor()
+{
+   using gpstk::RangeCode;
+   using gpstk::CarrierCode;
+   using gpstk::StringUtils::asString;
+   
+   out << "Done processing data." << endl << endl;
+
+   out << endl << "Navigation Subframe message summary:" << endl;
+   if (firstNav)
+      out << "  No Navigation Subframe messages processed." << endl;
+   else
+   {
+      out << "  navSubframeCount: " << navSubframeCount << endl;
+      out << "  badNavSubframeCount: " << badNavSubframeCount << endl;
+   }
+
+   cout << "Parity Errors" << endl;
+   cout << "# snr ";
+   std::map<RangeCarrierPair, Histogram>::const_iterator peh_itr;
+   for (peh_itr = peHist.begin(); peh_itr != peHist.end(); peh_itr++)
+   {
+      const RangeCarrierPair& rcp=peh_itr->first;
+      cout << "    " << asString(rcp.second)
+           << "-"    << leftJustify(asString(rcp.first), 2);
+   }
+   cout << endl;
+
+   Histogram::BinRangeList::const_iterator brl_itr;
+   for (brl_itr = bins.begin(); brl_itr != bins.end(); brl_itr++)
+   {
+      const Histogram::BinRange& br = *brl_itr ;
+      std::cout << right << setw(2) << br.first << "-"
+                << left  << setw(2) << br.second << ":";
+
+      for (peh_itr = peHist.begin(); peh_itr != peHist.end(); peh_itr++)
+      {
+         const RangeCarrierPair& rcp=peh_itr->first;
+         Histogram h=peh_itr->second;
+         cout << right << setw(9) << h.bins[br];
+      }
+
+      cout << endl;
+   }
+
+   // Whoever would write a reference like this should be shot...
+   cout << right << setw(2) << peHist.begin()->second.bins.begin()->first.first
+        << "-" << left  << setw(2) << peHist.begin()->second.bins.rbegin()->first.second
+        << ":";
+
+   for (peh_itr = peHist.begin(); peh_itr != peHist.end(); peh_itr++)
+      cout << right <<  setw(9) << peh_itr->second.total;
+      
+   out << endl;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPNavProcessor::process(const gpstk::MDPNavSubframe& msg)
+{
+   if (firstNav)
+   {
+      firstNav = false;
+      if (verboseLevel)
+         out << msg.time.printf(timeFormat)
+             << "  Received first Navigation Subframe message"
+             << endl;
+   }
+
+   navSubframeCount++;
+   RangeCarrierPair rcp(msg.range, msg.carrier);
+   NavIndex ni(rcp, msg.prn);
+
+   gpstk::MDPNavSubframe umsg = msg;
+
+   ostringstream oss;
+   oss << umsg.time.printf(timeFormat)
+       << "  PRN:" << setw(2) << umsg.prn
+       << " " << asString(umsg.carrier)
+       << ":" << setw(2) << left << asString(umsg.range)
+       << "  ";
+   string msgPrefix = oss.str();
+   
+   // First try the data assuming it is already upright
+   umsg.cooked = true;
+   bool parityGood = umsg.checkParity();
+   if (!parityGood)
+   {
+      if (verboseLevel>3)
+         out << msgPrefix << "Raw subframe" << endl;
+      umsg.cooked = false;
+      umsg.cookSubframe();
+      parityGood = umsg.checkParity();
+   }
+   else
+   {
+      if (verboseLevel>3)
+         out << msgPrefix << "Cooked subframe" << endl;
+   }
+
+   if (!parityGood)
+   {
+      badNavSubframeCount++;
+      if (verboseLevel)
+         out << msgPrefix << "Parity error"
+             << " SNR:" << fixed << setprecision(1) << snr[ni]
+             << " EL:" << el[ni]
+             << endl;
+
+      if (peHist.find(rcp) == peHist.end())
+         peHist[rcp].resetBins(bins);
+
+      if (binByElevation)
+         peHist[rcp].addValue(el[ni]);
+      else
+         peHist[rcp].addValue(snr[ni]);
+
+      return;
+   }
+
+   short sfid = umsg.getSFID();
+   short svid = umsg.getSVID();
+   bool isAlm = sfid > 3;
+   long sow = umsg.getHOWTime();
+   short page = ((sow-6) / 30) % 25 + 1;
+
+   if (((isAlm && almOut) || (!isAlm && ephOut))
+       && verboseLevel>2)
+   {
+      out << msgPrefix
+          << "SOW:" << setw(6) << sow
+          << " NC:" << static_cast<int>(umsg.nav)
+          << " I:" << umsg.inverted
+          << " SFID:" << sfid;
+      if (isAlm)
+         out << " SVID:" << svid
+             << " Page:" << page;
+      out << endl;
+   }
+
+   // Sanity check on the header time versus the HOW time
+   short week = umsg.time.GPSfullweek();
+   if (sow <0 || sow>=604800)
+   {
+      badNavSubframeCount++;
+      if (verboseLevel>1)
+         out << msgPrefix << "  Bad SOW: " << sow << endl;
+      return;
+   }
+      
+   DayTime howTime(week, umsg.getHOWTime());
+   if (howTime == umsg.time)
+   {
+      if (verboseLevel && ! (bugMask & 0x4))
+         out << msgPrefix << " Header time==HOW time" << endl;
+   }
+   else if (howTime != umsg.time+6)
+   {
+      badNavSubframeCount++;
+      if (verboseLevel>1)
+         out << msgPrefix << " HOW time != hdr time+6, HOW:"
+             << howTime.printf(timeFormat)
+             << endl;
+      return;
+   }
+
+   prev[ni] = curr[ni];
+   curr[ni] = umsg;
+
+   if (isAlm && almOut)
+   {
+      AlmanacPages& almPages = almPageStore[ni];
+      EngAlmanac& engAlm = almStore[ni];
+      SubframePage sp(sfid, page);
+      almPages[sp] = umsg;
+      almPages.insert(make_pair(sp, umsg));
+
+      if (makeEngAlmanac(engAlm, almPages, !minimalAlm))
+      {
+         out << msgPrefix << "Built complete almanac" << endl;
+         if (verboseLevel>2)
+            dump(out, almPages);
+         if (verboseLevel>1)
+            engAlm.dump(out);
+         almPages.clear();
+         engAlm = EngAlmanac();
+      }            
+   }
+   if (!isAlm && ephOut)
+   {
+      EphemerisPages& ephPages = ephPageStore[ni];
+      ephPages[sfid] = umsg;
+      EngEphemeris engEph;
+      try
+      {
+         if (makeEngEphemeris(engEph, ephPages))
+         {
+            out << msgPrefix << "Built complete ephemeris, iocd:0x"
+                << hex << setw(3) << engEph.getIODC() << dec
+                << endl;
+         if (verboseLevel>2)
+            dump(out, ephPages);
+            if (verboseLevel>1)
+               out << engEph;
+            ephStore[ni] = engEph;
+         }
+      }
+      catch (gpstk::Exception& e)
+      {
+         out << e << endl;
+      }
+   }
+
+   if (verboseLevel>3)
+      out << endl;
+
+}  // end of process()
+
+
+void  MDPNavProcessor::process(const gpstk::MDPObsEpoch& msg)
+{
+   if (!msg)
+      return;
+
+   for (gpstk::MDPObsEpoch::ObsMap::const_iterator i = msg.obs.begin();
+        i != msg.obs.end(); i++)
+   {
+      const gpstk::MDPObsEpoch::Observation& obs=i->second;      
+      NavIndex ni(RangeCarrierPair(obs.range, obs.carrier), msg.prn);
+      snr[ni] = obs.snr;
+      el[ni] = msg.elevation;
+   }
+}
diff --git a/dev/apps/MDPtools/NavProc.hpp b/dev/apps/MDPtools/NavProc.hpp
new file mode 100644
index 0000000..bc80a4e
--- /dev/null
+++ b/dev/apps/MDPtools/NavProc.hpp
@@ -0,0 +1,72 @@
+#pragma ident "$Id$"
+
+#ifndef MDPNAV_HPP
+#define MDPNAV_HPP
+
+#include <map>
+
+#include "EngEphemeris.hpp"
+#include "EngAlmanac.hpp"
+
+#include "Histogram.hpp"
+
+#include "MDPProcessors.hpp"
+
+
+//-----------------------------------------------------------------------------
+class MDPNavProcessor : public MDPProcessor
+{
+public:
+   MDPNavProcessor(gpstk::MDPStream& in, std::ofstream& out);
+   ~MDPNavProcessor();
+
+   virtual void process(const gpstk::MDPNavSubframe& msg);
+   virtual void process(const gpstk::MDPObsEpoch& msg);
+   
+   bool firstNav;
+
+   static const int maxChannel=12;
+
+   // First time is of the first missed epoch, second time is the last missed epoch
+   // (i.e. first = previous + obsRateEst, second=current-ObsRateEst
+   typedef std::pair<gpstk::DayTime, gpstk::DayTime> DayTimePair;
+   typedef std::list<DayTimePair> DayTimePairList;
+
+   // Used to control whether we process the engineering eph/alms.
+   bool ephOut;
+   bool almOut;
+   bool minimalAlm;  // Set true to allow an alm to be built from a minimal set of pages
+
+   // This is really a triple: RangeCode, CarrierCode, prn
+   typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> RangeCarrierPair;
+   typedef std::pair<RangeCarrierPair, short> NavIndex;
+
+   // This class can keep track of a subframe and where it came from
+   typedef std::map<NavIndex, gpstk::MDPNavSubframe> NavMap;
+   
+   // A note on nomenclature. A navigation subframe is as defined in the
+   // '200 and refers a set of 300 bits of the navigation that can be modulated
+   // on the various codes. Think of it as the raw bits.  A navigation message
+   // is a logical set of these subframes. For an ephemeris it will always consist
+   // of three subframes, with subframe ids of 1, 2, and 3. For an almanac it will
+   // consist of an undefined number of subframes, all with subframe ids of 4 and 5.
+
+   NavMap prev, curr;
+
+   std::map<NavIndex, gpstk::AlmanacPages> almPageStore;
+   std::map<NavIndex, gpstk::EngAlmanac> almStore;
+
+   std::map<NavIndex, gpstk::EphemerisPages> ephPageStore;
+   std::map<NavIndex, gpstk::EngEphemeris> ephStore;
+
+   std::list<gpstk::MDPNavSubframe> badList;
+   unsigned long badNavSubframeCount, navSubframeCount;
+
+   std::map<NavIndex, double> snr; // 'current' SNR
+   std::map<NavIndex, double> el;  // 'current' elevation
+
+   bool binByElevation;
+   std::map<RangeCarrierPair, gpstk::Histogram> peHist;
+   gpstk::Histogram::BinRangeList bins;
+};
+#endif
diff --git a/dev/apps/MDPtools/SummaryProc.cpp b/dev/apps/MDPtools/SummaryProc.cpp
new file mode 100644
index 0000000..021325e
--- /dev/null
+++ b/dev/apps/MDPtools/SummaryProc.cpp
@@ -0,0 +1,559 @@
+#pragma ident "$Id$"
+
+
+/*
+  This intended to perform a quick summary/analysis of the data in a MDP file
+  or stream. The idea is teqc +meta or +mds with a little bit of +qc thrown
+  in for good measure.
+
+  Mainly driven by the needs of the receiver test cases. The following are
+  some of the test cases that this class is to support.
+
+  RS-13  | needs to report data gaps |
+  RS-16  | needs to report data gaps (based upon presense of data, not sv visibility) |
+  RS-31  | Needs to report jumps in the clock offset reported in the PVT messages |
+  RS-32  | "" |
+  RS-58  | Analyze the changes in lock count over tracking anomolies |
+  RS-72  | |
+  RS-133  | Heh, need to run this for 90 days... |
+*/
+
+#include "Geodetic.hpp"
+#include "EngEphemeris.hpp"
+#include "SummaryProc.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace gpstk::StringUtils;
+
+
+//-----------------------------------------------------------------------------
+MDPSummaryProcessor::MDPSummaryProcessor(gpstk::MDPStream& in, std::ofstream& out)
+   : MDPProcessor(in, out),
+     numEpochs(0), numObsEpochMsg(0),
+     firstObs(true), firstPvt(true), firstNav(true), firstSelftest(true),
+     firstObsTime(gpstk::DayTime::END_OF_TIME),
+     lastObsTime(gpstk::DayTime::BEGINNING_OF_TIME),
+     firstNavTime(gpstk::DayTime::END_OF_TIME),
+     lastNavTime(gpstk::DayTime::BEGINNING_OF_TIME),
+     prevEpochTime(gpstk::DayTime::BEGINNING_OF_TIME),
+     obsRateEst(0), pvtRateEst(0),
+     prevObs(maxChannel+1),
+     chanGapList(maxChannel+1),
+     svCountErrorCount(0)
+{
+   elevBins.push_back(elevationPair(10,90));
+   elevBins.push_back(elevationPair( 0, 5));
+   elevBins.push_back(elevationPair( 5,10));
+   elevBins.push_back(elevationPair(10,20));
+   elevBins.push_back(elevationPair(20,60));
+   elevBins.push_back(elevationPair(60,90));
+   processBad = true;
+}
+
+
+//-----------------------------------------------------------------------------
+MDPSummaryProcessor::~MDPSummaryProcessor()
+{
+   using gpstk::RangeCode;
+   using gpstk::CarrierCode;
+   using gpstk::StringUtils::asString;
+   
+   out << "Done processing data." << endl << endl;
+
+   out << endl << "Header summary:" << endl;
+   cout << "  Processed "<< msgCount << " headers." << endl
+        << "  First freshness count was " << hex << firstFC << dec << endl
+        << "  Last freshness count was  " << hex << lastFC << dec << endl
+        << "  Encountered " << fcErrorCount << " breaks in the freshness count" << endl;
+
+   out << endl << "Observation Epoch message summary:" << endl;
+
+   if (firstObs)
+      out << "No Observation Epoch messages processed." << endl;
+   else
+   {
+      double dt = lastObsTime - firstObsTime;
+      out << "  Processed " << numObsEpochMsg
+          << " observation epoch messages spanning "
+          << numEpochs << " epochs."
+          << endl
+          << "  Obs data spans " << firstObsTime.printf(timeFormat) 
+          << " to " << lastObsTime.printf(timeFormat)
+          << " (" << secondsAsHMS(dt) << ")"
+          << endl
+          << "  Obs output rate is " << setprecision(2) << obsRateEst
+          << " sec."
+          << endl;
+
+      for (DayTimePairList::const_iterator i=epochGapList.begin(); i!=epochGapList.end(); i++)
+         if (std::abs(i->first - i->second - obsRateEst) > 1e-3)
+            out << "  Data gap from " << i->second.printf(timeFormat)
+                << " to " << i->first.printf(timeFormat)
+                << " ( " << secondsAsHMS(i->first - i->second) << " )."
+                << endl;
+      
+      for (elevBinList::const_iterator i=elevBins.begin(); i!=elevBins.end(); i++)
+      {
+         const ocm &oc = whack[*i];
+         if (oc.size())
+            out << "  Elevation: " << i->first << "..." << i->second << endl;
+         for (ocm::const_iterator j=oc.begin(); j!=oc.end(); j++)
+         {
+            const rc_set ccs = j->first;
+            if (ccs.size())
+            {
+               out << setw(10) << j->second << "   ";
+               for (rc_set::const_iterator k=ccs.begin(); k!=ccs.end(); k++)
+                  out << "(" << asString(k->second)
+                      << ", " << asString(k->first) << ")";
+               out << endl;
+            }
+         }
+         if (verboseLevel<2)
+            break;
+         out << endl;
+      }
+      
+      cout << "Encountered " << svCountErrorCount << " SV count errors." << endl;
+   }
+
+   out << endl << "PVT Solution message summary:" << endl;
+   if (firstPvt)
+      out << "  No PVT Solution messages processed." << endl;
+   else
+   {
+      double dt = lastPvtTime - firstPvtTime;
+      out << "  Pvt data spans " << firstPvtTime.printf(timeFormat) 
+          << " to " << lastPvtTime.printf(timeFormat)
+          << " (" << secondsAsHMS(dt) << ")"
+          << endl
+          << "  PVT output rate is " << setprecision(2) << pvtRateEst << " sec."
+          << endl << endl;
+   }
+
+
+   out << endl << "Navigation Subframe message summary:" << endl;
+   if (firstNav)
+      out << "  No Navigation Subframe messages processed." << endl;
+   else
+   {
+      double dt = lastNavTime - firstNavTime;
+      out << "  Nav data spans " << firstNavTime.printf(timeFormat) 
+          << " to " << lastNavTime.printf(timeFormat)
+          << " (" << secondsAsHMS(dt) << ")"
+          << endl << endl;
+   }
+      
+   out << endl;
+   if (badMessages.size())
+   {
+      out << "Received " << badMessages.size() << " messages with an error." << endl;
+      if (verboseLevel>1)
+      {
+         out << "Headers from the bad messages:" << endl;
+         for (MDPList::const_iterator i=badMessages.begin(); i!=badMessages.end(); i++)
+            i->dump(out);
+      }
+   }
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPSummaryProcessor::process(const gpstk::MDPObsEpoch& msg)
+{
+   if (!msg)
+   {
+      badMessages.push_back(msg);
+      return;
+   }
+
+   // First, do gross accounting on the rate the obs are coming in
+   if (firstObs)
+   {
+      firstObsTime = msg.time;
+      firstObs = false;
+      if (verboseLevel)
+         out << msg.time.printf(timeFormat)
+             << "  Received first Observation Epoch message"
+             << endl;
+   }
+   else
+   {
+      double dt = msg.time - prevEpochTime;
+      if (std::abs(dt - obsRateEst) > 1e-3 && dt>0)
+      {
+         if (obsRateEst > 0)
+         {
+            gpstk::DayTime first =  prevEpochTime + dt;
+            gpstk::DayTime second = msg.time - dt;
+            epochGapList.push_back(DayTimePair(first, second));
+            if (verboseLevel)
+            {
+               out << msg.time.printf(timeFormat)
+                   << "  Obs output rate " << dt << " sec";
+               if (obsRateEst != 0)
+                  out << " (was " << obsRateEst << " sec).";
+               out << endl;
+            }
+         }
+         obsRateEst = dt;
+      }
+   }
+
+   lastObsTime=msg.time;
+
+   // Next, make a set of the obs that this epoch has and add 
+   // this to a list in the appropriate elevation bin
+   rc_set ccs;
+   for (gpstk::MDPObsEpoch::ObsMap::const_iterator i = msg.obs.begin();
+        i != msg.obs.end(); i++)
+   {
+      const gpstk::MDPObsEpoch::Observation& obs=i->second;
+      rcpair rcPair(obs.range, obs.carrier);
+      ccs.insert(rcPair);
+   }
+
+   // figure out what bins we should update code/carrier counts on
+   for (elevBinList::const_iterator i=elevBins.begin(); i!=elevBins.end(); i++)
+      if (msg.elevation >= i->first && msg.elevation <= i->second)
+         whack[*i][ccs]++;
+
+   // This part does some accounting on a per channel basis. The intent is to
+   // look for when there is a gap in the data on a channel.
+   int prn=msg.prn;
+   int chan=msg.channel;
+
+   // First check to see if this channel has been used yet...
+   if (prevObs[chan].prn == 0)
+   {
+      // do nothing
+   }
+   else
+   {
+      // flag when there is a gap on this channel
+      double dt = msg.time - prevObs[chan].time;
+      if (std::abs(dt) < 1e-3)
+      {
+         out << msg.time.printf(timeFormat)
+             << "  Got two consecutive obs on channel "
+             << chan << " with the same time." << endl;
+         if (verboseLevel)
+            msg.dump(out), prevObs[chan].dump(out);
+      }
+      else if (obsRateEst == 0)
+      {
+         // do nothing
+      }
+      else if ( (std::abs(dt - obsRateEst) > 1e-3) &&
+                (prevObs[chan].prn == msg.prn) )
+      {
+         gpstk::DayTime first =  prevObs[chan].time + dt;
+         gpstk::DayTime second = msg.time - dt;
+         chanGapList[chan].push_back(DayTimePair(first, second));
+         if (verboseLevel>1)
+            out << msg.time.printf(timeFormat)
+                << "  Data gap on channel " << chan
+                << ", " << secondsAsHMS(dt)
+                << endl;
+         if (verboseLevel>2)
+         {
+            out << "  prev obs on chan " << chan << endl;
+            prevObs[chan].dump(out);
+            out << "  curr obs:" << endl;
+            msg.dump(out);
+         }
+      }
+
+      // Look for discontinuities in the lock count
+      // Since this can be quite verbose, only do it in verbose mode
+      if (verboseLevel)
+      {
+         for (gpstk::MDPObsEpoch::ObsMap::const_iterator i = msg.obs.begin();
+              i != msg.obs.end(); i++)
+         {
+            const gpstk::MDPObsEpoch::Observation& curr=i->second;
+            if (prevObs[chan].haveObservation(i->first.first, i->first.second))
+            {
+               gpstk::MDPObsEpoch::Observation prev = prevObs[chan].getObservation(i->first.first, i->first.second);
+               if (curr.lockCount - prev.lockCount != 1)
+               {
+                  // The current ash2mdp has periods where it outputs every
+                  // message with a lock count of zero 
+                  if ((prev.lockCount > 0 && verboseLevel>1) || verboseLevel>2)
+                     out << msg.time.printf(timeFormat)
+                         << "  Lock count reset prn " << prn
+                         << ", chan " << chan
+                         << ", " << asString(i->first.first)
+                         << " "  << asString(i->first.second)
+                         << " (" << prev.lockCount
+                         << " -> " << curr.lockCount
+                         << ")" << endl;
+               }
+            }
+         }
+      }
+   }
+
+   // Keep track of the number of epochs we have processed and check the
+   // numSVs field.
+   if (prevEpochTime != msg.time)
+   {
+      numEpochs++;
+
+      int prevActual=0;
+      int prevReported=0;
+      for (size_t i = 1; i<=maxChannel; i++)
+      {
+         if (prevObs[i].time == prevEpochTime)
+         {
+            prevActual++;
+            if (prevReported==0)
+               prevReported = prevObs[i].numSVs;
+         }
+      }
+      if (prevActual != prevReported)
+      {
+         svCountErrorCount++;
+         if (! (bugMask & 0x01))
+         cout << prevEpochTime.printf(timeFormat)
+              << "  Epoch claimed " << prevReported
+              << " SVs but only received " << prevActual << endl;
+      }
+   }
+
+   prevObs[chan] = msg;
+   prevEpochTime = msg.time;
+   numObsEpochMsg++;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPSummaryProcessor::process(const gpstk::MDPPVTSolution& msg)
+{
+   if (!msg)
+   {
+      badMessages.push_back(msg);
+      return;
+   }
+
+   if (firstPvt)
+   {
+      firstPvt = false;
+      firstPvtTime = msg.time;
+      if (verboseLevel)
+         out << msg.time.printf(timeFormat)
+             << "  Received first PVT Solution message"
+             << endl;
+   }
+   else
+   {
+      double dt = msg.time - prevPvt.time;
+      if (std::abs(dt - pvtRateEst) > 1e-3 && dt>0)
+      {
+         if (verboseLevel)
+         {
+            out << msg.time.printf(timeFormat)
+                << "  PVT output rate " << dt << " sec";
+            if (pvtRateEst != 0)
+               out << "(was " << pvtRateEst << " sec).";
+            out << endl;
+         }
+         pvtRateEst = dt;
+
+         // flag when there is a gap
+         double dt = msg.time - prevPvt.time;
+         if (std::abs(dt) < 1e-3)
+         {
+            out << msg.time.printf(timeFormat)
+                << "  Got two consecutive PVT messages with the same time." << endl;
+            if (verboseLevel)
+               msg.dump(out), prevPvt.dump(out);
+         }
+         else if (pvtRateEst == 0)
+         {
+            // do nothing
+         }
+         else if ( std::abs(dt - pvtRateEst) > 1e-3 )
+         {
+            gpstk::DayTime first =  prevPvt.time + dt;
+            gpstk::DayTime second = msg.time - dt;
+            if (verboseLevel)
+               out << msg.time.printf(timeFormat)
+                   << "  Gap in PVT messages: "  << secondsAsHMS(dt)
+                   << endl;
+            if (verboseLevel>2)
+            {
+               out << "  prev pvt:" << endl;
+               prevPvt.dump(out);
+               out << "  curr obs:" << endl;
+               msg.dump(out);
+            }
+         }
+      
+         // Look for discontinuities in the recevier clock estimate
+         double ddt = msg.dtime - prevPvt.dtime;
+         double dtdt = ddt/(msg.time - prevPvt.time);
+         double dtdtErr = std::abs(dtdt - msg.ddtime);
+         if (dtdt > 1e-6)
+            out << msg.time.printf(timeFormat)
+                << "  Clock jump: " << setprecision(3) << scientific << ddt
+                << " sec, (" << dtdt << " vs " << msg.ddtime
+                << " sec/sec)"
+                << fixed << endl;
+         else if (dtdtErr > 1e-8 && verboseLevel)
+            out << msg.time.printf(timeFormat)
+                << "  Clock error: " << setprecision(3) << scientific << ddt
+                << " sec, (" << dtdt << " vs " << msg.ddtime
+                << " sec/sec)"
+                << fixed << endl;
+      }
+
+      prevPvt = msg;
+      lastPvtTime = msg.time;
+   }
+}
+
+//-----------------------------------------------------------------------------
+void MDPSummaryProcessor::process(const gpstk::MDPNavSubframe& msg)
+{
+   if (!msg)
+   {
+      badMessages.push_back(msg);
+      return;
+   }
+
+   gpstk::MDPNavSubframe umsg = msg;
+
+   // First try the data assuming it is already upright
+   umsg.cooked = true;
+   bool parityGood = umsg.checkParity();
+   if (!parityGood)
+   {
+      if (verboseLevel>2)
+         out << msg.time.printf(timeFormat)
+             << "  Subframe appears raw" << endl;
+      umsg.cooked = false;
+      umsg.cookSubframe();
+      parityGood = umsg.checkParity();
+   }
+   else
+   {
+      if (verboseLevel>2)
+         out << msg.time.printf(timeFormat)
+             << "  Subframe appears cooked" << endl;
+   }
+
+
+   if (!(bugMask & 0x2) && !parityGood)
+   {
+      MDPNavSubframe tmp(msg);
+      tmp.setstate(parbit);
+      badMessages.push_back(tmp);
+      return;
+   }
+
+   long how_sow = umsg.getHOWTime();
+   long hdr_sow = static_cast<long>(umsg.time.GPSsow());
+   if (how_sow < 0 || how_sow >= 604800)
+   {
+      if (verboseLevel)
+         out << umsg.time.printf(timeFormat)
+             << "  Bogus HOW SOW (" << how_sow << ")"
+             << endl;
+      MDPNavSubframe tmp(umsg);
+      tmp.setstate(fmtbit);
+      badMessages.push_back(tmp);
+      return;
+   }
+
+   if ( (how_sow != hdr_sow+6 && how_sow != hdr_sow) ||
+        (how_sow == hdr_sow && !(bugMask & 0x4))        )
+   {
+      if (verboseLevel)
+         out << umsg.time.printf(timeFormat)
+             << "  Navigation Subframe HOW/header time mismatch ("
+             << how_sow << " vs " << hdr_sow << ")"
+             << endl;
+      MDPNavSubframe tmp(umsg);
+      tmp.setstate(fmtbit);
+      badMessages.push_back(tmp);
+      return;
+   }
+
+   if (umsg && firstNav)
+   {
+      firstNav = false;
+      firstNavTime = umsg.time;
+      if (verboseLevel)
+         out << umsg.time.printf(timeFormat)
+             << "  Received first Navigation Subframe message"
+             << endl;
+   }
+
+   lastNavTime = umsg.time;
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPSummaryProcessor::process(const gpstk::MDPSelftestStatus& msg)
+{
+   if (!msg)
+   {
+      badMessages.push_back(msg);
+      return;
+   }
+
+   if (firstSelftest)
+   {
+      firstSelftest = false;
+      if (verboseLevel)
+         out << msg.time.printf(timeFormat)
+             << "  Received first Selftest Status message"
+             << endl;
+   } 
+
+   if (verboseLevel)
+   {
+      if (msg.extFreqStatus != prevSelftestStatus.extFreqStatus)
+         out << msg.time.printf(timeFormat)
+             << "  External Frequency Status: "
+             << msg.extFreqStatus
+             << endl;
+      
+      if (msg.saasmStatusWord != prevSelftestStatus.saasmStatusWord)
+         out << msg.time.printf(timeFormat)
+             << "  SAASM Status Word: 0x"
+             << hex << msg.saasmStatusWord << dec
+             << endl;
+   }
+   prevSelftestStatus = msg;
+}
+
+std::string MDPSummaryProcessor::secondsAsHMS(double seconds) const
+{
+   std::ostringstream oss;
+   oss << setfill('0');
+   
+   if (seconds<0)
+      oss << "-";
+
+   seconds = std::abs(seconds);
+   long d=0,h=0,m=0,s=0;
+   s = static_cast<long>(std::floor(seconds));
+   seconds -= static_cast<double>(s);
+
+   if (s > 86400) { d = s/86400; s %= 86400; }
+   if (s > 3600)  { h = s/3600;  s %= 3600;  }
+   if (s > 60)    { m = s/60;    s %= 60;    }
+
+   if (d) oss << d << " d ";
+   if (h) oss << setw(2) << h << ":";
+   if (m) oss << setw(2) << m << ":";
+   if (h || m)
+      oss << fixed << setprecision(1) << setw(4) << seconds+s;
+   else
+      oss << setfill(' ') << fixed << setprecision(1) << seconds+s << " s";
+
+   return oss.str();
+}
diff --git a/dev/apps/MDPtools/SummaryProc.hpp b/dev/apps/MDPtools/SummaryProc.hpp
new file mode 100644
index 0000000..ce8d8a6
--- /dev/null
+++ b/dev/apps/MDPtools/SummaryProc.hpp
@@ -0,0 +1,70 @@
+#pragma ident "$Id$"
+
+
+#ifndef MDPSUMMARY_HPP
+#define MDPSUMMARY_HPP
+
+#include "MDPProcessors.hpp"
+
+//-----------------------------------------------------------------------------
+class MDPSummaryProcessor : public MDPProcessor
+{
+public:
+   MDPSummaryProcessor(gpstk::MDPStream& in, std::ofstream& out);
+   ~MDPSummaryProcessor();
+
+   virtual void process(const gpstk::MDPObsEpoch& msg);
+   virtual void process(const gpstk::MDPPVTSolution& msg);
+   virtual void process(const gpstk::MDPNavSubframe& msg);
+   virtual void process(const gpstk::MDPSelftestStatus& msg);
+   
+   unsigned long numEpochs;
+   unsigned long numObsEpochMsg;
+
+   std::string secondsAsHMS(double s) const;
+
+   typedef std::pair<int, int> elevationPair;  // first is min, second is max
+   typedef std::list<elevationPair> elevBinList;
+   elevBinList elevBins;
+
+   // Used to determine how many of each type of obs we get
+   typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> rcpair;
+   typedef std::set<rcpair> rc_set;
+   typedef std::map<rc_set, unsigned long> ocm;
+   typedef std::map<elevationPair, ocm> ebocm;
+   ebocm whack;
+
+   // How many epochs that have the incorrect number of SVs in them.
+   unsigned long svCountErrorCount;
+
+   // the time of the first epoch processed and the last epoch processed
+   gpstk::DayTime firstObsTime, lastObsTime;
+   gpstk::DayTime firstPvtTime, lastPvtTime;
+   gpstk::DayTime firstNavTime, lastNavTime;
+
+   bool firstObs, firstPvt, firstNav, firstSelftest;
+   double obsRateEst, pvtRateEst;
+   gpstk::DayTime prevEpochTime;
+
+   static const int maxChannel=12;
+
+   // First time is of the first missed epoch, second time is the last missed epoch
+   // (i.e. first = previous + obsRateEst, second=current-ObsRateEst
+   typedef std::pair<gpstk::DayTime, gpstk::DayTime> DayTimePair;
+   typedef std::list<DayTimePair> DayTimePairList;
+   DayTimePairList epochGapList;
+   std::vector<DayTimePairList> chanGapList;
+
+   // This is used to record the previous obs on each channel
+   typedef std::vector<gpstk::MDPObsEpoch> ObsEpochVector;
+   ObsEpochVector prevObs;
+
+   gpstk::MDPPVTSolution prevPvt;
+   gpstk::MDPSelftestStatus prevSelftestStatus;
+
+   // A list of all messages that had an error decoding them...
+   typedef std::list<gpstk::MDPHeader> MDPList;
+   MDPList badMessages;
+
+};
+#endif
diff --git a/dev/apps/MDPtools/TrackProc.cpp b/dev/apps/MDPtools/TrackProc.cpp
new file mode 100644
index 0000000..99cfbda
--- /dev/null
+++ b/dev/apps/MDPtools/TrackProc.cpp
@@ -0,0 +1,142 @@
+#pragma ident "$Id$"
+
+
+/*
+  This intended to perform a quick summary/analysis of the data in a MDP file
+  or stream. The idea is teqc +meta or +mds with a little bit of +qc thrown
+  in for good measure
+*/
+
+#include "Geodetic.hpp"
+#include "TrackProc.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace gpstk::StringUtils;
+
+
+//-----------------------------------------------------------------------------
+MDPTrackProcessor::MDPTrackProcessor(gpstk::MDPStream& in, std::ofstream& out)
+   : MDPProcessor(in, out),
+   currCv(13), prevCv(13)
+{
+   timeFormat = "%02H:%02M:%04.1f";
+   for (int i=1; i<currCv.size(); i++)
+      currCv[i].prn=-1;
+   for (int i=1; i<prevCv.size(); i++)
+      prevCv[i].prn=-1;
+
+   obsOut = true;
+}
+
+
+MDPTrackProcessor::~MDPTrackProcessor()
+{}
+
+void MDPTrackProcessor::process(const gpstk::MDPObsEpoch& oe)
+{
+   if (oe.time != currTime)
+   {
+      printChanges();
+      prevTime = currTime;
+      currTime = oe.time;
+      prevCv = currCv;
+      for (int i=1; i<currCv.size(); i++)
+         currCv[i].prn=-1;
+   }
+
+   int prn=oe.prn;
+   int chan=oe.channel;
+
+   if (chan<1 || chan >12)
+      cout << "Bad channel" << endl, exit(-1);
+
+   // make a set of the obs that this epoch has
+   rc_set ccs;
+   currCv[chan].codes = "    ";
+   for (gpstk::MDPObsEpoch::ObsMap::const_iterator i = oe.obs.begin();
+        i != oe.obs.end(); i++)
+   {
+      const gpstk::MDPObsEpoch::Observation& obs=i->second;
+      rcpair rcPair(obs.range, obs.carrier);
+      ccs.insert(rcPair);
+      if (obs.carrier == ccL1)
+      {
+         if      (obs.range == rcCA)       currCv[chan].codes[0] = 'c';
+         if      (obs.range == rcPcode)    currCv[chan].codes[1] = 'p';
+         else if (obs.range == rcYcode)    currCv[chan].codes[1] = 'y';
+         else if (obs.range == rcCodeless) currCv[chan].codes[1] = 'z';
+      }
+      else if (obs.carrier == ccL2)
+      {
+         if      (obs.range == rcCM)       currCv[chan].codes[2] = 'm';
+         else if (obs.range == rcCL)       currCv[chan].codes[2] = 'l';
+         else if (obs.range == rcCMCL)     currCv[chan].codes[2] = 'x';
+         else if (obs.range == rcCA)       currCv[chan].codes[2] = 'c';
+         if      (obs.range == rcPcode)    currCv[chan].codes[3] = 'p';
+         else if (obs.range == rcYcode)    currCv[chan].codes[3] = 'y';
+         else if (obs.range == rcCodeless) currCv[chan].codes[3] = 'z';
+      }
+   }
+   currCv[chan].obs = ccs;
+   currCv[chan].prn = oe.prn;
+   currCv[chan].elevation = oe.elevation;
+}
+
+void MDPTrackProcessor::printChanges()
+{
+   if (verboseLevel)
+   {
+      // This is the one line per channel format.
+      for (int i = 1; i < currCv.size(); i++)
+      {
+         // This means that there has been a change in track
+         bool change = currCv[i].obs != prevCv[i].obs ||
+            currCv[i].prn != prevCv[i].prn ||
+            (prevCv[i].prn == -1 && currCv[i].prn == -1);
+         if (change)
+         {
+            if (prevCv[i].prn == -1 && currCv[i].prn == -1)
+               continue;
+            out << currTime.printf(timeFormat) << "  Ch:" << setw(2) <<  i;
+            if (currCv[i].prn >0)
+            {
+               out << "  Prn: " << setw(2) << currCv[i].prn
+                   << "  Elev: " << fixed <<  setprecision(1) << setw(4) 
+                   << currCv[i].elevation << " ";
+               const rc_set &ccs = currCv[i].obs;
+               for (rc_set::const_iterator j=ccs.begin(); j!=ccs.end(); j++)
+                  out << " (" << asString(j->second)
+                      << ", " << asString(j->first) << ")";
+            }
+            else
+            {
+               out << "  unused";
+            }
+            out << endl;
+         }
+      }
+   }
+   else
+   {
+      // This is the one line per epoch with changes
+      bool change=false;
+      for (int i = 1; i < currCv.size() && change==false; i++)
+         change = (currCv[i].obs != prevCv[i].obs ||
+                   currCv[i].prn != prevCv[i].prn) &&
+            (prevCv[i].prn != -1 || currCv[i].prn != -1);
+
+      if (change)
+      {
+         out << currTime.printf(timeFormat);
+         for (int i = 1; i < currCv.size(); i++)
+         {
+            if (currCv[i].prn >0)
+               out << setw(4) << currCv[i].prn << currCv[i].codes;
+            else
+               out << setw(4) << "  -" << "    ";
+         }
+         out << endl;
+      }
+   }
+}
diff --git a/dev/apps/MDPtools/TrackProc.hpp b/dev/apps/MDPtools/TrackProc.hpp
new file mode 100644
index 0000000..98450ad
--- /dev/null
+++ b/dev/apps/MDPtools/TrackProc.hpp
@@ -0,0 +1,37 @@
+#pragma ident "$Id$"
+
+
+#ifndef MDPTRACK_HPP
+#define MDPTRACK_HPP
+
+#include "MDPProcessors.hpp"
+
+//-----------------------------------------------------------------------------
+class MDPTrackProcessor : public MDPProcessor
+{
+public:
+   MDPTrackProcessor(gpstk::MDPStream& in, std::ofstream& out);
+   ~MDPTrackProcessor();
+
+   virtual void process(const gpstk::MDPObsEpoch& oe);
+   
+   // Used to determine how many of each type of obs we get
+   typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> rcpair;
+   typedef std::set<rcpair> rc_set;
+
+   struct ChanRec
+   {
+      int prn;
+      float elevation;
+      rc_set obs;
+      std::string codes;
+   };
+
+   // This is a list of what is being received for each channel
+   typedef std::vector<ChanRec> ChanVector;
+   ChanVector currCv, prevCv;
+   gpstk::DayTime currTime, prevTime;
+
+   void printChanges();
+};
+#endif
diff --git a/dev/apps/MDPtools/UniqueAlmStore.cpp b/dev/apps/MDPtools/UniqueAlmStore.cpp
new file mode 100644
index 0000000..a5c0519
--- /dev/null
+++ b/dev/apps/MDPtools/UniqueAlmStore.cpp
@@ -0,0 +1,406 @@
+#pragma ident "$Id$"
+
+
+//lgpl-license START
+//lgpl-license END
+
+//dod-release-statement START
+//dod-release-statement END
+
+/**
+ * @file UniqueAlmStore.cpp
+ */
+#include <iostream>   
+#include "UniqueAlmStore.hpp"
+
+#include "FICData162.hpp"
+#include "FICData62.hpp"
+#include "gps_constants.hpp"
+
+namespace gpstk
+{
+   using namespace std;
+   using namespace gpstk;
+
+      // Table 20-V from IS-GPS-200.  Negative numbers inidicate
+      // that the SVID given is nominal, but substitutions are allowe4. 
+   static short SVIDOrder[] = { 57,  1, 
+                                25,  2,
+                                26,  3,
+                                27,  4,
+                                28,  5,
+                                57,  6,
+                                29,  7,
+                                30,  8,
+                                31,  9,
+                                32, 10,
+                                57, 11,
+                                62, 12,
+                                52, 13,
+                                53, 14,
+                                54, 15,
+                                57, 16,
+                                55, 17,
+                                56, 18,
+                               -58, 19,
+                               -59, 20,
+                                57, 21,
+                               -60, 22,
+                               -61, 23,
+                                62, 24,
+                                63, 51 };
+   
+   UniqueAlmStore::UniqueAlmStore( NavIndex ni, NavCode nc )
+   {
+      state = WAITING;
+      prn = ni.second;
+      range = (ni.first).first;
+      navCode = nc;
+      startingSOW = -10;
+      candidateToa = -10;
+      written = false;
+      numPagesExamined = 0;
+      ToaTime = gpstk::DayTime::BEGINNING_OF_TIME;
+   }
+
+   pmCI UniqueAlmStore::begin() const { return(pageMap.begin()); }
+   pmCI UniqueAlmStore::end() const { return(pageMap.end()); } 
+
+   void UniqueAlmStore::newSubframe( gpstk::MDPNavSubframe nav )
+   {
+      numPagesExamined++;
+      
+         // Check parity
+      long sfa[10];
+      nav.fillArray(sfa);
+      uint32_t uint_sfa[10];
+      for (int j=0; j<10; ++j) uint_sfa[j] = static_cast<uint32_t>( sfa[j] );
+      if (!gpstk::EngNav::checkParity(uint_sfa)) 
+         return;
+         
+         // Pull the SVID and time from the subframe
+      short SVID = nav.getSVID();
+      short week = nav.time.GPSfullweek();
+      long sow = nav.getHOWTime();
+      if ( sow >604800)
+         return;
+
+      DayTime howTime(week, sow);
+
+      //if (nav.prn==1) cout << "state, SFID, SVID: " << state << ", " << nav.getSFID() << ", " << SVID;
+         // Definitions that appear to need to be outside the switch
+      short expectedSVID; 
+      bool optional;
+      bool storePage;
+      int newState = state;
+      switch (state)
+      {
+         // If WAITING, we're looking for SF 5, Page 25
+         // which is SVID 51.
+         case WAITING:
+            if (SVID!=51) break;
+            candidateToa = getToa(nav);
+            newState = START_ON_NEXT_FRAME;
+            if (nav.prn==1) cout << "State Change:START_ON_NEXT_FRAME" << endl;
+               // Clear the subframe map so it's ready to fill
+            pageMap.clear();
+            ToaTime = DayTime::BEGINNING_OF_TIME;
+            startingSOW = -10;
+            written = false;
+            break;
+            
+            // The previous SF5 was pg 25, SVID 51.
+            // The next almanac SF we see should be
+            // Pg 4, pg 1, SVID 57 with a time that's
+            // equal to an even 12.5 min (750 sec) epoch
+            // (lus an appropriate offset for the 3 ephemeris
+            // pages and the usual +6 HOW offset( 
+            // in terms of SOW.  If so, we've established
+            // sync with the almanac cycle and we should
+            // start collecting.
+         case START_ON_NEXT_FRAME:
+         {
+            if (nav.prn==1) cout << "Checking for start.  SFID, SVID = " << nav.getSFID() << ", " << SVID << endl;
+            if (nav.getSFID()!=4 ||
+                SVID!=57 ) { newState=WAITING; break; }
+            long test = nav.getHOWTime();
+            long remainder = test % ALMANAC_PERIOD;
+            if (remainder!=SF4_OFFSET) {newState=WAITING; break; }
+            // Appear to have sync, initialize collection
+            SVIDOrderNdx = 0; 
+            startingSOW = test;
+            newState = COLLECTING;
+            if (nav.prn==1) cout << "State Change:COLLECTING" << endl;
+            //break;            - NO BREAK, we WANT to drop through
+            //                    to COLLECTING and process this SF
+         }
+         
+         // If COLLECTING, we're 
+         case COLLECTING:
+            if (nav.prn==1) cout << "COLLECTING: SVID = : " << SVID << endl;
+            if (isToaPage(SVID))
+            {
+               if (candidateToa!=getToa(nav))
+               {
+                  newState = WAITING;          // Toa mismatch, restart
+                  if (nav.prn==1) cout << "State change: WAITING.  toa mismatch" << endl;
+                  break; 
+               }
+            }
+             
+              // Test the SVID
+            storePage = true;
+            expectedSVID = SVIDOrder[SVIDOrderNdx];
+            optional = false;
+            if (expectedSVID < 0)
+            {
+               optional = true;
+               expectedSVID = -1 * expectedSVID;
+            }
+               // Test if page is for unavailable SV
+            if (nav.getSVID()==0 &&
+               expectedSVID>1 && expectedSVID<=32)
+            {
+               storePage = false;
+            }
+               // A relaxed test if page is optional in the ordering
+            else if (optional &&
+                nav.getSVID()!=expectedSVID)
+            {
+               cerr << "WARNING: Expected SVID " << expectedSVID << 
+                       ", received SVID " << nav.getSVID() << 
+                       ". Continuing." << endl;
+            }
+            else if (expectedSVID!=nav.getSVID())
+            {
+               newState = WAITING;    // SVID  mismatch, restart 
+               if (nav.prn==1) cout << "State change: WAITING.  SVID out of order" << endl;
+               break; 
+            }
+            SVIDOrderNdx++;
+            
+              // All tests passed.  Store the page.
+            if (storePage) pageMap.insert( make_pair(expectedSVID,nav) );
+             
+              // If this is true, we've collected the final
+              // page of a possible set.  Perform some checks.  
+            if (expectedSVID==51)
+            {
+               if (nav.prn==1) cout << "Testing for end of cycle." << endl;
+                 // Check 12.5 min period
+                 // First page in set was a SF4, we're now
+                 // 24 frames later with a SF5.   
+                 //    (24 frames * 30s/frame) + 6s = 726s; 
+               long nowHOW = nav.getHOWTime();
+               long diff = nowHOW - startingSOW;
+               if (diff!= (ALMANAC_PERIOD+6-30) ) 
+               {
+                  newState = WAITING;   // Wrong time for cycle, restart
+                  if (nav.prn==1) cout << "State change: WAITING. Cycle time of " << diff << " incorrect" << endl;
+                  break;        
+               }
+               
+                  // Check for page completeness
+               if (!completeSetOfPages())
+               {
+                  newState = WAITING;     // Incomplete attempt, restart
+                  if (nav.prn==1) cout << "State change: WAITING. incomplete set of pages" << endl;
+                  break;
+               }
+                  
+                  // Set ToA time
+               short currentWeek = nav.time.GPSfullweek();
+               uint32_t word = nav.subframe[3];
+               word &= 0x00003FC0;
+               word >>= 6;
+               short toaWeek = fullWeekFrom8Bit( currentWeek, (short) word );
+               ToaTime = DayTime( toaWeek, candidateToa );
+               
+               newState = COMPLETE;
+               if (nav.prn==1) cout << "State Change:COMPLETE !!!" << endl;
+            }
+            break;
+      
+         // If COMPLETE, then we're
+         // watching the stream to see a change in the 
+         // Toa.  At that point, we flush the current 
+         // data and start all over again in WAITING.
+         case COMPLETE:
+            if (isToaPage(SVID))
+            {
+               if (candidateToa!=getToa(nav))
+               {
+                  newState = WAITING;             // New Toa, start a new collection
+                  if (nav.prn==1) cout << "State Change:WAITING. Found new Toa" << endl;
+               }
+            }
+            break;
+      }
+         // update the state
+      state = newState;
+      return;
+   }
+   
+   bool UniqueAlmStore::completeSetOfPages() const
+   {
+      int n;
+      
+         // SV IDs 51-57, 62, and 63 must be present
+      pmCI p;
+      for (n=51;n<57;++n)
+      {
+         p = pageMap.find(n);
+         if ( p==pageMap.end() ) return(false);
+      }
+      p = pageMap.find(62);
+      if ( p==pageMap.end() ) return(false);
+      p = pageMap.find(63);
+      if ( p==pageMap.end() ) return(false);
+      
+         // For SVID 1-32, should have a page for every
+         // SV where health is not 0x3F.
+         // First unpack the health for all 32 SVs 
+         // (we already proved we have the health pages).
+      short tempHealth[gpstk::MAX_PRN+2];   // a. want to index 1-32
+      short SVndx = 0;                      // b. there's a "don't care" 33
+      
+         // SF5, pg 25, SVID 51.  Health for PRNID 1-24
+      p = pageMap.find(51); 
+      const MDPNavSubframe& nav51 = p->second;
+      uint32_t word;
+      uint32_t h;
+      for (n=4;n<=9;++n)
+      {
+         word = nav51.subframe[n];
+         word >>= 6;                         // Remove parity
+         for (int bndx=4;bndx>=1;--bndx)
+         {
+            h = word & 0x0000003F;
+            int pndx = SVndx+bndx;
+            tempHealth[pndx] = (short) h;
+            word >>= 6;
+         }
+         SVndx += 4;
+      }
+      
+         // SF4, pg 25, SVID 63.  Health for PRNID 25-32
+      p = pageMap.find(63); 
+      const MDPNavSubframe& nav63 = p->second;
+      
+         // PRN 25 is all by itself
+      word = nav63.subframe[8];
+      word >>= 6;
+      h = word & 0x0000003F;
+      tempHealth[SVndx++] = (short) h;
+      
+         // PRN 26-32 (plus a bogus extra)
+      for (n=9;n<=10;++n)
+      {
+         word = nav63.subframe[n];
+         word >>= 6;                         // Remove parity
+         for (int bndx=4;bndx>=1;--bndx)
+         {
+            h = word & 0x0000003F;
+            int pndx = SVndx+bndx;
+            tempHealth[pndx] = (short) h;
+            word >>= 6;
+        }
+         SVndx += 4;
+      }
+      
+      //debug
+      //for (n=1;n<=gpstk::MAX_PRN;++n)
+      //{
+      //   cout << n << ":" << tempHealth[n] << ",  ";
+      //   if (n%6==0) cout << endl;
+      //}
+      
+         // Now perform the checks
+      bool tripwire = false;
+      for (n=1;n<=gpstk::MAX_PRN;++n)
+      {
+         if (tempHealth[n]!=DEAD_HEALTH && pageMap.find(n)==pageMap.end())
+         {
+            //cout << "Health = " << tempHealth[n] << " for PRNID " << n << " and no page found. " << endl;
+            tripwire = true;
+         }
+      }
+      if (tripwire) return(false);
+      return(true);
+   }
+   
+   short UniqueAlmStore::fullWeekFrom8Bit( const short full, const short eightBit )
+   {
+      short curr8bitWeek = full & 0x00FF;
+      short diff = curr8bitWeek - eightBit;
+      short retArg = eightBit;
+      short upperBits = full & 0xFF00; 
+      if (diff>HALF_8BITS) upperBits -= 0x0100;
+      else if (diff<-HALF_8BITS) upperBits += 0x0100;
+      retArg = upperBits | eightBit;
+      return(retArg);
+   }
+   
+   bool UniqueAlmStore::isToaPage( const short SVID ) 
+   {
+      if (SVID>=1 && SVID<=38) return(true);
+      if (SVID==51) return(true);
+      return(false);
+   }
+
+   long UniqueAlmStore::getToa( const MDPNavSubframe& nav )
+   {
+      uint32_t word;
+      long retToa = -1;
+      short SVID = nav.getSVID();
+      if (SVID>=1 && SVID<=38)
+      {
+         word = nav.subframe[4];
+         word &= 0x3FFFFFFF;
+         word >>= 22;
+         retToa = (long) word;
+      }
+      else if (SVID==51)
+      {
+         word = nav.subframe[3];
+         word &= 0x003FC000;
+         word >>= 14;
+         retToa = (long) word;
+      }
+      return(retToa);
+   }
+   
+   bool UniqueAlmStore::readyToWrite() const
+   {
+      if (state==COMPLETE && !written) return(true);
+      return(false);
+   }
+   
+   void UniqueAlmStore::write(gpstk::FICStream& out)
+   {
+         // Cycle through complete almanac
+         // For each page, convert to 162, output 162, 
+         //      convert to 62, output 62
+         // NOTE: This would be a BAD IDEA for a real-time
+         // implementation.  In such a system, the converts
+         // would be better spaced out as the pages are collected;
+      for (pmCI p1=pageMap.begin();p1!=pageMap.end();++p1)
+      {
+         const MDPNavSubframe& nav = p1->second;
+         FICData162 new162( nav.prn,
+                            nav.getSVID(),
+                            nav.time.GPSfullweek(),                            
+                            ToaTime.GPSfullweek(),
+                            nav.subframe);
+         FICData62 new62( new162);
+         out << new162;
+         out << new62;
+      }
+        
+         // Set the flag so we don't keep writing the same data 
+         // over and over.
+      written = true;
+   }
+   
+   
+}   // namespace
diff --git a/dev/apps/MDPtools/UniqueAlmStore.hpp b/dev/apps/MDPtools/UniqueAlmStore.hpp
new file mode 100644
index 0000000..d8c296e
--- /dev/null
+++ b/dev/apps/MDPtools/UniqueAlmStore.hpp
@@ -0,0 +1,93 @@
+#pragma ident "$Id$"
+
+
+/**
+ * @file UniqueAlmStore.hpp
+ *
+ *  Almanac uniqueness has always be a challenge.  Here's the working definition
+ *  for this module:
+ *    1. Collected within a single 12.5 min cycle
+ *    2. Collected from the same PRN
+ *    3. SF5,Pg25 - complete cycle - SF4,Pg1 all must have the same Toa
+ *    4. SVID available for each PRN for which almanac health is other than
+ *          "6 ones"  (see IS-GPS-200, 20.3.????)
+ */
+
+#ifndef GPSTK_UNIQUEALMSTORE_HPP
+#define GPSTK_UNIQUEALMSTORE_HPP
+
+//lgpl-license START
+//lgpl-license END
+
+//dod-release-statement START
+//dod-release-statement END
+#include "FICStream.hpp"
+#include "DayTime.hpp"
+#include "EngAlmanac.hpp"
+
+   // Project
+#include "miscdefs.hpp"
+#include "MDPNavSubframe.hpp"
+
+namespace gpstk
+{
+      //  The int in the following map is the SVID
+      //  The MDPNavSubframe is the corresponding almanac page
+   typedef std::map <short,gpstk::MDPNavSubframe> PageMap;
+   typedef PageMap::iterator pmI;
+   typedef PageMap::const_iterator pmCI;
+   
+   class UniqueAlmStore
+   {
+   public:
+         /// Default constructor
+      UniqueAlmStore( gpstk::NavIndex ni, gpstk::NavCode nc );
+      
+         /// Destructor
+      virtual ~UniqueAlmStore( ) {}
+
+      pmCI begin() const;
+      pmCI end() const; 
+      void newSubframe( gpstk::MDPNavSubframe sf );
+      bool readyToWrite() const;
+      void write(gpstk::FICStream& out);
+      
+   protected:
+      bool completeSetOfPages() const;
+      short fullWeekFrom8Bit( const short full, const short eightBit );
+      bool isToaPage( const short SVID );
+      long getToa( const MDPNavSubframe& nav );
+   
+      int state;              /// Current state of the collection process
+                              ///    See table of static const below.
+      bool written;           /// Has this almanac been written out since last collection?
+      long numPagesExamined;
+      
+      int prn;                /// < The SV's PRN
+      CarrierCode carrier;    /// < This almanac's carrier frequency code
+      RangeCode range;        /// < This alamanc's range code
+      NavCode navCode;            /// < This almanac's nav code
+      
+      short SVIDOrderNdx;
+      
+      long startingSOW;
+      long candidateToa;
+      gpstk::DayTime ToaTime;    // Not valid until state==COMPLETE | WRITTEN
+      PageMap pageMap; 
+      
+         // Possible values for member state
+      static const int WAITING = 0;
+      static const int START_ON_NEXT_FRAME = 3;
+      static const int COLLECTING = 1;
+      static const int COMPLETE = 2;
+      
+         // Other useful "magic numbers"
+      static const long ALMANAC_PERIOD = 750;      // 12.5 minutes in seconds
+      static const long SF4_OFFSET = 24;
+      static const short HALF_8BITS = 128;
+      static const short DEAD_HEALTH = 0x3F;
+      
+   }; // class UniqueAlmStore
+} // namespace
+
+#endif
diff --git a/dev/apps/MDPtools/mdp2fic.cpp b/dev/apps/MDPtools/mdp2fic.cpp
new file mode 100644
index 0000000..f73947b
--- /dev/null
+++ b/dev/apps/MDPtools/mdp2fic.cpp
@@ -0,0 +1,450 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/** @file Converts an MDP stream into FIC nav file */
+
+#include "StringUtils.hpp"
+#include "LoopedFramework.hpp"
+
+#include "FICStream.hpp"
+#include "FICHeader.hpp"
+#include "FICData.hpp"
+
+#include "MDPStream.hpp"
+#include "MDPNavSubframe.hpp"
+#include "MDPObsEpoch.hpp"
+#include "FICData109.hpp"
+#include "FICData9.hpp"
+#include "BELogEntry.hpp"
+#include "UniqueAlmStore.hpp"
+#include "miscdefs.hpp"
+
+#include "FormatConversionFunctions.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+
+class MDP2FIC : public gpstk::LoopedFramework
+{
+public:
+   MDP2FIC(const std::string& applName)
+      throw()
+      : LoopedFramework(applName, "Converts an MDP stream to FIC."),
+        navFileOpt('n', "nav",   "Filename to which FIC nav data will be written.", true),
+        mdpFileOpt('i', "mdp-input", "Filename to read MDP data from. The filename of '-' means to use stdin.", true),
+        logFileOpt('l', "log", "Filename for (optional) output log file",false)
+   {
+      navFileOpt.setMaxCount(1);
+      mdpFileOpt.setMaxCount(1);
+   }
+
+   bool initialize(int argc, char *argv[]) throw()
+   {
+      if (!LoopedFramework::initialize(argc,argv)) return false;
+
+      if (mdpFileOpt.getCount())
+         if (mdpFileOpt.getValue()[0] != "-")
+            mdpInput.open(mdpFileOpt.getValue()[0].c_str());
+         else
+         {
+            
+            if (debugLevel)
+               cout << "Taking input from stdin." << endl;
+            mdpInput.copyfmt(std::cin);
+            mdpInput.clear(std::cin.rdstate());
+            mdpInput.std::basic_ios<char>::rdbuf(std::cin.rdbuf());
+         }
+
+      if (navFileOpt.getCount())
+         FICOutput.open(navFileOpt.getValue()[0].c_str(), std::ios::out);
+      else
+         FICOutput.clear(std::ios::badbit);
+      
+      logActive = false;
+      if (logFileOpt.getCount())
+      {
+         logfp = fopen( logFileOpt.getValue()[0].c_str(), "wt");
+         if (logfp!=0) logActive = true;
+          else cout << "Log file open failed.  Continuing" << endl;
+      }
+      obsCount = 0;
+      firstObs = true;
+      numSubframesCollected = 0;
+      paritySuccessCount = 0;
+      parityFailCount = 0;
+      firstNavSF = true;
+      earliestTime = gpstk::DayTime::BEGINNING_OF_TIME;
+      latestTime = gpstk::DayTime::END_OF_TIME;
+      
+      debugCount = 0;
+      
+      return true;
+   }
+   
+protected:
+   virtual void spinUp()
+   {
+      if (!mdpInput)
+      {
+         cout << "Error: could not open input." << endl;
+         exit(-1);
+      }
+
+      mdpInput.exceptions(fstream::failbit);
+
+      if (FICOutput)
+         FICOutput.exceptions(fstream::failbit);
+      
+      DayTime timeNow;
+      std::string timeStr = timeNow.printf("%02H:%02M, %02m/%02d/%02y");
+      fich.header = "Generated by mdp2fic on " + timeStr;
+
+      if (FICOutput)
+         FICOutput << fich;
+   }
+
+   virtual void process(MDPNavSubframe& nav)
+   {
+      short sfid = nav.getSFID();
+         // For now, only consider SF 1-3 (ephemeris).  
+         // Ignore the almanac 
+      if (sfid > 3) processSubframes4and5(nav);
+       else  processSubframes1to3(nav);
+      return;
+   }
+
+   void processSubframes4and5(MDPNavSubframe& nav)
+   {
+         // For now, just look at L1 C/A navigation message
+      if (nav.range != rcCA || nav.carrier != ccL1)
+         return;
+      
+         // Pull the time from the subframe
+      short week = nav.time.GPSfullweek();
+      long sow = nav.getHOWTime();
+      if ( sow >604800)
+         return;
+
+      DayTime howTime(week, sow);
+
+      NavIndex ni(RangeCarrierPair(nav.range, nav.carrier), nav.prn);
+      
+      AlmMap::iterator a;
+      a = almData.find(ni);
+      if (a==almData.end())
+      {
+         UniqueAlmStore init( ni, nav.nav );
+         //pair<NavIndex,gpstk::UniqueAlmStore> = node(ni,init);
+         almData.insert( make_pair(ni,init) );
+         a = almData.find(ni);
+         if (a==almData.end())
+         {
+            cerr << "Almanac map insertion failed in mdp2fic.processSubframes4and5." << endl;
+            exit(1);
+         }
+         cout << "Inserted a new almanac map for PRN " << nav.prn << endl;
+      }
+      UniqueAlmStore& uas = a->second;
+      uas.newSubframe(nav);
+      if (uas.readyToWrite()) uas.write( FICOutput );
+   }
+   
+   void processSubframes1to3(MDPNavSubframe& nav)
+   {
+         // For now, just look at L1 C/A navigation message
+      if (nav.range != rcCA || nav.carrier != ccL1)
+         return;
+
+      NavIndex ni(RangeCarrierPair(nav.range, nav.carrier), nav.prn);
+      ephData[ni] = nav;
+
+      long sfa[10];
+      nav.fillArray(sfa);
+      uint32_t uint_sfa[10];
+ 
+      for( int j = 0; j < 10; j++ )
+         uint_sfa[j] = static_cast<uint32_t>( sfa[j] );
+
+      numSubframesCollected++;
+      if (gpstk::EngNav::checkParity(uint_sfa))
+      {
+         paritySuccessCount++;
+         ephPageStore[ni][nav.getSFID()] = nav;
+         EngEphemeris engEph;
+         if (makeEngEphemeris(engEph, ephPageStore[ni]))
+         {
+            currentPRN = engEph.getPRNID();     // debug
+            if (firstNavSF) 
+            {
+               earliestTime = engEph.getTransmitTime();
+               firstNavSF = false;
+            }
+            latestTime = engEph.getTransmitTime();
+            processEphemeris( engEph, ephPageStore[ni] );
+         }
+      }
+      else parityFailCount++;
+   } // end of process(MDPNavSubframe)
+
+   virtual void process()
+   {
+      MDPHeader header;
+      MDPNavSubframe nav;
+      MDPObsEpoch obs;
+
+         // Ought to be able to catch EOF here....
+      try
+      {
+         mdpInput >> header;
+         switch (header.id)
+         {
+            case MDPNavSubframe::myId :
+               mdpInput >> nav;
+               process(nav);
+               break;
+            
+            case MDPObsEpoch::myId :
+               mdpInput >> obs;
+               obsCount++;
+               if (debugLevel && (obsCount % 1000)==0) cout << "obsCount: " << obsCount << endl;
+               break;
+         }
+      }
+      catch (gpstk::Exception &exc)
+      { 
+         cout << "Caught a GPSTk Exception in process()." << endl;
+         cout << exc << endl; 
+         timeToDie = true;
+         return;
+      }  
+      catch (std::exception &exc)
+      {
+         cout << "Trapped an exception in process()." << endl;
+         timeToDie = true;
+         return;
+      }
+      catch (...)
+      {
+         cout << "I don't know HOW we got here, but we caught an unexpcted exception." << endl;
+         timeToDie = true;
+         return;
+      }
+      timeToDie = !mdpInput;
+   }
+
+   virtual void shutDown()
+   {
+      cout << "Entering shutDown()." << endl;
+      writeLogFile( );
+   }
+   
+   void writeLogFile( )
+   {
+      typedef PrnBELogMap::const_iterator ciPRN;
+      if (logActive)
+      {
+         std::string timestring = "%02m/%02d/%02y %03j %02H:%02M:%02S, GPS Week %F, SOW %6.0g";
+         fprintf(logfp,"Output log from mdp2fic.\n");
+         fprintf(logfp,"Earliest Transmit Time: %s\n",earliestTime.printf(timestring).c_str());
+         fprintf(logfp,"Latest Transmit Time  : %s\n",latestTime.printf(timestring).c_str());
+         fprintf(logfp,"Statistics on parity checks\n");
+         fprintf(logfp,"Total number of subframes processed: %7d\n",numSubframesCollected);
+         fprintf(logfp,"Number of successful parity checks : %7d\n",paritySuccessCount);
+         fprintf(logfp,"Number of failed parity chekcs     : %7d\n",parityFailCount);
+         double perCentFail = (parityFailCount *100.0) / numSubframesCollected;
+         fprintf(logfp,"Percent of subframes failing parity: %7.2lf\n",perCentFail);
+ 
+         ciPRN pp;
+         for (pp=prnBEmap.begin();pp!=prnBEmap.end();++pp)
+         {
+            int prnID = pp->first;
+            const BELogMap& blm = pp->second;
+            int numEntries = blm.size();
+            typedef BELogMap::const_iterator ciBLM;
+            fprintf(logfp,"\nSummary of Broadcast Ephemerides for PRN %02d\n",prnID);
+            fprintf(logfp,"%d unique ephemerides found.\n",numEntries);
+            fprintf(logfp,"%s\n",BELogEntry::header.c_str());
+            
+               // NOTE: The table is stored in the wrong order for output.
+               // I had to use the Toe in the key for uniqueness, however,
+               // I want the ending table ordered by earliest HOW.  HOW is 
+               // in the object, so now that we have a unique list, it can
+               // be used to re-order a new map
+            std::map<double,BELogEntry> reorder;
+            for (ciBLM bp=blm.begin();bp!=blm.end();++bp)
+            {
+               const BELogEntry& ble = bp->second;
+               double HOW = ble.getHOW().GPSsow();
+               pair<double,BELogEntry> node(HOW,ble);
+               reorder.insert(node);
+            }
+            typedef std::map<double,BELogEntry>::const_iterator rei;
+            for (rei rp=reorder.begin();rp!=reorder.end();++rp)
+            {
+               const BELogEntry& bler = rp->second;
+               fprintf(logfp,"%s\n", bler.getStr().c_str() );
+            }
+         }
+         fclose(logfp);
+      }
+   }
+
+   void processEphemeris( gpstk::EngEphemeris engEph, 
+                          gpstk::EphemerisPages ephPages )
+   {
+
+         // Construct a BELogEntry and see if it already exists in the 
+         // map for the PRN.  If it does, we already stored this one, 
+         // move on.  If not, convert the information to Block 109 and 
+         // Block 9 and write it out.               
+      BELogEntry curBELog( engEph );
+      unsigned long key = curBELog.getKey();
+      pair<long,BELogEntry> qnode(key,curBELog);
+
+      bool needToOutput = false;
+      PrnBELogMap::iterator pmap = prnBEmap.find( engEph.getPRNID() );
+            
+          // May need to add this PRN to the map.
+      if (pmap==prnBEmap.end())
+      {
+         pair<long,BELogEntry> qnode(key,curBELog);
+         BELogMap blm;
+         blm.insert(qnode);
+         pair<int,BELogMap> pnode( (int) engEph.getPRNID(), blm);
+         prnBEmap.insert( pnode );
+         needToOutput = true;
+      }
+      else
+      {
+         BELogMap& blmr = pmap->second;
+         BELogMap::iterator iBLM = blmr.find( key );
+         if (iBLM==blmr.end())
+         {
+            pair<long,BELogEntry> qnode(key,curBELog);
+            blmr.insert(qnode);
+            needToOutput = true;
+         }
+         else
+         {
+            BELogEntry& ble = iBLM->second;
+            ble.increment();
+         }
+      }
+      
+      if (needToOutput)
+      {
+         EphemerisPages::const_iterator MDPsf[4];
+         MDPsf[1] = ephPages.find(1);
+         MDPsf[2] = ephPages.find(2);
+         MDPsf[3] = ephPages.find(3);
+         FICData109 new109( engEph.getPRNID(),
+                            MDPsf[1]->second.subframe,
+                            MDPsf[2]->second.subframe,
+                            MDPsf[3]->second.subframe );
+         FICData9   new9( new109, engEph );
+         FICOutput << new109;
+         FICOutput << new9;
+      }
+   }
+   
+private:
+   gpstk::FICHeader fich;
+   MDPStream mdpInput;
+   FICStream FICOutput;
+   MDPEpoch epoch;
+   
+   long obsCount;
+   
+      // Defs and maps related to ephemeris handling
+   //typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> RangeCarrierPair;
+   //typedef std::pair<RangeCarrierPair, short> NavIndex;
+   typedef std::map<NavIndex, gpstk::MDPNavSubframe> NavMap;
+   NavMap ephData;
+   std::map<NavIndex, gpstk::EphemerisPages> ephPageStore;
+   std::map<NavIndex, gpstk::EngEphemeris> ephStore;
+   
+      // Ordered list of BELogEntries   
+   typedef std::map<long,BELogEntry> BELogMap;
+   
+      // For each PRN, there is a map pointing to the BE logs for that SV
+   typedef std::map<int, BELogMap> PrnBELogMap;
+   PrnBELogMap prnBEmap;
+
+      // Def and maps related to almanac handling
+   typedef std::map<NavIndex, gpstk::UniqueAlmStore> AlmMap;
+   AlmMap almData;
+
+      // Output file
+   FILE *logfp;
+   bool logActive;
+   
+      //debug
+   int currentPRN;
+   int debugCount;
+   
+   long numSubframesCollected;
+   long paritySuccessCount;
+   long parityFailCount;
+   bool firstNavSF;
+   gpstk::DayTime earliestTime;
+   gpstk::DayTime latestTime;
+   
+   bool firstObs;
+   gpstk::DayTime prevTime;
+   gpstk::CommandOptionWithAnyArg mdpFileOpt, navFileOpt, logFileOpt;
+};
+
+
+int main(int argc, char *argv[])
+{
+   try
+   {
+      MDP2FIC crap(argv[0]);
+      if (!crap.initialize(argc, argv))
+         exit(0);
+
+      crap.run();
+   }
+   catch (gpstk::Exception &exc)
+   { cout << exc << endl; }
+   catch (std::exception &exc)
+   { cout << "Caught std::exception " << exc.what() << endl;  }
+   catch (...)
+   { cout << "Caught unknown exception" << endl; }
+}
diff --git a/dev/apps/MDPtools/mdp2rinex.cpp b/dev/apps/MDPtools/mdp2rinex.cpp
new file mode 100644
index 0000000..7caba26
--- /dev/null
+++ b/dev/apps/MDPtools/mdp2rinex.cpp
@@ -0,0 +1,355 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/** @file Converts an MDP stream into RINEX obs/nav files */
+
+#include "StringUtils.hpp"
+#include "LoopedFramework.hpp"
+
+#include "RinexObsStream.hpp"
+#include "RinexObsData.hpp"
+#include "RinexNavStream.hpp"
+#include "RinexNavData.hpp"
+
+#include "MDPStream.hpp"
+#include "MDPNavSubframe.hpp"
+#include "MDPObsEpoch.hpp"
+
+#include "RinexConverters.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+
+class MDP2Rinex : public gpstk::LoopedFramework
+{
+public:
+   MDP2Rinex(const std::string& applName)
+      throw()
+      : LoopedFramework(applName, "Converts an MDP stream to RINEX."),
+        obsFileOpt('o', "obs",   "Filename to write RINEX obs data to. The filename of '-' means to use stdout.", true),
+        navFileOpt('n', "nav",   "Filename to write RINEX nav data to."),
+        mdpFileOpt('i', "mdp-input", "Filename to read MDP data from. The filename of '-' means to use stdin.", true),
+        c2Opt('c', "l2c", "Enable output of L2C data in C2"),
+        antPosOpt('p',"pos", "Antenna position to write into obs file header.  Format as string: \"X Y Z\"."),
+        thinningOpt('t', "thinning", "A thinning factor for the data, specified in seconds between points. Default: none.")
+   {
+      navFileOpt.setMaxCount(1);
+      obsFileOpt.setMaxCount(1);
+      mdpFileOpt.setMaxCount(1);
+      antPosOpt.setMaxCount(1);
+   } //MDP2Rinex::MDP2Rinex()
+
+   bool initialize(int argc, char *argv[]) throw()
+   {
+      if (!LoopedFramework::initialize(argc,argv)) return false;
+
+      if (mdpFileOpt.getCount())
+         if (mdpFileOpt.getValue()[0] != "-")
+            mdpInput.open(mdpFileOpt.getValue()[0].c_str());
+         else
+         {
+            if (debugLevel)
+               cout << "Taking input from stdin." << endl;
+            mdpInput.copyfmt(std::cin);
+            mdpInput.clear(std::cin.rdstate());
+            mdpInput.basic_ios<char>::rdbuf(std::cin.rdbuf());
+         }
+      
+
+      if (obsFileOpt.getCount())
+         if (obsFileOpt.getValue()[0] != "-")
+            rinexObsOutput.open(obsFileOpt.getValue()[0].c_str(), std::ios::out);
+         else
+         {
+            if (debugLevel)
+               cout << "Sending output to stdout." << endl;
+            rinexObsOutput.copyfmt(std::cout);
+            rinexObsOutput.clear(std::cout.rdstate());
+            rinexObsOutput.basic_ios<char>::rdbuf(std::cout.rdbuf());
+         }
+
+      if (navFileOpt.getCount())
+         rinexNavOutput.open(navFileOpt.getValue()[0].c_str(), std::ios::out);
+      else
+         rinexNavOutput.clear(std::ios::badbit);
+
+      if (thinningOpt.getCount())
+      {
+         thin = true;
+         thinning = gpstk::StringUtils::asInt(thinningOpt.getValue()[0]);
+         if (debugLevel)
+            cout << "Thinning data modulo " << thinning << " seconds." << endl;
+      }
+      else
+         thin = false;
+
+      firstObs = true;;
+      firstEph = true;
+
+      MDPHeader::debugLevel = debugLevel;
+
+      return true;
+   } // MDP2Rinex::initialize()
+   
+protected:
+   virtual void spinUp()
+   {
+      if (!mdpInput)
+      {
+         cout << "Error: could not open input." << endl;
+         exit(-1);
+      }
+
+      mdpInput.exceptions(fstream::failbit);
+      rinexObsOutput.exceptions(fstream::failbit);
+      if (rinexNavOutput)
+         rinexNavOutput.exceptions(fstream::failbit);
+      
+      roh.valid |= gpstk::RinexObsHeader::allValid21;
+      roh.fileType = "Observation";
+      roh.fileProgram = "mdp2rinex";
+      roh.markerName = "Unknown";
+      roh.observer = "Unknown";
+      roh.agency = "Unknown";
+      roh.antennaOffset = gpstk::Triple(0,0,0);
+      //roh.antennaPosition = gpstk::Triple(0,0,0);
+      roh.wavelengthFactor[0] = 1;
+      roh.wavelengthFactor[1] = 1;
+      roh.recType = "Unknown MDP";
+      roh.recVers = "Unknown";
+      roh.recNo = "1";
+      roh.antType = "Unknown";
+      roh.antNo = "1";
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::C1);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::P1);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::L1);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::D1);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::S1);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::P2);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::L2);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::D2);
+      roh.obsTypeList.push_back(gpstk::RinexObsHeader::S2);
+      if (antPosOpt.getCount())
+      {
+        double x, y, z;
+        sscanf(antPosOpt.getValue().front().c_str(),"%lf %lf %lf", &x, &y, &z);
+        antPos = gpstk::Triple(x,y,z);
+      }
+      else
+        antPos = gpstk::Triple(0,0,0);
+        
+      roh.antennaPosition = antPos;
+        
+      if (c2Opt.getCount())
+         roh.obsTypeList.push_back(gpstk::RinexObsHeader::C2);
+
+      rnh.valid = gpstk::RinexNavHeader::allValid21;
+      rnh.fileType = "Navigation";
+      rnh.fileProgram = "mdp2rinex";
+      rnh.fileAgency = "Unknown";
+      rnh.version = 2.1;
+   } // MDP2Rinex::spinUp()
+
+
+   virtual void process(MDPNavSubframe& nav)
+   {
+      if (!rinexNavOutput)
+         return;
+
+      if (firstEph)
+      {
+         rinexNavOutput << rnh;
+         cout << "Got first nav SF" << endl;
+      }
+
+      firstEph=false;
+
+      nav.cookSubframe();
+      if (!nav.checkParity())
+         return;
+
+      short sfid = nav.getSFID();
+      if (sfid > 3)
+         return;
+
+      short week = nav.time.GPSfullweek();
+      long sow = nav.getHOWTime();
+      if ( sow >604800)
+         return;
+
+      DayTime howTime(week, sow);
+
+      if (nav.range != rcCA || nav.carrier != ccL1)
+         return;
+
+      NavIndex ni(RangeCarrierPair(nav.range, nav.carrier), nav.prn);
+      ephData[ni] = nav;
+
+      ephPageStore[ni][sfid] = nav;
+      EngEphemeris engEph;
+      if (makeEngEphemeris(engEph, ephPageStore[ni]))
+      {
+         RinexNavData rnd(engEph);
+         rinexNavOutput << rnd;
+         ephPageStore[ni].clear();
+      }
+   } // MDP2Rinex::process(MDPNavSubframe)
+
+
+   virtual void process(MDPObsEpoch& obs)
+   {
+      if (!rinexObsOutput)
+         return;
+
+      const DayTime& t=epoch.begin()->second.time;
+
+      if (!firstObs && t<prevTime)
+      {
+         if (debugLevel)
+            cout << "Out of order data at " << t << endl;
+         return;
+      }
+
+      if (epoch.size() > 0 && t != obs.time)
+      {
+         if (!thin || (static_cast<int>(t.DOYsecond()) % thinning) == 0)
+         {
+            if (firstObs)
+            {
+               roh.firstObs = t;
+               rinexObsOutput << roh;
+               firstObs=false;
+               if (debugLevel)
+                  cout << "Got first obs" << endl;
+            }
+
+            RinexObsData rod;
+            rod = makeRinexObsData(epoch);
+            rinexObsOutput << rod;
+         }
+         epoch.clear();
+         prevTime = t;
+      }
+      epoch[obs.prn] = obs;
+   } // MDP2Rinex::process(MDPObsEpoch)
+
+
+   virtual void process()
+   {
+      MDPHeader header;
+      MDPNavSubframe nav;
+      MDPObsEpoch obs;
+
+      try 
+      {
+         mdpInput >> header;
+         switch (header.id)
+         {
+            case MDPNavSubframe::myId :
+               mdpInput >> nav;
+               if (nav.rdstate())
+                  cout << "Error decoding nav " << nav.rdstate() << endl;
+               else
+                  process(nav);
+               break;
+            
+            case MDPObsEpoch::myId :
+               mdpInput >> obs;
+               if (obs.rdstate())
+                  cout << "Error decoding obs " << obs.rdstate() << endl;
+               else
+                  process(obs);
+               break;
+         }
+      }
+      catch (EndOfFile& e)
+      {
+         if (debugLevel)
+            cout << e << endl;
+         timeToDie = true;
+      }
+      timeToDie |= !mdpInput;
+   } // MDP2Rinex::process()
+
+   virtual void shutDown()
+   {}
+
+private:
+   gpstk::RinexObsHeader roh;
+   gpstk::RinexNavHeader rnh;
+   MDPStream mdpInput;
+   RinexObsStream rinexObsOutput;
+   RinexNavStream rinexNavOutput;
+   MDPEpoch epoch;
+
+   typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> RangeCarrierPair;
+   typedef std::pair<RangeCarrierPair, short> NavIndex;
+   typedef std::map<NavIndex, gpstk::MDPNavSubframe> NavMap;
+   NavMap ephData;
+   std::map<NavIndex, gpstk::EphemerisPages> ephPageStore;
+   std::map<NavIndex, gpstk::EngEphemeris> ephStore;
+
+   bool thin;
+   int thinning;
+   bool firstObs, firstEph;
+   gpstk::DayTime prevTime;
+   gpstk::Triple antPos;
+   gpstk::CommandOptionWithAnyArg mdpFileOpt, navFileOpt, obsFileOpt;
+   gpstk::CommandOptionWithAnyArg thinningOpt, antPosOpt, c2Opt;
+};
+
+
+int main(int argc, char *argv[])
+{
+   try
+   {
+      MDP2Rinex crap(argv[0]);
+
+      if (!crap.initialize(argc, argv))
+         exit(0);
+
+      crap.run();
+   }
+   catch (gpstk::Exception &exc)
+   { cout << exc << endl; }
+   catch (std::exception &exc)
+   { cout << "Caught std::exception " << exc.what() << endl; }
+   catch (...)
+   { cout << "Caught unknown exception" << endl; }
+}
diff --git a/dev/apps/MDPtools/mdpscreen/Jamfile b/dev/apps/MDPtools/mdpscreen/Jamfile
new file mode 100644
index 0000000..3afbf0a
--- /dev/null
+++ b/dev/apps/MDPtools/mdpscreen/Jamfile
@@ -0,0 +1,11 @@
+# $Id$
+
+SubDir TOP apps MDPtools mdpscreen ;
+
+LINKLIBS += -lncurses ;
+
+SEARCH_SOURCE += $(TOP)/apps/MDPtools ;
+
+GPSLinkLibraries mdpscreen : gpstk rxio ;
+
+GPSMain mdpscreen : mdpscreen.cpp MDPProcessors.cpp ScreenProc.cpp ;
diff --git a/dev/apps/MDPtools/mdpscreen/ScreenProc.cpp b/dev/apps/MDPtools/mdpscreen/ScreenProc.cpp
new file mode 100644
index 0000000..8c0fbfe
--- /dev/null
+++ b/dev/apps/MDPtools/mdpscreen/ScreenProc.cpp
@@ -0,0 +1,436 @@
+#pragma ident "$Id$"
+
+
+#include "Geodetic.hpp"
+#include "GPSGeoid.hpp"
+#include "StringUtils.hpp"
+
+#include "ScreenProc.hpp"
+#include "RinexConverters.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace gpstk::StringUtils;
+
+extern "C" void wench(int sig);
+
+/*           1         2         3         4         5         6         7
+   01234567890123456789012345678901234567890123456789012345678901234567890123456789
+  |--------------------------------------------------------------------------------
+ 0|hostname:port                                        02:43:12  7/7/06 GPS
+ 1|                   
+ 2|PVT: 02:43:23.5   Offset: 234,456.1 ns  Drift: 13.235 ns/d
+ 3|Lon: -179.122345   Lat: 30.12345 N   Ht: 230.0 m      Rate: 1.0 s
+ 4| Vx:  0.0 cm/s      Vy:  0.0 cm/s    Vz:  0.00 cm/s    FOM: 12.2
+ 5| Trx: 28 C   ExtFreq: Unlocked    StartTime: 12:34 6/2/06   SSW: 311
+ 6|Tant: 39 C  Selftest: 0x000001     TestTime: 02:30 7/7/06
+ 7|
+ 8|Obs Rate: 1.5 s
+ 9|                   C1    P1      C2    P2      lock             res   
+10|Ch Prn   Az  El    SNR   SNR     SNR   SNR     count  iodc  h   (m)
+11|-- ---  ---  ---  ----  ------  ----  ------  ------  ----  -  -----
+12| 1   8  133  22+  41.2  33.8 Y  38.1  39.2 Y  432000   2ba  0  
+13| 2  31
+14| 3  23
+15| 4  27
+16| 5  28
+17| 6   3
+18| 7  11
+19| 8  13
+20| 9   7
+21|10  19
+22|11  --
+23|12  --
+  |-------------------------------------------------------------------------------
+*/
+int pvtRow=2;
+int tCol=5, offCol=26, driftCol=47;
+int lonCol=5, latCol=23, altCol=40, prateCol=59;
+
+int stsRow=5;
+int trxCol=6, freqCol=22, stsTimeCol=44, stsSSWCol=65;
+
+int chanRow=11;
+int prnCol=3, azCol=8, elCol=13, c1snrCol=18, p1snrCol=24, c2snrCol=32;
+int p2snrCol=38, resCol=63, lockCol=46, iodcCol=54, healthCol=60, orateCol=10;
+
+bool MDPScreenProcessor::gotWench;
+
+MDPScreenProcessor::MDPScreenProcessor(gpstk::MDPStream& in, std::ofstream& out):
+   MDPProcessor(in, out),
+   updateRate(0.5), obsRate(-1), pvtRate(-1)
+{
+
+   // First set up curses
+   signal(SIGWINCH, wench);
+   win = initscr();
+   cbreak();
+   nodelay(win,true);
+   noecho();
+   nonl();
+   intrflush(win, true);
+   keypad(win, true);
+   prev_curs = ::curs_set(0);   // we want an invisible cursor. 
+   gotWench=false;
+
+   host=in.filename;
+
+   drawBase();
+
+   obsOut = true;
+   pvtOut = true;
+   navOut = true;
+   tstOut = true;
+}
+
+
+MDPScreenProcessor::~MDPScreenProcessor()
+{
+   curs_set(prev_curs);
+   endwin();
+}
+
+
+//-----------------------------------------------------------------------------
+void MDPScreenProcessor::process(const MDPObsEpoch& oe)
+{
+   int chan=oe.channel;
+
+   if (chan>12)
+      return;
+
+   // Figure out whether the SV is rising or setting
+   if ((currentObs[chan]).elevation > oe.elevation)
+      elDir[chan]=-1;
+   else if ((currentObs[chan]).elevation < oe.elevation)
+      elDir[chan]=+1;
+   else
+      elDir[chan]=0;
+
+   // Determine the obs output rate
+   double dt = oe.time - currentObs[chan].time;
+   if (currentObs[chan].prn > 0 && std::abs(dt-obsRate) > 1e-3)
+      obsRate=dt;
+
+   currentObs[chan] = oe;
+   
+   // Set channels inactive if we haven't seen data from them recently
+   if (obsRate>0)
+      for (int i=1; i<=12; i++)
+         if (currentObs[i].prn > 0 && oe.time - currentObs[i].time > obsRate*2)
+            currentObs[i].prn = 0;
+
+   drawChan(chan);
+   redraw();
+}
+
+//-----------------------------------------------------------------------------
+void MDPScreenProcessor::process(const MDPPVTSolution& pvt)
+{
+   if (host=="")
+   {
+      host=in.filename;
+      drawBase();
+   }
+
+   double dt = pvt.time - currentPvt.time;
+   if (std::abs(dt-pvtRate) > 1e-3)
+      pvtRate=dt;
+   currentPvt = pvt;
+   drawPVT();
+   redraw();
+}
+
+
+void MDPScreenProcessor::process(const gpstk::MDPNavSubframe& sf)
+{
+   short sfid = sf.getSFID();
+ 
+   NavIndex ni(RangeCarrierPair(sf.range, sf.carrier), sf.prn);
+   prev[ni] = curr[ni];
+   curr[ni] = sf;
+
+   long sfa[10];
+   sf.fillArray(sfa);
+   if (gpstk::EngNav::subframeParity(sfa))
+   {
+      if (sfid > 3)
+         return;
+      
+      EphemerisPages& ephPages = ephPageStore[ni];
+      ephPages[sfid] = sf;
+      EngEphemeris engEph;
+      
+      if (makeEngEphemeris(engEph, ephPages))
+         ephStore[ni] = engEph;
+   }
+   else
+   {
+      parErrCnt[ni]++;
+   }
+}
+
+void MDPScreenProcessor::process(const gpstk::MDPSelftestStatus& sts)
+{
+   currentSts = sts;
+   drawSTS();
+   redraw();
+}
+
+// Yes, one would think that sun would have a working curses but NO!!
+// They require a non-const string to be passed to mvwaddstr()
+// grrr.
+void writeAt(WINDOW* win, int row, int col, const string s)
+{
+   char *str = const_cast<char*>(s.c_str());
+   mvwaddstr(win, row, col, str);
+}
+
+void MDPScreenProcessor::redraw()
+{
+   gpstk::DayTime now;
+   if (now - lastUpdateTime > updateRate)
+   {
+      string time=currentPvt.time.printf(" %02H:%02M:%02S %2m/%d/%02y");
+      writeAt(win, 0, COLS-time.length()-5, time.c_str());
+      lastUpdateTime = now;
+      int ch = getch();
+      if (tolower(ch)=='q')
+      {
+         die=true;
+         writeAt(win, 0, 0, "Exiting program.");
+         // Use this to indicate that it is time to quit
+         in.setstate(ios_base::failbit);
+      }
+
+      /* should consider doing endwin(), initscr() and redrawing the window */
+      if (gotWench || tolower(ch)=='r')
+      {
+         char buff[30];
+         sprintf(buff, "%2d x %2d (wench)", LINES, COLS);
+         writeAt(win, 0, COLS/2-15, buff);
+         gotWench=false;
+         clearok(win,true);
+         drawBase();
+      }
+   }
+   wrefresh(win);
+}
+
+void MDPScreenProcessor::drawSTS()
+{
+   string firstTime=currentSts.firstPVTTime.printf("%02H:%02M %m/%d/%2Y  ");
+   writeAt(win, stsRow, stsTimeCol, firstTime.c_str());
+
+   string testTime=currentSts.selfTestTime.printf("%02H:%02M %m/%d/%2Y  ");
+   writeAt(win, stsRow+1, stsTimeCol, testTime.c_str());
+
+   if (currentSts.extFreqStatus)
+      writeAt(win, stsRow, freqCol, "Locked  ");
+   else
+      writeAt(win, stsRow, freqCol, "UnLocked");
+
+   string sts=leftJustify(int2x(currentSts.status), 8);
+   writeAt(win, stsRow+1, freqCol, sts.c_str());
+
+   string trx=leftJustify(asString(currentSts.receiverTemp, 0), 2) + "C";
+   writeAt(win, stsRow, trxCol, trx.c_str());
+   
+   string tant=leftJustify(asString(currentSts.antennaTemp, 0), 2) + "C";
+   writeAt(win, stsRow+1, trxCol, tant.c_str());
+
+   string ssw=leftJustify(int2x(currentSts.saasmStatusWord), 3);
+   writeAt(win, stsRow, stsSSWCol, ssw.c_str());
+}
+
+void MDPScreenProcessor::drawPVT()
+{
+   string s=rightJustify(asString(pvtRate,1), 3) + " s";
+   writeAt(win, pvtRow+1 , prateCol, s.c_str());
+
+   string time=currentPvt.time.printf("%02H:%02M:%04.1f");
+   writeAt(win, pvtRow, tCol, time.c_str());
+   string off=rightJustify(asString(currentPvt.dtime*1e9, 1), 9) + " ns";
+   writeAt(win, pvtRow, offCol, off.c_str());
+
+   gpstk::GPSGeoid gm;
+   gpstk::Geodetic llh(currentPvt.x, &gm);
+
+   string lat, lon, alt;
+   if (llh[0] > 0)
+      lat=leftJustify(asString(llh[0],5)+" N", 12);
+   else
+      lat=leftJustify(asString(std::abs(llh[0]),5)+" S", 12);
+   if (llh[1] < 180)
+      lon=leftJustify(asString(llh[1],5)+" E", 12);
+   else
+      lon=leftJustify(asString(360.0-llh[1],5)+" W", 12);
+   alt=leftJustify(asString(llh[2],3) + " m", 12);
+   writeAt(win, pvtRow+1, latCol, (const char *)lat.c_str());
+   writeAt(win, pvtRow+1, lonCol, lon.c_str());
+   writeAt(win, pvtRow+1, altCol, alt.c_str());
+
+   string drift=rightJustify(asString(currentPvt.ddtime*1e9*86400, 2), 9) + " ns/d";
+   writeAt(win, pvtRow, driftCol, drift.c_str());
+   string vx, vy, vz;
+   vx=leftJustify(asString(currentPvt.v[0] * 100, 2)+" cm/s", 11);
+   vy=leftJustify(asString(currentPvt.v[1] * 100, 2)+" cm/s", 11);
+   vz=leftJustify(asString(currentPvt.v[2] * 100, 2)+" cm/s", 11);
+   writeAt(win, pvtRow+2, lonCol, vx.c_str());
+   writeAt(win, pvtRow+2, latCol, vy.c_str());
+   writeAt(win, pvtRow+2, altCol, vz.c_str());
+
+   string fom = leftJustify(asString((int)currentPvt.fom), 3);
+   writeAt(win, pvtRow+2, prateCol, fom.c_str());
+   fom = leftJustify(asString((int)currentPvt.pvtMode), 2);
+   writeAt(win, pvtRow+2, prateCol+4, fom.c_str());
+   fom = leftJustify(asString((int)currentPvt.corrections), 2);
+   writeAt(win, pvtRow+2, prateCol+6, fom.c_str());
+}
+
+void MDPScreenProcessor::drawChan(int chan)
+{
+   if (chan>12 || chan < 1)
+      return;
+
+   for (int i=1; i<=12; i++)
+      if (currentObs[i].prn == 0)
+      {
+         writeAt(win, chanRow+i, prnCol, " --");
+         wclrtoeol(win);
+      }
+
+   int row = chanRow + chan;
+   const MDPObsEpoch& obs=currentObs[chan];
+   if (obs.prn == 0)
+      return;
+
+   string orate = leftJustify(asString(obsRate,1)+" s", 7);
+   writeAt(win, chanRow-3 , orateCol, orate.c_str());
+
+   string prn=rightJustify(asString((int)obs.prn), 3);
+   string az=rightJustify(asString(obs.azimuth, 0), 3);
+   string el=rightJustify(asString(obs.elevation, 0), 2);
+   string health = rightJustify(int2x(obs.status), 2);
+
+   if (elDir[chan] > 0)
+      el=el+"+";
+   else if (elDir[chan] < 0)
+      el=el+"-";
+
+   writeAt(win, row, prnCol, prn.c_str());
+   writeAt(win, row, azCol, az.c_str());
+   writeAt(win, row, elCol, el.c_str());
+   writeAt(win, row, healthCol, health.c_str());
+
+   // RangeCode:  rcUnknown, rcCA, rcPcode, rcYcode, rcCodeless, rcL2CM, rcL2CL, rcMcode1, rcMcode2
+   // NavCode:  ncUnknown, ncICD_200_2, ncICD_700_M, ncICD_705_L5, ncICD_200_4
+   if (obs.haveObservation(ccL1, rcCA))
+   {
+      MDPObsEpoch::Observation o = obs.getObservation(ccL1, rcCA);
+      string snr = rightJustify(asString(o.snr, 1), 4);
+      string lockCount = rightJustify(asString(o.lockCount), 6);
+      writeAt(win, row, c1snrCol, snr.c_str());
+      writeAt(win, row, lockCol, lockCount.c_str());
+   }
+
+   if (obs.haveObservation(ccL2,rcCM))
+   {
+      MDPObsEpoch::Observation o = obs.getObservation(ccL2, rcCM);
+      string snr = rightJustify(asString(o.snr, 1), 4);
+      string lockCount = rightJustify(asString(o.lockCount), 6);
+      writeAt(win, row, c2snrCol, snr.c_str());
+   }
+
+   if (obs.haveObservation(ccL1, rcYcode))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL1, rcYcode);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " Y";
+      writeAt(win, row, p1snrCol, snr.c_str());
+   }
+   else if (obs.haveObservation(ccL1, rcPcode))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL1, rcPcode);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " P";
+      writeAt(win, row, p1snrCol, snr.c_str());
+   }
+   else if (obs.haveObservation(ccL1, rcCodeless))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL1, rcCodeless);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " Z";
+      writeAt(win, row, p1snrCol, snr.c_str());
+   }
+
+   if (obs.haveObservation(ccL2, rcYcode))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL2, rcYcode);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " Y";
+      writeAt(win, row, p2snrCol, snr.c_str());
+   }
+   else if (obs.haveObservation(ccL2, rcPcode))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL2, rcPcode);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " P";
+      writeAt(win, row, p2snrCol, snr.c_str());
+   }
+   else if (obs.haveObservation(ccL2, rcCodeless))
+   {
+      MDPObsEpoch::Observation o=obs.getObservation(ccL2, rcCodeless);
+      string snr = rightJustify(asString(o.snr, 1), 4) + " Z";
+      writeAt(win, row, p2snrCol, snr.c_str());
+   }
+
+   EphStore::const_iterator es_itr;
+   for (es_itr=ephStore.begin(); es_itr != ephStore.end(); es_itr++)
+      if (es_itr->first.second == obs.prn)
+         break;
+
+   if (es_itr == ephStore.end())
+      return;
+
+   const NavIndex& ni = es_itr->first;
+   const gpstk::EngEphemeris& eph=es_itr->second;
+   const gpstk::RangeCode rc = ni.first.first;
+   const gpstk::CarrierCode cc = ni.first.second;
+
+   string iodc=rightJustify(int2x(eph.getIODC()), 4);
+   writeAt(win, row, iodcCol, iodc);
+}
+
+void MDPScreenProcessor::drawBase()
+{
+   wclear(win);
+   char buff[80];
+   sprintf(buff, "%2d x %2d", LINES, COLS);
+   writeAt(win, 0, COLS/2-4, buff);
+
+   writeAt(win, 0, 0, host.c_str());
+   writeAt(win, 0, COLS-3, "GPS");
+
+   writeAt(win, pvtRow,   0, "PVT:              Offset:");
+   writeAt(win, pvtRow+1, 0, "Lon:              Lat:              Ht:              Rate:");
+   writeAt(win, pvtRow,   0, "PVT:              Offset:               Drift:");
+   writeAt(win, pvtRow+2, 0, " Vx:               Vy:              Vz:              FOM:");
+
+   writeAt(win, stsRow,   0, " Trx:        ExtFreq:            StartTime:                  SSW:");
+   writeAt(win, stsRow+1, 0, "Tant:       Selftest:             TestTime:              ");
+
+   writeAt(win, chanRow-3, 0, "Obs Rate:");
+   writeAt(win, chanRow-2, 0, "                   C1    P1      C2    P2      lock           ");
+   writeAt(win, chanRow-1, 0, "Ch Prn   Az  El    SNR   SNR     SNR   SNR     count  iodc   h");
+   writeAt(win, chanRow,   0, "-- ---  ---  --   ----  ------  ----  ------  ------  ----  --");
+   for (int i=1; i<=12; i++)
+   {
+      string str=rightJustify(asString(i),2);
+      writeAt(win, chanRow+i, 0, str.c_str());
+      writeAt(win, chanRow+i, prnCol, " --");
+      wclrtoeol(win);
+   }
+   redraw();
+}
+
+/* should consider doing endwin(), initscr() and redrawing the window */
+void wench(int sig)
+{
+   MDPScreenProcessor::gotWench=true;
+}
diff --git a/dev/apps/MDPtools/mdpscreen/ScreenProc.hpp b/dev/apps/MDPtools/mdpscreen/ScreenProc.hpp
new file mode 100644
index 0000000..90d0d24
--- /dev/null
+++ b/dev/apps/MDPtools/mdpscreen/ScreenProc.hpp
@@ -0,0 +1,75 @@
+#pragma ident "$Id$"
+
+
+#ifndef MDPSCREEN_HPP
+#define MDPSCREEN_HPP
+
+#include "EngEphemeris.hpp"
+
+#include "MDPProcessors.hpp"
+
+#include <signal.h>
+#ifdef LINUX
+#include <bits/signum.h>
+#endif
+
+// This prevents forte from using the macro implementation of many of
+// the curses calls. They tend to interfere with parts of the STL.
+#define NOMACROS
+#include <curses.h>
+#if !defined( __linux__) && !defined(NCURSES_ATTR_T)
+typedef int attr_t;
+#endif
+
+
+class MDPScreenProcessor : public MDPProcessor
+{
+public:
+   MDPScreenProcessor(gpstk::MDPStream& in, std::ofstream& out);
+   ~MDPScreenProcessor();
+
+   virtual void process(const gpstk::MDPObsEpoch& obs);
+   virtual void process(const gpstk::MDPPVTSolution& pvt);
+   virtual void process(const gpstk::MDPNavSubframe& sf);
+   virtual void process(const gpstk::MDPSelftestStatus& sts);
+
+   void redraw();
+
+   void drawChan(int chan=0);
+   void drawPVT();
+   void drawSTS();
+   void drawBase();
+
+   std::string host;
+   float updateRate;
+   double obsRate, pvtRate;
+   WINDOW *win;
+   int prev_curs;
+
+   static bool gotWench;
+
+   int elDir[13];
+   gpstk::MDPObsEpoch currentObs[13];
+   gpstk::MDPPVTSolution currentPvt;
+   gpstk::MDPSelftestStatus currentSts;
+   gpstk::DayTime lastUpdateTime;
+
+   // This is really a triple: prn, RangeCode, CarrierCode
+   typedef std::pair<gpstk::RangeCode, gpstk::CarrierCode> RangeCarrierPair;
+   typedef std::pair<RangeCarrierPair, short> NavIndex;
+
+   // This class can keep track of a subframe and where it came from
+   typedef std::map<NavIndex, gpstk::MDPNavSubframe> NavMap;
+   
+   NavMap prev, curr;
+
+   typedef std::map<NavIndex, gpstk::EphemerisPages> EphPageStore;
+   EphPageStore ephPageStore;
+
+   typedef std::map<NavIndex, gpstk::EngEphemeris> EphStore;
+   EphStore ephStore;
+
+   std::map<NavIndex, unsigned long> parErrCnt;
+};
+
+#endif
diff --git a/dev/apps/MDPtools/mdpscreen/mdpscreen.cpp b/dev/apps/MDPtools/mdpscreen/mdpscreen.cpp
new file mode 100644
index 0000000..1d99612
--- /dev/null
+++ b/dev/apps/MDPtools/mdpscreen/mdpscreen.cpp
@@ -0,0 +1,115 @@
+#pragma ident "$Id$"
+
+
+//lgpl-license START
+//lgpl-license END
+
+#include "StringUtils.hpp"
+#include "LoopedFramework.hpp"
+#include "CommandOption.hpp"
+
+#include "DeviceStream.hpp"
+#include "TCPStreamBuff.hpp"
+
+#include "MDPProcessors.hpp"
+#include "ScreenProc.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class MDPTool : public BasicFramework
+{
+public:
+   MDPTool(const std::string& applName)
+      throw()
+      : BasicFramework(applName, "A curses based near-real-time display of an "
+                       " MDP stream."),
+        mdpInputOpt('i', "input", "Where to get the MDP data from. The default "
+                    "is to use stdin. If the file name begins with \"tcp:\" "
+                    "the remainder is assumed to be a hostname[:port] and the "
+                    "source is taken from a tcp socket at this address. If the "
+                    "port number is not specified a default of 8910 is used.")
+   {}
+
+   bool initialize(int argc, char *argv[]) throw()
+   {
+      if (!BasicFramework::initialize(argc,argv)) 
+         return false;
+
+      if (debugLevel)
+         cout << "debugLevel: " << debugLevel << endl
+              << "verboseLevel: " << verboseLevel << endl;
+
+      string fn;
+      if (mdpInputOpt.getCount())
+         fn =  mdpInputOpt.getValue()[0];
+      DeviceStream<ifstream> *inputDev = new DeviceStream<ifstream>(fn, ios::in);
+      mdpInput.std::basic_ios<char>::rdbuf(inputDev->std::basic_ios<char>::rdbuf());
+      mdpInput.filename = inputDev->getTarget();
+      
+      processor = new MDPScreenProcessor(mdpInput, output);
+
+      processor->debugLevel = debugLevel;
+      processor->verboseLevel = verboseLevel;
+
+      MDPHeader::debugLevel = debugLevel;
+
+      return true;
+   }
+   
+protected:
+   virtual void spinUp()
+   {
+      if (!processor)
+         cout << "No processor assigned." << endl, exit(-1);
+   }
+
+   virtual void process()
+   {      
+      try
+      {
+         processor->process();
+      }
+      catch (gpstk::Exception &e) 
+      {
+         cout << e << endl;
+      }
+      catch (std::exception &e)
+      {
+         cout << e.what() << endl;
+      }
+   }
+   
+   virtual void shutDown()
+   {
+      delete processor;
+   }
+
+private:
+   MDPStream mdpInput;
+   ofstream output;
+   TCPStreamBuff rdbuf;
+   CommandOptionWithAnyArg mdpInputOpt;
+
+   MDPProcessor* processor;
+};
+
+
+int main(int argc, char *argv[])
+{
+try
+   {
+      MDPTool crap(argv[0]);
+
+      if (!crap.initialize(argc, argv))
+         exit(0);
+
+      crap.run();
+   }
+   catch (gpstk::Exception &exc)
+   { cout << exc << endl; }
+   catch (std::exception &exc)
+   { cout << "Caught std::exception " << exc.what() << endl; }
+   catch (...)
+   { cout << "Caught unknown exception" << endl; }
+}
diff --git a/dev/apps/MDPtools/mdptool.cpp b/dev/apps/MDPtools/mdptool.cpp
new file mode 100644
index 0000000..3460c49
--- /dev/null
+++ b/dev/apps/MDPtools/mdptool.cpp
@@ -0,0 +1,267 @@
+#pragma ident "$Id$"
+
+
+/** @file Various utility functions on MDP streams/files */
+
+//lgpl-license START
+//lgpl-license END
+
+#include "StringUtils.hpp"
+#include "LoopedFramework.hpp"
+#include "CommandOptionWithTimeArg.hpp"
+
+#include "DeviceStream.hpp"
+
+#include "MDPProcessors.hpp"
+#include "SummaryProc.hpp"
+#include "TrackProc.hpp"
+#include "NavProc.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class MDPTool : public gpstk::BasicFramework
+{
+public:
+   MDPTool(const std::string& applName)
+      throw()
+      : BasicFramework(
+         applName,
+         "Perform various functions on a stream of MDP "
+         "data. In the summary mode, the default is to only "
+         "summarize the obs data above 10 degrees. Increasing "
+         "the verbosity level will also summarize the data below "
+         "10 degrees."),
+        mdpInputOpt(
+           'i', "input", 
+           "Where to get the MDP data from. The default "
+           "is to use stdin. If the file name begins with \"tcp:\" "
+           "the remainder is assumed to be a hostname[:port] and the "
+           "source is taken from a tcp socket at this address. If the "
+           "port number is not specified a default of 8910 is used."),
+        outputOpt(
+           '\0', "output",
+           "Where to send the output. The default is stdout."),
+        styleOpt(
+           's', "output-style",
+           "What type of output to produce from the "
+           "MDP stream. Valid styles are: brief, verbose, table, track, "
+           "null, mdp, nav, and summary. The default is summary. Some "
+           "modes aren't quite complete. Sorry."),
+        pvtOpt('p', "pvt",  "Enable pvt output"),
+        navOpt('n', "nav",  "Enable nav output"),
+        tstOpt('t', "test", "Enable selftest output"),
+        obsOpt('o', "obs",  "Enable obs output"),
+        hexOpt('x', "hex",  "Dump all messages in hex"),
+        badOpt('b', "bad",  "Try to process bad messages also."),
+        bugMaskOpt('m', "bug-mask", "What RX bugs to be quite about. "
+                   "1 SV count, 2 nav parity/fmt, 4 HOW/hdr time equal."),
+        almOpt(
+           'a', "almanac",
+           "Build and process almanacs. Only applies to the nav style"),
+        ephOpt(
+           'e', "ephemeris",
+           "Build and process engineering ephemerides. Only applies to the "
+           "nav style"),
+        minimalAlmOpt(
+           '\0', "min-alm",
+           "This allows a complete almanac to be constructed from fewer than "
+           "50 pages. It is required for receivers the Ashtech Z(Y)12. The "
+           "default is to require all 50 pages."),
+        startTimeOpt(
+           '\0', "start-time", "%4Y/%03j/%02H:%02M:%05.2f",
+           "Ignore data before this time. (%4Y/%03j/%02H:%02M:%05.2f)"),
+        stopTimeOpt(
+           '\0',  "stop-time", "%4Y/%03j/%02H:%02M:%05.2f",
+           "Ignore any data after this time"),
+        timeSpanOpt('l', "time-span", "How much data to process, in seconds")
+   {
+      pvtOpt.setMaxCount(1);
+      navOpt.setMaxCount(1);
+      obsOpt.setMaxCount(1);
+      tstOpt.setMaxCount(1);
+      styleOpt.setMaxCount(1);
+   }
+
+   bool initialize(int argc, char *argv[]) throw()
+   {
+      if (!BasicFramework::initialize(argc,argv)) return false;
+
+      if (debugLevel)
+         cout << "debugLevel: " << debugLevel << endl
+              << "verboseLevel: " << verboseLevel << endl;
+
+      string fn;
+      if (mdpInputOpt.getCount())
+         fn =  mdpInputOpt.getValue()[0];
+      inputDev.open(fn, ios::in);
+      if (debugLevel)
+         cout << "Taking input from " << inputDev.getTarget() << endl;
+      mdpInput.std::basic_ios<char>::rdbuf(inputDev.std::basic_ios<char>::rdbuf());
+
+      if (outputOpt.getCount())
+      {
+         output.open(outputOpt.getValue()[0].c_str(), std::ios::out);
+         if (debugLevel)
+            cout << "Sending output to" 
+                 << outputOpt.getValue()[0]
+                 << endl;
+      }
+      else
+      {
+         if (debugLevel)
+            cout << "Sending output to stdout" << endl;
+         output.copyfmt(std::cout);
+         output.clear(std::cout.rdstate());
+         output.std::basic_ios<char>::rdbuf(std::cout.rdbuf());
+      }
+
+      style = "summary";
+      if (styleOpt.getCount())
+         style = styleOpt.getValue()[0];
+
+      if (style == "brief")
+         processor = new MDPBriefProcessor(mdpInput, output);
+      else if (style == "table")
+         processor = new MDPTableProcessor(mdpInput, output);
+      else if (style == "verbose")
+         processor = new MDPVerboseProcessor(mdpInput, output);
+      else if (style == "summary")
+         processor = new MDPSummaryProcessor(mdpInput, output);
+      else if (style == "null")
+         processor = new MDPNullProcessor(mdpInput, output);
+      else if (style == "track")
+         processor = new MDPTrackProcessor(mdpInput, output);
+      else if (style == "nav")
+         processor = new MDPNavProcessor(mdpInput, output);
+      else
+      {
+         cout << "Style " << style << " is not a valid style. (it may just not be implimented yet.)" << endl;
+         return false;
+      }
+      
+      if (debugLevel)
+         cout << "Using style: " << style << endl;
+
+      processor->pvtOut |= pvtOpt;
+      processor->obsOut |= obsOpt;
+      processor->navOut |= navOpt;
+      processor->tstOut |= tstOpt;
+      processor->processBad |= badOpt;
+      
+      // Some nav specific options
+      if (style == "nav")
+      {
+         processor->navOut = true;
+         processor->obsOut = true; // needed to know elevation/SNR of SVs
+         MDPNavProcessor& np=dynamic_cast<MDPNavProcessor&>(*processor);
+         np.almOut = almOpt;
+         np.ephOut = ephOpt;
+         np.minimalAlm = minimalAlmOpt;
+      }
+
+      // If no outputs are specified, then at least set the obs output
+      if (!processor->pvtOut && !processor->obsOut
+          && !processor->navOut  && !processor->tstOut)
+      {
+         if (style == "summary")
+            processor->obsOut = processor->pvtOut = processor->navOut = processor->tstOut = true;
+         else
+            processor->obsOut = true;
+      }
+
+      for (int i=0; i<bugMaskOpt.getCount(); i++)
+         processor->bugMask |= StringUtils::asUnsigned(bugMaskOpt.getValue()[i]);
+      
+      if (debugLevel)
+      {
+         string msgList;
+         if (processor->pvtOut) msgList += "pvt ";
+         if (processor->obsOut) msgList += "obs ";
+         if (processor->navOut) msgList += "nav ";
+         if (processor->tstOut) msgList += "tst ";
+         if (msgList.size()==0)
+            msgList = "no ";
+         cout  << "Processing " << msgList << "messages." << endl;
+      }
+
+      processor->debugLevel = debugLevel;
+      processor->verboseLevel = verboseLevel;
+
+      MDPHeader::hexDump = hexOpt;
+      MDPHeader::debugLevel = debugLevel;
+
+      if (startTimeOpt.getCount())
+         processor->startTime = startTimeOpt.getTime()[0];
+      if (stopTimeOpt.getCount())
+         processor->stopTime = stopTimeOpt.getTime()[0];
+      if (timeSpanOpt.getCount())
+         processor->timeSpan = StringUtils::asDouble(timeSpanOpt.getValue()[0]);
+
+      return true;
+   }
+   
+protected:
+   virtual void spinUp()
+   {
+      if (!processor)
+         cout << "No processor assigned." << endl, exit(-1);
+   }
+
+   virtual void process()
+   {      
+      try
+      {
+         processor->process();
+      }
+      catch (gpstk::Exception &e) 
+      {
+         cout << e << endl;
+      }
+      catch (std::exception &e)
+      {
+         cout << e.what() << endl;
+      }
+   }
+   
+   virtual void shutDown()
+   {
+      delete processor;
+   }
+
+private:
+   DeviceStream<ifstream> inputDev;
+   MDPStream mdpInput;
+   ofstream output;
+   gpstk::CommandOptionWithAnyArg mdpInputOpt, outputOpt;
+
+   gpstk::CommandOptionNoArg pvtOpt, obsOpt, navOpt, tstOpt, hexOpt, badOpt;
+   gpstk::CommandOptionNoArg almOpt, ephOpt, minimalAlmOpt;
+   gpstk::CommandOptionWithAnyArg styleOpt;
+   gpstk::CommandOptionWithNumberArg timeSpanOpt, bugMaskOpt;
+   gpstk::CommandOptionWithTimeArg startTimeOpt, stopTimeOpt;
+
+   string style;
+
+   MDPProcessor* processor;
+};
+
+
+int main(int argc, char *argv[])
+{
+   try
+   {
+      MDPTool crap(argv[0]);
+
+      if (!crap.initialize(argc, argv))
+         exit(0);
+
+      crap.run();
+   }
+   catch (gpstk::Exception &exc)
+   { cout << exc << endl; }
+   catch (std::exception &exc)
+   { cout << "Caught std::exception " << exc.what() << endl; }
+   catch (...)
+   { cout << "Caught unknown exception" << endl; }
+}
diff --git a/dev/apps/MDPtools/tcptest.cpp b/dev/apps/MDPtools/tcptest.cpp
new file mode 100644
index 0000000..adbfd91
--- /dev/null
+++ b/dev/apps/MDPtools/tcptest.cpp
@@ -0,0 +1,86 @@
+#pragma ident "$Id$"
+
+
+#include <iostream>
+#include <string>
+using std::istream;
+using std::cout;
+using std::endl;
+
+#include "TCPStream.hpp"
+#include "CommandOption.hpp"
+#include "CommandOptionParser.hpp"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      gpstk::SocketAddr client(std::string("localhost"), 4621);
+      gpstk::IPaddress any;
+      gpstk::SocketAddr server(any, 4621);
+      int debugLevel = 0;
+      gpstk::CommandOptionNoArg
+         debugOption('d', "debug", "Enable debug output."),
+         serverOption('s', "server", "Run as server, not client");
+      std::string appDescription("Test program for the TCPStream stuff.");
+      gpstk::CommandOptionParser cop(appDescription);
+      cop.parseOptions(argc, argv);
+      if (cop.hasErrors())
+      {
+         cop.dumpErrors(cout);
+         cop.displayUsage(cout);
+         exit(0);
+      }
+
+      debugLevel = debugOption.getCount();
+
+      gpstk::TCPStream tcpStream;
+
+      if (serverOption.getCount())
+      {
+         cout << "Running as a server on " << server << endl;
+         int listening_socket = ::socket(AF_INET,SOCK_STREAM,0);
+         if (listening_socket < 0)
+            cout << "Couldn't create listening socket"
+                 << " (" << strerror(errno) << ")" << endl, exit(-1);
+         
+         int value = 1;
+         int rc=0;
+         
+         if (::setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR,
+                          (char*)&value, sizeof(value)))
+            cout << "Couldn't set reuse address"
+                 << " (" << strerror(errno) << ")" << endl, exit(-1);
+         
+         if (::bind(listening_socket, (sockaddr *)server, sizeof(sockaddr)))
+            cout << "Couldn't bind"
+                 << " (" << strerror(errno) << ")" << endl, exit(-1);
+         
+         if (::listen(listening_socket, 5))
+            cout << "Couldn't listen"
+                 << " (" << strerror(errno) << ")" << endl, exit(-1);
+         
+         gpstk::TCPStream link;
+         gpstk::SocketAddr peeraddr = gpstk::SocketAddr(any,1);
+         link.rdbuf()->accept(listening_socket,peeraddr);
+         cout << "Accepted connection from " << peeraddr << endl;
+         link.close();
+      }
+      else
+      {
+         cout << "Running as client, :" << client << endl;
+         
+         tcpStream.connect(client);
+         if( !tcpStream.good() )
+            cout << "Connection failed!" << endl, exit(0);
+      }
+   }
+   catch (...)
+   {
+      cout << "Caught one" << endl;
+   }
+}
diff --git a/dev/apps/Makefile.am b/dev/apps/Makefile.am
new file mode 100644
index 0000000..44e3328
--- /dev/null
+++ b/dev/apps/Makefile.am
@@ -0,0 +1,4 @@
+# $Id$
+SUBDIRS = DataAvailability MDPtools Rinextools checktools converters \
+differential difftools filetools geomatics ionosphere mergetools multipath \
+positioning receiver reszilla time visibility
diff --git a/dev/apps/RinexPlot/README b/dev/apps/RinexPlot/README
new file mode 100644
index 0000000..6bf601a
--- /dev/null
+++ b/dev/apps/RinexPlot/README
@@ -0,0 +1,31 @@
+RinexPlot (/apps/RinexPlot)
+
+   This directory contains a Perl script which uses the Perl::Tk module to
+provide a menu-driven GUI that will plot the data in Rinex observation files.
+It makes use of the Rinex tools, found in /apps/Rinextools, by calling them on
+the command line from within Perl.
+
+   RinexPlot will run under Unix, Linux and Windows. For Windows, you must
+have Perl and Perl::Tk installed on your system; the best place to get these 
+(free) is at ActiveState:
+
+http://www.activestate.com/Products/ActivePerl/ 
+
+Be sure to get Perl::Tk. Normally the Tk module will come with ActivePerl;
+you also get a Perl Package Manager which allows you to update your perl,
+or you can do this with CPAN. Under Windows you can use the
+batch file RinexPlot.bat.
+
+   Two examples are included here, using scripts goRP and goRP1 (Unix), or
+goRP.bat and goRP1.bat (Windows).
+
+   Getting started is easy. Run the example scripts (batch files) first. The
+GUI is menu and mouse driven, and you can get some brief instructions from the
+Help/Topics menu item. You can also provide options on the command line; see
+under Help/Topics again, or just look in the example scripts.
+
+   RinexPlot is young; some menu items are yet to be implemented, and it still
+has a few rough edges. I expect to it be improved greatly in the near future.
+
+Brian Tolman
+btolman at arlut.utexas.edu
diff --git a/trunk/apps/RinexPlot/RSW214B.obs b/dev/apps/RinexPlot/RSW214B.obs
similarity index 100%
rename from trunk/apps/RinexPlot/RSW214B.obs
rename to dev/apps/RinexPlot/RSW214B.obs
diff --git a/trunk/apps/RinexPlot/RinexPlot.bat b/dev/apps/RinexPlot/RinexPlot.bat
similarity index 100%
rename from trunk/apps/RinexPlot/RinexPlot.bat
rename to dev/apps/RinexPlot/RinexPlot.bat
diff --git a/dev/apps/RinexPlot/RinexPlot.pl b/dev/apps/RinexPlot/RinexPlot.pl
new file mode 100644
index 0000000..188f853
--- /dev/null
+++ b/dev/apps/RinexPlot/RinexPlot.pl
@@ -0,0 +1,3487 @@
+#!/usr/bin/perl
+#
+# RinexPlot - plot Rinex data
+# 
+# $Id$
+#
+#============================================================================
+#
+#  This file is part of GPSTk, the GPS Toolkit.
+#
+#  The GPSTk 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
+#  any later version.
+#
+#  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation, Inc.,
+#  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#  
+#  Copyright 2004, The University of Texas at Austin
+#
+#============================================================================
+#-------------------------------------------------------------------------------------
+# TD
+# (option?) don't plot points with value 0
+# it doesn't plot a symbol for the first point.
+# need to figure out the height and width of a character in pixels.  **
+# you can open a file in a re-entrant sub .. see the Perl books .. use for --load
+# make it safe to run two copies at once .. use unique name for $CFG{'datafile'}
+# Right axis
+# Time limits
+# Gnuplot output
+# postscript output?
+# RAIM -- how to plot?
+# Dataset/Configure ? ... all ResCor options
+
+#-------------------------------------------------------------------------------------
+use strict;
+use Tk;
+use Tk::Dialog;
+use Tk::DialogBox;
+use Tk::ROText;
+use Tk::BrowseEntry;
+use File::stat;
+use File::Basename;
+use IO::Handle;
+
+#-------------------------------------------------------------------------------------
+# Version number
+my $VERSION = "1.2 (10/1/2004)";
+# Message box description of this program
+my $ABOUT_TEXT = "\nRinexPlot is a GUI for utility programs developed with\n"
+               . "the GPS Toolkit (GPSTk) that will read, manipulate and plot\n"
+               . "data in a Rinex file.\n";
+my $AUTHOR_TEXT = "RinexPlot is written in Perl/Tk by Dr. Brian W. Tolman.\n";
+
+#-------------------------------------------------------------------------------------
+# configuration - most things are stored in a hash - see Defaults()
+my (%CFG,%OPT); # CFG has one value per key, OPT can have many values per key
+my ($key,$val);
+my %Options;    # valid options for command line -- see Defaults()
+my %Grow;       # valid options that can have many values (OPT) -- see Defaults()
+
+my $SLASH="/";
+if ($^O eq "MSWin32") {
+   $SLASH="\\";
+}
+my $menutear = 1;
+
+#-------------------------------------------------------------------------------------
+# drawing area TD add to CFG
+my $BCOLOR       = 'white';        # background color for canvas
+my $FCOLOR       = 'blue';         # foreground color for canvas
+my $MCOLOR       = 'black';        # color of mouse rectangles
+my $firstcanvas = 0;               # mark very first resizing of canvas
+
+# cursors -- see
+# Win32 : \perl\site\lib\Tk\X11\cursorfont.h
+# Linux : /usr/X11R6/include/X11/cursorfont.h
+my ($cursor,$waitcursor,$crosshair)=('crosshair','watch','crosshair');
+
+#my @items = ();
+
+#-------------------------------------------------------------------------------------
+# Widgets
+my $w_top;                         # top level window
+my $w_canvas;                      # canvas
+my $statusbar;                     # status bar
+my $menubar;                       # menu bar
+
+#-------------------------------------------------------------------------------------
+# Plot configuration
+my ($Xrate, $Yrate);
+my $FirstAutoscale = 1;            # flag showing need to call autoscale()
+my $LimitsSet = 0;                 # flag indicating LoadConfig has set limits
+my $UsingDefaults = 1;             # flag indicating all labels are defaults
+my $LogOpen = 0;                   # flag saying when LOG is open
+my $MainUp = 0;                    # flag saying main window is up
+my $ScaleDefined = 0;              # flag saying scales have been defined (Rates())
+my ($begW,$begS)=(0,0);            # used in DataTime
+my ($endW,$endS);
+
+#-------------------------------------------------------------------------------------
+# Axes
+# Call NiceScale(datamin,datamax,$ScaleN,totalpixels);
+# Labels are ($ScaleMin + ($i-1) * $ScaleStep) foreach $i (1..$ScaleN).
+# Multiply labels by 10**ScaleExp to make them all (minimum size) integers.
+# There are $ScalePPL pixels per label.
+my ($ScaleMin,$ScaleStep,$ScaleExp,$ScaleN,$ScalePPL)=(0,0,0,0,0);
+my @NiceUnits=(0,12,15,20,25,30,40,50,60,80,100,120,150);
+my ($YScaleMin,$YScaleStep,$YScaleExp)=(0,0,0);
+my ($XScaleMin,$XScaleStep,$XScaleExp)=(0,0,0);
+# if plot limits (XMin,etc) are fixed by user, set this =1, else (autoscale) set =0
+my $ScaleFixed=0;
+# tics
+my ($XTicLen,$YTicLen)=(5,5);
+# use these as input to NewScale
+my ($XMin,$XMax,$YMin,$YMax);
+# save previous values for 'unzoom'
+my ($OldXMin,$OldXMax,$OldYMin,$OldYMax)=('','','','');
+
+#-------------------------------------------------------------------------------------
+# Curves to plot (ncurv of them)
+# each curve defined by Sat, OT, column in file, color, symbol type
+my $Reconfigure;       # flag that says call ConfigureCurves()
+my $ncurv;             # number of curves defined
+my @curvSV = ();       # satellite for this curve
+my @curvOT = ();       # obs type (from @obslist)
+my @curvON = ();       # switch to turn off curve, on is non-zero
+my @curvCol = ();      # column in $CFG{'datafile'} where data is found
+my @curvLines = ();    # plot with lines or not (0)
+my @curvSymbs = ();    # plot with symbols
+my @symbols     = ('none','cross','plus','diamond','square','circle','del','delta');
+my @curvColor = ();    # color to plot
+my @colors = (
+      '#0000ff', # bright blue
+      '#ff0000', # bright red
+      '#00ff00', # bright green
+      '#ff00ff', # magenta
+      '#00ffff', # cyan
+      '#80ff00', # electric green
+      '#ff0080', # pink
+      '#8000ff', # purple
+      '#ffff00', # yellow
+      '#ff8000', # orange
+      '#00ff80', # sea green
+      '#0080ff', # sky blue
+      '#000080', # dark blue
+      '#800000', # dark red almost brown
+      '#008000', # dark green
+);
+#'#000000', # black
+#'#ffffff', # white
+#'#a0b7ce', # "MSWin blue"
+
+#-------------------------------------------------------------------------------------
+# Mouse rectangle
+my $MOUSE_RECT;                              # the rectangle id
+my $MAKE_RECT=0;                             # state, initially off
+my ($RECT_X0,$RECT_X1,$RECT_Y0,$RECT_Y1);    # rectangle limits
+my $CLICK = 0;                               # is the mouse button down?
+
+#-------------------------------------------------------------------------------------
+# Data file(s)
+#my $AutoView=1;           # flag that says view RinSum output when created
+my ($filename,$ConfigFile,$ResCorfilename);
+my ($RinexInSummary,$RinexRCSummary);
+my @filelist=();
+my ($numobs, $numsvs);    # number of obs types and SVs in Rinex file
+my ($nobssel, $nsvssel);  # number of obs types and SVs (above) that are selected
+my @obslist=();           # list of obs types in Rinex header
+my @obsdesc=();           # descriptions of obs types from Rinex header dump
+my @obsselect=();         # list of switches (0,1) parallel to obslist
+my @svlist=();            # list of SVs in Rinex file
+my @svselect=();          # list of switches (0,1) parallel to svlist
+my @svbegin=();           # list of SV begin times (W,SOW)
+my @svend=();             # list of SV end times (W,SOW)
+
+#-------------------------------------------------------------------------------------
+# Computation using ResCor (create file $ResCorfilename)
+# list of extended obs types that ResCor can compute, with desc., units, dependence
+my (@ExtOT, @ExtDesc, @ExtDep, @ExtSelect);     # these kept parallel
+# hash: ExtUnit{$ExtOT[$i]} = units of that OT
+my %ExtUnit=('L1','cycles','L2','cycles','C1','meters','P1','meters','P2','meters',
+   'D1','Hz','D2','Hz','S1','dB-Hz','S2','dB-Hz');
+# flags giving dependence of the Ext obs types on standard obs types
+my ($DepL1,$DepL2,$DepC1,$DepP1,$DepP2,$DepEP,$DepPS) = (1,2,4,8,16,32,64);
+my $ETdb;    # dialog box that selects extended obs types ... create only once
+# for use as input to ResCor -- receiver label, position
+my ($RxLabel,$RxX,$RxY,$RxZ,$RxComment) = ('','','','','');
+# types that ResCor will debias: SP VP L3 L4 L5 MP M1 M2 M3 M4 M5 XR XI X1 X2
+# (XR should not be debiased by ResCor)
+# TD add config option to turn this on/off
+my %MayDebias=('SP',1,'VP',1,'L3',1,'L4',1,'L5',1,
+               'MP',1,'M1',1,'M2',1,'M3',1,'M4',1,'M5',1,
+               'XR',0,'XI',1,'X1',1,'X2',1);
+#
+my $MyPrgmDir='unknown';
+if ($^O eq 'MSWin32') {
+   $MyPrgmDir="C:\\Code\\GPSLIB\\Working\\dev\\apps\\Rinextools";
+}
+elsif ($^O eq 'linux') {
+   $MyPrgmDir="/home/btolman/mybin";
+}
+#-------------------------------------------------------------------------------------
+#-------------------------------------------------------------------------------------
+# This lists and defines(!) all valid options for CFG and OPT. This string is written
+# in the Help/Topics window, and is printed to the screen when '--help' is the only
+# cmdline option.
+# It is also parsed by Defaults() into the default CFG and OPT settings, with rules:
+# 0. one key/value pair per line.
+# 1. the key follows '--' and does not contain whitespace.
+# 2. the value is in parentheses at the end : ($val)\n$
+# 3. the option is repeatable (cmdline only) if the string '(can repeat)' appears.
+# 4. everything after 'Example:' is ignored.
+# 5. ignore any line that starts (col 1) with '#'.
+my $CmdText =
+"# RinexPlot, part of the GPSTk : Plot Rinex data\n" .
+"# Usage: perl RinexPlot.pl [--option <arg>]\n" .
+"# Input options (put on command line or in config file) :\n" .
+"# Rinex tools directory:\n" .
+" --prgmdir <dir>     directory of GPSTK programs (". $MyPrgmDir . ")\n" .
+"# input:\n" .
+" --load <file>       load options file, but don't nest (can repeat) ()\n" .
+" --Rinex <file>      load and summarize this Rinex observation file ()\n" .
+" --nav <file>        load this Rinex navigation file ()\n" .
+"# output:\n" .
+" --log <file>        send all diagnostic output to <file> (SCREEN)\n" .
+" --datafile <file>   store data to be plotted in flat <file> (RP.dat)\n" .
+"# create new obs types:\n" .
+" --AO <ExOT>         add extended obs types (can repeat) ()\n" .
+" --create [on|off]   run ResCor to create any new (AO) obs types (on)\n" .
+" --debias <limit>    limit that triggers bias reset in ResCor (100)\n" .
+"# plot datasets\n" .
+" --sat <sat>         select satellite for plot (can repeat) ()\n" .
+" --obs <OT>          select obs type for plot (can repeat) ()\n" .
+" --refresh [on|off]  draw the screen (only if --sat and --obs) (on)\n" .
+" --begin <wk,sow>    begin GPS time -- do not read data before this time (0,0)\n" .
+" --end <wk,sow>      end GPS time -- do not read data after this time (9999,0)\n" .
+"# plot configuration:\n" .
+" --width <pixels>    width of plot surface, L-axis to R-axis (640)\n" .
+" --height <pixels>   height of plot surface, B-axis to T-axis (480)\n" .
+" --lines [on|off]    draw a line when drawing curves (on)\n" .
+" --points [on|off]   draw points when drawing curves (off)\n" .
+" --XMin <x>          set minimum value on X axis, omit to autoscale ()\n" .
+" --XMax <x>          set maximum value on X axis, omit to autoscale ()\n" .
+" --YMin <y>          set minimum value on Y axis, omit to autoscale ()\n" .
+" --YMax <y>          set maximum value on Y axis, omit to autoscale ()\n" .
+" --Week <w>          week number associated with XMin/Max (for Blabel) ()\n" .
+" --Bmargin <pixels>  distance between graph and window bottom (30)\n" .
+" --Tmargin <pixels>  distance between graph and window top (30)\n" .
+" --Lmargin <pixels>  distance between graph and window left (40)\n" .
+" --Rmargin <pixels>  distance between graph and window right (30)\n" .
+" --BticN <n>         number of tics on bottom axis (5)\n" .
+" --TticN <n>         number of tics on top axis (5)\n" .
+" --LticN <n>         number of tics on left axis (5)\n" .
+" --RticN <n>         number of tics on right axis (5)\n" .
+"# plot labels:\n" .
+" --Blabel <string>   label below the bottom axis (GPS Seconds of Week)\n" .
+" --Tlabel <string>   label above the top axis, i.e title (Title)\n" .
+" --Llabel <string>   label above the left axis ()\n" .
+" --Rlabel <string>   label above the right axis ()\n" .
+"# switches:\n" .
+" --verbose [on|off]  output more information to log file (off)\n" .
+" --keepdata [on|off] on exit, do not delete the data file RP.dat (off)\n" .
+" --autoview [on|off] automatically display data file summary (on)\n" .
+" --Cforce [on|off]   force C1 to replace P1 (off)\n" .
+" --Callow [on|off]   allow C1 to replace a missing P1 (on)\n" .
+"# other:\n" .
+" --zoomX <frac>      zoom commands expand(contract) by fraction (0.1)\n" .
+" --help [on|off]     show the help window (off)\n" .
+"#\n" .
+" Example:\natom>./RinexPlot --Rinex ../gdms/data/04032/alic0320.04o --autoview off" .
+"\n  --AO L4 --AO M5 --sat G08 --sat G30 --obs L4 --obs M5 --refresh\n";
+
+#-------------------------------------------------------------------------------------
+# Temp data
+my (@opt, $file, $dummy, $i, $j, $ans, $msg);
+my ($cmd,$buffer,$len);
+
+#-------------------------------------------------------------------------------------
+# computation of scales and coordinate transformations
+#-------------------------------------------------------------------------------------
+# compute a new 'nice' scale, using $XMin,$XMax,$YMin,$YMax.
+# call with first arg 'fixed' if plot limits are fixed, else 'auto' to "autoscale"
+# second arg is a label printed on LOG and Status
+sub NewScale {
+   my ($flag,$str)=@_;
+
+   ($OldXMin,    $OldXMax,    $OldYMin,    $OldYMax) = 
+   ($CFG{'XMin'},$CFG{'XMax'},$CFG{'YMin'},$CFG{'YMax'});
+
+   if($YMin > $YMax) { $ans=$YMax; $YMax=$YMin; $YMin=$ans; }
+   if($XMin > $XMax) { $ans=$XMax; $XMax=$XMin; $XMin=$ans; }
+
+   my $tics = $CFG{'BticN'};
+   if($flag eq 'fixed') { $tics = $CFG{'BticN'} + 2; }
+   NiceScale($XMin,$XMax,$tics,$CFG{'width'});
+   if($flag eq 'fixed') { $XScaleMin = $ScaleMin+$ScaleStep; }
+   if($flag eq 'auto')  { $XScaleMin = $ScaleMin; }
+   $XScaleStep = $ScaleStep;
+   $XScaleExp = $ScaleExp;
+
+   $tics = $CFG{'LticN'};
+   if($flag eq 'fixed') { $tics = $CFG{'LticN'} + 2; }
+   NiceScale($YMin,$YMax,$tics,$CFG{'height'});
+   if($flag eq 'fixed') { $YScaleMin = $ScaleMin+$ScaleStep; }
+   if($flag eq 'auto')  { $YScaleMin = $ScaleMin; }
+   $YScaleStep = $ScaleStep;
+   $YScaleExp = $ScaleExp;
+
+   if($flag eq 'auto') {
+      # let NiceScale determine the plot limits
+      $YMin = $YScaleMin;
+      $YMax = $YScaleMin+($CFG{'LticN'}-1)*$YScaleStep;
+      $XMin = $XScaleMin;
+      $XMax = $XScaleMin+($CFG{'BticN'}-1)*$XScaleStep;
+   }
+
+   ($CFG{'XMin'},$CFG{'XMax'},$CFG{'YMin'},$CFG{'YMax'})=($XMin,$XMax,$YMin,$YMax);
+   Rates();  # Rates() can change XMin, etc.
+
+   print LOG "$str: new limits are $YMin, $YMax, $XMin, $XMax\n";
+   Status("$str: new limits are X: "
+   . sprintf("%10.3f",$XMin) . ", "
+   . sprintf("%10.3f",$XMax) .  ", Y: "
+   . sprintf("%.3f",$YMin) . ", "
+   . sprintf("%.3f",$YMax) .  ".");
+
+   if($flag eq 'fixed') { $ScaleFixed=1; } else { $ScaleFixed=0; }
+   $FirstAutoscale = 0;
+}
+
+# compute the 'rates' needed for coordinate transformations
+sub Rates {
+   # check for division by 0
+   if($CFG{'XMin'} == $CFG{'XMax'}) {
+      if($CFG{'XMax'} == 0) { $CFG{'XMax'} = 1; }
+      else { $CFG{'XMax'} += 1.5 * $CFG{'XMax'}; }
+   }
+   if($CFG{'YMin'} == $CFG{'YMax'}) {
+      if($CFG{'YMax'} == 0) { $CFG{'YMax'} = 1; }
+      else { $CFG{'YMax'} += 1.5 * $CFG{'YMax'}; }
+   }
+   $Xrate = $CFG{'width'}/($CFG{'XMax'}-$CFG{'XMin'});
+   $Yrate = -$CFG{'height'}/($CFG{'YMax'}-$CFG{'YMin'});
+
+	$ScaleDefined = 1;
+}
+
+# convert data X units,    ($CFG{'XMin'} <= $rawx <= $CFG{'XMax'} )
+# into screen coordinates  (0     <= $x    <= $CFG{'width'})
+sub Xdata2scr {
+   my $rawx = shift;
+   my $x = $CFG{'Lmargin'} + ($rawx-$CFG{'XMin'}) * $Xrate;
+   return int($x);
+}
+
+# convert data Y units,    ($CFG{'YMin'}   <= $rawy <= $CFG{'YMax'})
+# into screen coordinates  ($CFG{'height'} <= $y    <= 0    )
+sub Ydata2scr {
+   my $rawy = shift;
+   my $y = $CFG{'Tmargin'} + $CFG{'height'} + ($rawy-$CFG{'YMin'}) * $Yrate;
+   return int($y);
+}
+
+# convert screen coordinates  (0     <= $x    <= $CFG{'width'})
+# into data units,            ($CFG{'XMin'} <= $rawx <= $CFG{'XMax'} )
+sub scr2Xdata {
+   my $x = shift;
+   my $rawx = $CFG{'XMin'} + ($x - $CFG{'Lmargin'})/$Xrate;
+   return $rawx;
+}
+
+# convert screen coordinates  ($CFG{'height'} <= $y    <= 0    )
+# into data units,            ($CFG{'YMin'}   <= $rawy <= $CFG{'YMax'})
+sub scr2Ydata {
+   my $y = shift;
+   my $rawy = ($y - $CFG{'Tmargin'} - $CFG{'height'})/$Yrate + $CFG{'YMin'};
+   return $rawy;
+}
+
+#-------------------------------------------------------------------------------------
+# menu callbacks
+#-------------------------------------------------------------------------------------
+sub FileLoad {
+   $filename = $w_top->getOpenFile(-title => 'Load a configuration file',
+      -initialfile => basename($ConfigFile),
+      #-initialdir => $dir,
+      #-defaultextension => $ext,
+   );
+   if($filename eq '') { return; }
+
+   $LimitsSet = 0;                      # LoadConfig will notice if limits are set
+   $ans = LoadConfig($filename);
+   if($ans ne 'fail') {
+      ProcessConfig();
+   }
+}
+
+sub FileSave {
+   print LOG "Save the configuration file $ConfigFile\n";
+   $ans = SaveConfig($ConfigFile);
+   if($ans eq 'fail') {
+      print LOG "Could not save configuration file $ConfigFile\n";
+   }
+}
+
+sub FileSaAs {
+   my $Savefilename = $w_top->getSaveFile(-title => 'Save configuration in a file');
+   $ans = SaveConfig($Savefilename);
+   if($ans eq 'fail') {
+      print LOG "Could not save configuration file $Savefilename\n";
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub FileExit {
+   Status("Cleaning up...");
+   print LOG "Clean up and exit.\n";
+   if($CFG{'keepdata'} ne 'on') {
+      $dummy = `rm -f $CFG{'datafile'}`;
+      if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+   }
+   if($CFG{'log'} ne '') { close LOG; }
+   exit;
+}
+
+#-------------------------------------------------------------------------------------
+sub FileInpu {
+   Status("Get the name of a Rinex observation file...");
+   $filename = $w_top->getOpenFile(-title => 'Choose a Rinex obs file');
+   if($filename eq '') { return; }
+   DataInputProcess($filename);
+}
+
+#-------------------------------------------------------------------------------------
+sub FileSumm {
+   $file = $RinexInSummary;
+   if($ResCorfilename ne '') { $file=$RinexRCSummary; }
+   if($file eq '') {
+      PopNotice("Error: no file","No input Rinex observation file has been chosen\n"
+            . "Go to File/Rinex Obs");
+      #$ans = $w_top->messageBox(-title => 'Error: no file',
+      #-message => "No input observation file has been chosen\n"
+      #. "Go to File/Rinex Obs",
+      #-type => 'OK', -icon => 'info');
+      return;
+   }
+   Status("Press the Close button to return to the main window...");
+   #print LOG "View data from $file\n";
+   $buffer = '';
+   my $len=0;
+   if (not open(F, "<$file")) {
+      $w_top->Dialog(
+         -title => 'File not found',
+         -text => "Could not open the file $file",
+         -bitmap => 'error',
+      )->Show;
+      return;
+   }
+   $len = read(F, $buffer, 100000);
+   #print LOG "Read $len bytes\n";
+   my @list = split("\n", $buffer);
+   my $width=0; foreach $i (@list) { if(length($i)>$width) { $width=length($i); } }
+   my $TWdb = $w_top->DialogBox(
+      -title => "View RinSum output ($len bytes): $file",
+      -buttons => ['Close'],
+      -popover => $w_top,
+      -overanchor => 'w',
+      -popanchor => 'w',
+   );
+   my $text_win = $TWdb->Scrolled('Text',
+      -setgrid => 'true',
+      -scrollbars => 'e',
+      -height => '40',   #lines
+      -width => $width,  #characters
+      )->pack(-expand => 'yes', -fill => 'both');
+   $text_win->insert('end', $buffer);  # add text at end of (empty) widget
+   $text_win->see('end');              # scroll to end of text
+   $TWdb->Show();
+   close F;
+   Status("...");
+}
+
+#-------------------------------------------------------------------------------------
+sub FileSele {
+   Status('');
+
+   # figure out the width of the box
+   my $len = length($CFG{'Rinex'});
+   if($len < length($CFG{'nav'})) { $len = length($CFG{'nav'}); }
+   if($RxLabel ne '' && $len < 60) { $len = 60; }
+   if($ResCorfilename ne '' && $len < length($ResCorfilename)) {
+      $len = length($ResCorfilename);
+   }
+   if($len < 20) { $len = 20; }
+
+   # are there extended obs types defined?
+   $dummy = 0;
+   $msg = '';
+   foreach $i (0..$#ExtOT) {
+      if($ExtSelect[$i] != 0) {
+         $dummy++;
+         $msg = $msg . $ExtOT[$i] . " ";
+      }
+   }
+
+   my $FSdb = $w_top->DialogBox(
+      -title =>  "Current Selections",
+      -buttons => ['Ok','Clear'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $FSdb->add('Label',-text => "Rinex Observation File:")->pack(-anchor => 'w');
+   my $f1=$FSdb->Frame(-borderwidth => 2, -relief => 'groove',
+      )->pack(-anchor => 'w');
+   $FSdb->add('Label',-text => "Rinex Navigation File:")->pack(-anchor => 'w');
+   my $f2=$FSdb->Frame(-borderwidth => 2, -relief => 'groove',
+      )->pack(-anchor => 'w');
+   my ($f3,$f4,$f5,$f6);
+   if($RxLabel ne '') {
+      $FSdb->add('Label',-text => "Receiver Position:")->pack(-anchor => 'w');
+      $f3=$FSdb->Frame(-borderwidth => 2, -relief => 'groove',
+         )->pack(-anchor => 'w');
+   }
+   if($ResCorfilename ne '') {
+      $FSdb->add('Label',-text => "Computed Observations File")->pack(-anchor => 'w');
+      $f4=$FSdb->Frame(-borderwidth => 2, -relief => 'groove',
+         )->pack(-anchor => 'w');
+   }
+   if($dummy > 0) {
+      $FSdb->add('Label',-text => "Computed Observation Types")->pack(-anchor => 'w');
+      $f5=$FSdb->Frame(-borderwidth => 2, -relief => 'groove',
+         )->pack(-anchor => 'w');
+   }
+   $FSdb->add('Label',-text => "Current configuration file")->pack(-anchor => 'w');
+   $f6=$FSdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+
+
+   my $e1=$f1->Entry(
+      -textvariable => \$CFG{'Rinex'}, -width => $len, -justify => 'left',
+      )->pack(-side => 'top', -anchor => 'w');
+   $f1->Label(-text => 'Summary file:')->pack(-side => 'left');
+   $f1->Label(-text => $RinexInSummary, -justify => 'left')->pack(-side => 'left');
+   my $e2=$f2->Entry(
+      -textvariable => \$CFG{'nav'}, -width => $len)->pack(-side => 'left');
+   if($RxLabel ne '') {
+      my $f31=$f3->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+      $f31->Label(-text => "Label : ")->pack(-side => 'left');
+      $f31->Entry(-textvariable => \$RxLabel, -width => 5)->pack(-side => 'left');
+      my $f32=$f3->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+      $f32->Label(-text => "X : ")->pack(-side => 'left');
+      $f32->Entry(-textvariable => \$RxX, -width => 15)->pack(-side => 'left');
+      $f32->Label(-text => "  Y : ")->pack(-side => 'left');
+      $f32->Entry(-textvariable => \$RxY, -width => 15)->pack(-side => 'left');
+      $f32->Label(-text => "  Z : ")->pack(-side => 'left');
+      $f32->Entry(-textvariable => \$RxZ, -width => 15)->pack(-side => 'left');
+      my $f33=$f3->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+      $f33->Label(-text => "Comment: ")->pack(-side => 'left');
+      $f33->Entry(-textvariable => \$RxComment, -width => 50)->pack(-side => 'left');
+   }
+   if($ResCorfilename ne '') {
+      $f4->Label(-text => $ResCorfilename, -justify => 'left',
+         )->pack(-side => 'top', -anchor => 'w');
+      $f4->Label(-text => 'Summary file:'
+         )->pack(-side => 'left', -anchor => 'w');
+      $f4->Label(-text => $RinexRCSummary, -justify => 'left',
+         )->pack(-side => 'left', -anchor => 'w');
+   }
+
+   #TD add ExtOT, Selected sats/obs ? 
+   if($dummy > 0) {
+      $f5->Label(-text => $msg, -justify => 'left',
+         )->pack(-side => 'top', -anchor => 'w');
+   }
+
+   $f6->Label(-text => $ConfigFile, -justify => 'left',
+      )->pack(-side => 'top', -anchor => 'w');
+
+   $ans = $FSdb->Show();
+   $e1->focus; $e1->icursor('end');
+   if($ans eq 'Clear') {
+      FileClea();
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub FileClea {
+   $ans = $w_top->messageBox(
+      -title => "Clear all selections",
+      -message => "Are you sure you want to clear\n"
+         . "all file names and all data selections?     ",
+      -type => 'YesNo',
+      -icon => 'question',
+      #no popover for messageBox
+   );
+   if($ans eq 'yes') { ClearAll(); }
+}
+
+#-------------------------------------------------------------------------------------
+sub ClearAll {
+   Status("All the input files and data selection have been cleared.");
+   $CFG{'nav'} = '';
+   $CFG{'Rinex'} = '';
+   $ResCorfilename = '';
+   $RinexInSummary = '';
+   $RinexRCSummary = '';
+   $RxLabel = '';
+   @obslist = ();
+   @obsdesc = ();
+   @obsselect = ();
+   @svlist = ();
+   @svselect = ();
+   $dummy = `rm -f $CFG{'datafile'}`;
+   if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+   @svbegin = ();
+   @svend = ();
+   $numobs = 0;
+   $numsvs = 0;
+   $nobssel = 0;
+   $nsvssel = 0;
+   $FirstAutoscale = 1;
+   ClearExot();
+}
+
+#-------------------------------------------------------------------------------------
+sub DataInputProcess {
+   ($filename) = @_;
+   # first see that it exists
+   if (not open(F, "<$filename")) {
+      PopNotice("Empty file","File $filename        \ndoes not exist or is empty.");
+      return;
+   }
+   print LOG "Selected input Rinex obs file $filename\n";
+   ClearAll();
+   WaitCursor();
+
+	# get extended obs type information here (once) from ResCor
+   ExtDialogInit();
+
+   Status("Summarizing obs file $filename...please wait...");
+   $CFG{'Rinex'} = $filename;
+   $ResCorfilename = '';
+   $RinexRCSummary = '';
+   $RinexInSummary = basename($filename) . ".sum";
+   $cmd = $CFG{'prgmdir'} . $SLASH . "RinSum -g -i" . $CFG{'Rinex'}
+      . " > $RinexInSummary";
+   print LOG "Summarize obs file: Execute $cmd\n";
+   $dummy = `$cmd`; if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+
+   # pull out obs types from $RinexInSummary
+   $ans = DataGather($RinexInSummary);
+   if($ans == -1) {
+      Status("Choose another observation file");
+      PopNotice("Error: wrong file type",
+         $CFG{'Rinex'} . "         \nis NOT a Rinex observation file.\n");
+      $CFG{'Rinex'} = '';
+      $ResCorfilename = '';
+      $RinexInSummary = '';
+   }
+   else {
+      if($CFG{'autoview'} eq 'on') { FileSumm(); }
+      $FirstAutoscale = 1;
+   }
+   Status("Summary complete for $filename.");
+   NormalCursor();
+}
+
+#-------------------------------------------------------------------------------------
+sub DataGather {
+   ($file) = @_;
+   open FILE, "$file" or die "Error: DataGather could not open file $file\n";
+   @obslist = ();
+   @obsdesc = ();
+   @obsselect = ();
+   @svlist = ();
+   @svselect = ();
+   $dummy = `rm -f $CFG{'datafile'}`; if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+   @svbegin = ();
+   @svend = ();
+   $nobssel = 0;
+   $nsvssel = 0;
+   my $EOH = 0;
+   while(<FILE>) {
+      chomp;
+      $_ =~ s/^\s+//;
+      if(m/is not a Rinex observation file/) {
+         return -1;
+      }
+      if(m/END OF HEADER/) { $EOH=1; }
+
+      @opt = split (/\s+/, $_);
+      if($opt[0] eq "Type") {
+         push @obslist, $opt[3];
+         push @obsdesc, substr($_,13,length($_)-13);
+         push @obsselect, 0;
+      }
+      # following condition (..eq "G") means only GPS satellites get picked up
+      if($EOH==1 && $opt[0] eq "Sat" && substr($opt[1], 0, 1) eq "G") {
+         $numobs = $#obslist + 1;
+         push @svlist, $opt[1];
+         push @svselect, 0;
+         push @svbegin, [ $opt[$numobs+3], $opt[$numobs+4] ];
+         push @svend, [ $opt[$numobs+6], $opt[$numobs+7] ];
+      }
+      if($opt[0] eq "WARNING:" && $opt[1] eq "ObsType") {
+         $dummy = $opt[2];
+         foreach $i (0..$numobs-1) {
+            if($obslist[$i] eq $dummy) {
+               splice(@obslist, $i, 1);
+               splice(@obsdesc, $i, 1);
+               splice(@obsselect, $i, 1);
+               $numobs--;
+               last;
+            }
+         }
+      }
+   }
+   close FILE;
+   $numsvs = $#svlist + 1;
+
+   #print LOG "Obs types ($numobs) found: @obslist\n";
+   #print LOG "Found $numsvs SVs, here are start and stop times:\n";
+   #foreach $i (0..$numsvs-1) {
+   #   print LOG "$svlist[$i] ($svbegin[$i][0],$svbegin[$i][1])"
+   #   . " - ($svend[$i][0],$svend[$i][1])\n";
+   #}
+   return 0;
+}
+
+#-------------------------------------------------------------------------------------
+sub DataSats {
+   if($numsvs <= 0) {
+      $msg = "There are no satellites to select!"
+         . "\nGo to File/Input to load a new Rinex data file";
+      PopNotice("Error: no satellites",$msg);
+      return;
+   }
+
+   Status("Select satellites to be plotted.");
+   my $SLdb = $w_top->DialogBox(
+      -title => 'Select satellite(s) for plot',
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor=> 'n',
+      -popanchor => 'n',
+   );
+   my (@cb);
+   my $rows = 6;
+   my ($r,$c,$cols)=(1,0,int 1+$numsvs/$rows);
+
+   # label and frame for satellites
+   $SLdb->add('Label',
+      -text => "Select Satellite(s)",
+      -justify => 'center')->grid(-columnspan => "$cols");
+   my $f1=$SLdb->Frame(-borderwidth => 2, -relief => 'groove')->grid();
+   foreach $i (0..$numsvs-1) {
+      $cb[$i] = $f1->Checkbutton(
+         -text => $svlist[$i],
+         -variable => \$svselect[$i],
+         -relief => 'flat')->grid(-row => $r, -column => $c, -ipadx => '2');
+      $r++;
+      if($r > $rows) { $r = 1; $c++; }
+   }
+   # frame for two buttons
+   $dummy = $rows+1;
+   my $f2 = $f1->Frame(-borderwidth => 2, -relief => 'flat'
+      )->grid(-row => $dummy, -columnspan => "$cols");
+   $dummy = int($cols/2);
+   if($dummy == 0) { $dummy=1; }
+   my $cb2 = $f2->Button(
+      -text => 'All',
+      -width => '15',
+      -relief => 'groove',
+      -command => \&SetAllSats)->grid(-row => 1, -column => 1);
+   my $cb1 = $f2->Button(
+      -text => 'Clear',
+      -width => '15',
+      -relief => 'groove',
+      -command => \&ClearSats)->grid(-row => 1, -column => 2);
+
+   # save a copy to see when sats are un-selected
+   my @saveselect = @svselect;
+   $ans = $SLdb->Show();
+   if($ans eq "Cancel") {
+      @svselect = @saveselect;
+      return;
+   }
+
+   $dummy = 0;
+   $filename = "$CFG{'datafile'}";
+   foreach $i (0..$numsvs-1) {
+      if($saveselect[$i] != $svselect[$i]) {
+         if($svselect[$i] == 0) {
+            if($dummy == 0) { print LOG "DataSet/Sats Selected Sats:"; $dummy=1; }
+            print LOG " -$svlist[$i]";
+         }
+         else {
+            if(-e $filename) {
+               $dummy = `rm -f $filename`;
+               if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+            }
+         }
+         $Reconfigure = 1;
+      }
+      if($svselect[$i] != 0) {
+         if($dummy == 0) { print LOG "DataSet/Sats Selected Sats:"; $dummy=1; }
+         print LOG " $svlist[$i]";
+         $nsvssel++;
+      }
+   }
+   if($dummy == 1) { print LOG "\n"; }
+   Status("Satellites selected ... go to DataSet/Obs to select obs?");
+}
+
+#-------------------------------------------------------------------------------------
+sub DataObst {
+   if($numobs <= 0) {
+      $msg = "There are no observation types selected!"
+         . "\nGo to File/Input to load a new Rinex data file";
+      PopNotice("Error: no obs types",$msg);
+      return;
+   }
+
+   Status("Select observations to be plotted.");
+   my $SLdb = $w_top->DialogBox(
+      -title => 'Select observation type(s) for plotting',
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor=> 'n',
+      -popanchor => 'n',
+   );
+   my (@cb);
+   my $rows = 6;
+   my ($r,$c,$cols)=(1,0,int 1+$numobs/$rows);
+
+   # label and frame for obs types
+   $SLdb->add('Label',
+      -text => "\nSelect Obs Type(s)",
+      -justify => 'center')->grid(-columnspan => "$cols");
+   my $f2=$SLdb->Frame(-borderwidth => 2, -relief => 'groove')->grid();
+   foreach $i (0..$numobs-1) {
+      $cb[$i] = $f2->Checkbutton(
+         -text => $obslist[$i] . " = " . $obsdesc[$i],
+         -variable => \$obsselect[$i],
+         -relief => 'flat')->grid(-sticky => 'w');
+   }
+   # frame for two buttons
+   $dummy = $numobs+1;
+   my $f1 = $f2->Frame(-borderwidth => 2, -relief => 'flat'
+      )->grid(-row => $dummy, -columnspan => "$cols");
+   my $cb2 = $f1->Button(
+      -text => 'Clear',
+      -width => '15',
+      -relief => 'groove',
+      -command => \&ClearObs);
+   my $cb1 = $f1->Button(
+      -text => 'All',
+      -width => '15',
+      -relief => 'groove',
+      -command => \&SetAllObs)->grid($cb2);
+
+   # save a copy to see when sats are un-selected
+   my @saveselect = @obsselect;
+   $ans = $SLdb->Show();
+   if($ans eq "Cancel") {
+      @obsselect = @saveselect;
+      return;
+   }
+
+   $dummy = 0;
+   foreach $i (0..$numobs-1) {
+      if($saveselect[$i] != $obsselect[$i]) {
+         $Reconfigure = 1;
+         $ans = `rm -f $CFG{'datafile'}`; if($CFG{'verbose'} eq 'on') { print LOG $ans; }
+      }
+      if($obsselect[$i] != 0) {
+         if($dummy == 0) { print LOG "DataSet/ObsTypes Selected Obs:"; $dummy=1; }
+         print LOG "  $obslist[$i]";
+         $nobssel++;
+      }
+   }
+   if($dummy == 1) { print LOG "\n"; }
+   Status("Observations selected ... Refresh?");
+}
+
+#-------------------------------------------------------------------------------------
+sub DataTime {
+   # put up a dialog box with number entry box
+   Status("Dataset/Times Enter new time limits");
+   my $DTdb = $w_top->DialogBox(
+      -title => 'Time Limits',
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+
+   ($begW,$begS) = split(',',$CFG{'begin'});
+   ($endW,$endS) = split(',',$CFG{'end'});
+
+   $DTdb->add('Label',-text => "  GPS   Week  Seconds of week")->pack(-anchor => 'w');
+   my $f1=$DTdb->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+   #$DTdb->add('Label')->pack(-anchor => 'w');
+   my $f2=$DTdb->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+
+   $f1->Label(-text => "Begin : ")->pack(-side => 'left');
+   my $e1=$f1->Entry(-textvariable => \$begW, -width => 5)->pack(-side => 'left');
+   $f1->Entry(-textvariable => \$begS, -width => 15)->pack(-side => 'left');
+   $f1->Button(-text => 'Clear', -width => '10', -relief => 'groove',
+      -command => \&ClearDTL)->pack(-side => 'right', -padx => '10');
+
+   $f2->Label(-text => "End    : ")->pack(-side => 'left');
+   $f2->Entry(-textvariable => \$endW, -width => 5)->pack(-side => 'left');
+   $f2->Entry(-textvariable => \$endS, -width => 15)->pack(-side => 'left');
+   $f2->Button(-text => 'Set to Data', -width => '10', -relief => 'groove',
+      -command => \&SetDTL)->pack(-side => 'right', -padx => '10');
+
+   $e1->focus;
+   $e1->icursor('end');
+   $ans = $DTdb->Show();
+   if($ans eq 'Ok') {
+      print LOG "Dataset/Time limits are ($begW,$begS)-($endW,$endS)\n";
+      $CFG{'begin'} = "$begW,$begS";
+      $CFG{'end'} = "$endW,$endS";
+   }
+   Status('');
+}
+sub ClearDTL {
+   $begW = 0;
+   $begS = 0;
+   $endW = 9999;
+   $endS = 0;
+}
+sub SetDTL {
+   ClearDTL();
+   if($numsvs <= 0) { return; }
+   my $firstime = 1;
+   foreach $i (0..$numsvs-1) {
+      if($svselect[$i] != 0) {
+         if($firstime) {
+            $begW = $svbegin[$i][0];
+            $begS = $svbegin[$i][1];
+            $endW = $svend[$i][0];
+            $endS = $svend[$i][1];
+            $firstime = 0;
+         }
+         else {
+            if( $svbegin[$i][0] <  $begW ||
+                   ($svbegin[$i][0] == $begW && $svbegin[$i][1] < $begS)) {
+               $begW = $svbegin[$i][0];
+               $begS = $svbegin[$i][1];
+            }
+            if( $svend[$i][0] >  $endW ||
+                   ($svend[$i][0] == $endW && $svend[$i][1] > $endS)) {
+               $endW = $svend[$i][0];
+               $endS = $svend[$i][1];
+            }
+         }
+      }
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub DataConf {
+   #PopNotice("DataSet/Configure","DataSet/Configure is not yet implemented");
+   # Iono height
+   # debiasing
+   # $CFG{'debias'}
+   my ($db,$ih)=($CFG{'debias'},$CFG{'ionoht'});
+   Status("Modify data settings");
+   my $DCdb = $w_top->DialogBox(
+      -title =>  "Data configuration",
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $DCdb->add('Label',-text => "Data Settings:")->pack(-anchor => 'w');
+   # debias limit for ResCor
+   my $f1=$DCdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f1->Label(-text => " Limit on ResCor debias reset :"
+      )->pack(-side => 'top', -anchor => 'w');
+   $f1->Label(-text => " ")->pack(-side => 'left');
+   my $e1=$f1->Entry(-textvariable => \$db, -width => '20')->pack(-side => 'left');
+
+   # put it up
+   $ans = $DCdb->Show();
+   if($ans eq 'Ok') {
+      $CFG{'debias'} = $db;
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub SetAllSats {
+   if($numsvs <= 0) { return; }
+   foreach $i (0..$numsvs-1) { $svselect[$i] = 1; }
+   $nsvssel = $numsvs;
+   $FirstAutoscale = 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub ClearSats {
+   if($numsvs <= 0) { return; }
+   foreach $i (0..$numsvs-1) {
+      if($svselect[$i] != 0) {
+         $filename = "$CFG{'datafile'}";
+         if(-e $filename) {
+            $dummy = `rm -f $filename`;
+            if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+         }
+      }
+      $svselect[$i] = 0;
+   }
+   $nsvssel = 0;
+   $FirstAutoscale = 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub SetAllObs {
+   if($numobs <= 0) { return; }
+   foreach $i (0..$numobs-1) { $obsselect[$i] = 1; }
+   $nobssel = $numobs;
+   $FirstAutoscale = 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub ClearObs {
+   if($numobs <= 0) { return; }
+   foreach $i (0..$numobs-1) { $obsselect[$i] = 0; }
+   $nobssel = 0;
+   $FirstAutoscale = 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub ConfigureCurves {
+   # each curve defined by Sat, OT, column in file, color, symbol type
+   # this routine must be called whenever selected svs OR selected ot changes
+   # when selected ot changes, the file $CFG{'datafile'} must be deleted
+   # if not already called, call this routine: Refresh, Graph/Curves, Graph/Limits
+   if($Reconfigure == 0) { return; }
+   print LOG "Configure curves:";
+   WaitCursor();
+   Status("Configure curves from selected data...please wait...");
+   $ncurv = 0;
+   $dummy = 0;       # count colors
+   # count symbols, if symbols 'on' (symbols[0]='none')
+   if($CFG{'points'} eq 'on') { $ans = 1; } else { $ans = 0; }
+   @curvSV = ();       # satellite for this curve
+   @curvOT = ();       # obs type (from @obslist)
+   @curvON = ();       # switch to turn off curve, on is non-zero
+   @curvCol = ();      # column in $CFG{'datafile'} where data is found
+   @curvLines = ();    # plot with lines or not (0)
+   @curvSymbs = ();    # plot with symbols (1-?) or not (0)
+   @curvColor = ();    # color to plot
+   foreach $i (0..$numsvs-1) {
+      if($svselect[$i] != 0) {                  # each satellite <-> file
+         my $n=0;   # n just counts the columns <-> obstype
+         foreach $j (0..$numobs-1) {
+            if($obsselect[$j] != 0) {           # each obstype
+               $curvSV[$ncurv] = $svlist[$i];
+               $curvOT[$ncurv] = $obslist[$j];
+               $curvON[$ncurv] = 1;
+               $curvCol[$ncurv] = 3*($n+1);
+               $n++;
+               $curvColor[$ncurv] = $colors[$dummy];
+               $dummy++; if($dummy > $#colors) { $dummy=0; }
+               if($CFG{'lines'} eq 'on') { $curvLines[$ncurv] = 1; }
+               else { $curvLines[$ncurv] = 0; }
+               $curvSymbs[$ncurv] = $symbols[$ans];
+               if($CFG{'points'} eq 'on') {
+                  $ans++;
+                  if($ans > $#symbols) { $ans=1; }
+               }
+               $ncurv++;
+               print LOG " $ncurv:$svlist[$i]/$obslist[$j]";
+            }
+         }
+      }
+   }
+   print LOG "\n";
+
+   if($ncurv <= 0) { # TD message? popup?
+      NormalCursor();
+      Status('');
+      return;
+   }
+
+   #print LOG "Number svs is $numsvs\n";
+   my $file = $CFG{'Rinex'};
+   if($ResCorfilename ne '') { $file = $ResCorfilename; }
+   $filename = "$CFG{'datafile'}";
+   if(not -e $filename) {
+      Status("Configure curves from selected data...please wait...");
+      $cmd = $CFG{'prgmdir'} . $SLASH . "RinexDump " . $file;
+      foreach $i (0..$numsvs-1) {
+         if($svselect[$i] != 0) { $cmd = $cmd . " $svlist[$i]"; }
+      }
+      foreach $j (0..$numobs-1) {
+         if($obsselect[$j] != 0) { $cmd = $cmd . " $obslist[$j]"; }
+      }
+      $cmd = $cmd . " > $filename";
+      print LOG "Collect data for plotting: Execute $cmd\n";
+      $dummy = `$cmd`; if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+   }
+
+   if($FirstAutoscale == 1) { autoscale(); }
+   if($Reconfigure == 1 && $UsingDefaults) { DefaultLabels(); }
+   $Reconfigure = 0;
+   NormalCursor();
+   Status("Curves configured...Refresh?");
+}
+
+#-------------------------------------------------------------------------------------
+sub DefaultLabels {
+   print LOG "Configure default labels\n";
+   WaitCursor();
+   Status("Configure labels from selected data...please wait...");
+
+   my (@ot, at sv, at un);
+   my %SVseen = ();
+   my %OTseen = ();
+   my %UNseen = ();
+   foreach $i (0..$ncurv-1) {
+      if($curvON[$i] != 0) {
+         # TD decide if they are plotted on left or right
+         unless($SVseen{$curvSV[$i]}) {
+            $SVseen{$curvSV[$i]} = 1;
+            push @sv, $curvSV[$i];
+         }
+         unless($OTseen{$curvOT[$i]}) {
+            $OTseen{$curvOT[$i]} = 1;
+            push @ot, $curvOT[$i];
+         }
+         unless($UNseen{$ExtUnit{$curvOT[$i]}}) {
+            $UNseen{$ExtUnit{$curvOT[$i]}} = 1;
+            push @un, $ExtUnit{$curvOT[$i]};
+         }
+      }
+   }
+
+   #print LOG "Unique SV @sv\n";
+   #print LOG "Unique OT @ot (@un)\n";
+   if($#un > 0) {
+      print LOG "WARNING: Inconsistent units on left axis (@un).\n";
+   }
+   $CFG{'Tlabel'} = "OTs (@ot) for Sats (@sv) vs. Time";
+   $CFG{'Llabel'} = "@ot (@un)";
+   $CFG{'Rlabel'} = '';
+   $CFG{'Blabel'} ="GPS Seconds of Week $CFG{'Week'}";
+
+   NormalCursor();
+   $UsingDefaults = 1;
+   Status("Labels configured.");
+}
+
+#-------------------------------------------------------------------------------------
+sub CompObst {
+   Status("Select new observation types to create...");
+   $ans = $ETdb->Show();
+   if($ans eq "Cancel") {
+      Status('');
+      return;
+   }
+   Status("New observation types have been selected..." .
+      "go to Compute/Create to compute.");
+
+   $dummy = 0;
+   foreach $i (0..$#ExtOT) {
+      if($ExtSelect[$i] != 0) {
+         if($dummy == 0) { print LOG "Compute/New Selected New Obs:"; $dummy=1; }
+         print LOG " $ExtOT[$i]";
+      }
+   }
+   if($dummy == 1) { print LOG "\n"; }
+}
+
+#-------------------------------------------------------------------------------------
+sub ExtDialogInit {
+   if($#ExtOT == -1) {  # only do this once...
+      $cmd = $CFG{'prgmdir'} . $SLASH . "ResCor -h > syntax.rc";
+      print LOG "Get list of possible new obs types: Execute $cmd\n";
+      $dummy = `$cmd`; if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+      open FILE, "syntax.rc" or die "Error: could not open syntax.rc\n";
+      my @opt = <FILE>;
+      my @ExtROT;
+      $ans = 0;
+      for($i=0; $i <= $#opt; $i++) {
+         chomp $opt[$i];
+         if($opt[$i] =~ /OT Description/) { $ans = 1; }
+         if($ans == 2) {
+            if($opt[$i] =~ /End of list of extended observation types/) { last; }
+            push @ExtROT, $opt[$i];
+            #print LOG "Line $i is $opt[$i]\n";
+         }
+         if($ans == 1 && $opt[$i] =~ /^  -- --/) { $ans = 2; }
+      }
+      for($i=0; $i <= $#ExtROT; $i++) {
+         push @ExtOT, substr($ExtROT[$i],2,2);
+			#print "New OT ",substr($ExtROT[$i],2,2);
+         my $desc = substr($ExtROT[$i],5,20);
+			#print " desc ",$desc;
+         #$desc =~ s/\s*$//;
+			push @ExtDesc, $desc;
+         my @fields = split(" ", substr($ExtROT[$i],26,length($ExtROT[$i])-26));
+         #push @ExtUnit, $fields[0];
+         $ExtUnit{$ExtOT[$i]} = $fields[0];
+			#print " units ",$ExtUnit{$ExtOT[$i]},"\n";
+         my $dep=0;
+         for($j=1; $j <= $#fields; $j++) {
+            if($fields[$j] eq 'L1') { $dep |= $DepL1; }
+            if($fields[$j] eq 'L2') { $dep |= $DepL2; }
+            if($fields[$j] eq 'P1') { $dep |= $DepP1; }
+            if($fields[$j] eq 'P2') { $dep |= $DepP2; }
+            if($fields[$j] eq 'EP') { $dep |= $DepEP; }
+            if($fields[$j] eq 'PS') { $dep |= $DepPS; }
+         }
+         push @ExtDep, $dep;
+         push @ExtSelect, 0;
+         #for($j=1; $j <= $#fields; $j++) { print LOG "/$fields[$j]"; }  # depends
+         #print LOG "\n";
+      }
+      #print LOG "Extended types:\n";
+      #for($i=0; $i <= $#ExtROT; $i++) {
+      #print LOG "$ExtOT[$i] $ExtDesc[$i] $ExtUnit{$ExtOT[$i]} dep(";
+      #if($ExtDep[$i] & $DepL1) { print LOG " L1"; }
+      #if($ExtDep[$i] & $DepL2) { print LOG " L2"; }
+      #if($ExtDep[$i] & $DepP1) { print LOG " P1"; }
+      #if($ExtDep[$i] & $DepP2) { print LOG " P2"; }
+      #if($ExtDep[$i] & $DepEP) { print LOG " EP"; }
+      #if($ExtDep[$i] & $DepPS) { print LOG " PS"; }
+      #print LOG ")\n";
+      #}
+
+      #now make up the dialog box
+      $ETdb = $w_top->DialogBox(
+         -title => 'Select extended observation type for processing',
+         -buttons => ['Ok','Cancel'],
+         -default_button => 'Ok',
+         -popover => $w_top,
+         -overanchor=> 'n',
+         -popanchor => 'n',
+      );
+      my (@cb);
+      my $rows = 1+$#ExtOT/3;
+      my ($r,$c,$cols)=(1,0,3);
+      $ETdb->add('Label',
+         -text => "Select new observation type",
+         -justify => 'center')->grid(-columnspan => "$cols");
+      my $f1=$ETdb->Frame(-borderwidth => 2, -relief => 'groove')->grid();
+      foreach $i (0..$#ExtOT) {
+         my $cb = $f1->Checkbutton(
+            -text => $ExtOT[$i] . " : " . $ExtDesc[$i],
+            -variable => \$ExtSelect[$i],
+            -relief => 'flat')->grid(
+            -sticky => 'w',
+            -row => $r,
+            -column => $c,
+            -ipadx => '2');
+         $r++;
+         if($r > $rows) { $r = 1; $c++; }
+      }
+      my $cb1 = $f1->Button(
+         -text => 'Clear',
+         -width => '15',
+         -relief => 'groove',
+         -command => \&ClearExot)->grid(-columnspan => "$cols");
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub ClearExot {
+   foreach $i (0..$#ExtOT) { $ExtSelect[$i] = 0; }
+}
+
+#-------------------------------------------------------------------------------------
+sub CompNavf {
+   Status("Get the name of a Rinex navigation file...");
+   $filename = $w_top->getOpenFile(-title => 'Choose a Rinex nav file');
+   if($filename eq '') { return; }
+   Status("The Rinex navigation file is $filename");
+   # ought to check that it is a Rinex nav file...
+   $CFG{'nav'} = $filename;
+}
+
+#-------------------------------------------------------------------------------------
+sub CompPosnSele {
+   Status("Select a receiver position");
+   $buffer='';
+   $len=0;
+   if (not open(F, "<positions.txt")) {
+      $w_top->Dialog(
+         -title => 'File \'positions.txt\' not found',
+         -text => "Could not open the file \'positions.txt\'",
+         -bitmap => 'error',
+      )->Show;
+      return;
+   }
+   $len = read(F, $buffer, 100000);
+   #print LOG "Read $len bytes\n";
+   close F;
+   my $CPdb = $w_top->DialogBox(
+      -title => "Compute/Receiver position",
+      -buttons => ['Ok','New','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $CPdb->Label(
+      -text => "Select a receiver position by highlighting\n"
+         . "a selection and pushing \'Ok\'.\n\n"
+         . "( The current Rinex observation file name is\n" . $CFG{'Rinex'}
+         . " )\n",
+      -justify => 'left',
+   )->pack(-side => 'top',-anchor => 'w');
+   my $list_win = $CPdb->Scrolled('Listbox',
+      -setgrid => 'true',
+      -selectmode => 'single',
+      -scrollbars => 'e',
+      -height => '20',   #lines
+      -width => '40',   #characters
+      );
+   $list_win->pack(-expand => 'yes', -fill => 'both');
+   #$list_win->bind('<Double-1>' =>
+   #sub {
+   #print LOG "You selected " . $_[0]->get('active') . "\n";
+   #}
+   #);
+   my @list = split("\n", $buffer);
+   $list_win->insert(0, @list);
+   $ans = $CPdb->Show();
+   #print LOG "ans is $ans\n";
+   if($ans eq 'Ok') {
+      #print LOG "You selected " . $list_win->get('active') . "\n";
+      ($RxLabel,$RxX,$RxY,$RxZ,$RxComment) = split('\s+',$list_win->get('active'));
+   }
+   elsif($ans eq 'New') {
+      Status("Compute/Receiver Position : Enter a new Receiver position");
+      my $NRdb = $w_top->DialogBox(
+         -title => 'Enter New Receiver',
+         -buttons => ['Ok','Cancel'],
+         -default_button => 'Ok',
+         -popover => $w_top,
+         -overanchor => 'c',
+         -popanchor => 'c',
+      );
+
+      my ($x,$y,$z,$label,$comment)=('','','','name','');
+
+      my $f1=$NRdb->Frame(-borderwidth => 2, -relief => 'flat'
+         )->pack(-anchor => 'w');
+      $NRdb->add('Label',-text => "\nECEF XYZ Coordinates (meters) :"
+         )->pack(-anchor => 'w');
+      my $f2=$NRdb->Frame(-borderwidth => 2, -relief => 'groove'
+         )->pack(-anchor => 'w');
+      $NRdb->add('Label',-text => "\nComment (optional)"
+         )->pack(-anchor => 'w');
+      my $f3=$NRdb->Frame(-borderwidth => 2, -relief => 'flat'
+         )->pack(-anchor => 'w');
+
+      $f1->Label(-text => "Site label for New Receiver (required, 4 characters) : "
+         )->pack(-side => 'left');
+      my $e1=$f1->Entry(-textvariable => \$label, -width => 5
+         )->pack(-side => 'left');
+      $f2->Label(-text => "X : ")->pack(-side => 'left');
+      $f2->Entry(-textvariable => \$x, -width => 15)->pack(-side => 'left');
+      $f2->Label(-text => "  Y : ")->pack(-side => 'left');
+      $f2->Entry(-textvariable => \$y, -width => 15)->pack(-side => 'left');
+      $f2->Label(-text => "  Z : ")->pack(-side => 'left');
+      $f2->Entry(-textvariable => \$z, -width => 15)->pack(-side => 'left');
+      #$f3->Label(-text => "Comment: ")->pack(-side => 'left');
+      $f3->Entry(-textvariable => \$comment, -width => 60)->pack(-side => 'left');
+
+      $e1->focus;
+      $e1->icursor('end');
+      $ans = $NRdb->Show();
+      if($ans eq 'Ok') {
+         #print LOG "New Rx position is $label, $x, $y, $z, $comment\n";
+         $RxLabel = $label;
+         $RxX = $x;
+         $RxY = $y;
+         $RxZ = $z;
+         $RxComment = $comment;
+         # TD add it to positions.txt
+      }
+   }
+   print LOG "Selected Rx Position is $RxLabel: ($RxX, $RxY, $RxZ) $RxComment\n";
+   Status('');
+}
+
+#-------------------------------------------------------------------------------------
+sub CompPosnRAIM {
+   PopNotice("Compute/Rx Position/RAIM",
+      "Compute/Rx Position/RAIM is not yet implemented");
+}
+
+#-------------------------------------------------------------------------------------
+sub CompConf {
+   my ($cf,$ca)=(0,0);
+   if($CFG{'Cforce'} eq 'on') { $cf=1; }
+   if($CFG{'Callow'} eq 'on') { $ca=1; }
+   Status("Modify computation configuration as preferred");
+   my $CCdb = $w_top->DialogBox(
+      -title =>  "Computation configuration",
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $CCdb->add('Label',-text => "Computation Configuration")->pack(-anchor => 'w');
+   my $f2=$CCdb->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+   $f2->Checkbutton(
+         -text => "Allow C1 to replace missing P1",
+         -variable => \$ca,
+         -relief => 'flat')->pack(-anchor => 'w');
+
+   my $f1=$CCdb->Frame(-borderwidth => 2, -relief => 'flat')->pack(-anchor => 'w');
+   $f1->Checkbutton(
+         -text => "Force C1 to replace P1",
+         -variable => \$cf,
+         -relief => 'flat')->pack(-anchor => 'w');
+
+   $ans = $CCdb->Show();
+   if($ans eq 'Ok') {
+      if($ca) { $CFG{'Callow'} = 'on'; } else { $CFG{'Callow'} = 'off'; }
+      if($cf) { $CFG{'Cforce'} = 'on'; } else { $CFG{'Cforce'} = 'off'; }
+   }
+   Status('');
+}
+
+#-------------------------------------------------------------------------------------
+sub CompCrea {
+   Status("Create new observation types by computation...");
+   my ($depend,$have,$ok,$need)=(0,0,0,0);
+   my @opt = ();  # options passed to ResCor
+   $dummy = 0;    # count the number of selected ExtOT
+   for($i=0; $i <= $#ExtOT; $i++) {
+      if($ExtSelect[$i] != 0) { $dummy++; $depend |= $ExtDep[$i]; }
+   }
+   if($dummy == 0) {
+      $msg = "There are no extended obs types defined.\n"
+         . "Go to Compute/Select New Obs types to make a choice      ";
+      PopNotice("Error: no new obs types",$msg);
+      return;
+   }
+
+   foreach $i (0..$numobs-1) {
+      if($obslist[$i] eq 'C1') { $have |= $DepC1; }
+      if($obslist[$i] eq 'L1') { $have |= $DepL1; }
+      if($obslist[$i] eq 'L2') { $have |= $DepL2; }
+      if($obslist[$i] eq 'P1') { $have |= $DepP1; }
+      if($obslist[$i] eq 'P2') { $have |= $DepP2; }
+      if($CFG{'nav'} ne '') { $have |= $DepEP; }
+      if($RxLabel ne '') { $have |= $DepPS; }
+   }
+   #if(($have & $DepC1) && ($have & $DepP1)) {
+      #print LOG "Dep bits for C1 and P1 are " . sprintf("%X %X\n",$DepC1,$DepP1);
+      #print LOG "old have is " . sprintf("%X",$have) . "\n";
+      #$have |= $DepP1;
+      #$have &= ~($DepC1);
+      #print LOG "new have is " . sprintf("%X",$have) . "\n";
+   #}
+   $ok = $depend & $have;   # those we need AND have
+   $need = $ok ^ $depend;   # those we need but do NOT have
+   #print "Have $have\n";
+   #print "Depend $depend\n";
+   #print "Need and have $ok\n";
+   #print "Need and don't have $need\n";
+   # can C1 substitute for P1?
+   if(($need & $DepP1) && ($have & $DepC1)
+      && ($CFG{'Callow'} eq 'on' || $CFG{'Cforce'} eq 'off')) {
+      $need &= ~($DepP1);
+   }
+   if($need != 0) {
+      my $needdata = 0;
+      $msg = "The selected new observation types cannot            \n"
+         . "be computed because some input is missing :\n\n";
+      if($need & $DepL1) { $msg = $msg . " L1 is missing\n"; $needdata++; }
+      if($need & $DepL2) { $msg = $msg . " L2 is missing\n"; $needdata++; }
+      if($need & $DepP1) {
+         if($have & $DepC1) {
+            if($CFG{'Callow'} eq 'off') {
+               $msg = $msg . " P1 is missing (Hint: Select 'Allow use of C1'\n"
+               . "   under Compute/Configure)\n";
+               $needdata++;
+            }
+         }
+         else { $msg = $msg . " P1 is missing\n"; $needdata++; }
+      }
+      if($need & $DepP2) { $msg = $msg . " P2 is missing\n"; $needdata++; }
+      #if($need & $DepEP) { $msg = $msg . " EP is missing\n"; }
+      #if($need & $DepPS) { $msg = $msg . " PS is missing\n"; }
+      #$msg = $msg . "\n";
+      if($needdata != 0) { $msg = $msg
+         . "\n-> Computation cannot proceed with this obs file.         \n";
+      }
+      if($need & $DepEP) { $msg = $msg
+         . "\n-> Go to Compute/Nav File to select a navigation file.        \n";
+      }
+      if($need & $DepPS) { $msg = $msg
+         . "\n-> Go to Compute/Rx Position to input a receiver position.         \n";
+      }
+      PopNotice("Error: Some input missing",$msg);
+      #$ans = $w_top->messageBox(-title => 'Error: Some input missing',
+      #-message => $msg, -type => 'OK', -icon => 'info');
+      return;
+   }
+
+   # call ResCor
+   WaitCursor();
+   Status("Creating new observation types...please wait");
+   $ResCorfilename = basename($CFG{'Rinex'}) . ".rc";
+   print LOG "Create new obs types; output to file $ResCorfilename\n";
+   push @opt, "-IF$CFG{'Rinex'}";
+   push @opt, "-OF$ResCorfilename";
+   if($CFG{'nav'} ne '') { push @opt, "--nav $CFG{'nav'}"; }
+   if($RxX ne '') { push @opt, "--RxXYZ $RxX,$RxY,$RxZ"; }
+   #TD allow this to be configured
+   foreach $i (0..$#ExtOT) { if($ExtSelect[$i] != 0) {
+      push @opt, "-AO$ExtOT[$i]";
+      if($MayDebias{$ExtOT[$i]}) {
+         push @opt, "--debias $ExtOT[$i],$CFG{'debias'}";
+      }
+   } }
+   # use C1 when P1 is not there
+   if(($have & $DepC1) && $CFG{'Callow'} eq 'on') { push @opt, "--Callow"; }
+   if($CFG{'Cforce'} eq 'on') { push @opt, "--Cforce"; }
+   push @opt, "--verbose";
+
+   $cmd = $CFG{'prgmdir'} . $SLASH . "ResCor " . " @opt";
+   
+   print LOG "Create new obs types: Execute $cmd\n";
+   $buffer = `$cmd`;
+   #print LOG "Result is\n$buffer\n";
+   Status("New observation types are ready");
+   $RinexRCSummary = $ResCorfilename . ".sum";
+   $cmd = $CFG{'prgmdir'} . $SLASH . "RinSum -g -i" . $ResCorfilename
+      . " > $RinexRCSummary";
+   Status("Summarizing new obs file $filename...please wait...");
+   print LOG "Summarize obs file: Execute $cmd\n";
+   $dummy = `$cmd`; if($CFG{'verbose'} eq 'on') { print LOG $dummy; }
+   if($CFG{'autoview'} eq 'on') { FileSumm(); }
+   $ans = DataGather($RinexRCSummary);
+   NormalCursor();
+   Status("Summary complete for $filename.");
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapLims {
+   #print LOG "Graph/Limits\n";
+   ConfigureCurves();
+
+   # put up a dialog box with number entry box
+   Status("Graph/Limits: enter new limits, or autoscale (determine from data)");
+   my $DLdb = $w_top->DialogBox(
+      -title => 'Plotting Limits',
+      -buttons => ['Ok','Auto','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+
+   ($YMin,$YMax,$XMin,$XMax)=($CFG{'YMin'},$CFG{'YMax'},$CFG{'XMin'},$CFG{'XMax'});
+
+   $DLdb->add('Label',-text => "Y: data")->pack(-anchor => 'w');
+   my $f1=$DLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $DLdb->add('Label',-text => "X: time")->pack(-anchor => 'w');
+   my $f2=$DLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+
+   #$f1->Label(-text => "Y: data :    ")->pack(-side => 'left');
+   my $e1=$f1->Entry(-textvariable => \$YMin, -width => 15)->pack(-side => 'left');
+   $f1->Entry(-textvariable => \$YMax, -width => 15)->pack(-side => 'left');
+
+   #$f2->Label(-text => "X: time : ")->pack(-side => 'left');
+   $f2->Entry(-textvariable => \$XMin, -width => 15)->pack(-side => 'left');
+   $f2->Entry(-textvariable => \$XMax, -width => 15)->pack(-side => 'left');
+
+   $e1->focus;
+   $e1->icursor('end');
+   $ans = $DLdb->Show();
+   if($ans eq 'Ok') {
+      print LOG "Graph/Limits are Y: $YMin, $YMax, X: $XMin, $XMax\n";
+      NewScale('fixed','Graph/Limits');
+      Refresh();
+   }
+   elsif($ans eq 'Auto') {
+      autoscale();
+      Refresh();
+   }
+   else { Status(''); }
+}
+
+#-------------------------------------------------------------------------------------
+sub AutoScale {
+   my $fas=$FirstAutoscale;
+   ConfigureCurves();
+   if($fas == 0) { autoscale(); Refresh(); }
+}
+
+#-------------------------------------------------------------------------------------
+# compute max and min y and x from file(s) -- consider time limits
+sub autoscale {
+   Status("Autoscale - redefine limits using data min/max");
+   $filename = "$CFG{'datafile'}";
+   open FILE, "$filename" or die "Error: autoscale could not open $filename\n";
+   my ($first,$firstime,$thistime,$sat)=(1,1,0,'');
+   my ($bw,$bs)=split(',',$CFG{'begin'});
+   my ($ew,$es)=split(',',$CFG{'end'});
+   while(<FILE>) {
+      chomp;
+      $_ =~ s/^\s_//;
+      if(m/Rinexdump/) { next; }
+      if(m/Week/) { next; }
+      s/^\s+//;               # no leading white space
+      @opt = split(/\s+/, $_);
+      #$week = $opt[0];
+      #$sow = $opt[1];
+      next if($opt[0] < $bw);
+      next if($opt[0] == $bw && $opt[1] < $bs);
+      last if($opt[0] > $ew);
+      last if($opt[0] == $ew && $opt[1] > $es);
+      $sat = $opt[2];
+      $thistime = 0;
+      foreach $j (0..$ncurv-1) {
+         if($curvON[$j] == 0) { next; }
+         if($curvSV[$j] eq $sat) {
+            $thistime = 1;
+            if($first == 1) {
+               $YMax = $opt[$curvCol[$j]];
+               $YMin = $opt[$curvCol[$j]];
+               $first = 0;
+            }
+            else {
+               if($opt[$curvCol[$j]] < $YMin) { $YMin = $opt[$curvCol[$j]]; }
+               if($opt[$curvCol[$j]] > $YMax) { $YMax = $opt[$curvCol[$j]]; }
+            }
+         }
+      }
+      if($thistime == 1) {
+         if($firstime == 1) {
+            $XMax = $opt[1];
+            $XMin = $opt[1];
+            $CFG{'Week'} = $opt[0];
+            $firstime = 0;
+         }
+         else {
+            $i = ($opt[0]-$CFG{'Week'})*604800 + $opt[1];
+            if($i < $XMin) { $XMin = $i; }
+            if($i > $XMax) { $XMax = $i; }
+         }
+      }
+   }
+
+   if($firstime == 1) {
+      PopNotice("Graph/Autoscale","Autoscale finds no data (within time limits)!");
+   }
+   else {
+      # NewScale requires $XMin,$XMax,$YMin,$YMax as input
+      NewScale('auto','Autoscale');
+   }
+   if($UsingDefaults) { DefaultLabels(); }
+}
+
+#-------------------------------------------------------------------------------------
+sub NiceScale {
+   # call with args Min Max Ntics Npixels
+   # results stored in $ScaleMin $ScaleStep $ScaleExp $ScaleN
+   my ($dmin,$dmax,$ntic,$npix)=@_;
+   #print LOG "NiceScale called with $dmin $dmax $ntic $npix\n";
+   ($ScaleMin,$ScaleStep,$ScaleExp,$ScaleN,$ScalePPL)=(0,0,0,0);
+   if($dmax < $dmin) { $dummy=$dmax; $dmax=$dmin; $dmin=$dummy; }
+   if($npix < 2 || $npix > 10000) {
+      print LOG "NiceScale: Number of pixels is unreasonable\n";
+      return;
+   }
+   $ScalePPL = ($npix-1)/($ntic-1);        # pixels per label
+   if($ScalePPL <= 0 || $ScalePPL >= $npix) {
+      print LOG "NiceScale: Number of pixels per label is unreasonable\n";
+      return;
+   }
+   $ScaleN = 1+($npix-1)/$ScalePPL;
+   $dummy = abs($dmax);
+   if(abs($dmin) > $dummy) { $dummy=abs($dmin); }
+   if($dmax-$dmin <= 5.0e-8 * $dummy) {   # effectively equal limits
+      if($dmax < 0)     { $dmax=0.0; }
+      elsif($dmax == 0) { $dmax=1.0; }
+      elsif($dmax > 0)  { $dmax=0.0; }
+   }
+   my $finter=$npix/$ScalePPL;
+   #print LOG "PPL is $ScalePPL, and finter is $finter\n";
+   my $scale=($dmax-$dmin)*1.00002/$finter;
+   $ScaleExp = 0;
+   while($scale <= 10) { $scale *= 10; $ScaleExp++; }
+   while($scale > 100) { $scale /= 10; $ScaleExp--; }
+   #print LOG "scale is $scale\n";
+   #print LOG "Exponent is $ScaleExp\n";
+   my $iunit;
+   foreach $i (1..$#NiceUnits-1) {
+      $iunit = $i;
+      if($scale <= $NiceUnits[$i]) { last; }
+   }
+   #print LOG "NiceUnit[$iunit] is $NiceUnits[$iunit]\n";
+   my ($istay,$aj,$tstep)=(1,0,0);
+   while($istay == 1) {
+      $ScaleStep = $NiceUnits[$iunit] / (10.0 ** $ScaleExp);
+      $aj = 0;
+      while(1) {
+         $aj++;
+         $dummy = int(($NiceUnits[$iunit]+0.1)/$aj);
+         if($NiceUnits[$iunit]-0.1 > $aj*$dummy) { next; }
+         $tstep = $ScaleStep/$aj;
+         $dummy = $dmin/$tstep + $aj*(0.5/$ScalePPL - $finter*1.0e-5);
+         $ScaleMin = $tstep * int($dummy);
+         if($dummy < 0 && $dummy != int($dummy)) { $ScaleMin -= $tstep; }
+         if($dmax < $ScaleMin + $ScaleStep*($finter*0.99999-(0.5/$ScalePPL))) {
+            $istay=0; last;
+         }
+         $dummy = 1.0 - 1.0/($aj * $finter);
+         # TD this fails occasionally: divide by zero
+         if(($NiceUnits[$iunit]/$NiceUnits[$iunit+1])*$dummy < 0.7) {
+            next;
+         }
+         $iunit++;
+         if($iunit > 11) { $istay=0; last; }   # TD ??
+      }
+   }
+   foreach $i (1,2) {
+      $aj *= 10;
+      if($NiceUnits[$iunit]-0.1 < $aj*int(($NiceUnits[$iunit]+0.1)/$aj)) {
+         $ScaleExp--;
+      }
+   }
+
+   #print LOG "NiceScale finds exp $ScaleExp ";
+   #foreach $i (0..$ScaleN-1) {
+   #   $dummy = $ScaleMin + $i * $ScaleStep;
+   #   # $dummy are labels; multiply by 10**ScaleExp to make them all integers
+   #   if($ScaleExp > 0) { print LOG sprintf(" %.$ScaleExp"."f",$dummy); }
+   #   else { print LOG " $dummy"; }
+   #}
+   #print LOG "\n";
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapAxes {
+   PopNotice("Graph/Axes","Graph/Axes is not yet implemented");
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapLabe {
+   Status("Labels on the graph");
+   my $GLdb = $w_top->DialogBox(
+      -title =>  "Graph Labels",
+      -buttons => ['Ok','Default','Clear'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $GLdb->add('Label',-text => "Title:")->pack(-anchor => 'w');
+   my $f1=$GLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $GLdb->add('Label',-text => "Bottom (X) axis label:")->pack(-anchor => 'w');
+   my $f2=$GLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $GLdb->add('Label',-text => "Left (Y) axis label:")->pack(-anchor => 'w');
+   my $f3=$GLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $GLdb->add('Label',-text => "Right (Y) axis label:")->pack(-anchor => 'w');
+   my $f4=$GLdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+
+   my $len = length($CFG{'Tlabel'});
+   if($len < length($CFG{'Blabel'})) { $len = length($CFG{'Blabel'}); }
+   if($len < length($CFG{'Llabel'})) { $len = length($CFG{'Llabel'}); }
+   if($len < length($CFG{'Rlabel'})) { $len = length($CFG{'Rlabel'}); }
+   if($len < 20) { $len = 20; }
+
+   my $e1=
+   $f1->Entry(-textvariable => \$CFG{'Tlabel'},-width => $len)->pack(-side => 'left');
+   $f2->Entry(-textvariable => \$CFG{'Blabel'},-width => $len)->pack(-side => 'left');
+   $f3->Entry(-textvariable => \$CFG{'Llabel'},-width => $len)->pack(-side => 'left');
+   $f4->Entry(-textvariable => \$CFG{'Rlabel'},-width => $len)->pack(-side => 'left');
+
+   $ans = $GLdb->Show();
+   $e1->focus; $e1->icursor('end');
+   if($ans eq 'Default') {
+      DefaultLabels();
+      GrapLabe();
+   }
+   if($ans eq 'Clear') {
+      $CFG{'Tlabel'}='';
+      $CFG{'Blabel'}='';
+      $CFG{'Llabel'}='';
+      $CFG{'Rlabel'}='';
+      GrapLabe();
+   }
+   else {
+      $UsingDefaults = 0;
+   }
+   Status('');
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapCurv {
+   ConfigureCurves();
+
+   if($ncurv <= 0) {
+      $msg = "There are no curves defined.\n" .
+         "Go to DataSet/Select to make a choice      ";
+      PopNotice("Error: no curves",$msg);
+      return;
+   }
+
+   Status("Curves to be plotted");
+   #PopNotice("Graph/Curves","Graph/Curves is not yet implemented");
+   my $GCdb = $w_top->DialogBox(
+      -title => 'Curves defined for plotting',
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor=> 'n',
+      -popanchor => 'n',
+   );
+
+   $GCdb->add('Label', -text => "CURVES", -justify => 'center')->pack(-side => 'top');
+   $GCdb->add('Label', -text =>
+      ' N   On          Sat         OT       Color    Line      Symbol              ',
+      #-font => 'C_small',
+      -justify => 'left')->pack(-side => 'top');
+   my @fr;   # frame for each curve
+   my @sats = @curvSV;
+   my @otss = @curvOT;
+   my @swit = @curvON;
+   my @line = @curvLines;
+   my @symb = @curvSymbs;
+   my @colo = @curvColor;
+   my @cb;
+   $dummy = '   ';
+   foreach $i (0..$ncurv-1) {
+      # frame for each curve
+      $fr[$i]=$GCdb->Frame(-borderwidth => 2, -relief => 'groove'
+         )->pack(-side => 'top');
+      # label and entry for Sat
+      $fr[$i]->Label(-text => sprintf("%2d",$i), -font => 'C_small'
+         )->pack(-side => 'left');
+      $fr[$i]->Checkbutton(-text => '  ', -variable => \$swit[$i],
+         -relief => 'flat')->pack(-side => 'left');
+      $fr[$i]->Label(-text => " ")->pack(-side => 'left');
+      $fr[$i]->Entry(-textvariable => \$sats[$i], -width => 4)->pack(-side => 'left');
+      $fr[$i]->Label(-text => " ")->pack(-side => 'left');
+      $fr[$i]->Entry(-textvariable => \$otss[$i], -width => 3)->pack(-side => 'left');
+      $fr[$i]->Label(-text => "  ")->pack(-side => 'left');
+      # color - colored button calls up color selection dialog
+      $cb[$i] = $fr[$i]->Button(-text => '  ',
+         -width => 2,
+         -background => $colo[$i],
+         -activebackground => $colo[$i],
+         -command => sub {
+            $dummy = $fr[$i]->chooseColor(
+               -title => 'choose a new color',
+               -initialcolor => $colo[$i]);
+            if($dummy ne '') {
+               $colo[$i] = $dummy;
+               $cb[$i]->configure('-background' => $colo[$i],
+                               '-activebackground' => $colo[$i]);
+            }
+         })->pack(-side => 'left', -ipadx => '2');
+      # line checkbox
+      $fr[$i]->Checkbutton(-text => '', -variable => \$line[$i],
+         -relief => 'flat')->pack(-side => 'left', -ipadx => '4');
+      # symbol browseentry
+      $fr[$i]->BrowseEntry(-label => ' ',
+         -variable => \$symb[$i],
+         -choices => \@symbols,
+         -width => 6,
+         -listwidth => 18,
+         -state => 'readonly',
+         )->pack(-side => 'left', -ipadx => '2');
+      #space on the right
+      $fr[$i]->Label(-text => " ")->pack(-side => 'left');
+   }
+
+   $ans = $GCdb->Show();
+   Status('');
+   if($ans eq "Cancel") { return; }
+
+   $dummy = 0;
+   foreach $i (0..$#curvON) {
+      if($curvON[$i] != $swit[$i]) {
+         if($dummy == 0) { print LOG "Graph/Curves toggles"; $dummy=1; }
+         print LOG " $i:";
+         if($swit[$i] == 0) { print LOG "OFF"; } else { print LOG "ON"; }
+      }
+   }
+   if($dummy != 0) { print LOG "\n"; }
+   @curvON = @swit;
+   @curvLines = @line;
+   @curvSymbs = @symb;
+   @curvColor = @colo;
+}
+
+#sub GrapColo {
+#   #print LOG "Graph/Colors\n";
+#   $FCOLOR= $w_top->chooseColor(-title => "Choose color for plot",
+#      -initialcolor => $FCOLOR);
+#   print LOG "New color is $FCOLOR\n";
+#}
+
+#-------------------------------------------------------------------------------------
+sub GrapZoom {
+   # (for some reason, when called from menu, arguments are different)
+   my $io;
+   ($dummy, $io) = @_;           # ignore dummy here
+   $dummy = $CFG{'zoomX'} / 2;
+
+   my $size=$CFG{'XMax'}-$CFG{'XMin'};
+   if($io eq 'In') { $size = -$size; }
+   $XMin = $CFG{'XMin'} - $CFG{'zoomX'} * $size;
+   $XMax = $CFG{'XMax'} + $CFG{'zoomX'} * $size;
+
+   $size=$CFG{'YMax'}-$CFG{'YMin'};
+   if($io eq 'In') { $size = -$size; }
+   $YMin = $CFG{'YMin'} - $CFG{'zoomX'} * $size;
+   $YMax = $CFG{'YMax'} + $CFG{'zoomX'} * $size;
+
+   # NewScale requires $XMin,$XMax,$YMin,$YMax as input
+   NewScale('fixed','Graph/Zoom');
+   Refresh();
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapUnzo {
+   if($OldXMin eq '') { return; }
+   ($XMin,$XMax,$YMin,$YMax) = ($OldXMin,$OldXMax,$OldYMin,$OldYMax);
+   # NewScale requires $XMin,$XMax,$YMin,$YMax as input
+   NewScale('fixed','Graph/Unzoom');
+   Refresh();
+}
+
+#-------------------------------------------------------------------------------------
+sub GrapGnup {
+   PopNotice("Graph/Gnuplot","Graph/Gnuplot is not yet implemented.");
+}
+
+#-------------------------------------------------------------------------------------
+sub HelpTopi {
+   #PopNotice("Help/Topics","Help/Topics is    \nnot yet implemented.");
+
+   #if(! Exists($HTmw)) {  # not necessary
+   my $HTmw = MainWindow->new();
+   $HTmw->title("Help on RinexPlot");
+   my $b = $HTmw->Button(
+      -text => 'Close this Help window',
+      -width => '40',
+      -command => sub { $HTmw->withdraw },
+      )->pack();
+   my $t = $HTmw->Scrolled('ROText',
+      -setgrid => 'true',
+      -width => '65',
+      -height => '30',
+      -font => 'normal',
+      -wrap => 'word',
+      -scrollbars => 'e');
+   $t->pack(qw/-expand yes -fill both/);
+
+   # Set up display styles.
+   $HTmw->fontCreate(qw/C_small -family courier   -size 10/);
+   $HTmw->fontCreate(qw/C_big   -family courier   -size 14 -weight bold/);
+   $HTmw->fontCreate(qw/C_vbig  -family helvetica -size 24 -weight bold/);
+   $HTmw->fontCreate(qw/C_bold  -family courier -size 12 -weight bold -slant italic/);
+   $t->tag(qw/configure bold    -font C_bold/);
+   $t->tag(qw/configure big     -font C_big/);
+   $t->tag(qw/configure verybig -font C_vbig/);
+   $t->tag(qw/configure small   -font C_small/);
+   if ($HTmw->depth > 1) {
+   $t->tag(qw/configure color1 -background/ => '#a0b7ce'); #this color is "MSWin blue"
+   $t->tag(qw/configure color2 -foreground red/);
+   $t->tag(qw/configure raised -relief raised -borderwidth 1/);
+   $t->tag(qw/configure sunken -relief sunken -borderwidth 1/);
+   } else {
+   $t->tag(qw/configure color1 -background black -foreground white/);
+   $t->tag(qw/configure color2 -background black -foreground white/);
+   $t->tag(qw/configure raised -background white -relief raised -bd 1/);
+   $t->tag(qw/configure sunken -background white -relief sunken -bd 1/);
+   }
+   $t->tag(qw/configure bgstipple  -background black -borderwidth 0
+      -bgstipple gray12/);
+   $t->tag(qw/configure fgstipple  -fgstipple gray50/);
+   $t->tag(qw/configure underline  -underline on/);
+   $t->tag(qw/configure overstrike -overstrike on/);
+   $t->tag(qw/configure right      -justify right/);
+   $t->tag(qw/configure center     -justify center/);
+   $t->tag(qw/configure super      -offset 4p -font C_small/);
+   $t->tag(qw/configure sub        -offset -2p -font C_small/);
+   $t->tag(qw/configure margins    -lmargin1 12m -lmargin2 6m -rmargin 10m/);
+   $t->tag(qw/configure spacing     -spacing1 10p -spacing2 2p
+      -lmargin1 12m -lmargin2 6m -rmargin 10m/);
+
+
+   # now insert text
+   $t->insert('0.0', "RinexPlot is a GUI for the GPS Toolkit (gpstk) utility "
+      . "programs that will read, manipulate and plot data in a Rinex file.\n\n",
+      'big');
+   $t->insert('insert',"Here is the general flow of things:\n");
+   $t->insert('insert',"  1.",'big');
+   $t->insert('insert'," Select a Rinex observation file by choosing "
+     . "'File/Obs file' from the main menu. Wait for the summary window to come up,"
+     . " then close it to return to the main menu.\n");
+   $t->insert('insert',"  2.",'big');
+   $t->insert('insert'," (2-4 Optional) Select new observation types by choosing "
+     . "'Compute/New Data types' from the main menu\n");
+   $t->insert('insert',"  3.",'big');
+   $t->insert('insert'," Choose a Rinex navigation file (Compute/Nav file)"
+     . " and a Receiver position (Compute/Rx position)...these may or may not be "
+     . "required, depending on the choices you made in Step 2 (Step 4 will inform"
+     . " you if more input is needed.\n");
+   $t->insert('insert',"  4.",'big');
+   $t->insert('insert'," Compute the new types by choosing 'Compute/Create new data'."
+     . " (This step is important! - if your new obs types do not show up under"
+     . " Dataset/Obs types, you may have skipped this step.)\n");
+   $t->insert('insert',"  5.",'big');
+   $t->insert('insert'," Select dataset(s) to plot by choosing 'Dataset/Satellites'"
+     . " AND 'Dataset/Obs types'; optionally also limit the plot in time using"
+     . " 'Dataset/Times', and in other ways under Dataset/Configure.\n");
+   $t->insert('insert',"  6.",'big');
+   $t->insert('insert'," Now press 'Refresh' to re-draw the screen...whenever the "
+     . "data and/or graph are changed, press Refresh to draw the new plot.\n");
+   $t->insert('insert',"  7.",'big');
+   $t->insert('insert'," Features of the plot, such as limits and colors, can be seen"
+     . " and set under 'Graph' on the main menu.\n");
+   $t->insert('insert',"  8.",'big');
+   $t->insert('insert'," Preferences and switches can be set under 'Settings' on the"
+     . " main menu.\n");
+  #$t->insert('insert',"  9.",'big');
+  #$t->insert('insert',"\n");
+   $t->insert('insert',"\n(Usually you can look at the status bar at the bottom of "
+     . "the window to get hints about what has just happened or what to do next.)\n");
+
+   $t->insert('insert',"\nSwitches and options\n",'big');
+   $t->insert('insert',"- Summary files automatically shown in window.\n");
+   $t->insert('insert',"- Refresh automatically after selection/zoom/resizing.\n");
+   $t->insert('insert',"- GPS satellites only.\n");
+   $t->insert('insert',"- Output to a log file or the screen\n");
+   $t->insert('insert',"- Directory of GPSTk programs\n");
+
+   $t->insert('insert',"\nCommand line arguments:\n",'big');
+   $t->insert('insert',"  Command line arguments may come in any order, but note that"
+     . " many depend on others and have no effect unless those others are present."
+     . "\n\n");
+   $t->insert('insert',"All command line options:\n");
+   $t->insert('insert',"  [Note pattern: --opt <arg> OR --opt=<arg>]\n",'small');
+   $t->insert('insert',$CmdText,'small');
+
+   $t->insert('insert',"\nMouse things\n",'big');
+   $t->insert('insert',"- Click on any point in the graph; the status bar will show "
+     . "coordinates (both screen and data).\n");
+   $t->insert('insert',"- Click, drag and un-click to make a rectangle on the graph."
+     . " A box pops up allowing you to zoom the plot to the rectangle.\n");
+
+   $t->mark(qw/set insert 0.0/);
+
+   #} else {       # HTmw already exists
+   #$HTmw->deiconify();
+   #$HTmw->raise();
+   #}
+}
+
+#-------------------------------------------------------------------------------------
+sub HelpAbou {
+   #print LOG "Help/About\n";
+   my $HAdb = $w_top->DialogBox(
+      -title => 'About RinexPlot',
+      -buttons => ['Ok'],
+      -popover => $w_top,
+      -overanchor => 'n',
+      -popanchor => 'n',
+   );
+   $HAdb->add('Label', -text => $ABOUT_TEXT,
+      -justify => 'left')->pack;
+   $HAdb->add('Label', -text => $AUTHOR_TEXT,
+      -justify => 'left')->pack;
+   $HAdb->add('Label', -text => "Version is " . $VERSION . "\n",
+      -justify => 'left')->pack;
+   $HAdb->add('Label', -text => "Operating System is $^O",
+      -justify => 'left')->pack;
+   $HAdb->Show();
+}
+
+#-------------------------------------------------------------------------------------
+sub SettPref {
+   # menu tearoff (command line only - before creation) my $menutear = 1; ???
+   # (to do this you would have to scan args before calling init() )
+   my ($av,$ud,$lf,$zf)=(0,$CFG{'prgmdir'},$CFG{'log'},$CFG{'zoomX'});
+   my ($vb,$lr,$lo,$so)=(0,0,0,0);
+   if($CFG{'autoview'} eq 'on') { $av=1; }
+   if($CFG{'verbose'} eq 'on') { $vb=1; }
+   if($CFG{'keepdata'} eq 'on') { $lr=1; }
+   if($CFG{'lines'} eq 'on') { $lo=1; }
+   if($CFG{'points'} eq 'on') { $so=1; }
+   Status("Modify settings of global configuration as preferred");
+   my $SPdb = $w_top->DialogBox(
+      -title =>  "Preferences",
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $SPdb->add('Label',-text => "Global Settings:")->pack(-anchor => 'w');
+   # AutoView
+   my $f1=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f1->Checkbutton(
+         -text => "Autoview: automatically show summary file  ",
+         -variable => \$av,
+         -relief => 'flat')->pack(-anchor => 'w');
+   # Log file name
+   my $f2=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f2->Label(-text => " Output log file :"
+      )->pack(-side => 'top', -anchor => 'w');
+   $f2->Label(-text => " ")->pack(-side => 'left');
+   if($lf eq '') { $lf="SCREEN"; }
+   my $e1=$f2->Entry(-textvariable => \$lf, -width => '20')->pack(-side => 'left');
+   $f2->Checkbutton(-text => "Verbose output", -variable => \$vb, -relief => 'flat',
+      )->pack(-side => 'left');
+   # GPSTk directory
+   my $f3=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f3->Label(-text => " Directory of GPSTk utilities :"
+      )->pack(-side => 'top', -anchor => 'w');
+   $f3->Label(-text => " ")->pack(-side => 'left');
+   my $e1=$f3->Entry(-textvariable => \$ud, -width => '37')->pack(-side => 'left');
+   $e1->focus; $e1->icursor('end');
+   # Default lines
+   my $f4=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f4->Label(-text => "Default curves have:")->pack(-side => 'left');
+   $f4->Checkbutton(
+         -text => "Lines",
+         -variable => \$lo,
+         -relief => 'flat')->pack(-side => 'left');
+   # Default symbols
+   $f4->Checkbutton(
+         -text => "Points     ",
+         -variable => \$so,
+         -relief => 'flat')->pack(-side => 'left');
+   # Zoom factor
+   my $f5=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f5->Label(-text => " Zoom factor (0 < zf < 1) :  ")->pack(-side => 'left');
+   my $e2=$f5->Entry(-textvariable => \$zf, -width => '17')->pack(-side => 'left');
+   $e2->icursor('end');
+   # GPS only
+   #my $f5=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   #$f5->Checkbutton(
+   #      -text => "GPS satellites only                                        ",
+   #      -variable => \$go,
+   #      -relief => 'flat')->pack(-anchor => 'w');
+   # Leave $CFG{'datafile'}
+   my $f6=$SPdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f6->Checkbutton(
+         -text => "Leave data file ($CFG{'datafile'}) after exit                 ",
+         -variable => \$lr,
+         -relief => 'flat')->pack(-anchor => 'w');
+
+   # put it up
+   $ans = $SPdb->Show();
+   if($ans eq 'Ok') {
+      if($av) { $CFG{'autoview'} = 'on'; } else { $CFG{'autoview'} = 'off'; }
+      if($lo) { $CFG{'lines'} = 'on'; } else { $CFG{'lines'} = 'off'; }
+      if($so) { $CFG{'points'} = 'on'; } else { $CFG{'points'} = 'off'; }
+      $CFG{'zoomX'} = $zf;
+      if($lr) { $CFG{'keepdata'} = 'on'; } else { $CFG{'keepdata'} = 'off'; }
+      if($lf eq 'SCREEN') { $CFG{'log'} eq ''; } else {
+         close LOG;
+         $CFG{'log'} = $lf;
+         open LOG, ">$CFG{'log'}" or die "Could not open log file $CFG{'log'}\n";
+         autoflush LOG;
+         print LOG "Log file for RinexPlot\n";
+      }
+      if($vb) { $CFG{'verbose'} = 'on'; }
+      if($ud ne $CFG{'prgmdir'}) {
+         my($ResCor,$RinSum,$RinexDump)=('ResCor','RinSum','RinexDump');
+         if ($^O eq "MSWin32") {
+            $ResCor='ResCor.exe';
+            $RinSum='RinSum.exe';
+            $RinexDump='RinexDump.exe';
+         }
+         if(not -e $ud . $SLASH . $RinSum
+            || not -e $ud . $SLASH . $ResCor
+            || not -e $ud . $SLASH . $RinexDump) {
+            PopNotice("Error:","Error: GPSTk utilities not found in directory $ud.\n"
+               . "Go to Settings/Preferences to change it");
+         }
+         else { $CFG{'prgmdir'} = $ud; }
+      }
+   }
+   Status('');
+}
+
+#-------------------------------------------------------------------------------------
+sub SettCurs {
+   ## Open file that contains all available cursors
+   ## Might have to change this if your cursorfont.h is elsewhere
+   ## On Win32 systems look in C:\Perl\lib\site\Tk\X11\cursorfont.h
+   my $cursorfile='/usr/X11R6/include/X11/cursorfont.h';
+   if ($^O eq "MSWin32") {
+      $cursorfile = '\\perl\\site\\lib\\Tk\\X11\\cursorfont.h';
+   }
+   my @cursors=();
+   open (FH, $cursorfile) or die "Couldn't open cursor file.\n";
+   while (<FH>) { push(@cursors, $1) if (/\#define XC_(\w+) /); }
+   close(FH);
+
+   my $HSdb = $w_top->DialogBox(
+      -title => 'Available cursors',
+      -buttons => ['Ok','Cancel'],
+      -popover => $w_top,
+      -overanchor => 'n',
+      -popanchor => 'n',
+   );
+   my $lab = $HSdb->add('Label',-text =>
+      "Select a cursor name by clicking\n" .
+      "on it, then move the mouse onto\n" .
+      "the canvas to see the result.\n" .
+      "The default cursor is crosshair.\n" .
+      "The current cursor is $cursor")->pack(-anchor => 'w');
+   my $scroll = $HSdb->Scrollbar;
+   my $lb = $HSdb->Listbox(-selectmode => 'single',
+                   -yscrollcommand => [set => $scroll]);
+   $scroll->configure(-command => [yview => $lb]);
+   $scroll->pack(-side => 'right', -fill => 'y');
+   $lb->pack(-side => 'left', -fill => 'both');
+   $lb->insert('end', sort @cursors);
+   $lb->bind('<ButtonPress-1>', 
+      sub {
+         $cursor = $lb->get($lb->curselection);
+         $w_canvas->configure(-cursor => $cursor);
+         $w_top->update;
+         print LOG "Configure cursor $cursor\n";
+      }
+   );
+   $ans = $HSdb->Show();
+   if($ans eq 'Cancel') {
+      $cursor = $crosshair;
+      NormalCursor();
+   }
+}
+
+#-------------------------------------------------------------------------------------
+# this is the guts of OutputCommand() and SaveConfig()
+# call with argument 'config' for config file format : ^key = value$
+# otherwise for command line format :  (sp)--key value
+sub ConfigString {
+   ($ans) = @_;
+   my $cmd='';
+   my ($p1,$p2,$p3)=(' --',' ','');     # for the command line
+   if($ans eq 'config') {               # for the config file
+      $p1 = '';
+      $p2 = ' = ';
+      $p3 = "\n";
+   }
+
+   foreach $key (sort keys %CFG) {
+      next if($CFG{$key} eq '');
+      if($CFG{$key} =~ m/\s+/) {
+         $cmd = $cmd . $p1 . $key . $p2 . "\"$CFG{$key}\"" . $p3;
+      }
+      else {
+         $cmd = $cmd . $p1 . $key . $p2 . $CFG{$key} . $p3;
+      }
+   }
+   #foreach $key (sort keys %OPT) {
+   #   next if($OPT{$key}[0] eq '');
+   #   foreach $i (@{$OPT{$key}}) {
+   #      $cmd = $cmd . " --$key $i";
+   #   }
+   #}
+
+   # --AO <OT>        extended obs type
+   $dummy = 0;
+   foreach $i (0..$#ExtOT) {
+      if($ExtSelect[$i] != 0) {
+         $dummy =1;
+         $cmd = $cmd . $p1 . "AO" . $p2 . $ExtOT[$i] . $p3;
+      }
+   }
+   # --create        call create only if --AO
+   if($dummy == 1) {
+      $cmd = $cmd . $p1 . "create" . $p2 . "on" . $p3;
+   }
+   # --sat <sat>      select sat
+   $dummy = 0;
+   foreach $i (0..$#svlist) {
+      if($svselect[$i] != 0) {
+         $cmd = $cmd . $p1 . "sat" . $p2 . $svlist[$i] . $p3;
+         $dummy = 1;
+      }
+   }
+   # --obs <ot>       select obs type
+   $ans = 0;
+   foreach $i (0..$#obslist) {
+      if($obsselect[$i] != 0) {
+         $cmd = $cmd . $p1 . "obs" . $p2 . $obslist[$i] . $p3;
+         $ans = 1;
+      }
+   }
+   # --refresh        call refresh only if --sat and --obs
+   if($dummy != 0 && $ans != 0) {
+      $cmd = $cmd . $p1 . "refresh" . $p2 . "on" . $p3;
+   }
+
+   if($ans ne 'config') {
+      $cmd = $cmd . "\n";
+   }
+
+   return $cmd;
+}
+
+#-------------------------------------------------------------------------------------
+sub OutputCommand {
+   Status("Output the current configuration as a command line...");
+   # ask user for name of file to write to ... or screen
+   my $file = "LOG";
+   my $SOdb = $w_top->DialogBox(
+      -title =>  "Command line output",
+      -buttons => ['Ok','Cancel'],
+      -default_button => 'Ok',
+      -popover => 'cursor',
+      -overanchor => 'c',
+      -popanchor => 'nw',
+   );
+   #$SOdb->add('Label',-text => "Command line output")->pack(-anchor => 'w');
+   my $f1=$SOdb->Frame(-borderwidth => 2, -relief => 'groove')->pack(-anchor => 'w');
+   $f1->Label(-text => " Output file name for command line\n"
+      . " (SCREEN for stdout, LOG for logfile) :"
+      )->pack(-side => 'top', -anchor => 'w');
+   my $e1=$f1->Entry(-textvariable => \$file, -width => '30')->pack(-side => 'left');
+   $ans = $SOdb->Show();
+   if($ans eq 'Ok') {
+      my $cmd;
+      if($^O eq "MSWin32") { $cmd = "call RinexPlot"; }
+      elsif($^O eq "linux") { $cmd = "perl RinexPlot.pl"; }
+      else { $cmd = "RinexPlot"; }
+   
+      $msg = ConfigString();
+      $cmd = $cmd . ' ' . $msg;
+
+      if($file eq "SCREEN") {
+         open(CMDOUT,">-") or die "Could not re-open STDOUT\n";
+         print CMDOUT "$cmd";
+      }
+      elsif($file eq "LOG") {
+         print LOG "$cmd";
+      }
+      else {
+         open CMDOUT, ">$file" or die "Could not open log file $file\n";
+         print CMDOUT "$cmd";
+         close CMDOUT;
+      }
+   }
+   Status('');
+}
+
+#-------------------------------------------------------------------------------------
+sub Refresh {
+   #print LOG "Refresh\n";
+   ConfigureCurves();
+
+   if($ncurv <= 0) {
+      $msg = "There are no curves defined.\n" .
+         "Go to DataSet/Select to make a choice      ";
+      PopNotice("Error: no curves",$msg);
+      return;
+   }
+
+   WaitCursor();
+   Status("Refreshing the screen...");
+
+   # clear the screen
+   #$w_canvas->createRectangle(0,0,$CFG{'Lmargin'}+$CFG{'width'}+$CFG{'Rmargin'},
+   #  $CFG{'Tmargin'}+$CFG{'height'}+$CFG{'Bmargin'},
+   #  -outline => $BCOLOR, -fill => $BCOLOR);
+   $w_canvas->delete("all");
+
+   DrawBase();
+
+   PlotData();
+
+   NormalCursor();
+   Status("Refresh done.");
+}
+
+#-------------------------------------------------------------------------------------
+sub DrawBase {
+   # draw around the matte
+   $w_canvas->createRectangle($CFG{'Lmargin'}, $CFG{'Tmargin'},
+      $CFG{'width'}+$CFG{'Lmargin'}, $CFG{'height'}+$CFG{'Tmargin'},
+      -outline => 'black');
+
+   print LOG "Draw base: limits are X: ";
+   if($XScaleExp > 0) {
+      print LOG sprintf("%.$XScaleExp"."f",$CFG{'XMin'}) . ", "
+      . sprintf("%.$XScaleExp"."f",$CFG{'XMax'}) .  ", Y: ";
+   }
+   else { print LOG "$CFG{'XMin'},$CFG{'XMax'}, Y: "; }
+   if($YScaleExp > 0) {
+      print LOG sprintf("%.$YScaleExp"."f",$CFG{'YMin'}) . ", "
+      . sprintf("%.$YScaleExp"."f",$CFG{'YMax'}) .  "\n";
+   }
+   else { print LOG "$CFG{'YMin'},$CFG{'YMax'}\n"; }
+
+   print LOG "XScale : exp $XScaleExp ";
+   foreach $i (0..$CFG{'BticN'}-1) {
+      $dummy = $XScaleMin + $i * $XScaleStep;
+      if($XScaleExp > 0) { print LOG sprintf(" %.$XScaleExp"."f",$dummy); }
+      else { print LOG " $dummy"; }
+   }
+   print LOG "\n";
+   print LOG "YScale : exp $YScaleExp ";
+   foreach $i (0..$CFG{'LticN'}-1) {
+      $dummy = $YScaleMin + $i * $YScaleStep;
+      if($YScaleExp > 0) { print LOG sprintf(" %.$YScaleExp"."f",$dummy); }
+      else { print LOG " $dummy"; }
+   }
+   print LOG "\n";
+
+   # title above plot
+   $ans = $w_canvas->createText($CFG{'Lmargin'}+$CFG{'width'}/2,$CFG{'Tmargin'}/2,
+      -justify => 'center', -fill => 'black', -text => $CFG{'Tlabel'},
+      # -font => 'fontname',
+      );
+   # bottom label
+   $ans = $w_canvas->createText($CFG{'Lmargin'}+$CFG{'width'}/2,
+      $CFG{'Tmargin'}+$CFG{'height'}+$CFG{'Bmargin'}-8,
+      -justify => 'center', -fill => 'black', -text => $CFG{'Blabel'},
+      # -font => 'fontname',
+      );
+   # left label
+   $ans = $w_canvas->createText(0.28*$CFG{'Lmargin'}+2*length($CFG{'Llabel'}),
+      0.6*$CFG{'Tmargin'}, -justify => 'left', -fill => 'black',
+      -text => $CFG{'Llabel'},
+      # -font => 'fontname',
+      );
+   # right label
+   $ans = $w_canvas->createText(
+      $CFG{'Lmargin'}+$CFG{'width'}+0.70*$CFG{'Rmargin'}-2*length($CFG{'Rlabel'}),
+      0.6*$CFG{'Tmargin'}, -justify => 'right', -fill => 'black',
+      -text => $CFG{'Rlabel'},
+      # -font => 'fontname',
+      );
+   
+   DrawLabels();
+}
+
+#-------------------------------------------------------------------------------------
+sub DrawLabels {
+   # tics and numeric labels
+   # (M,M)   (M+W,M)
+   # (M,M+H) (M+W,M+H)
+   my ($min,$max)=(0,0);
+   foreach $i (-1..$CFG{'BticN'}) {                             # X axes
+      $dummy = Xdata2scr($XScaleMin+$i*$XScaleStep);
+      if($dummy >= $CFG{'Lmargin'} && $dummy <= $CFG{'width'}+$CFG{'Lmargin'}) {
+         # bottom tic
+         $ans = $w_canvas->createLine($dummy,$CFG{'Tmargin'}+$CFG{'height'},$dummy,
+            $CFG{'Tmargin'}+$CFG{'height'}-$XTicLen, -fill => 'black');
+         # bottom numeric label
+         $msg = sprintf("%d",$XScaleMin+$i*$XScaleStep);
+         $ans = $w_canvas->createText($dummy, $CFG{'Tmargin'}+$CFG{'height'}+10,
+            -justify => 'center', -fill => 'black', -text => $msg);
+         # save for next if block
+         if($min == 0) { $min = $dummy; }
+         $max = $dummy;
+
+         # top tic
+         $ans = $w_canvas->createLine($dummy, $CFG{'Tmargin'}, $dummy,
+            $CFG{'Tmargin'}+$XTicLen, -fill => 'black');
+      }
+   }
+   if($ScaleFixed == 1) {        # numeric labels at ends
+      my $limit=$CFG{'width'}/(2*$CFG{'BticN'});
+      $msg = sprintf("%d",$CFG{'XMin'});
+      if($min-$CFG{'Lmargin'} > $limit) { # don't print if too close to another label
+         $ans = $w_canvas->createText(
+            $CFG{'Lmargin'}, $CFG{'Tmargin'}+$CFG{'height'}+10,
+            -justify => 'center', -fill => 'black', -text => $msg);
+      }
+      $msg = sprintf("%d",$CFG{'XMax'});
+      if($CFG{'Lmargin'}+$CFG{'width'}-$max > $limit) {
+         $ans = $w_canvas->createText(
+            $CFG{'Lmargin'}+$CFG{'width'},$CFG{'Tmargin'}+$CFG{'height'}+10,
+            -justify => 'center', -fill => 'black', -text => $msg);
+      }
+   }
+   #($min,$max)=(0,0);
+   foreach $i (-1..$CFG{'LticN'}) {                             # Y axes
+      $dummy = Ydata2scr($YScaleMin+$i*$YScaleStep);
+      if($dummy >= $CFG{'Tmargin'} && $dummy <= $CFG{'Tmargin'}+$CFG{'height'}) {
+         # left tic
+         $ans = $w_canvas->createLine(
+            $CFG{'Lmargin'},$dummy,$CFG{'Lmargin'}+$YTicLen,$dummy,
+            -fill => 'black');  # tics
+         if($YScaleExp > 0) {
+            $msg = sprintf(" %.$YScaleExp"."f",$YScaleMin+$i*$YScaleStep);
+         }
+         else { $msg = sprintf("%.0f",$YScaleMin+$i*$YScaleStep); }
+         # left numeric label
+         $ans = $w_canvas->createText($CFG{'Lmargin'}-2*length($msg)-5, $dummy,
+            -justify => 'left', -fill => 'black', -text => $msg);
+         # save for next if block
+         #if($min == 0) { $min = $dummy; }
+         #$max = $dummy;
+
+         # right tic
+         $ans = $w_canvas->createLine(
+            $CFG{'Lmargin'}+$CFG{'width'}, $dummy,
+            $CFG{'Lmargin'}+$CFG{'width'}-$YTicLen, $dummy, -fill => 'black');
+      }
+   }
+   #if($ScaleFixed) {       # numeric labels at ends
+   #   my $limit=20;           # need character size in pixels here...
+   #   $msg = sprintf("%d",$CFG{'YMin'});
+   #   if($min-$CFG{'Tmargin'} > $limit) {# don't print if too close to another label
+   #      $ans = $w_canvas->createText($CFG{'Lmargin'}-2*length($msg)-5,
+   #         $CFG{'Tmargin'}+$CFG{'height'},
+   #         -justify => 'left', -fill => 'black', -text => $msg);
+   #   }
+   #   if($CFG{'Tmargin'}+$CFG{'height'}-$max > $limit) {
+   #      $msg = sprintf("%d",$CFG{'YMax'});
+   #      $ans = $w_canvas->createText($CFG{'Lmargin'}-2*length($msg)-5,
+   #      $CFG{'Tmargin'}, -justify => 'left', -fill => 'black', -text => $msg);
+   #   }
+   #}
+}
+
+#-------------------------------------------------------------------------------------
+sub PlotData { # plot the data
+   my (@penup, at y, at prevx, at prevy);
+   my ($x,$id);
+   my ($xfr,$xto,$yfr,$yto,$tfl,$ffl);
+   ($begW,$begS) = split(',',$CFG{'begin'});
+   ($endW,$endS) = split(',',$CFG{'end'});
+   my ($sat,$c,$week,$sow);
+   my $sow0 = 0;
+   $CFG{'Week'} = 0;
+
+   # lift all pens
+   foreach $j (0..$ncurv-1) { $penup[$j] = 1; }
+
+   # open the data file and read
+   $filename = "$CFG{'datafile'}";
+   print LOG "Open $filename to plot ...";
+   open FILE, "$filename" or die "Error: Refresh could not open $filename\n";
+   Status("Refresh is plotting data in file $filename ...");
+
+   # loop over data in this file
+   while(<FILE>) {
+      chomp;
+      $_ =~ s/^\s_//;
+      if(m/Rinexdump/) { next; }
+      if(m/Week/) { next; }
+      # data
+      #print LOG "Read $_\n";
+      s/^\s+//;               # no leading white space
+      @opt = split(/\s+/, $_);
+      $week = $opt[0];                    # GPS week
+      $sow = $opt[1];                     # GPS seconds of week
+      # ignore if outside time limits
+      next if($week < $begW);
+      next if($week == $begW && $sow < $begS);
+      last if($week > $endW);
+      last if($week == $endW && $sow > $endS);
+      if($CFG{'Week'} == 0) { $CFG{'Week'} = $week; $sow0 = $sow; }
+      $x = Xdata2scr($sow + ($week-$CFG{'Week'})*604800.);
+      $sat = $opt[2];
+      #print LOG "Sat is $sat   ncurves $ncurv\n";
+      foreach $j (0..$ncurv-1) {
+         if($curvON[$j] == 0) { next; }
+         if($curvSV[$j] eq $sat) {
+            $y[$j] = Ydata2scr($opt[$curvCol[$j]]);
+				#print LOG "Data $sat: x=$opt[1] y=$opt[$curvCol[$j]] "
+				#. "sx=$x sy=$y[$j] pen=$penup[$j]\n";
+            if($penup[$j] == 1) {
+               $penup[$j] = 0;
+            }
+            elsif($penup[$j] == 0) {
+					#print LOG "Plot [$j]: ($prevx[$j],$prevy[$j]) to ($x,$y[$j])\n";
+               $xfr = $prevx[$j]; $xto = $x; $yfr = $prevy[$j]; $yto = $y[$j];
+               # clip
+					#print LOG "Clip [$j]: ($xfr,$yfr,$xto,$yto,$tfl,$ffl)\n";
+               if(Clip(\$xfr,\$yfr,\$xto,\$yto,\$tfl,\$ffl) != 0) {
+                  $c = $curvColor[$j];
+						#print LOG "Draw [$j]: ($xfr,$yfr) to ($xto,$yto)\n";
+                  if($curvLines[$j] != 0) {
+                     # draw line
+                     $id = $w_canvas->createLine($xfr,$yfr,$xto,$yto,-fill => $c);
+                  }
+                  if($tfl != 0 && $curvSymbs[$j] ne 'none') {
+                     # pt visible, symb selected -> draw symbol
+                     # (no clipping here ... maybe should)
+                     if($curvSymbs[$j] eq 'cross') {
+                        $id = $w_canvas->createLine(  # why 2/3 you ask? I don't know
+                           $xto-2,$yto-2,$xto+3,$yto+3,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+2,$yto-2,$xto-3,$yto+3,-fill => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'plus') {
+                        $id = $w_canvas->createLine(
+                           $xto-2,$yto,$xto+3,$yto,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto,$yto-2,$xto,$yto+3,-fill => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'diamond') {
+                        $id = $w_canvas->createLine(
+                           $xto-3,$yto,$xto,$yto+3,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto,$yto+3,$xto+3,$yto,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+3,$yto,$xto,$yto-3,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto,$yto-3,$xto-3,$yto,-fill => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'square') {
+                        $id = $w_canvas->createLine(
+                           $xto-2,$yto-2,$xto-2,$yto+2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto-2,$yto+2,$xto+2,$yto+2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+2,$yto+2,$xto+2,$yto-2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+2,$yto-2,$xto-2,$yto-2,-fill => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'circle') {
+                        $id = $w_canvas->createOval($xto-2,$yto-2,
+                           $xto+2,$yto+2,-outline => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'del') {
+                        $id = $w_canvas->createLine(
+                           $xto,$yto+3,$xto+3,$yto-2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+3,$yto-2,$xto-2,$yto-2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto-2,$yto-2,$xto,$yto+3,-fill => $c);
+                     }
+                     elsif($curvSymbs[$j] eq 'delta') {
+                        $id = $w_canvas->createLine(
+                           $xto-2,$yto+2,$xto+3,$yto+2,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto+3,$yto+2,$xto,$yto-3,-fill => $c);
+                        $id = $w_canvas->createLine(
+                           $xto,$yto-3,$xto-2,$yto+2,-fill => $c);
+                     }
+                  }
+               }
+            }
+            $prevy[$j] = $y[$j];
+            $prevx[$j] = $x;
+            next;
+         }
+      }
+   }
+   close FILE;
+   print LOG " done.\n";
+}
+
+#-------------------------------------------------------------------------------------
+sub Clip {
+   # (M,M)   (M+W,M)
+   # (M,M+H) (M+W,M+H)
+   #my $fflag=0;   # flag = 1,0 as inside, outside region
+   #my $tflag=0;   # 'f' is from, 't' is to
+   my $xf = shift;
+   my $yf = shift;
+   my $xt = shift;
+   my $yt = shift;
+   my $tflag = shift;
+   my $fflag = shift;
+   $$tflag = 0;
+   $$fflag = 0;
+   if($$xt >= $CFG{'Lmargin'} && $$xt <= $CFG{'Lmargin'}+$CFG{'width'}
+      && $$yt >= $CFG{'Tmargin'} && $$yt <= $CFG{'Tmargin'}+$CFG{'height'}) {
+         $$tflag = 1;
+   }
+   if($$xf >= $CFG{'Lmargin'} && $$xf <= $CFG{'Lmargin'}+$CFG{'width'}
+      && $$yf >= $CFG{'Tmargin'} && $$yf <= $CFG{'Tmargin'}+$CFG{'height'}) {
+         $$fflag = 1;
+   }
+   if($$tflag == 1 && $$fflag == 1) { return 1; }
+
+   # Two line segments intersect at (x,y) where
+   #                   * (x3,y3)
+   #                   |
+   #                   |
+   # (x2,y2) * --------+-----------* (x1,y1)
+   #                   |
+   #                   |
+   #                   |
+   #                   |
+   #                   * (x4,y4)
+   #
+   # and x = [(x1*y2-x2*y1)*(x3-x4)-(x3*y4-x4*y3)*(x1-x2)]
+   #       / [(x1-x2)*(y3-y4)-(x3-x4)*(y1-y2)] ,
+   #     y = [(x1*y2-x2*y1)*(y3-y4)-(x3*y4-x4*y3)*(y1-y2)]
+   #       / [(x1-x2)*(y3-y4)-(x3-x4)*(y1-y2)]
+   # Note correct reduction when horiz or vert: x=x3 when x3=x4, y=y1 when y1=y2, etc.
+
+   my ($p,$x1,$y1,$x2,$y2);   # intersections
+   my $npt=0;
+   $ans = $$yt-$$yf; # y3-y4
+   if($ans != 0) {
+      $dummy = ($$xt*$$yf-$$xf*$$yt)/$ans;
+      $msg = ($$xt-$$xf)/$ans;
+      # intersect top    x2=$CFG{'Lmargin'}, x1=$CFG{'Lmargin'}+$CFG{'width'},
+      #                  y1=y2=$CFG{'Tmargin'}
+      $ans = $CFG{'Tmargin'};
+      $p = int($ans*$msg-$dummy+0.5);
+      if($p > $CFG{'Lmargin'} && $p < $CFG{'Lmargin'}+$CFG{'width'}
+            && ($$yt-$ans)*($$yf-$ans) < 0) {
+         $x1 = $p; $y1 = $CFG{'Tmargin'}; $npt = 1;
+      }
+      # intersect bottom x2=$CFG{'Lmargin'}, x1=$CFG{'Lmargin'}+$CFG{'width'},
+      #                  y1=y2=$CFG{'Tmargin'}+$CFG{'height'}
+      $ans = $CFG{'Tmargin'}+$CFG{'height'};
+      $p = int($ans*$msg-$dummy+0.5);
+      if($p > $CFG{'Lmargin'} && $p < $CFG{'Lmargin'}+$CFG{'width'}
+            && ($$yt-$ans)*($$yf-$ans) < 0) {
+         if($npt == 0) { $x1 = $p; $y1 = $CFG{'Tmargin'}+$CFG{'height'}; }
+         else { $x2 = $p; $y2 = $CFG{'Tmargin'}+$CFG{'height'}; }
+         $npt++;
+      }
+   }
+   $ans = $$xt-$$xf;  # x3-x4
+   if($ans != 0) {
+      $dummy = ($$yt*$$xf-$$yf*$$xt)/$ans;
+      $msg = ($$yt-$$yf)/$ans;
+      # intersect left   x3=x4=$CFG{'Lmargin'},s
+      #                  y3=$CFG{'Tmargin'}, $y4=$CFG{'Tmargin'}+$CFG{'height'}
+      $ans = $CFG{'Lmargin'};
+      $p = int($ans*$msg-$dummy+0.5);
+      if($p > $CFG{'Tmargin'} && $p < $CFG{'Tmargin'}+$CFG{'height'}
+            && ($$xt-$ans)*($$xf-$ans) < 0) {
+         if($npt == 0) { $y1 = $p; $x1 = $CFG{'Lmargin'}; }
+         else { $y2 = $p; $x2 = $CFG{'Lmargin'}; }
+         $npt++;
+      }
+
+      # intersect right  x3=x4=$CFG{'Lmargin'}+$CFG{'width'},
+      #                  y3=$CFG{'Tmargin'}, $y4=$CFG{'Tmargin'}+$CFG{'height'}
+      $ans = $CFG{'Lmargin'}+$CFG{'width'};
+      $p = int($ans*$msg-$dummy+0.5);
+      if($p > $CFG{'Tmargin'} && $p < $CFG{'Tmargin'}+$CFG{'height'}
+            && ($$xt-$ans)*($$xf-$ans) < 0) {
+         if($npt == 0) { $y1 = $p; $x1 = $CFG{'Lmargin'}+$CFG{'width'}; }
+         else { $y2 = $p; $x2 = $CFG{'Lmargin'}+$CFG{'width'}; }
+         $npt++;
+      }
+   }
+
+   if($npt == 0) { return 0; }          # no intersections
+
+   if($$tflag == 1 && $$fflag == 0) {     # outside to inside
+      $$xf = $x1; $$yf = $y1;
+   }
+   elsif($$tflag == 0 && $$fflag == 1) {  # inside to outside
+      $$xt = $x1; $$yt = $y1;
+   }
+   else {                               # outside to outside
+      if($npt!=2) {
+         print LOG "WARNING: Clip finds outside to outside with"
+                . " $npt intersections\n";
+         return 0;
+      }
+      if((($$xt-$x1)*($$xt-$x1)+($$yt-$y1)*($$yt-$y1)) <
+         (($$xf-$x1)*($$xf-$x1)+($$yf-$y1)*($$yf-$y1))) {
+         $$xt = $x1; $$yt = $y1;
+         $$xf = $x2; $$yf = $y2;
+      }
+      else {
+         $$xt = $x2; $$yt = $y2;
+         $$xf = $x1; $$yf = $y1;
+      }
+   }
+   return 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub CanvConf {
+   my $w = $w_canvas->width;
+   my $h = $w_canvas->height;
+   my $W = $CFG{'Lmargin'}+$CFG{'width'}+$CFG{'Rmargin'};
+   my $H = $CFG{'Tmargin'}+$CFG{'height'}+$CFG{'Bmargin'};
+   if($w != $CFG{'Lmargin'}+$CFG{'width'}+$CFG{'Rmargin'} ||
+      $h != $CFG{'Tmargin'}+$CFG{'height'}+$CFG{'Bmargin'}) {
+      $CFG{'width'} = $w - $CFG{'Lmargin'} - $CFG{'Rmargin'};
+      $CFG{'height'} = $h - $CFG{'Tmargin'} - $CFG{'Bmargin'};
+      Rates();
+      if($firstcanvas == 1) {
+         Status("Plot reconfigured to $CFG{'width'} x $CFG{'height'} ...Refresh?");
+      }
+      $firstcanvas = 1;
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub PopNotice {
+   my ($dummy1, $dummy2) = @_;
+   #$ans = $w_top->messageBox(
+   #-title => $dummy1,
+   #-message => $dummy2 . "        ",
+   #-type => 'OK',
+   #-icon => 'info',
+   ##no popover for messageBox
+   #);
+   my $PNdb = $w_top->DialogBox(
+      -title => $dummy1,
+      -buttons => ['Ok'],
+      -popover => $w_top,
+      -overanchor => 'c',
+      -popanchor => 'c',
+   );
+   $PNdb->add('Label', -text => $dummy2, -justify => 'left')->pack;
+   $PNdb->Show();
+}
+
+#-------------------------------------------------------------------------------------
+sub Status {
+   ($dummy) = (@_);
+   $statusbar->delete('0.0','end');
+   $statusbar->insert('0.0',$dummy);
+   $w_top->update;
+}
+
+#-------------------------------------------------------------------------------------
+sub WaitCursor {
+   $w_canvas->configure(-cursor => $waitcursor);
+   $w_top->update;
+}
+
+#-------------------------------------------------------------------------------------
+sub NormalCursor {
+   $w_canvas->configure(-cursor => $cursor);
+   $w_top->update;
+}
+
+#-------------------------------------------------------------------------------------
+#-------------------------------------------------------------------------------------
+sub mouse_events {
+	# do not enter this routine unless screen is up and scale has been defined
+	if($MainUp == 0 || $ScaleDefined == 0) { return; }
+
+   my ($canv, $x, $y, $mousemsg) = @_;
+
+   if($mousemsg eq "motion") {
+      if($MAKE_RECT) {
+         # first erase the old one
+         $canv->delete($MOUSE_RECT);
+
+         # where is the corner now?
+         $RECT_X1 = $canv->canvasx($x);
+         $RECT_Y1 = $canv->canvasy($y);
+
+         # draw a new foreground-color rectangle
+         $MOUSE_RECT = $canv->createRectangle(
+            $RECT_X0,$RECT_Y0, $RECT_X1,$RECT_Y1,
+            -outline => $MCOLOR );
+      }
+   }
+   else {
+      #print LOG "mouse$msg: (x,y) = ", $canv->canvasx($x), ", ", $canv->canvasy($y),
+      #   " (datax,datay) = ", scr2Xdata($canv->canvasx($x)), " , ",
+      #   scr2Ydata($canv->canvasy($y)),
+      #   "\n";
+      if($mousemsg eq "push left") {
+         $RECT_X0 = $canv->canvasx($x);
+         $RECT_Y0 = $canv->canvasy($y);
+         #print LOG "push: RECT_X0 $RECT_X0, RECT_Y0 $RECT_Y0\n";
+         $MAKE_RECT = 1;
+      }
+      if($mousemsg eq "rel left") {
+         $RECT_X1 = $canv->canvasx($x);
+         $RECT_Y1 = $canv->canvasy($y);
+         #print LOG "rele: RECT_X1 $RECT_X1, RECT_Y1 $RECT_Y1\n";
+         #print LOG "just checking: (x,y) = ", $RECT_X1, ", ", $RECT_Y1,
+         #   " (datax,datay) = ", scr2Xdata($RECT_X1), " , ", scr2Ydata($RECT_Y1),
+         #   "\n";
+         Status("(x,y) = screen("
+            . $RECT_X1 . ","
+            . $RECT_Y1 . ") = data("
+            . sprintf("%10.3f",scr2Xdata($RECT_X1)) . ","
+            . sprintf("%.3f",scr2Ydata($RECT_Y1)) . ")");
+         $canv->delete($MOUSE_RECT);
+         $MAKE_RECT = 0;
+
+         # ignore zero-area rectangles
+         if(($RECT_X0 == $RECT_X1) || ($RECT_Y0 == $RECT_Y1)) {
+            return;
+         }
+
+         $MOUSE_RECT = $canv->createRectangle(
+            $RECT_X0,$RECT_Y0, $RECT_X1,$RECT_Y1,
+            -outline => $MCOLOR );
+
+         # put up a dialog asking what to do with the rectangle
+         my $db = $w_top->DialogBox(
+            -title => 'Mouse Rectangle',
+            -buttons => ['Zoom', 'Cancel'],
+            -default_button => 'Zoom',
+            -popover => 'cursor', -overanchor => 'c', -popanchor => 'nw',
+         );
+         $db->add('Label', -text => "Zoom: are you sure?    ")->pack;
+         $ans = $db->Show();
+         if($ans eq "Zoom") {
+            #print LOG "You said to zoom to mouse rectangle\n";
+            #$canv->move($MOUSE_RECT, 100, 100);
+            ($YMin, $YMax, $XMin, $XMax) =
+               (scr2Ydata($RECT_Y1), scr2Ydata($RECT_Y0),
+                scr2Xdata($RECT_X0), scr2Xdata($RECT_X1) );
+            if($YMin > $YMax) {
+               $ans = $YMax;
+               $YMax = $YMin;
+               $YMin = $ans;
+            }
+            if($XMin > $XMax) {
+               $ans = $XMax;
+               $XMax = $XMin;
+               $XMin = $ans;
+            }
+            # NewScale requires $XMin,$XMax,$YMin,$YMax as input
+            NewScale('fixed','Mouse zoom');
+            Refresh();
+         }
+         if($ans eq "Cancel") {
+            #print LOG "You said to cancel mouse rectangle\n";
+            $canv->delete($MOUSE_RECT);
+            Status('');
+         }
+      }
+   }
+}
+
+#-------------------------------------------------------------------------------------
+#-------------------------------------------------------------------------------------
+# string CmdText defines configuration constants and options on command line,
+# with their default values. See documentation of CmdText and sub CommandLine.
+sub Defaults {
+   # parse CmdText
+   my @lines = split('\n',$CmdText);
+   foreach (@lines) {
+      last if(m/Example:/);
+      next if(m/^#/);
+      if(m/(can repeat)/) { $dummy='grow'; } else { $dummy=''; }
+      s/--(\S+)\s.*\((.*)\)$/$1 = $2/;
+      s/^\s+//;
+      s/\s+$//;
+      ($key, $val) = split(/ = /,$_,2);
+      if($val eq '') { $key =~ s/ =$//; }
+      #print "Line:",$_,"\n";
+      #print "key |$key| value |$val|\n";
+      #AddDefault($key,$val,$dummy);
+      $Options{$key} = 1;         # this says 'key' is a valid option
+      if($dummy eq 'grow') {
+         $Grow{$key} = 1;         # this says 'key' belongs to OPT, not CFG
+         if($val ne '') { push(@{$OPT{$key}}, $val); }   # OPT -- 1 to many
+      }
+      else {
+         if($val ne '') { $CFG{$key} = $val; }            # CFG -- 1 to 1
+      }
+   }
+   # TD this should belong to CFG also
+   $ConfigFile = 'rp.cfg';
+}
+
+#-------------------------------------------------------------------------------------
+# command line arguments -- see CmdText above for doc.
+# process the command line, converting command line into key/val pairs,
+# where --key=val --key val --key (val='on')
+# don't allow -key because this makes val = (negative number) impossible
+sub CommandLine {
+   if($#ARGV == 0 && $ARGV[0] eq '--help') {
+      print $CmdText,"\n";
+      exit;
+   }
+   $LimitsSet = 0;                  # notice if limits are set
+   $key = '';
+   $val = '';
+   while($#ARGV >= 0) {
+      $dummy = shift @ARGV;
+      if(substr($dummy,0,2) eq "--") {                # '--' => its key or key=value
+         $dummy = substr($dummy,2,length($dummy)-2);     # remove --
+         if($key ne '' && $val eq 'on') {                # was prev arg a key alone?
+            AddToConfig($key,$val);
+            $key = '';
+            $val = '';
+         }
+         if($dummy =~ '=') {                             # is there an '='?
+            ($key, $val) = split(/=/,$dummy,2);
+            AddToConfig($key,$val);
+            $key = '';
+            $val = '';
+         }
+         else {                                          # no =; val is 'on' for now
+            $key = $dummy;
+            $val = 'on';
+         }
+      }
+      else {                                          # no '--' => its val or an error
+         if($key ne '' && $val eq 'on') {                # prev arg was --key
+            $val = $dummy;
+            AddToConfig($key,$val);
+            $key = '';
+            $val = '';
+         }
+         else {                                          # isolated and no '--' => err
+            print "Unrecognized argument: $dummy\n";
+         }
+      }
+   }
+   # the last arg
+   if($key ne '' && $val eq 'on') { AddToConfig($key,$val); }
+}
+
+#-------------------------------------------------------------------------------------
+sub AddToConfig {
+   my ($k,$v) = @_;
+   if($Options{$k} == 1) {
+      if($Grow{$k} == 1) {
+         push(@{$OPT{$k}}, split(/\s+/,$v));
+      }
+      else {
+         if($k eq 'prgmdir') {
+            $v =~ s/\//$SLASH/g;
+            $v =~ s/\\/$SLASH/g;
+         }
+         # save it
+         $CFG{$k} = $v;
+         # set flags here -- any option set by the user will pass here
+         if($k eq 'XMin' or $k eq 'XMax' or $k eq 'YMin' or $k eq 'YMax') {
+            $LimitsSet = 1;
+         }
+         if(($k eq 'Blabel' && $v ne 'GPS Seconds of Week') or
+            ($k eq 'Tlabel' && $v ne 'Title') or
+            $k eq 'Rlabel' or
+            $k eq 'Llabel') {
+            $UsingDefaults = 0;
+         }
+      }
+
+      # open file and read as config; do here so later cmds may override
+      if($k eq 'load') {
+         my @c = @{$OPT{'load'}};
+         while($#c >= 0) {
+            $i = shift @c;
+            last unless($i ne '');
+            delete(@{$OPT{'load'}}[0]);
+            LoadConfig($i);
+         }
+      }
+
+      return 'ok';
+   }
+   else {
+      print "Invalid configuration option: $k\n";
+      return 'fail';
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub LoadConfig {
+   ($filename) = @_;
+   if($CFG{'verbose'}) {
+      if($LogOpen) {
+         print LOG "Attempt to open and read configuration file $filename\n";
+      }
+      else {
+         print "Attempt to open and read configuration file $filename\n";
+      }
+   }
+
+   $dummy = 0;                   # dummy will count the number of read errors
+   $LimitsSet = 0;
+   if(open(CONFIG, "$filename")) {
+      while (<CONFIG>) {
+         if($dummy > 20) {
+            close CONFIG;
+            if($LogOpen) {
+               print LOG "\nAbort - Too many read errors loading configuration\n";
+               print LOG "  File \"$filename\" may not be a configuration file.\n";
+            }
+            else {
+               print "\nAbort - Too many read errors loading configuration\n";
+               print "  File \"$filename\" may not be a configuration file.\n";
+            }
+            if($MainUp) {
+               PopNotice("Error loading file",
+               "Abort - Too many read errors while loading a\n" .
+               "configuration file. This may not be a configuration file:\n" .
+               "   $filename");
+            }
+            return 'fail';
+         }
+         chomp;                  # no newline
+         s/^\s+//;               # no leading whitespace
+         s/#.*//;                # no comments
+         s/\s+$//;               # no trailing whitespace
+         s/^--//;                # no leading --
+         next unless length;     # anything left?
+         unless(m/=/) {          # ignore if no =
+            print "Warning: configuration option has no '=' : $_\n";
+            $dummy++;
+            next;
+         }
+         ($key, $val) = split(/\s*=\s*/, $_, 2);
+         # nested conf files do not work
+         if($key eq 'load') {
+            print "Warning: nested --load <file> do not work\n";
+            next;
+         }
+         if($val =~ m/^"/ && $val =~ m/"$/) {
+            # remove quotes around values with whitespace
+            $val =~ s/^"//;
+            $val =~ s/"$//;
+            $ans = AddToConfig($key,$val);
+         }
+         else {
+            $ans = AddToConfig($key,$val);
+         }
+         if($ans eq 'fail') { $dummy++; }
+      }
+      close CONFIG;
+
+      $ConfigFile = $filename;
+      return 'ok';
+   }
+   else {
+      # don't put a popup here b/c top level window may not exist yet.
+      if($LogOpen) {
+         print LOG "Error: Could not open configuration file $filename\n";
+      }
+      else {
+         print "Error: Could not open configuration file $filename\n";
+      }
+      #$ConfigFile = '';
+      return 'fail';
+   }
+}
+
+#-------------------------------------------------------------------------------------
+sub SaveConfig {
+   my ($file)=@_;
+   if($file eq '') { return 'ok'; }
+   print LOG "Save the configuration to the file $file\n";
+
+   my $cmd = ConfigString('config');
+
+   if(open(CONFIG, ">$file")) {
+      print CONFIG $cmd;
+      close CONFIG;
+      return 'ok';
+   }
+   else {
+      return 'fail';
+   }
+}
+
+#-------------------------------------------------------------------------------------
+# process the config, used for command line processing and after config file loaded.
+sub ProcessConfig {
+   if($CFG{'log'} ne '' && $CFG{'log'} ne 'SCREEN') {
+      open LOG, ">$CFG{'log'}" or die "Could not open log file $CFG{'log'}\n";
+      autoflush LOG;
+      $LogOpen = 1;
+      if($CFG{'verbose'}) {
+         print "RP Output directed to log file $CFG{'log'}\n";
+         print LOG "Log file for RinexPlot\n";
+      }
+   }
+   else { open(LOG,">-") or die "Could not re-open STDOUT\n"; }
+
+   if($CFG{'Rinex'} ne '') {
+      DataInputProcess($CFG{'Rinex'});
+
+      my %index;
+      my (@AOs, at Sats, at Obs)=((),(),());
+      if(defined(@{$OPT{'AO'}})) { @AOs = @{$OPT{'AO'}}; }
+      if(defined(@{$OPT{'sat'}})) { @Sats = @{$OPT{'sat'}}; }
+      if(defined(@{$OPT{'obs'}})) { @Obs = @{$OPT{'obs'}}; }
+
+      if($#AOs >= 0) {
+         %index=();
+         $j=0;
+         foreach $i (@ExtOT) {
+            unless($index{$i}) {
+               $index{$i}=$j;
+               $j++;
+            }
+         }
+         $dummy = 0;
+         foreach $i (@AOs) {
+            $j = $index{$i};
+            if($dummy == 0) { print LOG "Selected new obs types"; $dummy=1; }
+            print LOG " $i";
+            $ExtSelect[$j] = 1;
+         }
+         if($dummy==1) { print LOG "\n"; }
+         if($CFG{'create'} eq 'on') { CompCrea(); }
+      }
+      if($#Sats >= 0) {
+         %index=();
+         $j=0;
+         foreach $i (@svlist) {
+            unless($index{$i}) {
+               $index{$i}=$j;
+               $j++;
+            }
+         }
+         $dummy = 0;
+         foreach $i (@Sats) {
+            $j = $index{$i};
+            if($j eq '') {
+               if($dummy==1) { print LOG "\n"; }
+               print LOG "Error: satellite $i is not found in file!\n";
+               print LOG "  Satellites in file are: @svlist\n";
+            }
+            else {
+               if($dummy == 0) { print LOG "Selected Sats:"; $dummy=1; }
+               print LOG " $i";
+               $svselect[$j] = 1;
+            }
+         }
+         if($dummy==1) { print LOG "\n"; }
+      }
+      if($#Obs >= 0) {
+         %index=();
+         $j=0;
+         foreach $i (@obslist) {
+            unless($index{$i}) {
+               $index{$i}=$j;
+               $j++;
+            }
+         }
+         $dummy = 0;
+         foreach $i (@Obs) {
+            $j = $index{$i};
+            if($j eq '') {
+               if($dummy==1) { print LOG "\n"; }
+               print LOG "Error: obs type $i is not found in file!\n";
+               print LOG "  Obs types in file are: @obslist\n";
+            }
+            else {
+               if($dummy == 0) { print LOG "Selected Obs:"; $dummy=1; }
+               print LOG " $i";
+               $obsselect[$j] = 1;
+            }
+         }
+         if($dummy==1) { print LOG "\n"; }
+      }
+      if($CFG{'refresh'} eq 'on' && $#Obs >= 0 && $#Sats >= 0) {
+         # NewScale requires $XMin,$XMax,$YMin,$YMax as input
+         ($XMin,$XMax,$YMin,$YMax)
+            = ($CFG{'XMin'},$CFG{'XMax'},$CFG{'YMin'},$CFG{'YMax'});
+         NewScale('fixed','Command line');
+         if($LimitsSet == 1) {
+            $FirstAutoscale = 0;
+            $LimitsSet = 0;
+         }
+         else { $FirstAutoscale = 1; }
+         $Reconfigure = 1;
+         ConfigureCurves();
+         Refresh();
+      }
+   }
+   if($CFG{'help'} eq 'on') { HelpTopi(); }
+
+   $LimitsSet = 0;
+   # delete these, as they are used only for cmdline and config file input
+   delete($OPT{'load'});
+   delete($OPT{'AO'});
+   delete($CFG{'create'});
+   delete($OPT{'sat'});
+   delete($OPT{'obs'});
+   delete($CFG{'refresh'});
+   delete($CFG{'help'});
+}
+
+#-------------------------------------------------------------------------------------
+#-------------------------------------------------------------------------------------
+# Create the menubar and everything under it.
+sub create_menu {
+  [
+    [ 'cascade', '~File', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', '~Load File ...', -accelerator => 'Ctrl-L',
+           -command => \&FileLoad ],
+        ['command', '~Save File', -accelerator => 'Ctrl-S', -command => \&FileSave ],
+        ['command', 'Save ~As ...', -command => \&FileSaAs ],
+        ['command', '~Output as command ...', -command => \&OutputCommand ],
+        '',
+        ['command', '~Rinex Obs File ...', -command => \&FileInpu ],
+        '',
+        ['command', 'View ~Data Summary ...', -command => \&FileSumm ],
+        ['command', '~View Selections ...', -command => \&FileSele ],
+        ['command', '~Clear all selections', -command => \&FileClea ],
+        '',
+        ['command', '~Exit',  -accelerator => 'Ctrl-Q', -command => \&FileExit ],
+      ],
+    ],
+
+    [ 'cascade', '~Compute', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', 'New ~Data types...', -command => \&CompObst ],
+        ['command', '~Nav File...', -command => \&CompNavf ],
+        ['cascade', '~Rx Position', -tearoff => $menutear, -menuitems =>
+          [
+            ['command', '~Select Rx...', -command => \&CompPosnSele ],
+            ['command', '~RAIM Solution', -command => \&CompPosnRAIM ],
+          ],
+        ],
+        ['command', 'Con~figure...', -command => \&CompConf ],
+        '',
+        ['command', '~Create new data', -command => \&CompCrea ],
+      ],
+    ],
+
+    [ 'cascade', '~DataSet', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', '~Satellites', -command => \&DataSats ],
+        ['command', '~Obs types', -command => \&DataObst ],
+        ['command', '~Times', -command => \&DataTime ],
+        ['command', '~Configure', -command => \&DataConf ],
+      ],
+    ],
+
+    [ 'cascade', '~Graph', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', '~AutoScale', -accelerator => '   Ctrl-A',
+           -command => \&AutoScale ],
+        ['command', '~Limits', -command => \&GrapLims ],
+        ['command', '~Axes', -command => \&GrapAxes ],
+        ['command', 'La~bels', -command => \&GrapLabe ],
+        ['command', '~Curves', -command => \&GrapCurv ],
+        ['command', 'Zoom ~In', '-accelerator', '    Ctrl-I',
+            -command => [ \&GrapZoom, 'dummy', 'In' ] ],
+        ['command', 'Zoom ~Out', '-accelerator', '    Ctrl-O',
+            -command => [ \&GrapZoom, 'dummy', 'Out' ] ],
+        ['command', '~UnZoom', '-accelerator', '    Ctrl-U',
+            -command => \&GrapUnzo ],
+        '',
+        ['command', '~Gnuplot', -command => \&GrapGnup ],
+      ],
+    ],
+
+    # this makes Refresh just a button - no menu under it
+    [ 'command', '~Refresh', -command => \&Refresh ],
+
+    [ 'cascade', '~Settings', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', '~Preferences', -command => \&SettPref ],
+        ['command', '~Set Cursor', -command => \&SettCurs ],
+      ],
+    ],
+
+    [ 'cascade', '~Help', -tearoff => $menutear, -menuitems =>
+      [
+        ['command', '~Topics', -command => \&HelpTopi ],
+        ['command', '~About', -command => \&HelpAbou ],
+      ],
+    ],
+
+  ]; 
+} # end sub create_menu
+
+#-------------------------------------------------------------------------------------
+# initialize: create menu system and define bindings
+sub init {
+   create_screen();
+
+   # key bindings
+   $w_top->bind("<Control-Key-l>", \&FileLoad);
+   $w_top->bind("<Control-Key-s>", \&FileSave);
+   $w_top->bind("<Control-Key-a>", \&AutoScale);
+   $w_top->bind("<Control-Key-q>", \&FileExit);
+   $w_top->bind("<Control-Key-i>", [ \&GrapZoom, "In"] );
+   $w_top->bind("<Control-Key-o>", [ \&GrapZoom, "Out"] );
+   $w_top->bind("<Control-Key-u>", \&GrapUnzo);
+   $w_top->bind("<Control-Key-r>", \&Refresh);
+
+   # mouse bindings
+   $w_canvas->CanvasBind("<ButtonPress-1>",
+      [ \&mouse_events, Ev('x'), Ev('y'), "push left" ]);
+   $w_canvas->CanvasBind("<ButtonPress-3>",
+      [ \&mouse_events, Ev('x'), Ev('y'), "push right" ]);
+   $w_canvas->CanvasBind("<ButtonRelease-1>",
+      [ \&mouse_events, Ev('x'), Ev('y'), "rel left" ]);
+   $w_canvas->CanvasBind("<ButtonRelease-3>",
+      [ \&mouse_events, Ev('x'), Ev('y'), "rel right" ]);
+   $w_canvas->CanvasBind("<Motion>",
+      [ \&mouse_events, Ev('x'), Ev('y'), "motion" ]);
+
+   # window resizing
+   $w_canvas->CanvasBind("<Configure>", \&CanvConf);
+
+   # fonts
+   $w_top->fontCreate(qw/C_small -family courier   -size 10/);
+   $w_top->fontCreate(qw/C_big   -family courier   -size 14 -weight bold/);
+   $w_top->fontCreate(qw/C_vbig  -family helvetica -size 24 -weight bold/);
+   $w_top->fontCreate(qw/C_bold -family courier -size 12 -weight bold -slant italic/);
+
+   $MainUp = 1;
+}
+
+#-------------------------------------------------------------------------------------
+sub create_screen {
+   $w_top = MainWindow->new(-title =>'Rinex Plot');
+
+   # menus
+   $w_top->configure(-menu => $menubar = $w_top->Menu(-menuitems => create_menu));
+
+   # status bar - putting statusbar before canvas means when resizing window,
+   #              canvas shrinks but status bar stays
+   $statusbar = $w_top->ROText(
+      -height => 1,
+      -relief => 'flat',
+      -background => '#a0b7ce',
+      -foreground => 'white',
+      -borderwidth => 2
+       )->pack(
+      -expand => '0',
+      -fill => 'both',
+      -side => 'bottom',
+      -anchor => 'w');
+   $statusbar->insert('0.0', 'Welcome to Rinex Plot');
+
+   # canvas
+   $w_canvas = $w_top->Canvas(
+      '-width'  => $CFG{'width'}+$CFG{'Lmargin'}+$CFG{'Rmargin'},
+      '-height' => $CFG{'height'}+$CFG{'Tmargin'}+$CFG{'Bmargin'},
+      '-border' => 1,
+      # this has no effect ... '-relief' => 'solid',
+      '-background' => $BCOLOR,
+      '-cursor' => $cursor);
+   $w_canvas->pack(-expand => '1', -anchor => 'nw', -fill => 'both');
+
+   # need update here to prevent a <Configure> event that calls CanvConf at odd time
+   $w_top->update;
+}
+
+#-------------------------------------------------------------------------------------
+# execution begins here
+#-------------------------------------------------------------------------------------
+# define default CFG values
+Defaults();
+
+# process command line arguments
+CommandLine();
+
+# create menu and screen and define bindings
+init();
+
+# process any command line input
+ProcessConfig();
+
+# make sure utilities are available
+my($ResCor,$RinSum,$RinexDump)=('ResCor','RinSum','RinexDump');
+if ($^O eq "MSWin32") {
+   $ResCor='ResCor.exe';
+   $RinSum='RinSum.exe';
+   $RinexDump='RinexDump.exe';
+}
+if(not -e $CFG{'prgmdir'} . $SLASH . $RinSum
+   || not -e $CFG{'prgmdir'} . $SLASH . $ResCor
+   || not -e $CFG{'prgmdir'} . $SLASH . $RinexDump) {
+   PopNotice("Error: GPSTk not found",
+      "Error: GPSTk utilities are not found in directory $CFG{'prgmdir'}.\n"
+      . "Go to Settings/Preferences to change this");
+}
+
+MainLoop();
+#-------------------------------------------------------------------------------------
diff --git a/trunk/apps/RinexPlot/alic0320.04o b/dev/apps/RinexPlot/alic0320.04o
similarity index 100%
rename from trunk/apps/RinexPlot/alic0320.04o
rename to dev/apps/RinexPlot/alic0320.04o
diff --git a/trunk/apps/RinexPlot/goRP b/dev/apps/RinexPlot/goRP
similarity index 100%
rename from trunk/apps/RinexPlot/goRP
rename to dev/apps/RinexPlot/goRP
diff --git a/trunk/apps/RinexPlot/goRP.bat b/dev/apps/RinexPlot/goRP.bat
similarity index 100%
rename from trunk/apps/RinexPlot/goRP.bat
rename to dev/apps/RinexPlot/goRP.bat
diff --git a/trunk/apps/RinexPlot/goRP1 b/dev/apps/RinexPlot/goRP1
similarity index 100%
rename from trunk/apps/RinexPlot/goRP1
rename to dev/apps/RinexPlot/goRP1
diff --git a/trunk/apps/RinexPlot/goRP1.bat b/dev/apps/RinexPlot/goRP1.bat
similarity index 100%
rename from trunk/apps/RinexPlot/goRP1.bat
rename to dev/apps/RinexPlot/goRP1.bat
diff --git a/trunk/apps/RinexPlot/positions.txt b/dev/apps/RinexPlot/positions.txt
similarity index 100%
rename from trunk/apps/RinexPlot/positions.txt
rename to dev/apps/RinexPlot/positions.txt
diff --git a/dev/apps/Rinextools/EditRinex.cpp b/dev/apps/Rinextools/EditRinex.cpp
new file mode 100644
index 0000000..3f38b57
--- /dev/null
+++ b/dev/apps/Rinextools/EditRinex.cpp
@@ -0,0 +1,371 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file EditRinex.cpp
+ * Edit a Rinex observation file using the RinexEditor in gpstk.
+ */
+
+#include "RinexObsBase.hpp"
+#include "RinexObsData.hpp"
+#include "RinexObsHeader.hpp"
+#include "RinexObsStream.hpp"
+#include "DayTime.hpp"
+#include "CommandOptionParser.hpp"
+#include "CommandOption.hpp"
+#include "RinexUtilities.hpp"
+#include "StringUtils.hpp"
+
+#include "RinexEditor.hpp"
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <time.h>
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+// NB Version for this prgm is just the RinexEditor version.
+
+//------------------------------------------------------------------------------------
+// data input from command line
+string LogFile("EditRinex.log");
+bool Verbose=false,Debug=false;
+string Title;
+// timer
+clock_t totaltime;
+// log file
+ofstream oflog;
+
+//------------------------------------------------------------------------------------
+// prototypes
+int GetCommandLine(int argc, char **argv, RinexEditor& re) throw(Exception);
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception);
+
+//------------------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+try {
+   totaltime = clock();
+
+   int iret;
+   DayTime last;
+   // NB. Do not instantiate editor outside main(), b/c DayTime::END_OF_TIME is a
+   // static const that can produce static intialization order problems under some OS.
+   RinexEditor REC;
+
+      // Title and description
+   Title = string("EditRinex, part of the GPS ToolKit, Ver ")
+            + REC.getRinexEditVersion() + string(", Run ");
+   time_t timer;
+   struct tm *tblock;
+
+   timer = time(NULL);
+   tblock = localtime(&timer);
+   last.setYMDHMS(1900+tblock->tm_year,1+tblock->tm_mon,
+               tblock->tm_mday,tblock->tm_hour,tblock->tm_min,tblock->tm_sec);
+   Title += last.printf("%04Y/%02m/%02d %02H:%02M:%02S\n");
+   cout << Title;
+
+      // define extended types
+   iret = RegisterARLUTExtendedTypes();
+   if(iret) goto quit;
+
+      // get command line
+   iret=GetCommandLine(argc, argv, REC);
+   if(iret) goto quit;
+
+   iret=REC.EditFile();
+   if(iret) goto quit;
+
+   quit:
+   // compute run time
+   totaltime = clock()-totaltime;
+   oflog << "EditRinex timing: " << setprecision(3)
+      << double(totaltime)/double(CLOCKS_PER_SEC) << " seconds.\n";
+
+   return iret;
+}
+catch(gpstk::FFStreamError& e) { cerr << e; }
+catch(gpstk::Exception& e) { cerr << e; }
+catch(exception& e) { cerr << e.what(); }
+catch (...) { cerr << "Unknown error.  Abort." << endl; }
+   return 1;
+}   // end main()
+
+//------------------------------------------------------------------------------------
+int GetCommandLine(int argc, char **argv, RinexEditor& REC) throw(Exception)
+{
+   bool help=false;
+   int i,j,iret=0;
+   vector<string> values; // to get values found on command line
+
+try {
+      // required options
+
+      // optional options
+      // this only so it will show up in help page...
+   CommandOption dashf(CommandOption::hasArgument, CommandOption::stdType,
+      'f',""," [-f|--file] <file>   file containing more options");
+
+   CommandOption dashl(CommandOption::hasArgument, CommandOption::stdType,
+      0,"log"," [-l|--log] <file>    Output log file name");
+   dashl.setMaxCount(1);
+   
+   CommandOptionNoArg dashh('h', "help",
+      " [-h|--help]          print syntax and quit.");
+
+   CommandOptionNoArg dashd('d', "debug",
+      " [-d|--debug]         print extended output info.");
+
+   CommandOptionNoArg dashv('v', "verbose",
+      " [-v|--verbose]       print extended output info."
+      "\n [<REC>]              Rinex editing commands - cf. following");
+
+   // ... other options
+   CommandOptionRest Rest("");
+
+   CommandOptionParser Par(
+      " Prgm EditRinex will open and read one RINEX file, apply editing commands,\n"
+      " and write the modified RINEX data to another RINEX file(s).\n"
+      " Input is on the command line, or of the same format in a file (-f<file>).\n");
+
+   // allow user to put all options in a file
+   // could also scan for debug here
+   vector<string> Args;
+   for(j=1; j<argc; j++) PreProcessArgs(argv[j],Args);
+
+   if(Args.size()==0 || dashh.getCount())
+      help = true;
+
+      // open the log file first
+   oflog.open(LogFile.c_str(),ios_base::out);
+   if(!oflog) {
+      cerr << "Failed to open log file " << LogFile << endl;
+      return -1;
+   }
+   cout << "EditRinex output directed to log file " << LogFile << endl;
+   REC.oflog = &oflog;
+   oflog << Title;
+
+   //if(Debug) {
+      //cout << "List passed to REditCommandLine:\n";
+      //for(i=0; i<Args.size(); i++) cout << i << " " << Args[i] << endl;
+      // strip out the REditCmds
+   //}
+
+   // set up editor and pull out (delete) editing commands
+   REC.REVerbose = Verbose;
+   REC.REDebug = Debug;
+   REC.AddCommandLine(Args);
+
+   //if(Debug) {
+      //deque<REditCmd>::iterator jt=REC.Cmds.begin();
+      //cout << "\nHere is the list of RE cmds\n";
+      //while(jt != REC.Cmds.end()) { jt->Dump(cout,string("")); ++jt; }
+      //cout << "End of list of RE cmds" << endl;
+   //}
+
+      // preprocess the commands
+   iret = REC.ParseCommands();
+   if(iret) {
+      cerr << "EditRinex Error: no " << (iret==-1 ? "input" : "output")
+         << " file specified\n";
+      oflog << "EditRinex Error: no " << (iret==-1 ? "input" : "output")
+         << " file specified\n";
+   }
+   //if(Debug) {
+      //cout << "\nHere is the parsed list of RE cmds\n";
+      //it=REC.Cmds.begin();
+      //while(it != REC.Cmds.end()) { it->Dump(cout,string("")); ++it; }
+      //cout << "End of sorted list of RE cmds" << endl;
+   //}
+
+      // pass the rest
+   argc = Args.size()+1;
+   char **CArgs=new char*[argc];
+   if(!CArgs) { cerr << "Failed to allocate CArgs\n"; return -1; }
+   CArgs[0] = argv[0];
+   for(j=1; j<argc; j++) {
+      CArgs[j] = new char[Args[j-1].size()+1];
+      if(!CArgs[j]) { cerr << "Failed to allocate CArgs[j]\n"; return -1; }
+      strcpy(CArgs[j],Args[j-1].c_str());
+   }
+
+   //if(Debug) {
+      //cout << "List passed to parse\n";
+      //for(i=0; i<argc; i++) cout << i << " " << CArgs[i] << endl;
+   //}
+   Par.parseOptions(argc, CArgs);
+   delete[] CArgs;
+
+   if(iret != 0 || dashh.getCount() > 0) {      // iret from ParseCommands
+      if(help) {
+         Par.displayUsage(cout,false);
+         cout << endl;
+         DisplayRinexEditUsage(cout);
+      }
+      else {
+         Par.displayUsage(oflog,false);
+         oflog << endl;
+         DisplayRinexEditUsage(oflog);
+      }
+      help = true;   //return 1;
+   }
+
+   if (Par.hasErrors())
+   {
+      cerr << "\nErrors found in command line input:\n";
+      oflog << "\nErrors found in command line input:\n";
+      Par.dumpErrors(cerr);
+      Par.dumpErrors(oflog);
+      cerr << "...end of Errors\n\n";
+      oflog << "...end of Errors\n\n";
+      help = true;
+   }
+   
+      // f never appears because we intercept it in PreProcessArgs
+   //if(dashf.getCount()) { cout << "Option f "; dashf.dumpValue(cout); }
+      // get log file name - pull out in PreProcessArgs
+   //if(dashl.getCount()) {
+   //   values = dashl.getValue();
+   //   LogFile = values[0];
+   //   if(help) cout << "Output log file is: " << LogFile << endl;
+   //}
+
+   //if(dashh.getCount() && help)
+   //   oflog << "Option h appears " << dashh.getCount() << " times\n";
+   if(dashv.getCount() && help) {
+      Verbose = true;
+      //if(help) oflog << "Option v appears " << dashv.getCount() << " times\n";
+   }
+   if(dashd.getCount() && help) {
+      Debug = true;
+      //if(help) oflog << "Option d appears " << dashd.getCount() << " times\n";
+   }
+
+   if(Rest.getCount() && help) {
+      oflog << "Remaining options:" << endl;
+      values = Rest.getValue();
+      for (i=0; i<values.size(); i++) oflog << values[i] << endl;
+   }
+   if(Verbose && help) {
+      oflog << "\nTokens on command line (" << Args.size() << ") are:" << endl;
+      for(j=0; j<Args.size(); j++) oflog << Args[j] << endl;
+   }
+   if(help) return 1;
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+// Pull out --debug --verbose -f<f> and --file <f> and -l<f> --log <f> options.
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception)
+{
+try {
+   static bool found_cfg_file=false;
+   static bool found_log_file=false;
+
+   if(found_cfg_file || (arg[0]=='-' && arg[1]=='f')) {
+      string filename(arg);
+      if(!found_cfg_file) filename.erase(0,2); else found_cfg_file = false;
+      if(Debug) cout << "Found a file of options: " << filename << endl;
+      ifstream infile(filename.c_str());
+      if(!infile) {
+         cout << "Error: could not open options file " << filename << endl;
+         return;
+      }
+
+      bool again_cfg_file=false;
+      bool again_log_file=false;
+      char c;
+      string buffer,word;
+      while(1) {
+         getline(infile,buffer);
+         stripTrailing(buffer,'\r');
+
+         // process the buffer before checking eof or bad b/c there can be
+         // a line at EOF that has no CRLF...
+         while(!buffer.empty()) {
+            word = firstWord(buffer);
+            if(again_cfg_file) {
+               word = "-f" + word;
+               again_cfg_file = false;
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else if(again_log_file) {
+               word = "-l" + word;
+               again_log_file = false;
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else if(word[0] == '#') { // skip to end of line
+               buffer.clear();
+            }
+            else if(word == "--file" || word == "-f")
+               again_cfg_file = true;
+            else if(word == "--log" || word == "-l")
+               again_log_file = true;
+            else if(word[0] == '"') {
+               word = stripFirstWord(buffer,'"');
+               buffer = "dummy " + buffer;            // to be stripped later
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else
+               PreProcessArgs(word.c_str(),Args);
+
+            word = stripFirstWord(buffer);      // now remove it from buffer
+         }
+         if(infile.eof() || !infile.good()) break;
+      }
+   }
+   else if(found_log_file || (arg[0]=='-' && arg[1]=='l')) {
+      LogFile = string(arg);
+      if(!found_log_file) LogFile.erase(0,2); else found_log_file = false;
+   }
+   else if((arg[0]=='-' && arg[1]=='d') || string(arg)==string("--debug"))
+      Debug = true;
+   else if((arg[0]=='-' && arg[1]=='v') || string(arg)==string("--verbose"))
+      Verbose = true;
+   else if(string(arg) == "--file" || string(arg) == "-f")
+      found_cfg_file = true;
+   else if(string(arg) == "--log")
+      found_log_file = true;
+   else Args.push_back(arg);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
diff --git a/dev/apps/Rinextools/Jamfile b/dev/apps/Rinextools/Jamfile
new file mode 100644
index 0000000..19144cd
--- /dev/null
+++ b/dev/apps/Rinextools/Jamfile
@@ -0,0 +1,13 @@
+#
+# $Id$
+#
+
+SubDir TOP apps Rinextools ;
+
+GPSLinkLibraries NavMerge RinexDump ResCor EditRinex RinSum : gpstk ;
+
+GPSMain NavMerge : NavMerge.cpp ;
+GPSMain RinexDump : RinexDump.cpp ;
+GPSMain RinSum : RinSum.cpp ;
+GPSMain EditRinex : EditRinex.cpp RinexEditor.cpp ;
+GPSMain ResCor : ResCor.cpp RinexEditor.cpp ;
diff --git a/dev/apps/Rinextools/Makefile.am b/dev/apps/Rinextools/Makefile.am
new file mode 100644
index 0000000..564aa4f
--- /dev/null
+++ b/dev/apps/Rinextools/Makefile.am
@@ -0,0 +1,14 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ./libtoolslib.la ../../src/libgpstk.la
+
+lib_LTLIBRARIES = libtoolslib.la
+libtoolslib_la_SOURCES = RinexEditor.cpp
+
+bin_PROGRAMS = NavMerge RinexDump ResCor EditRinex RinSum
+
+NavMerge_SOURCES = NavMerge.cpp
+RinexDump_SOURCES = RinexDump.cpp
+RinSum_SOURCES = RinSum.cpp
+EditRinex_SOURCES = EditRinex.cpp
+ResCor_SOURCES = ResCor.cpp
diff --git a/dev/apps/Rinextools/NavMerge.cpp b/dev/apps/Rinextools/NavMerge.cpp
new file mode 100755
index 0000000..c8e8c4d
--- /dev/null
+++ b/dev/apps/Rinextools/NavMerge.cpp
@@ -0,0 +1,276 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file NavMerge.cpp
+ * Read, summarize and optionally merge, Rinex navigation files.
+ * NavMerge will merge any number of Rinex nav files into a unique superset,
+ * and either write them out to a new Rinex file (if an output file is given),
+ * or write a summary of the data to the screen. NavMerge also finds
+ * and fixes full week number when it is inconsistent with epoch.
+ */
+
+//------------------------------------------------------------------------------------
+#include <string>
+#include <vector>
+
+#include "StringUtils.hpp"
+#include "DayTime.hpp"
+#include "RinexNavData.hpp"
+#include "RinexNavHeader.hpp"
+#include "RinexNavStream.hpp"
+#include "BCEphemerisStore.hpp"
+
+//------------------------------------------------------------------------------------
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+void BadArg(string& arg) { cout << "Error: nothing follows option " << arg << endl; }
+
+//------------------------------------------------------------------------------------
+// Returns 0 when successful.
+int main(int argc, char *argv[])
+{
+   if (argc<2)
+   {
+      cout <<
+"Usage: NavMerge [options] <RINEX nav file name(s)>\n"
+"  Options are:\n"
+"    [-o|--out] <file>     Output RINEX navigation file name. If omitted, a data summary is displayed.\n"
+"    [-tb|--begTime] <tb>  Output data only if epoch is within 4 hours of the interval (tb,te).\n"
+"    [-te|--endTime] <te>    If one of (te,tb) is omitted, they are made equal.\n"
+"                            Times are either 'year,mon,day,hr,min,sec' or 'GPSweek,secOfWeek'\n"
+"  NB. NavMerge corrects data for output when GPS full week number is inconsistent with epoch time.\n"
+      ;
+
+      return -1;
+   }
+
+   try
+   {
+      int i;
+      string arg,filename,outfile,YMDformat("%Y,%m,%d,%H,%M,%f"),GPSformat("%F,%g");
+      DayTime tb,te;
+
+      te = tb = DayTime::BEGINNING_OF_TIME;
+
+      i = 1;
+      while(i < argc) {
+         arg = string(argv[i]);
+         if(arg == "--out" || arg.substr(0,2) == "-o") {
+            if(arg == "--out") {
+               argv[i][0] = '\0';
+               if(++i == argc) { BadArg(arg); break; }
+               outfile = string(argv[i]);
+            }
+            else
+               outfile = arg.substr(2);
+            cout << "Output file name is " << outfile << endl;
+            argv[i][0] = '\0';
+         }
+         else if(arg == "--begTime" || arg == "-tb" || arg.substr(0,3) == "-tb") {
+            if(arg.substr(0,3) == "-tb" && arg.size() > 3)
+               arg = arg.substr(3);
+            else {
+               argv[i][0] = '\0';
+               if(++i == argc) { BadArg(arg); break; }
+               arg = string(argv[i]);
+            }
+
+            if(numWords(arg,',') == 2)
+               tb.setToString(arg,GPSformat);
+            else if(numWords(arg,',') == 6)
+               tb.setToString(arg,YMDformat);
+            else
+               cout << "Unable to understand timetag option: " << arg << endl;
+            argv[i][0] = '\0';
+         }
+         else if(arg == "--endTime" || arg == "-te" || arg.substr(0,3) == "-te") {
+            if(arg.substr(0,3) == "-te" && arg.size() > 3)
+               arg = arg.substr(3);
+            else {
+               argv[i][0] = '\0';
+               if(++i == argc) { BadArg(arg); break; }
+               arg = string(argv[i]);
+            }
+
+            if(numWords(arg,',') == 2)
+               te.setToString(arg,GPSformat);
+            else if(numWords(arg,',') == 6)
+               te.setToString(arg,YMDformat);
+            else
+               cout << "Unable to understand timetag option: " << arg << endl;
+            argv[i][0] = '\0';
+         }
+
+         i++;
+      }
+
+      if(te != DayTime::BEGINNING_OF_TIME &&
+         tb == DayTime::BEGINNING_OF_TIME) tb = te;
+      else
+      if(tb != DayTime::BEGINNING_OF_TIME &&
+         te == DayTime::BEGINNING_OF_TIME) te = tb;
+      if(tb > te) { DayTime tt=tb; tb=te; te=tt; }
+
+      if(tb != DayTime::BEGINNING_OF_TIME)
+         cout << "Time limits are " << tb.printf(YMDformat)
+               << " - " << te.printf(YMDformat) << endl;
+
+      RinexNavHeader rnh,rnhout;
+      RinexNavData rne;
+      BCEphemerisStore EphStore;
+      RinexNavStream RNFileOut;
+
+      if(outfile != string("")) {
+         RNFileOut.open(outfile.c_str(),ios::out);
+         RNFileOut.exceptions(fstream::failbit);
+         rnhout.version = 2.1;
+         rnhout.valid |= RinexNavHeader::versionValid;
+         rnhout.fileType = string("NAVIGATION");
+         rnhout.fileProgram = string("NavMerge");
+         rnhout.fileAgency = string("ARL:UT/SGL/GPSTK");
+         rnhout.valid |= RinexNavHeader::runByValid;
+         rnhout.commentList.clear();
+         rnhout.valid |= RinexNavHeader::commentValid;
+         rnhout.valid |= RinexNavHeader::endValid;
+      }
+
+      int na=1,n=0,nf;
+      while(na < argc) {
+         filename = string(argv[na]);
+         if(filename == string("")) { na++; continue; }
+
+         try {
+            RinexNavStream RNFileIn(filename.c_str());
+            if(!RNFileIn) {
+               cout << "Could not open file " << filename << endl;
+               na++;
+               continue;
+            }
+            RNFileIn.exceptions(fstream::failbit);
+      
+            RNFileIn >> rnh;
+            if(rnh.valid & RinexNavHeader::ionAlphaValid) {
+               for(i=0; i<4; i++) rnhout.ionAlpha[i]=rnh.ionAlpha[i];
+               rnhout.valid |= RinexNavHeader::ionAlphaValid;
+            }
+            if(rnh.valid & RinexNavHeader::ionBetaValid) {
+               for(i=0; i<4; i++) rnhout.ionBeta[i]=rnh.ionBeta[i];
+               rnhout.valid |= RinexNavHeader::ionBetaValid;
+            }
+            if(rnh.valid & RinexNavHeader::deltaUTCValid) {
+               rnhout.A0 = rnh.A0;
+               rnhout.A1 = rnh.A1;
+               rnhout.UTCRefWeek = rnh.UTCRefWeek;
+               rnhout.UTCRefTime = rnh.UTCRefTime;
+               rnhout.valid |= RinexNavHeader::deltaUTCValid;
+            }
+            if(rnh.valid & RinexNavHeader::leapSecondsValid) {
+               rnhout.leapSeconds = rnh.leapSeconds;
+               rnhout.valid |= RinexNavHeader::leapSecondsValid;
+            }
+
+            nf = 0;
+            while (RNFileIn >> rne)
+            {
+               nf++;
+               n++;
+               // check that week number (associated with HOW) is consistent with TOC.
+               // (NB. in Rinex nav file, the week number is associated with the TOE;
+               // RinexNavData converts it to associate with the HOW)
+               int wkTOC,wk;
+               wk = rne.weeknum;                // 'weeknum' associated with HOW
+               wkTOC = rne.time.GPSfullweek();  // 'time' comes from epoch line
+               if(ABS(wk-wkTOC) > 1) {          // HOW and TOC should be w/in 1 week
+                  double dt = double(wk-wkTOC)/1024.0;
+                  dt += (dt < 0.0 ? -0.5 : 0.5);
+                  wk -= int(dt) * 1024;
+                  if(ABS(wk-wkTOC) > 1) {
+                     cout << "WARNING: Ephemeris in " << filename
+                        << " for satellite G"
+                        << setw(2) << setfill('0') << rne.PRNID << setfill(' ')
+                        << " at time " << rne.time
+                        << " has inconsistent week number " << rne.weeknum << endl;
+                  }
+                  else {
+                     cout << "NavMerge corrected the week in G"
+                        << setw(2) << setfill('0') << rne.PRNID << setfill(' ')
+                        << " " << rne.time
+                        << " " << filename << endl;
+                     rne.weeknum = wk;
+                  }
+               }
+                  // if healthy, add to the store
+               if(rne.health == 0) EphStore.addEphemeris(rne);
+            }
+         }
+         catch(Exception& e) {
+            cout << "Exception: " << e << endl;
+         }
+         na++;
+         cout << "Read " << setw(4) << nf << " ephemerides from file "
+            << filename << endl;
+      }
+      cout << "Read " << setw(4) << n << " total ephemerides." << endl;
+
+         // pull out all the ephemerides
+      list<EngEphemeris> EphList;
+      i = EphStore.addToList(EphList);
+
+      if(outfile != string("")) {
+            // write the output header
+         RNFileOut << rnhout;
+
+            // write out all the ephemerides
+         list<EngEphemeris>::iterator it=EphList.begin();
+         n=0;
+         while(it != EphList.end()) {
+            rne = RinexNavData(*it);
+            if(tb == DayTime::BEGINNING_OF_TIME ||
+               (rne.time - tb > -14400.0 && rne.time - te < 14400.0))
+            {
+               n++;
+               RNFileOut << rne;
+            }
+            it++;
+         }
+         cout << "Wrote " << setw(3) << n << " unique ephemerides to file "
+            << outfile << endl;
+      }
+      else {
+         EphStore.dump(1);
+      }
+
+      return 0;
+   }
+   catch(Exception& e) { cout << e; }
+   catch (...) { cout << "unknown error.  Done." << endl; }
+   return 1;
+
+   return 0;
+}
diff --git a/dev/apps/Rinextools/README b/dev/apps/Rinextools/README
new file mode 100644
index 0000000..c0d12ce
--- /dev/null
+++ b/dev/apps/Rinextools/README
@@ -0,0 +1,45 @@
+Rinex tools (/apps/Rinextools)
+
+   This directory contains 4 standalone programs which are useful in
+manipulating Rinex observation files. They are:
+   RinexDump   dumps Rinex observation data in columns in a flat file, useful
+               for plotting
+   RinSum      summarizes a Rinex observation file
+   EditRinex   reads and edits a Rinex observation file, writing out the a
+               new, edited one.
+   ResCor      reads a Rinex file(s) and computes any of several residuals and
+               corrections from the data, and then writes them to an output
+               Rinex observation file.
+   NavMerge    reads any number of Rinex navigation files and either prints a
+               summary on the screen, or, if an output file is specified,
+               writes a unique superset of all the ephemeris data to that file.
+               It will also correct the week number in the ephemeris if it is
+               inconsistent with the time on the epoch line.
+
+   In addition there is the RinexEditor module, which implements a class that
+may be called within other programs to edit the Rinex data. The EditRinex program is really just a shell that calls the RinexEditor, while ResCor is a more complicated program that also calls the RinexEditor but also modifies the data (i.e. computes the residuals and corrections) before writing it out.
+
+   All of these programs are run from the command line, and input and outputs are all flat files. To see the command line syntax, run the program with either no arguments, or with arguments but including --help.
+
+   Examples are not included here; however, examples are available elsewhere. RinSum is very easy to run; try typing
+
+   RinSum -i<file>
+
+at the command line for any Rinex observation file <file>. Note, also, that the
+discontinuity corrector uses EditRinex to apply its corrections, and that an
+example of this is found in /apps/cycleslips/examples. Finally, the RinexPlot utility (/apps/RinexPlot) makes extensive use of the other utility programs
+here; it is actually a Perl script and runs these programs from the command 
+line.
+
+Brian Tolman
+btolman at arlut.utexas.edu
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/apps/Rinextools/ResCor.cpp b/dev/apps/Rinextools/ResCor.cpp
new file mode 100644
index 0000000..bb77aef
--- /dev/null
+++ b/dev/apps/Rinextools/ResCor.cpp
@@ -0,0 +1,2230 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file ResCor.cpp
+ * 'Residuals and Corrections'
+ * Open and read a single Rinex observation file, apply editing commands
+ * using the RinexEditor package, compute any of several residuals and corrections
+ * and register extended Rinex observation types for them, and then write
+ * the edited data, along with the new extended observation types,
+ * to an output Rinex observation file. Input is all on the command line.
+ * ResCor is implemented by deriving a special class from class RinexEditor and
+ * using its virtual functions to implement all the changes necessary to define
+ * and compute the residuals and corrections.
+ */
+
+//------------------------------------------------------------------------------------
+// ToDo
+// catch exceptions -- elsewhere and on reading header and obs
+// allow user to specify trop model, both for RAIM and for TR output
+//
+
+#include "StringUtils.hpp"
+#include "DayTime.hpp"
+#include "RinexSatID.hpp"
+#include "CommandOptionParser.hpp"
+#include "CommandOption.hpp"
+#include "CommandOptionWithTimeArg.hpp"
+#include "RinexObsBase.hpp"
+#include "RinexObsData.hpp"
+#include "RinexObsHeader.hpp"
+#include "RinexObsStream.hpp"
+#include "RinexNavStream.hpp"
+#include "RinexNavData.hpp"
+#include "SP3Stream.hpp"
+#include "SP3EphemerisStore.hpp"
+#include "BCEphemerisStore.hpp"
+#include "EphemerisRange.hpp"
+#include "TropModel.hpp"
+#include "PRSolution.hpp"
+#include "WGS84Geoid.hpp"           // for obliquity
+#include "Stats.hpp"
+#include "geometry.hpp"             // DEG_TO_RAD
+#include "icd_200_constants.hpp"    // PI,C_GPS_M,OSC_FREQ,L1_MULT,L2_MULT
+
+#include "RinexEditor.hpp"
+#include "RinexUtilities.hpp"
+#include "Position.hpp"
+
+#include <time.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+//------------------------------------------------------------------------------------
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+   // prgm data
+string PrgmName("ResCor");
+string PrgmVers("3.7 1/22/07");
+
+// data used in program
+const double CFF=C_GPS_M/OSC_FREQ;
+const double F1=L1_MULT;   // 154.0;
+const double F2=L2_MULT;   // 120.0;
+const double f12=F1*F1;
+const double f22=F2*F2;
+const double wl1=CFF/F1;
+const double wl2=CFF/F2;
+const double wl1r=F1/(F1+F2);
+const double wl2r=F2/(F1+F2);
+const double wl1p=wl1*F1/(F1-F2);
+const double wl2p=-wl2*F2/(F1-F2);
+const double if1r=f12/(f12-f22);
+const double if2r=-f22/(f12-f22);
+const double if1p=wl1*f12/(f12-f22);
+const double if2p=-wl2*f22/(f12-f22);
+const double gf1r=-1;
+const double gf2r=1;
+const double gf1p=wl1;
+const double gf2p=-wl2;
+const double alpha=f12/f22 - 1.0;
+const double FL1=F1*10.23e6;                 // Hz
+const double TECUperM=FL1*FL1*1.e-16/40.28;  // 6.1617 TECU/m (0.16229 m/TECU)
+
+clock_t totaltime;
+string Title;
+   // input flags and data
+bool Debug,Verbose,Callow,Cforce;
+double IonoHt;
+RinexSatID SVonly;
+string LogFile;
+ofstream logof,oferr;         // don't call it oflog - RinexEditor has that
+   // Rinex headers, input and output, saved
+RinexObsHeader rhead, rheadout;
+   // ephemeris
+string NavDir;
+vector<string> NavFiles;
+SP3EphemerisStore SP3EphList;
+BCEphemerisStore BCEphList;
+SimpleTropModel ggtm;
+   // for use with current position and in RefPosMap (RAIM and/or RefPosFile)
+typedef struct ReferencePositionFileData {
+   Position RxPos;              // XYZT
+   bool valid;
+   int NPRN;
+   double clk,PDOP,GDOP,RMS;
+} RefPosData;
+RefPosData CurrRef;        // current reference position
+   // reference and RAIM solution
+string RefPosFile,KnownPos;
+bool doRAIM,editRAIM,outRef,headRAIM,HaveRAIM;
+bool RefPosInput,KnownPosInput,KnownLLH,RefPosFlat;
+double minElev;
+vector<SatID> Sats;
+vector<double> PRange;
+//RAIMSolution RAIMSol;
+PRSolution prsol;
+Stats<double> ARSX,ARSY,ARSZ;      // average solution, for header output
+   // computation
+int inC1,inP1,inP2,inL1,inL2;      // indexes in rhead of C1, C1/P1, P2, L1 and L2
+int inEP,inPS;                     // flags for input of ephemeris, Rx position
+int inD1,inD2,inS1,inS2;
+DayTime CurrentTime, PrgmEpoch;
+// these 3 vectors parallel
+vector<string> OTstrings;          // list of OTs (strings) to be computed
+vector<RinexObsHeader::RinexObsType> OTList;
+vector<int> OTindex;
+int otC1,otP1,otP2,otL1,otL2;      // indexes in rheadout of C1, C1/P1, P2, L1 and L2
+int otD1,otD2,otS1,otS2;
+bool DoSVX;
+WGS84Geoid WGS84;
+// compute non-dispersive range, ionospheric delay, multipath (L1 and L2)
+bool DoXR;
+double XRM0[4],XRM1[4],XRM2[4],XRM3[4];
+double *XRM[4]={XRM0,XRM1,XRM2,XRM3};
+double XRdat[4],XRsol[4];
+   // structure for holding raw range and phase data during computation
+typedef struct range_and_phase_data {
+   double L1,L2,P1,P2;
+   int LL1,LL2;
+} RCData;
+   // map of <sat,RCData>
+RCData DataStore;
+map<RinexSatID,RCData> DataStoreMap;
+   // debiasing output data
+map<RinexObsHeader::RinexObsType,map<RinexSatID,double> > AllBiases; // (OT,SV)
+   // reference position as function of time (from input)
+map<DayTime,RefPosData> RefPosMap;
+double RefPosMapDT;
+
+string RxhelpString=
+"\n --RxFlat <fn> : fn is a file with reference receiver positions and times:\n"
+"  The first line in the file (other than comments, marked by # in column 1)\n"
+"  is the format for each line of the file, using the specifications in\n"
+"  DayTime::setToString() and Position::setToString().\n"
+"  The second line is a pattern made up of characters T, P and X indicating the\n"
+"  content of both the lines in the file and the format: (white-space-delimited)\n"
+"  words on each line are either part of the time(T) or position(P) specification,\n"
+"  or are to be ignored(X). For example, the file begins with these six lines:\n"
+"  # format:\n"
+"  t= %F %g p= %x %y %z\n"
+"  # pattern:\n"
+"  XTTXPPP\n"
+"  # data:\n"
+"  t= 1281 259200    p=   -2701232.4        6123085.7        1419837.5";
+
+//------------------------------------------------------------------------------------
+// inherit RinexEditor so that callback routines can be defined by Prgm ResCor
+class RCRinexEditor : public RinexEditor
+{
+   public:
+         /// Constructor.
+      RCRinexEditor() throw() {};
+
+         /// destructor
+      virtual ~RCRinexEditor() {}
+   
+         /// after reading input header and before calling
+         /// RinexEditor::EditHeader (pass input header)
+      virtual int BeforeEditHeader(const RinexObsHeader& rhin) throw(Exception);
+
+         /// after calling RinexEditor::EditHeader (pass output header)
+      virtual int AfterEditHeader(const RinexObsHeader& rhout) throw(Exception);
+
+         /// after reading input obs and before calling
+         /// RinexEditor::EditObs (pass input obs)
+      virtual int BeforeEditObs(const RinexObsData& roin) throw(Exception);
+
+         /// before writing out header (pass output header)
+      virtual int BeforeWritingHeader(RinexObsHeader& rhout) throw(Exception);
+
+         /// before writing out filled header
+      virtual int BeforeWritingFilledHeader(RinexObsHeader& rhout) throw(Exception);
+
+         /// just before writing output obs (pass output obs)
+      virtual int BeforeWritingObs(RinexObsData& roout) throw(Exception);
+
+}; // end class RCRinexEditor
+
+//------------------------------------------------------------------------------------
+// prototypes
+int GetCommandLine(int argc, char **argv, RCRinexEditor& rc) throw(Exception);
+int PrepareInput(void) throw(Exception);
+int LoopOverObs(void) throw(Exception);
+void SaveData(const RinexObsData& rod, const RinexObsHeader& rh,
+   int xL1, int xL2, int xP1, int xP2) throw(Exception);
+int UpdateRxPosition(void) throw(Exception);
+void ComputeNewOTs(RinexObsData& rod) throw(Exception);
+void CloseOutputFile(void) throw(Exception);
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception);
+int setBiasLimit(RinexObsHeader::RinexObsType& ot, double lim) throw(Exception);
+double removeBias(const RinexObsHeader::RinexObsType& ot, const RinexSatID& sat,
+   bool& reset, DayTime& tt, double delta) throw(Exception);
+
+//------------------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+try {
+   totaltime = clock();
+   int iret;
+   // NB. Do not instantiate editor outside main(), b/c DayTime::END_OF_TIME is a
+   // static const that can produce static intialization order problems under some OS.
+   RCRinexEditor REC;
+   CurrentTime = DayTime::BEGINNING_OF_TIME; // for same reason, init here...
+
+      // Title and description
+   Title = PrgmName + ", part of the GPS ToolKit, Ver. " + PrgmVers
+      + " (editor " + REC.getRinexEditVersion() + string("), Run ");
+   time_t timer;
+   struct tm *tblock;
+   timer = time(NULL);
+   tblock = localtime(&timer);
+   PrgmEpoch.setYMDHMS(1900+tblock->tm_year,1+tblock->tm_mon,
+               tblock->tm_mday,tblock->tm_hour,tblock->tm_min,tblock->tm_sec);
+   Title += PrgmEpoch.printf("%04Y/%02m/%02d %02H:%02M:%02S");
+   Title += "\n";
+   cout << Title;
+
+      // define extended types
+   iret = RegisterARLUTExtendedTypes();
+   if(iret) goto quit;
+
+   // Set defaults, define command line and parse it.
+   // Send REdit cmds to REC. Check validity of input.
+   iret = GetCommandLine(argc, argv, REC);
+   if(iret) goto quit;
+
+   // Initialize, read ephemerides, set flags and prepare for processing
+   iret = PrepareInput();
+   if(iret) goto quit;
+
+   // Edit the file, including callbacks
+   iret = REC.EditFile();
+   if(Debug) logof << "EditFile returned " << iret << endl;
+   if(iret) goto quit;
+
+   quit:
+   // compute run time
+   totaltime = clock()-totaltime;
+   logof << "ResCor timing: " << setprecision(3)
+      << double(totaltime)/double(CLOCKS_PER_SEC) << " seconds.\n";
+
+   logof.close();
+   cout << "End ResCor" << endl;
+   return iret;
+}
+catch(gpstk::FFStreamError& e) { cerr << e; }
+catch(gpstk::Exception& e) { cerr << e; }
+catch(exception& e) { cerr << e.what(); }
+catch (...) { cerr << "Unknown error.  Abort." << endl; }
+   return 1;
+}   // end main()
+
+//------------------------------------------------------------------------------------
+// Set defaults, define command line and parse it. Send REdit cmds to REC.
+// Check validity of input
+int GetCommandLine(int argc, char **argv, RCRinexEditor& REC) throw(Exception)
+{
+   bool help=false,Rxhelp=false,REChelp=false,ROThelp=false;
+   int i,j,iret;
+try {
+      // defaults
+   Debug = Verbose = false;
+
+   doRAIM = false;
+   KnownPosInput = RefPosInput = false;
+   outRef = true;
+   editRAIM = true;
+   headRAIM = false;
+   minElev = 0.0;
+   
+   IonoHt = 400.0;      // km
+
+   Callow = true;
+   Cforce = false;
+
+   LogFile = string("ResCor.log");
+
+      // -------------------------------------------------
+      // required options
+
+      // optional options
+
+      // this only so it will show up in help page...
+   CommandOption dashf(CommandOption::hasArgument, CommandOption::stdType,
+      'f',"","\nConfiguration input:\n --file <file>   File containing more options");
+
+   // ephemeris
+   CommandOption dashn(CommandOption::hasArgument, CommandOption::stdType,
+      0,"nav"," --nav <file>    Navigation (Rinex Nav OR SP3) file(s)");
+
+   CommandOption dashnd(CommandOption::hasArgument, CommandOption::stdType,
+      0,"navdir"," --navdir <dir>  Directory of navigation file(s)");
+   dashnd.setMaxCount(1);
+
+   // reference position(s)
+   CommandOption dashRx1(CommandOption::hasArgument,CommandOption::stdType,0,"RxLLH",
+      "Reference position input: (there are six ways to input the reference "
+      "position(s):\n --RxLLH <l,l,h> 1.Receiver position (static) in geodetic "
+      "lat, lon(E), ht (deg,deg,m)");
+   dashRx1.setMaxCount(1);
+
+   CommandOption dashRx2(CommandOption::hasArgument, CommandOption::stdType,0,"RxXYZ",
+      " --RxXYZ <x,y,z> 2.Receiver position (static) in ECEF coordinates (m)");
+   dashRx2.setMaxCount(1);
+
+   CommandOptionNoArg dashRx3(0,"Rxhere",
+      " --Rxhere        3.Reference site positions(time) from this file"
+      " (i.e. -IF<RinexFile>)");
+   dashRx3.setMaxCount(1);
+
+   CommandOption dashRx4(CommandOption::hasArgument, CommandOption::stdType,0,
+      "RxRinex"," --RxRinex <fn>  4.Reference site positions(time) from another "
+      "Rinex file named <fn>");
+   dashRx4.setMaxCount(1);
+
+   CommandOption dashRx5(CommandOption::hasArgument,CommandOption::stdType,0,"RxFlat",
+      " --RxFlat <fn>   5.Reference site positions and times given in a flat file"
+      " named <fn>");
+   dashRx5.setMaxCount(1);
+
+   CommandOptionNoArg dashRxhelp(0,"Rxhelp"," --Rxhelp        "
+      "(Enter --Rxhelp for a description of the -RxFlat file format)");
+   dashRxhelp.setMaxCount(1);
+
+   CommandOptionNoArg dashRx6(0,"RAIM",
+      " --RAIM          6.Reference site positions computed via RAIM"
+      " (requires P1,P2,EP)");
+   dashRx6.setMaxCount(1);
+
+   CommandOptionNoArg dashred(0,"noRAIMedit",
+      "  (NB the following four options apply only if --RAIM is found)\n"
+      " --noRAIMedit    Do not edit data based on RAIM solution");
+   dashred.setMaxCount(1);
+
+   CommandOptionNoArg dashrh(0,"RAIMhead",
+      " --RAIMhead      Output average RAIM solution to Rinex header "
+      "(if -HDf also appears)");
+   dashrh.setMaxCount(1);
+
+   CommandOptionNoArg dashro(0,"noRefout",
+      " --noRefout      Do not output reference solution to Rinex");
+   dashro.setMaxCount(1);
+
+   CommandOption dashelev(CommandOption::hasArgument,CommandOption::stdType,
+      0,"MinElev",
+      " --MinElev <el>  Minimum satellite elevation (deg) for output");
+   dashelev.setMaxCount(1);
+
+   // residual and correction computation, processing options
+   CommandOption dashdb(CommandOption::hasArgument, CommandOption::stdType,0,"debias",
+      "Residual/Correction computation:\n"
+      " --debias <OT,l> Debias new output type <OT>; "
+      "trigger a bias reset with limit <l>");
+
+   CommandOptionNoArg dashca(0,"Callow",
+      " --Callow        Allow C1 to replace P1 when P1 is not available");
+   dashca.setMaxCount(1);
+
+   CommandOptionNoArg dashcf(0,"Cforce",
+      " --Cforce        Force C/A code pseudorange C1 to replace P1");
+   dashcf.setMaxCount(1);
+
+   CommandOption dashih(CommandOption::hasArgument, CommandOption::stdType,0,"IonoHt",
+      " --IonoHt <ht>   Height of ionosphere in km (default 400) "
+      "(needed for LA,LO,VR,VP)");
+   dashih.setMaxCount(1);
+
+   CommandOption dashSV(CommandOption::hasArgument, CommandOption::stdType, 0,"SVonly",
+      " --SVonly <sat>  Process this satellite ONLY (use editing command -DS<Sat> to delete Sat)");
+   dashSV.setMaxCount(1);
+
+   // output files
+   CommandOption dashLog(CommandOption::hasArgument, CommandOption::stdType,
+      0,"Log","Output files:\n --Log <file>    Output log file name (rc.log)");
+   dashLog.setMaxCount(1);
+
+   //CommandOption dashErr(CommandOption::hasArgument, CommandOption::stdType,
+   //   0,"Err"," --Err <file>    Output error file name (rc.err)");
+   //dashErr.setMaxCount(1);
+
+   // help
+   CommandOptionNoArg dashVerb(0,"verbose",
+      "Help:\n --verbose       Print extended output to log file.");
+   dashVerb.setMaxCount(1);
+
+   CommandOptionNoArg dashDebug(0,"debug",
+      " --debug         Print debugging information to log file.");
+   dashDebug.setMaxCount(1);
+
+   CommandOptionNoArg dashh('h', "help"," --help [or -h]  Print syntax and quit.");
+   CommandOptionNoArg dashrech(0, "REChelp",
+      " --REChelp       Print syntax of RinexEditor commands and quit.");
+   CommandOptionNoArg dashexth(0, "ROThelp",
+      " --ROThelp       Print list of extended Rinex observation types and quit.");
+
+   // ... other options
+   CommandOptionRest Rest("");
+
+   CommandOptionParser Par(
+   "Prgm ResCor will open and read a single Rinex observation file, "
+   "apply editing commands\n"
+   "   using the RinexEditor package, compute any of several residuals "
+   "and corrections and\n"
+   "   register extended Rinex observation types for them, and then write "
+   "the edited data,\n"
+   "   along with the new extended observation types, to an output Rinex "
+   "observation file.\n"
+   "\nRequired arguments:\n"
+   " -IF and -OF (RinexEditor commands: cf. --REChelp) are required arguments.\n");
+
+      // -------------------------------------------------
+      // allow user to put all options in a file
+      // could also scan for debug here
+   vector<string> Args;
+   for(j=1; j<argc; j++) PreProcessArgs(argv[j],Args);
+   argc = Args.size();
+   if(argc==0) Args.push_back(string("--help"));
+
+   //if(Debug) {
+      //cout << "List after PreProcessArgs\n";
+      //for(i=0; i<argc; i++) cout << i << " " << Args[i] << endl;
+   //}
+
+      // add PRGM and RUNBY strings to the header
+   REC.REVerbose = Verbose;
+   REC.REDebug = Debug;
+   Args.push_back(string("-HDp") + PrgmName + string(" v.") + PrgmVers.substr(0,4));
+   Args.push_back(string("-HDrARL:UT/SGL/GPSTK"));
+
+   if(Debug) {
+      cout << "List passed to REditCommandLine:\n";
+      for(i=0; i<argc; i++) cout << i << " " << Args[i] << endl;
+   }
+
+      // Add RE cmds; this will strip out the REditCmds from Args
+   REC.AddCommandLine(Args);
+   if(Debug) {
+      cout << "List after REC.AddCommandLine(Args)\n";
+      argc = Args.size();
+      for(i=0; i<argc; i++) cout << i << " " << Args[i] << endl;
+   }
+
+      // get the list of commands and create OTstrings
+   vector<string> cmds=REC.CommandList();
+   if(Debug) cout << "Here is the list of RE commands:\n";
+   for(i=0; i<cmds.size(); i++) {
+      string cmd = cmds[i];
+      if(Debug) cout << "  " << cmd << endl;
+
+      vector<string> field;
+      while(cmd.size() > 0)
+         field.push_back(stripFirstWord(cmd,','));
+      if(field.size() < 5) continue;
+      if(field[0] == "AO")
+         OTstrings.push_back(field[4]);
+   }
+   if(Debug) cout << "End list of RE commands." << endl;
+
+      // preprocess the commands
+      // Return 0 ok, -1 no input file name, -2 no output file name
+   iret = REC.ParseCommands();
+   //if(Debug) {
+      //cout << "\nHere is the parsed list of RE cmds\n";
+      //it=REC.Cmds.begin();
+      //while(it != REC.Cmds.end()) { it->Dump(cout,string("")); ++it; }
+      //cout << "End of sorted list of RE cmds" << endl << endl;
+
+      // pass the rest to the regular command line processor
+   //}
+
+      // -------------------------------------------------------------------
+   argc = Args.size()+1;
+   char **CArgs=new char*[argc];
+   if(!CArgs) { cerr << "Failed to allocate CArgs\n"; return -1; }
+   CArgs[0] = argv[0];
+   for(j=1; j<argc; j++) {
+      CArgs[j] = new char[Args[j-1].size()+1];
+      if(!CArgs[j]) { cerr << "Failed to allocate CArgs[j]\n"; return -1; }
+      strcpy(CArgs[j],Args[j-1].c_str());
+   }
+
+   //if(Debug) {
+      //cout << "List passed to parser\n";
+      //for(i=0; i<argc; i++) cout << i << " " << CArgs[i] << endl;
+   //}
+
+   Par.parseOptions(argc, CArgs);
+   delete[] CArgs;
+
+      // -------------------------------------------------
+      // was help requested?
+   if(dashh.getCount() > 0) help=true;
+   if(dashRxhelp.getCount() > 0) Rxhelp=true;
+   if(dashrech.getCount() > 0) REChelp=true;
+   if(dashexth.getCount() > 0) ROThelp=true;
+      // if errors on the command line, dump them and turn on help
+   if(!(help || Rxhelp || REChelp || ROThelp) && (iret<0 || Par.hasErrors())) {
+      cout << "Errors found in command line input:\n";
+      if(iret==-1 || iret==-3) cout << "Input file name required: use -IF<name>\n";
+      if(iret==-2 || iret==-3) cout << "Output file name required: use -OF<name>\n";
+      Par.dumpErrors(cout);
+      cout << "...end of Errors\n\n";
+      help = true;
+   }
+      // display syntax page(s)
+   if(help || Rxhelp || REChelp || ROThelp) {
+      if(help)
+         Par.displayUsage(cout,false);
+      if(Rxhelp)
+         cout << RxhelpString << endl;
+      if(REChelp) {
+         if(help || Rxhelp) cout << endl;
+         cout << "ResCor is an implementation of the RinexEditor, therefore the"
+               << " following commands are accepted.\n";
+         DisplayRinexEditUsage(cout);
+      }
+      if(ROThelp) {
+         if(help || Rxhelp || REChelp) cout << endl;
+         DisplayStandardRinexObsTypes(cout);
+         cout << "End of list of standard observation types\n";
+         DisplayExtendedRinexObsTypes(cout);
+         cout << "End of list of extended observation types\n";
+      }
+      if(iret < 0) return iret;
+   }
+
+      // -------------------------------------------------
+      // get values found on command line
+   vector<string> values;
+
+   //dashf intercepted above by PreProcessArgs
+   //dashh Handled above (first)
+   //if(dashDebug.getCount()) Debug=true; done by PreProcessArgs
+   //if(dashVerb.getCount()) Verbose=true; done by PreProcessArgs
+
+      // now do the rest
+   // ephemeris input
+   if(dashnd.getCount()) {
+      values = dashnd.getValue();
+      NavDir = values[0];
+      if(help) cout << "Nav Directory is " << NavDir  << endl;
+   }
+   if(dashn.getCount()) {
+      values = dashn.getValue();
+      NavFiles = values;
+      if(help) {
+         cout << "Nav files are:";
+         for(i=0; i<NavFiles.size(); i++) cout << " " << NavFiles[i];
+         cout << endl;
+      }
+   }
+
+   // reference position
+   if(dashRx1.getCount()) {
+      values = dashRx1.getValue();
+      KnownPos = values[0];
+      KnownLLH = true;
+      KnownPosInput = true;
+      if(help) cout << "Get reference position from explicit input (LLH) "
+         << KnownPos << endl;
+   }
+   if(dashRx2.getCount()) {
+      values = dashRx2.getValue();
+      KnownPos = values[0];
+      KnownLLH = false;
+      KnownPosInput = true;
+      if(help) cout << "Get reference position from explicit input (XYZ) "
+         << KnownPos << endl;
+   }
+   if(dashRx3.getCount()) {       // get ref from this input file
+      RefPosInput = true;
+      if(help) cout << "Get reference position from this input file" << endl;
+   }
+   if(dashRx4.getCount()) {
+      values = dashRx4.getValue();
+      RefPosFile = values[0];
+      RefPosFlat = false;
+      if(help) cout << "Get reference position from Rinex file " << RefPosFile<<endl;
+   }
+   if(dashRx5.getCount()) {
+      values = dashRx5.getValue();
+      RefPosFile = values[0];
+      RefPosFlat = true;
+      if(help) cout << "Get reference position from flat file " << RefPosFile << endl;
+   }
+   if(dashRx6.getCount()) {
+      doRAIM = true;
+      if(help) cout << "Compute a RAIM solution" << endl;
+   }
+
+   // RAIM options
+   if(dashred.getCount()) {
+      if(doRAIM) {
+         editRAIM = false;
+         if(help) cout << "Do not edit data based on RAIM solution" << endl;
+      }
+      else if(help) cout << "Ignore --noRAIMedit: --RAIM was not set" << endl;
+   }
+   if(dashro.getCount()) {
+      outRef = false;
+      if(help) cout << "Do not output Reference solution to Rinex" << endl;
+   }
+   if(dashelev.getCount()) {
+      values = dashelev.getValue();
+      minElev = asDouble(values[0]);
+      if(help) cout << "Set minimum elevation angle "
+         << fixed << setprecision(2) << minElev << endl;
+   }
+   if(dashrh.getCount()) {
+      if(doRAIM) {
+         headRAIM = true;
+         if(help) cout << "Output average RAIM solution to header" << endl;
+      }
+      else if(help) cout << "Ignore --RAIMhead: --RAIM was not set" << endl;
+   }
+
+   if(dashdb.getCount()) {
+      values = dashdb.getValue();
+      vector<string> subfield;
+      string::size_type pos;
+      for(i=0; i<values.size(); i++ ) {
+         string argbias=values[i];
+         subfield.clear();
+         while(argbias.size() > 0) {
+            pos = argbias.find(",");
+            if(pos==string::npos) pos=argbias.size();
+            if(pos==0) subfield.push_back(" ");
+            else subfield.push_back(argbias.substr(0,pos));
+            if(pos >= argbias.size()) break;
+            argbias.erase(0,pos+1);
+         }
+         RinexObsHeader::RinexObsType OT;
+         OT = RinexObsHeader::convertObsType(subfield[0]);
+         double limit=asDouble(subfield[1]);
+         int iret=setBiasLimit(OT,limit);
+         if(iret) {
+            cout << "Error: '--debias <OT,lim>' input is invalid: "
+               << values[i] << endl;
+            cerr << "Error: '--debias <OT,lim>' input is invalid: "
+               << values[i] << endl;
+         }
+         else if(Debug)
+            cout << "Set bias limit for " << RinexObsHeader::convertObsType(OT)
+            << " to " << fixed << setprecision(3) << limit
+            << " (" << values[i] << ")" << endl;
+      }
+   }
+   if(dashca.getCount()) {
+      Callow = true;
+      if(help) cout << "Allow C1 to be P1 when P1 not available\n";
+   }
+   if(dashcf.getCount()) {
+      Cforce = true;
+      if(help) cout << "Force C1 to replace P1 when C1 available\n";
+   }
+   if(dashih.getCount()) {
+      values = dashih.getValue();
+      IonoHt = asDouble(values[0]);
+      if(help) cout << "Set ionosphere height to " << values[0] << " km" << endl;
+   }
+   if(dashSV.getCount()) {
+      values = dashSV.getValue();
+      SVonly.fromString(values[0]);
+      if(help) cout << "Process only satellite : " << SVonly << endl;
+   }
+   if(dashLog.getCount()) {
+      values = dashLog.getValue();
+      LogFile = values[0];
+      if(help) cout << "Log file is " << LogFile << endl;
+   }
+
+   if(Rest.getCount() && help) {
+      cout << "Remaining options:" << endl;
+      values = Rest.getValue();
+      for (i=0; i<values.size(); i++) cout << values[i] << endl;
+   }
+
+   //if(Verbose && help) {
+   //   cout << "\nTokens on command line (" << Args.size() << ") are:" << endl;
+   //   for(j=0; j<Args.size(); j++) cout << Args[j] << endl;
+   //}
+
+      // -------------------------------------------------
+      // now process some of the input
+   try {
+      logof.clear();
+      logof.exceptions(ios_base::badbit | ios_base::failbit);
+      logof.open(LogFile.c_str(),ios::out);
+      if(logof.fail()) {
+         cout << "Failed to open log file " << LogFile << endl;
+         return -1;
+      }
+      else {
+         cout << "Opened log file (for all output, including debug) "
+            << LogFile << endl;
+         logof << Title;
+      }
+      REC.oflog = &logof;
+   }
+   catch(ios_base::failure& e) {
+      cout << "Exception " << e.what() << endl;
+      return -1;
+   }
+
+   // check for multiple inputs
+   if(KnownPosInput || !RefPosFile.empty() || doRAIM || RefPosInput) {
+      i = 0;
+      if(KnownPosInput) i++;
+      if(!RefPosFile.empty()) i++;
+      if(doRAIM) i++;
+      if(RefPosInput) i++;
+      if(i > 1) {
+         ostringstream stst;
+         stst << "ERROR: multiple inputs inconsistent:";
+         if(KnownPosInput) stst << (KnownLLH ? " --RxLLH" : " --RxXYZ");
+         if(!RefPosFile.empty()) stst << (RefPosFlat ? " --RxFlat" : " --RxRinex");
+         if(doRAIM) stst << " --RAIM";
+         if(RefPosInput) stst << " --RxHere";
+         stst << endl;
+         logof << stst.str();
+         cerr << stst.str();
+         return -1;           // fail? or take default
+      }
+      else if(help) logof << "Position input ok\n";
+   }
+      // print config to log
+   if(Verbose) {
+      logof << "-------- Here is the program configuration:\n";
+      logof << "Input Rinex observation file name is: "
+         << REC.InputFileName() << endl;
+      logof << "Input Directory is " << REC.InputDirectory() << endl;
+      logof << "Output Rinex obs file name is: " << REC.OutputFileName() << endl;
+      logof << "Output Directory is " << REC.OutputDirectory() << endl;
+      if(REC.BeginTimeLimit() > DayTime::BEGINNING_OF_TIME)
+         logof << "Begin time limit is " << REC.BeginTimeLimit() << endl;
+      if(REC.EndTimeLimit() < DayTime::END_OF_TIME)
+         logof << "End time limit is " << REC.EndTimeLimit() << endl;
+      if(REC.Decimation() != 0) logof << "Decmimation time interval is "
+         << setprecision(2) << REC.Decimation() << " seconds." << endl;
+      logof << "Tolerance in time-comparisions is " << setprecision(8)
+         << REC.Tolerance() << " seconds." << endl;
+      logof << "Log file name is " << LogFile << " (this file)" << endl;
+      if(SVonly.id > 0) logof << "Process only satellite : " << SVonly << endl;
+
+      if(!NavDir.empty()) logof << "Nav Directory is " << NavDir  << endl;
+      if(NavFiles.size()) {
+         logof << "Nav files:";
+         for(i=0; i<NavFiles.size(); i++) logof << " " << NavFiles[i];
+         logof << endl;
+      }
+      if(KnownPosInput) logof << "Get reference position from explicit input ("
+         << (KnownLLH ? "LLH" : "XYZ") << ") : " << KnownPos << endl;
+      if(doRAIM) logof << "Compute a RAIM solution" << endl;
+      if(minElev > 0.0) logof << "Minimum elevation angle limit "
+         << fixed << setprecision(2) << minElev << " degrees." << endl;
+      if(RefPosInput) logof << "Get reference position from in-line headers in "
+         << "the input Rinex file" << endl;
+      if(!RefPosFile.empty())
+         logof << "Get reference position from a " << (RefPosFlat ? "flat" : "Rinex")
+            << " file: " << RefPosFile << endl;
+      if(!editRAIM) logof << "Do not ";
+      logof << "Edit data based on RAIM solution" << endl;
+      if(!outRef) logof << "Do not ";
+      logof << "Output Reference solution to Rinex" << endl;
+      if(!headRAIM) logof << "Do not ";
+      logof << "Output average RAIM solution to header" << endl;
+      if(Callow) logof << "Allow C1 to be P1 when P1 not available\n";
+      if(Cforce) logof << "Force C1 to replace P1 when C1 available\n";
+      logof << "Ionosphere height is " << IonoHt << " km" << endl;
+      if(AllBiases.size()) {
+         logof << "The list of de-biasing limits is:\n";
+         map<RinexObsHeader::RinexObsType,map<RinexSatID,double> >::iterator it;
+         for(it=AllBiases.begin(); it!=AllBiases.end(); it++) {
+            map<RinexSatID,double>::iterator jt;
+            for(jt=it->second.begin(); jt!=it->second.end(); jt++) {
+               logof << "  Bias limit(" << RinexObsHeader::convertObsType(it->first)
+                  << ") = " << fixed << setprecision(3) << jt->second << endl;
+            }
+         }
+      }
+      logof << "-------- End of the program configuration.\n";
+      logof << endl;
+   }
+
+   if(help) return 1;
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+// Initialize, read ephemerides, set flags and prepare for processing
+int PrepareInput(void) throw(Exception)
+{
+try {
+   int iret,i;
+
+      // set all input/output indexes to 'undefined'
+   inC1 = inP1 = inP2 = inL1 = inL2 = inEP = inPS = inD1 = inD2 = inS1 = inS2 = -1;
+   otC1 = otP1 = otP2 = otL1 = otL2 = otD1 = otD2 = otS1 = otS2 = -1;
+
+      // --------------------------------------------------------------------
+      // ephemeris
+      // add Nav directory to nav file names
+   if(!NavDir.empty() && NavFiles.size()>0) {
+      for(i=0; i<NavFiles.size(); i++)
+         NavFiles[i] = NavDir + string("/") + NavFiles[i];
+   }
+
+      // open nav files and read EphemerisStore -- set inEP and inPS
+   iret = FillEphemerisStore(NavFiles, SP3EphList, BCEphList);
+   if(SP3EphList.size()) {
+      if(Verbose) SP3EphList.dump(1,logof);
+      inEP = 1;
+   }
+   else if(Verbose) logof << "SP3 Ephemeris list is empty\n";
+
+   if(BCEphList.size()) {
+      BCEphList.SearchNear();
+      if(Verbose) BCEphList.dump(0,logof);
+      inEP = 1;
+   }
+   else if(Verbose) logof << "BC Ephemeris list is empty\n";
+
+      // --------------------------------------------------------------------
+      // position:
+      //    if KnownPosInput, position is input
+      //    if !RefPosFile.empty(), open file
+      //    if RefPosInput, use the aux headers in input file
+      //    if(doRAIM) set up RAIMsolution - including input of RMS, etc?
+   if(KnownPosInput) {            // parse the string to get position
+      vector<string> subfield;
+      string::size_type pos;
+      while(KnownPos.size() > 0) {
+         pos = KnownPos.find(",");
+         if(pos==string::npos) pos=KnownPos.size();
+         if(pos==0) subfield.push_back(" ");
+         else subfield.push_back(KnownPos.substr(0,pos));
+         if(pos >= KnownPos.size()) break;
+         KnownPos.erase(0,pos+1);
+      };
+
+      CurrRef.valid = true;
+      CurrRef.clk = 0;
+      CurrRef.NPRN = 0;
+      CurrRef.PDOP = 0;
+      CurrRef.GDOP = 0;
+      CurrRef.RMS = 0;
+      if(KnownLLH) {
+         CurrRef.RxPos.setGeodetic(asDouble(subfield[0]), asDouble(subfield[1]),
+            asDouble(subfield[2]));
+         CurrRef.RxPos.transformTo(Position::Cartesian);
+      }
+      else {
+         CurrRef.RxPos.setECEF(asDouble(subfield[0]), asDouble(subfield[1]),
+            asDouble(subfield[2]));
+      }
+
+      // output
+      logof << "Reference position comes from explicit input of "
+         << "position components:\n";
+      logof << " " << subfield[0] << " " << subfield[1] << " " << subfield[2] << endl;
+      logof << " =" << fixed
+            << " " << setw(13) << setprecision(3) << CurrRef.RxPos.X()
+            << " " << setw(13) << setprecision(3) << CurrRef.RxPos.Y()
+            << " " << setw(13) << setprecision(3) << CurrRef.RxPos.Z()
+            << endl;
+      logof << " = " << fixed
+            << setw(12) << setprecision(8) << CurrRef.RxPos.geodeticLatitude() << "N "
+            << setw(12) << setprecision(8) << CurrRef.RxPos.longitude() << "E "
+            << setw(9) << setprecision(3) << CurrRef.RxPos.height() << "m" << endl;
+      inPS = 1;
+   }
+   else if(!RefPosFile.empty()) {
+      DayTime timetag;
+      //logof << "Reference position from a file (" << RefPosFile << ")\n";
+      // make sure it exists first
+      ifstream inf(RefPosFile.c_str());
+      if(!inf) {
+         logof << "Error: could not open positions file " << RefPosFile << endl;
+         oferr << "Error: could not open positions file " << RefPosFile << endl;
+         return -1;
+      }
+      // fill the map<DayTime,RefPosData> RefPosMap;
+      RefPosMap.clear();
+      if(isRinexObsFile(RefPosFile)) {
+         if(Verbose) {
+            logof << "Reference position will come from input Rinex obs file "
+               << RefPosFile << endl;
+            if(RefPosFlat)
+               logof << " WARNING -- Reference position file is Rinex, not flat!\n";
+         }
+
+         inf.close();
+         RinexObsHeader header;
+         RinexObsData robs;
+         RinexObsStream rostream(RefPosFile.c_str());
+         rostream.exceptions(fstream::failbit);
+
+         rostream >> header;
+         //timetag = header.firstObs;
+         while(rostream >> robs) {
+            if(robs.epochFlag == 4) {
+               // TD: check this; often the in-line header has a bad epoch
+               // But if it has XYZT and DIAG, then GPSTk probably wrote it....
+               timetag = robs.time;
+               CurrRef.NPRN = 0;
+               CurrRef.valid = true;
+               CurrRef.clk = CurrRef.PDOP = CurrRef.GDOP = CurrRef.RMS = 0.0;
+               for(i=0; i<robs.auxHeader.commentList.size(); i++) {
+                  string s=robs.auxHeader.commentList[i];
+                  string t=stripFirstWord(s);
+                  if(t == string("XYZT")) {
+                     double x=asDouble(stripFirstWord(s));
+                     double y=asDouble(stripFirstWord(s));
+                     double z=asDouble(stripFirstWord(s));
+                     CurrRef.RxPos.setECEF(x,y,z);
+                     CurrRef.clk = asDouble(stripFirstWord(s));
+                  }
+                  else if(t==string("DIAG")) {
+                     CurrRef.NPRN = asInt(stripFirstWord(s));
+                     CurrRef.PDOP = asDouble(stripFirstWord(s));
+                     CurrRef.GDOP = asDouble(stripFirstWord(s));
+                     CurrRef.RMS = asDouble(stripFirstWord(s));
+                  }
+               }
+               RefPosMap[timetag] = CurrRef;
+            }
+         }
+         rostream.close();
+         inPS = 1;
+      }
+      else {            // flat file input
+         if(Verbose) {
+            logof << "Reference position will come from input flat file "
+               << RefPosFile << endl;
+            if(!RefPosFlat)
+               logof << " WARNING -- Reference position file is flat, not Rinex!\n";
+         }
+
+         bool ok,have=false,havefmt=false,havepat=false;
+         string line,format,pattern,lineT,lineP,word,fword,fmtT,fmtP;
+         Position pos;
+         CurrRef.NPRN = 0;
+         CurrRef.clk = CurrRef.PDOP = CurrRef.GDOP = CurrRef.RMS = 0.0;
+         while(!inf.eof() && inf.good()) {
+            ok = true;
+            while(line.size() > 0) {
+               if(Debug) logof << "echo: " << line << endl;
+               if(line[0] == '#') break;              // skip comments
+               if(!have) {
+                  if(!havefmt) {
+                     format = line; 
+                     havefmt = true;
+                     if(Debug) logof << "Format is " << format << endl;
+                  }
+                  else if(!havepat) {
+                     pattern = line; 
+                     havepat = true;
+                     if(Debug) logof << "Pattern is " << pattern << endl;
+                  }
+                  have = havefmt & havepat;
+                  break;
+               }
+               fmtT = fmtP = lineT = lineP = string("");
+               for(i=0; i<numWords(line); i++) {
+                  word = words(line,i,1);
+                  fword = words(format,i,1);
+                  if(pattern[i] == 'X') continue;
+                  else if(pattern[i] == 'T') {
+                     lineT += string(" ") + word;
+                     fmtT += string(" ") + fword;
+                  }
+                  else if(pattern[i] == 'P') {
+                     lineP += string(" ") + word;
+                     fmtP += string(" ") + fword;
+                  }
+               }
+               try {
+                  timetag.setToString(lineT,fmtT);
+               }
+               catch(Exception& dte) {
+                  logof << "ERROR: reading the receiver position flat file threw"
+                     << " a DayTime exception:\n"
+                     << "  This is the time format: " << fmtT << endl;
+                  ok = have = havefmt = false;
+                  break;
+               }
+               try {
+                  pos.setToString(lineP,fmtP);
+                  pos.transformTo(Position::Cartesian);
+                  CurrRef.RxPos = pos;
+               }
+               catch(Exception& ge) {
+                  logof << "ERROR: reading the receiver position flat file threw"
+                     << " a Position exception:\n"
+                     << "  This is the position format: " << fmtP << endl;
+                  ok = have = havefmt = havepat = false;
+               }
+               if(ok) {
+                  if(Debug)logof << "Result: t= " << timetag << " p= " << pos << endl;
+                  RefPosMap[timetag] = CurrRef;
+                  CurrRef.valid = true;
+               }
+               break;
+            }
+            if(!ok) break;
+            getline(inf,line);
+            stripTrailing(line,'\r');
+         }
+         inf.close();
+         if(!have) {
+            logof << "ERROR in reading receiver position file: ";
+            if(!havefmt) logof << "format ";
+            if(!havepat) {
+               if(!havefmt) logof << "and pattern ";
+               else logof << "pattern ";
+            }
+            logof << ((havepat || havefmt) ? "was " : "were ")
+               << "wrong or not found!\n";
+            logof << RxhelpString << endl;
+            logof << "  [The input format is " << format << "]" << endl;
+            logof << "  [The input pattern is " << pattern << "]" << endl;
+            return -2;
+         }
+         inPS = 1;
+      }  // end flat file input
+
+      // compute the nominal time spacing of the map
+      {
+         const int ndtmax=15;
+         double dt,bestdt[ndtmax];
+         int j,k,nleast,ndt[ndtmax]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+         DayTime prev(DayTime::BEGINNING_OF_TIME);
+         map<DayTime,RefPosData>::const_iterator it;
+
+         if(Debug) logof << "Here is the reference position map\n";
+         for(it=RefPosMap.begin(); it != RefPosMap.end(); it++) {
+            if(Debug) logof << "   " << it->first << " " << fixed
+                  << " " << setw(13) << setprecision(3) << it->second.RxPos.X()
+                  << " " << setw(13) << setprecision(3) << it->second.RxPos.Y()
+                  << " " << setw(13) << setprecision(3) << it->second.RxPos.Z()
+                  << endl;
+            if(prev != DayTime::BEGINNING_OF_TIME) {
+               dt = it->first - prev;
+               for(i=0; i<ndtmax; i++) {
+                  if(ndt[i] <= 0) { bestdt[i]=dt; ndt[i]=1; break; }
+                  if(fabs(dt-bestdt[i]) < 0.0001) { ndt[i]++; break; }
+                  if(i == ndtmax-1) {
+                     k = 0; nleast = ndt[k];
+                     for(j=1; j<ndtmax; j++) if(ndt[j] <= nleast) {
+                        k=j; nleast=ndt[j];
+                     }
+                     ndt[k]=1; bestdt[k]=dt;
+                  }
+               }
+            }
+            prev = it->first;
+         }
+         for(i=1,j=0; i<ndtmax; i++) if(ndt[i] > ndt[j]) j=i;
+         RefPosMapDT = bestdt[j];
+      }
+
+   }  // end non-empty RefPosFile name
+
+   else if(doRAIM) {
+      // if(Debug) prsol.Debug = true; // write to cout ...
+      prsol.Algebraic = false;
+      //prsol.MaxNIterations = PIC.NIter;    // TD add to command line?
+      //prsol.Convergence = PIC.Conv;
+      // set inPS below, when you know you can do RAIM
+      logof << "Reference position will come from RAIM\n";
+   }
+   else if(RefPosInput) {
+      logof << "Reference position will come from the input file\n";
+      inPS = 1;
+   }
+ 
+      // reset average RAIM solution
+   if(headRAIM) {
+      ARSX.Reset();
+      ARSY.Reset();
+      ARSZ.Reset();
+   }
+
+      // --------------------------------------------------------------------
+      // misc
+      // IonoHt used in meters
+   IonoHt *= 1000.0;
+
+      // search for SX,Y,Z input and set DoSX flag, also XR,XI,X1,X2 and DoXR
+   DoSVX = DoXR = false;
+   for(i=0; i<OTstrings.size(); i++) {
+      if(OTstrings[i]==string("SX")
+            || OTstrings[i]==string("SY")
+            || OTstrings[i]==string("SZ")) DoSVX = true;
+      if(OTstrings[i]==string("XR") || OTstrings[i]==string("XI")
+            || OTstrings[i]==string("X1") || OTstrings[i]==string("X2")) DoXR = true;
+   }
+
+   if(DoXR) {
+      int j;
+      // transformation matrix is constant
+      XRM0[0] = alpha+1;      XRM0[1] = -1;      XRM0[2] = 0;     XRM0[3] = 0;
+      XRM1[0] = 1;            XRM1[1] = -1;      XRM1[2] = 0;     XRM1[3] = 0;
+      XRM2[0] = -alpha-2;     XRM2[1] = 2;       XRM2[2] = alpha; XRM2[3] = 0;
+      XRM3[0] = -2*(alpha+1); XRM3[1] = alpha+2; XRM3[2] = 0;     XRM3[3] = alpha;
+      for(i=0; i<4; i++) for(j=0; j<4; j++) XRM[i][j] /= alpha;
+      if(Debug) {
+         logof << "XRM matrix is:\n" << fixed;
+         for(i=0; i<4; i++) {
+            for(j=0; j<4; j++) {
+               logof << " " << setw(20) << setprecision(4) << XRM[i][j];
+            }
+            logof << endl;
+         }
+      }
+   }
+
+   CurrRef.valid = false;
+   if(Debug) logof << "Return from PrepareInput" << endl;
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+// after reading input header and before calling REC.EditHeader (pass input header)
+int RCRinexEditor::BeforeEditHeader(const RinexObsHeader& rhin) throw(Exception)
+{
+try {
+   int i;
+
+      // save the header for later use by SaveData and ComputeNewOTs
+   rhead = rhin;
+
+      // -----------------------------------------------------------------------
+      // get indexes of input obs types, for dependence checking and fast access
+   for(i=0; i<rhin.obsTypeList.size(); i++) {
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("C1")) inC1=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("L1")) inL1=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("L2")) inL2=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("P1")) inP1=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("P2")) inP2=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("D1")) inD1=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("D2")) inD2=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("S1")) inS1=i;
+      if(rhin.obsTypeList[i] == RinexObsHeader::convertObsType("S2")) inS2=i;
+   }
+
+      // redefine inP1 based on inC1, Callow and Cforce
+   if(Callow && inC1 > -1 && inP1 == -1) inP1=inC1;
+   if(Cforce && inC1 > -1)               inP1=inC1;
+
+      // -----------------------------------------------------------------------
+      // Check dependences of input and output data types
+      // -----------------------------------------------------------------------
+      // check that we can do RAIM
+   if(doRAIM) {
+      if(inP1>-1 && inP2>-1) inPS=1;
+      else {
+         ostringstream stst;
+         stst << "Error: cannot compute RAIM solution: missing";
+         if(inP1 == -1) stst << " P1";
+         if(inP2 == -1) stst << " P2";
+         if(inEP == -1) stst << " EP";
+         stst << "; abort.\n";
+         logof << stst.str();
+         oferr << stst.str();
+         return -2;
+      }
+   }
+
+      // -----------------------------------------------------------------------
+      // Define bit flags for input data types
+   unsigned int InputData=0;
+   if(Verbose) logof << "Input data:\n";
+   if(inP1 > -1) {
+      InputData |= 0x08;
+      if(Verbose) logof << " P1(" << inP1 << ")";
+   }
+   if(inP2 > -1) {
+      InputData |= 0x10;
+      if(Verbose) logof << " P2(" << inP2 << ")";
+   }
+   if(inL1 > -1) {
+      InputData |= 0x02;
+      if(Verbose) logof << " L1(" << inL1 << ")";
+   }
+   if(inL2 > -1) {
+      InputData |= 0x04;
+      if(Verbose) logof << " L2(" << inL2 << ")";
+   }
+   if(inEP > -1) {
+      InputData |= RinexObsHeader::RinexObsType::EPdepend;
+      if(Verbose) logof << " EP";
+   }
+   if(inPS > -1) {
+      InputData |= 0x40;
+      if(Verbose) logof << " PS";
+   }
+   if(Verbose) logof << "(" << hex << InputData << ")" << dec << endl;
+
+      // -----------------------------------------------------------------------
+      // create list OTList of RinexObsTypes here, for use later
+      // check dependencies of requested output OTs
+   if(Verbose) logof << "Here is the list of added OTs:";
+   for(i=0; i<OTstrings.size(); i++) {
+      if(Verbose) logof << " " << OTstrings[i];
+      OTList.push_back(RinexObsHeader::convertObsType(OTstrings[i]));
+   }
+   if(Verbose) logof << endl;
+   bool ok=true;
+   for(i=0; i<OTList.size(); i++) {
+      if((InputData & OTList[i].depend) != OTList[i].depend) {
+         ostringstream stst;
+         ok = false;
+         stst << "ResCor Error: Abort: Output OT " << OTstrings[i]
+            << " requires missing input:";
+         unsigned int test=(InputData & OTList[i].depend);
+         test ^= OTList[i].depend;
+         if(test & rhin.obsTypeList[inL1].depend) stst << " L1";
+         if(test & rhin.obsTypeList[inL2].depend) stst << " L2";
+         if(test & rhin.obsTypeList[inP1].depend) stst << " P1";
+         if(test & rhin.obsTypeList[inP2].depend) stst << " P2";
+         if(test & RinexObsHeader::RinexObsType::EPdepend) stst << " EP";
+         if(test & RinexObsHeader::RinexObsType::PSdepend) stst << " PS";
+         stst << endl;
+         logof << stst.str();
+         oferr << stst.str();
+      }
+   }
+   if(!ok) return -3;
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// after calling REC.EditHeader (pass output header)
+int RCRinexEditor::AfterEditHeader(const RinexObsHeader& rhout) throw(Exception)
+{
+try {
+   int i,j;
+
+      // save header for later use by SaveData
+   rheadout = rhout;
+
+      // -----------------------------------------------------------------------
+      // define indexes of raw data in output header
+   for(i=0; i<rhout.obsTypeList.size(); i++) {
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("C1")) otC1=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("L1")) otL1=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("L2")) otL2=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("P1")) otP1=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("P2")) otP2=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("D1")) otD1=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("D2")) otD2=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("S1")) otS1=i;
+      if(rhout.obsTypeList[i] == RinexObsHeader::convertObsType("S2")) otS2=i;
+   }
+
+      // redefine otP1 based on otC1, inP1, inC1, Callow and Cforce
+   if(Callow && otC1 > -1 && inC1 > -1 && inP1 == -1) otP1=otC1;
+   if(Cforce && otC1 > -1)                            otP1=otC1;
+
+      // -----------------------------------------------------------------------
+      // create a list of indexes parallel to OTstrings and OTList
+   for(j=0; j<OTList.size(); j++) {
+      for(i=0; i<rhout.obsTypeList.size(); i++) {
+         if(rhout.obsTypeList[i] == OTList[j]) OTindex.push_back(i);
+      }
+   }
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// after reading input obs and before calling EditObs (pass input obs)
+int RCRinexEditor::BeforeEditObs(const RinexObsData& roin) throw(Exception)
+{
+try {
+   if(Debug) logof << "\n----------------------------- " << roin.time
+      << " ------------------------" << endl;
+
+   // -----------------------------------------------------------------------
+   // in-line header info
+   // note that often these have a bad (all zeros) epoch
+   if(roin.epochFlag != 0 && roin.epochFlag != 1) {
+      if(Debug) logof << "Found in-line header (dump comments only)" << endl;
+      //roin.auxHeader.dump(logof);
+      for(int i=0; i<roin.auxHeader.commentList.size(); i++) {
+         string s=roin.auxHeader.commentList[i];
+         if(Debug) logof << s << endl;
+         if(RefPosInput) {
+            string t=stripFirstWord(s);
+            if(t == string("XYZT")) {
+               double x=asDouble(stripFirstWord(s));
+               double y=asDouble(stripFirstWord(s));
+               double z=asDouble(stripFirstWord(s));
+               CurrRef.RxPos.setECEF(x,y,z);
+               CurrRef.clk = asDouble(stripFirstWord(s));
+            }
+            else if(t==string("DIAG")) {
+               CurrRef.NPRN = asInt(stripFirstWord(s));
+               CurrRef.PDOP = asDouble(stripFirstWord(s));
+               CurrRef.GDOP = asDouble(stripFirstWord(s));
+               CurrRef.RMS = asDouble(stripFirstWord(s));
+               CurrRef.valid = true;
+//logof << "Found position:\n" << CurrRef.RxPos.printf("%.4x %.4y %.4z\n");
+            }
+         }
+      }
+      return 0;
+   }
+   
+   // --------------------------------------------------------------------
+   // Save the time tag (wait to define until after in-line header info)
+   CurrentTime = roin.time;
+
+   // --------------------------------------------------------------------
+   // save the raw data, if they're not in the output
+   DataStoreMap.clear();
+   if((inL1>-1 && otL1==-1) || (inL2>-1 && otL2==-1) ||
+      (inP1>-1 && (otP1==-1 || (Cforce && otC1==-1))) || (inP2>-1 && otP2==-1)) {
+         SaveData(roin, rhead, inL1, inL2, inP1, inP2);
+   }
+   
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// before writing out header (pass output header)
+int RCRinexEditor::BeforeWritingHeader(RinexObsHeader& rhout) throw(Exception)
+{
+try {
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// before writing out filled header (pass output header)
+int RCRinexEditor::BeforeWritingFilledHeader(RinexObsHeader& rhout) throw(Exception)
+{
+try {
+   if(headRAIM) {
+         // put average RAIM position in header
+      rhout.antennaPosition[0] = ARSX.Average();
+      rhout.antennaPosition[1] = ARSY.Average();
+      rhout.antennaPosition[2] = ARSZ.Average();
+      rhout.valid |= RinexObsHeader::antennaPositionValid;
+      if(Verbose) logof << "Average RAIM solution (" << ARSX.N()
+         << ") at time " << CurrentTime << " : "
+         << " " << fixed << setw(16) << setprecision(6) << ARSX.Average()
+         << " +/- " << scientific << setw(8) << setprecision(2) << ARSX.StdDev()
+         << ", " << fixed << setw(16) << setprecision(6) << ARSY.Average()
+         << " +/- " << scientific << setw(8) << setprecision(2) << ARSY.StdDev()
+         << ", " << fixed << setw(16) << setprecision(6) << ARSZ.Average()
+         << " +/- " << scientific << setw(8) << setprecision(2) << ARSZ.StdDev()
+         << endl;
+   }
+
+   if(Verbose) logof << "\nHere is the output header after optional records filled\n";
+   rhout.dump(logof);
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// just before writing output obs (pass output obs)
+// return value of BeforeWritingObs determines what is written:
+// if return <0 abort
+//            0 write nothing
+//            1 write the obs data structure (note that if epochFlag==4,
+//               this will result in in-line header information only)
+//            2 write both header data (in auxHeader) and obs data
+int RCRinexEditor::BeforeWritingObs(RinexObsData& roout) throw(Exception)
+{
+try {
+   int i;
+      // what to do with other epochFlags (in-line header information, etc)
+   if(roout.epochFlag != 0 && roout.epochFlag != 1) return 0;
+
+      // save the data, if they're in the output
+   if(otL1>-1 || otL2>-1 || otP1>-1 || otP2>-1)
+      SaveData(roout, rheadout, otL1, otL2, otP1, otP2);
+
+      // update the receiver position (via RAIM or file input)
+   if(UpdateRxPosition()) {
+      logof << "Failed to update Rx position at time " << CurrentTime << endl;
+      cerr << "Failed to update Rx position at time " << CurrentTime << endl;
+      return -1;
+   }
+
+      // compute new OTs, and add to obs
+   ComputeNewOTs(roout);
+
+      // write RAIM position solution to in-line header
+   if(outRef && (HaveRAIM || !RefPosFile.empty())) {
+      ostringstream stst1,stst2;
+      roout.auxHeader.clear();
+      stst1 << "XYZT";
+      stst1 << fixed << " " << setw(13) << setprecision(3) << CurrRef.RxPos.X();
+      stst1 << fixed << " " << setw(13) << setprecision(3) << CurrRef.RxPos.Y();
+      stst1 << fixed << " " << setw(13) << setprecision(3) << CurrRef.RxPos.Z();
+      stst1 << fixed << " " << setw(13) << setprecision(3) << CurrRef.clk;
+      roout.auxHeader.commentList.push_back(stst1.str());
+      if(Verbose)
+         logof << "RAIM output: " << roout.time.printf("%02M:%04.1f ") << stst1.str();
+
+      //for(Nsvs=0,i=0; i<Sats.size(); i++) if(Sats[i].sat > 0) Nsvs++;
+      //PDOP = RSS(prsol.Covariance(0,0),
+      //      prsol.Covariance(1,1),prsol.Covariance(2,2));
+      //GDOP = RSS(PDOP, prsol.Covariance(3,3));
+      //rms = prsol.RMSResidual;
+      stst2 << "DIAG";
+      stst2 << " " << setw(2) << CurrRef.NPRN
+         << " " << fixed << setw(5) << setprecision(2) << CurrRef.PDOP
+         << " " << fixed << setw(5) << setprecision(2) << CurrRef.GDOP
+         << " " << fixed << setw(9) << setprecision(3) << CurrRef.RMS
+         << " (N,P-,G-Dop,RMS)";
+      roout.auxHeader.commentList.push_back(stst2.str());
+      if(Verbose) logof << " " << stst2.str() << endl;
+      roout.auxHeader.valid |= RinexObsHeader::commentValid;
+
+      return 4;         // write both header (with epochFlag=4) and obs data
+   }
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
+void SaveData(const RinexObsData& rod, const RinexObsHeader& rhd,
+   int xL1, int xL2, int xP1, int xP2) throw(Exception)
+{
+try {
+   RinexSatID sat;
+   RinexObsData::RinexObsTypeMap otmap;
+   RinexObsData::RinexSatMap::const_iterator it;
+   RinexObsData::RinexObsTypeMap::const_iterator jt;
+   map<RinexSatID,RCData>::const_iterator kt;
+
+   for(it=rod.obs.begin(); it != rod.obs.end(); ++it) { // loop over satellites
+      sat = RinexSatID(it->first.id,SatID::systemGPS);
+      otmap = it->second;
+      // find the saved input data for this sat, if any
+      kt = DataStoreMap.find(sat);
+      if(kt != DataStoreMap.end()) DataStore=kt->second;
+
+      if(xL1>-1 && (jt=otmap.find(rhd.obsTypeList[xL1])) != otmap.end()) {
+         DataStore.L1 = jt->second.data;
+         DataStore.LL1 = int(jt->second.lli);
+      }
+      if(xL2>-1 && (jt=otmap.find(rhd.obsTypeList[xL2])) != otmap.end()) {
+         DataStore.L2 = jt->second.data;
+         DataStore.LL2 = int(jt->second.lli);
+      }
+      if(xP1>-1 && (jt=otmap.find(rhd.obsTypeList[xP1])) != otmap.end()) {
+         DataStore.P1 = jt->second.data;
+      }
+      if(xP2>-1 && (jt=otmap.find(rhd.obsTypeList[xP2])) != otmap.end()) {
+         DataStore.P2 = jt->second.data;
+      }
+      DataStoreMap[sat] = DataStore;
+   }  // end loop over sats
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// fill global data CurrRef
+int UpdateRxPosition(void) throw(Exception)
+{
+try {
+   int iret,i;
+   double rho;
+   Xvt xvt;
+   RinexSatID sat;
+   CorrectedEphemerisRange CER;
+
+      // compute a RAIM solution, add it to average
+   HaveRAIM = false;
+   map<RinexSatID,RCData>::const_iterator kt;
+   if(doRAIM) {
+      Sats.clear();
+      PRange.clear();
+
+         //map<RinexSatID,RCData> DataStoreMap;
+      for(kt=DataStoreMap.begin(); kt != DataStoreMap.end(); kt++) {
+         if(kt->second.P1 == 0 || kt->second.P2 == 0) continue;
+         sat = kt->first;
+         if(minElev > 0.0 && CurrRef.valid) {
+            try {
+               if(SP3EphList.size() > 0)
+                  rho = CER.ComputeAtReceiveTime(CurrentTime, xvt, sat,
+                        SP3EphList);
+               else if(BCEphList.size() > 0)
+                  rho = CER.ComputeAtReceiveTime(CurrentTime, xvt, sat,
+                        BCEphList);
+               else continue;
+            }
+            catch(gpstk::EphemerisStore::NoEphemerisFound& e) {
+               continue;
+            }
+         }
+         Sats.push_back(sat);
+         PRange.push_back(if1r*kt->second.P1+if2r*kt->second.P2);
+      }
+
+      if(SP3EphList.size() > 0)
+         iret = prsol.RAIMCompute(CurrentTime, Sats, PRange, SP3EphList, &ggtm);
+      else if(BCEphList.size() > 0)
+         iret = prsol.RAIMCompute(CurrentTime, Sats, PRange, BCEphList, &ggtm);
+      else iret = -4;
+         //  2  failed to find a good solution (RMS residual or slope exceed limits)
+         //  1  solution is suspect (slope is large)
+         //  0  ok
+         // -1  failed to converge
+         // -2  singular problem
+         // -3  not enough good data to form a RAIM solution
+         //     (the 4 satellite solution might be returned - check isValid())
+         // -4  ephemeris not found for one or more satellites
+      HaveRAIM = (iret==0 || iret==1);
+      if(HaveRAIM) {
+         if(Verbose) {                          // output results and return value
+            int Nsvs;
+            for(Nsvs=0,i=0; i<Sats.size(); i++) if(Sats[i].id > 0) Nsvs++;
+            logof << "RPF " << setw(2) << Sats.size()-Nsvs
+               << " " << setw(4) << CurrentTime.GPSfullweek() << fixed
+               << " " << setw(10) << setprecision(3) << CurrentTime.GPSsecond()
+               << " " << setw(2) << Nsvs
+               << " " << setw(16) << setprecision(6) << prsol.Solution(0)
+               << " " << setw(16) << setprecision(6) << prsol.Solution(1)
+               << " " << setw(16) << setprecision(6) << prsol.Solution(2)
+               << " " << setw(16) << setprecision(6) << prsol.Solution(3)
+               << " " << setw(16) << setprecision(6) << prsol.RMSResidual
+               << " " << fixed << setw(7) << setprecision(1) << prsol.MaxSlope
+               << " " << prsol.NIterations
+               << " " << scientific
+               << setw(8) << setprecision(2) << prsol.Convergence;
+            for(i=0; i<Sats.size(); i++) logof << " " << setw(3) << Sats[i].id;
+            logof << " (" << iret << ")" << (prsol.isValid() ? " V":" NV") << endl;
+         }
+
+         CurrRef.RxPos.setECEF(prsol.Solution(0),prsol.Solution(1),
+            prsol.Solution(2));
+         CurrRef.valid = true;
+         CurrRef.clk = prsol.Solution(3);
+         CurrRef.NPRN = prsol.Nsvs;
+         CurrRef.PDOP = RSS(prsol.Covariance(0,0),prsol.Covariance(1,1),
+            prsol.Covariance(2,2));
+         CurrRef.GDOP = RSS(CurrRef.PDOP, prsol.Covariance(3,3));
+         CurrRef.RMS = prsol.RMSResidual;
+         if(headRAIM) {       // add to average
+            ARSX.Add(CurrRef.RxPos.X());
+            ARSY.Add(CurrRef.RxPos.Y());
+            ARSZ.Add(CurrRef.RxPos.Z());
+         }
+         inPS = 1;
+      }
+      else {                     // RAIM failed
+         if(Verbose) {
+            logof << "RAIM failed at " << CurrentTime << " : returned '";
+            if(iret == 2) logof << "failed to find a good solution "
+               << "(RMS residual or slope exceed limits)";
+            if(iret == -1) logof << "failed to converge";
+            if(iret == -2) logof << "singular problem";
+            if(iret == -3) logof << "not enough good data to form a RAIM solution";
+            if(iret == -4) {
+               logof << "ephemeris not found for satellite";
+               for(i=0; i<Sats.size(); i++) {
+                  if(Sats[i].id < 0) {
+                     Sats[i].id *= -1;
+                     logof << " " << Sats[i];
+                  }
+               }
+            }
+            logof << "'." << endl;
+         }
+         inPS=-1;
+      }
+   }
+   else if(!RefPosFile.empty()) { // update RxPos from map
+      map<DayTime,RefPosData>::iterator ite;
+      ite = RefPosMap.lower_bound(CurrentTime);
+      // ite points to first element with value >= CurrentTime
+      if(ite == RefPosMap.end() || fabs(ite->first - CurrentTime) > 0.1*RefPosMapDT) {
+         if(Verbose) logof << "No Rx position found at " << CurrentTime << endl;
+         CurrRef.valid = false;
+         inPS = -1;
+      }
+      else {
+         CurrRef.RxPos = ite->second.RxPos;
+         CurrRef.clk = ite->second.clk;
+         CurrRef.NPRN = ite->second.NPRN;
+         CurrRef.PDOP = ite->second.PDOP;
+         CurrRef.GDOP = ite->second.GDOP;
+         CurrRef.RMS = ite->second.RMS;
+         CurrRef.valid = true;
+         inPS = 1;
+      }
+   }
+
+   if(Debug && inPS > -1) {
+      logof << "RxPos " << CurrentTime
+         << " " << CurrentTime.printf("%04F %10.3g") << fixed << setprecision(3)
+         << " " << setw(13) << CurrRef.RxPos.X()
+         << " " << setw(13) << CurrRef.RxPos.Y()
+         << " " << setw(13) << CurrRef.RxPos.Z()
+         << endl;
+   }
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+void ComputeNewOTs(RinexObsData& rod) throw(Exception)
+{
+try {
+   bool HaveR,HaveP,HaveEphRange,ok,reset,HaveEphThisSat;
+   double rho,IPPLat,IPPLon,Obliq,Trop,Tgd;
+   RinexObsData::RinexSatMap::iterator it;         // for loop over sats
+   map<RinexSatID,RCData>::const_iterator kt;        // for DataStoreMap
+   vector<RinexSatID> SVDelete;
+   RinexSatID sat;
+   //RinexObsData::RinexObsTypeMap otmap;
+   CorrectedEphemerisRange CER;
+
+   if(Debug) {
+      logof << "Obs data before mods\n";
+      rod.dump(logof);
+   }
+
+      // loop over sats
+   for(it=rod.obs.begin(); it != rod.obs.end(); ++it) {
+      sat = RinexSatID(it->first.id,SatID::systemGPS);
+      //otmap = it->second; 
+
+         // delete this satellite if it is excluded, or if RAIM has marked it
+      if( (SVonly.id > 0 && sat != SVonly) ||
+           (editRAIM && HaveRAIM &&
+            find(Sats.begin(),Sats.end(),RinexSatID(-sat.id,sat.system))!=Sats.end()))
+      {
+         SVDelete.push_back(sat);
+         continue;
+      }
+
+         // --------------------------------------------------------
+         // find the saved input data for this sat
+      kt = DataStoreMap.find(sat);
+      HaveR = HaveP = false;
+      if(kt != DataStoreMap.end()) {       // have data
+         HaveR = (kt->second.P1 != 0.0 && kt->second.P2 != 0.0);
+         HaveP = (kt->second.L1 != 0.0 && kt->second.L2 != 0.0);
+      }
+      if(doRAIM && !HaveRAIM) inPS=-1;
+      
+         // --------------------------------------------------------
+         // compute ephemeris range and ionospheric pierce point
+      if(inEP > -1) HaveEphThisSat=true;
+      HaveEphRange = (HaveEphThisSat && inPS > -1);
+      rho = IPPLat = IPPLon = Obliq = Tgd = 0;
+      if(HaveEphRange) {
+         Xvt xvt;
+         xvt.x[0] = CurrRef.RxPos.X();
+         xvt.x[1] = CurrRef.RxPos.Y();
+         xvt.x[2] = CurrRef.RxPos.Z();
+         try {
+            if(SP3EphList.size() > 0)
+               rho = CER.ComputeAtReceiveTime(CurrentTime, xvt, sat, SP3EphList);
+            else if(BCEphList.size() > 0)
+               rho = CER.ComputeAtReceiveTime(CurrentTime, xvt, sat, BCEphList);
+            else {
+               gpstk::EphemerisStore::NoEphemerisFound e("No ephemeris in store");
+               GPSTK_THROW(e);
+            }
+         }
+         catch(gpstk::EphemerisStore::NoEphemerisFound& e) {
+            if(Verbose)
+               logof << "ComputeNewOTs failed to find ephemeris for satellite "
+               << sat << " at time " << CurrentTime << endl;
+            HaveEphThisSat = false;
+            HaveEphRange = false;
+         }
+
+         if(HaveEphRange) {
+            if(minElev > 0.0 && CER.elevation < minElev) {
+               HaveEphRange = HaveEphThisSat = false;
+               SVDelete.push_back(sat);
+            }
+            else {
+               Position IPP=CurrRef.RxPos.getIonosphericPiercePoint(
+                  CER.elevation,CER.azimuth, IonoHt);
+               IPPLat = IPP.geodeticLatitude();
+               IPPLon = IPP.longitude();
+               //Obliq = WGS84.a()*cos(CER.elevation*DEG_TO_RAD)/(WGS84.a()+IonoHt);
+               Obliq = cos(CER.elevation*DEG_TO_RAD)/(1.0+IonoHt/WGS84.a());
+               Obliq = ::sqrt(1.0-Obliq*Obliq);
+                  // NB other trop models may require a different call,
+                  // and will throw(InvalidTropModel) here
+               Trop = ggtm.correction(CER.elevation);
+               if(BCEphList.size() > 0) {
+                  const EngEphemeris& eph = BCEphList.findEphemeris(sat,CurrentTime);
+                  Tgd = C_GPS_M * eph.getTgd();
+               }
+            }
+         }
+      }
+
+         // --------------------------------------------------------
+         // compute XR,XI,X1,X2
+      if(DoXR && HaveR && HaveP) {
+         XRdat[0] = wl1 * kt->second.L1;
+         XRdat[1] = wl2 * kt->second.L2;
+         XRdat[2] = kt->second.P1;
+         XRdat[3] = kt->second.P2;
+         for(int i=0; i<4; i++) {
+            XRsol[i] = 0.0;
+            for(int j=0; j<4; j++) {
+               XRsol[i] += XRM[i][j] * XRdat[j];
+            }
+         }
+      }
+
+         // --------------------------------------------------------
+         // get satellite position (if not found above)
+      if(DoSVX && HaveEphThisSat && inPS == -1) {
+         unsigned long ref;
+         try {
+            if(SP3EphList.size())
+               CER.svPosVel = SP3EphList.getSatXvt(sat,CurrentTime);
+            else
+               CER.svPosVel = BCEphList.getSatXvt(sat,CurrentTime);
+         }
+         catch(EphemerisStore::NoEphemerisFound& e) {
+            HaveEphThisSat = false;
+         }
+      }
+
+         // --------------------------------------------------------
+         // now loop over new output OTs, compute and debias them
+      RinexObsData::RinexObsTypeMap::iterator jt;
+      for(int i=0; i<OTList.size(); i++) {
+         jt = it->second.find(OTList[i]);
+         if(jt == it->second.end()) continue;   // error. throw?
+
+         jt->second.data = 0.0;                 // default = marked bad
+         ok = false;
+
+         if(OTstrings[i] == string("ER")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = rho;
+         }
+         else if(OTstrings[i] == string("RI")) {
+            ok = HaveR;
+            if(ok) jt->second.data = (kt->second.P2 - kt->second.P1)/alpha;
+         }
+         else if(OTstrings[i] == string("PI")) {
+            ok = HaveP;
+            if(ok) jt->second.data = (wl1*kt->second.L1 - wl2*kt->second.L2)/alpha;
+         }
+         else if(OTstrings[i] == string("TR")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = Trop;
+         }
+         else if(OTstrings[i] == string("RL")) {
+            ok = HaveEphThisSat;
+            if(ok) jt->second.data = CER.relativity;
+         }
+         else if(OTstrings[i] == string("SC")) {
+            ok = HaveEphThisSat;
+            if(ok) jt->second.data = CER.svclkbias;
+         }
+         else if(OTstrings[i] == string("EL")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = CER.elevation;
+         }
+         else if(OTstrings[i] == string("AZ")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = CER.azimuth;
+         }
+         else if(OTstrings[i] == string("SR")) {
+            ok = HaveR;
+            if(ok) jt->second.data =
+               (kt->second.P2 - kt->second.P1 - Tgd)*TECUperM/alpha;
+         }
+         else if(OTstrings[i] == string("SP")) {
+            ok = HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  (wl1*kt->second.L1 - wl2*kt->second.L2)*TECUperM/alpha);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("VR")) {
+            ok = (HaveR && HaveEphRange);
+            if(ok) jt->second.data =
+               ((kt->second.P2 - kt->second.P1 - Tgd)*TECUperM/alpha)*Obliq;
+         }
+         else if(OTstrings[i] == string("VP")) {
+            ok = (HaveP && HaveEphRange);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  ((wl1*kt->second.L1 - wl2*kt->second.L2 - Tgd)*TECUperM/alpha)*Obliq);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("LA")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = IPPLat;
+         }
+         else if(OTstrings[i] == string("LO")) {
+            ok = HaveEphRange;
+            if(ok) jt->second.data = IPPLon;
+         }
+         else if(OTstrings[i] == string("P3")) {
+            ok = HaveR;
+            if(ok) jt->second.data = if1r*kt->second.P1 + if2r*kt->second.P2;
+         }
+         else if(OTstrings[i] == string("L3")) {
+            ok = HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  if1p*kt->second.L1 + if2p*kt->second.L2);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("PF")) {
+            ok = HaveR;
+            if(ok) jt->second.data = gf1r*kt->second.P1 + gf2r*kt->second.P2;
+         }
+         else if(OTstrings[i] == string("LF")) {
+            ok = HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  gf1p*kt->second.L1 + gf2p*kt->second.L2);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("PW")) {
+            ok = HaveR;
+            if(ok) jt->second.data = wl1r*kt->second.P1 + wl2r*kt->second.P2;
+         }
+         else if(OTstrings[i] == string("LW")) {
+            ok = HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  wl1p*kt->second.L1 + wl2p*kt->second.L2);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("MP") || OTstrings[i] == string("M3")) {
+            ok = (HaveP && HaveR);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  if1r*kt->second.P1 + if2r*kt->second.P2
+                  - (if1p*kt->second.L1 + if2p*kt->second.L2));
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("R1")) {
+            ok = (kt->second.P1 != 0 && kt->second.L1 != 0);
+            if(ok) jt->second.data = 0.5*(kt->second.P1 + kt->second.L1);
+         }
+         else if(OTstrings[i] == string("R2")) {
+            ok = (kt->second.P2 != 0 && kt->second.L2 != 0);
+            if(ok) jt->second.data = 0.5*(kt->second.P2 + kt->second.L2);
+         }
+         else if(OTstrings[i] == string("M1")) {
+            ok = (kt->second.P1 != 0 && kt->second.L1 != 0);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  kt->second.P1 - wl1*kt->second.L1);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("M2")) {
+            ok = (kt->second.P2 != 0 && kt->second.L2 != 0);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  kt->second.P2 - wl2*kt->second.L2);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         // M3 is MP
+         else if(OTstrings[i] == string("M4")) {
+            ok = (HaveP && HaveR);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  gf1r*kt->second.P1 + gf2r*kt->second.P2
+                  - (gf1p*kt->second.L1 + gf2p*kt->second.L2));
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("M5")) {
+            ok = (HaveP && HaveR);
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,
+                  wl1r*kt->second.P1 + wl2r*kt->second.P2
+                  - (wl1p*kt->second.L1 + wl2p*kt->second.L2));
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("XR")) {
+            ok = HaveR && HaveP;
+            if(ok) {
+               jt->second.data = XRsol[0];
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("XI")) {
+            ok = HaveR && HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,XRsol[1]);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("X1")) {
+            ok = HaveR && HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,XRsol[2]);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("X2")) {
+            ok = HaveR && HaveP;
+            if(ok) {
+               jt->second.data = removeBias(OTList[i], sat, reset, rod.time,XRsol[3]);
+               if(reset) jt->second.lli |= 1;
+            }
+         }
+         else if(OTstrings[i] == string("SX")) {
+            ok = HaveP && HaveEphThisSat;
+            if(ok) jt->second.data = CER.svPosVel.x[0];
+         }
+         else if(OTstrings[i] == string("SY")) {
+            ok = HaveP && HaveEphThisSat;
+            if(ok) jt->second.data = CER.svPosVel.x[1];
+         }
+         else if(OTstrings[i] == string("SZ")) {
+            ok = HaveP && HaveEphThisSat;
+            if(ok) jt->second.data = CER.svPosVel.x[2];
+         }
+         else ok = false;
+
+         if(!ok) continue;
+
+         // --------------------------------------------------------
+         // set LLI flag, if it depends on phase, and if phase LLI is set
+         unsigned int test=0;
+         if(inL1 > -1) test=rhead.obsTypeList[inL1].depend;
+         else if(otL1 > -1) test=rhead.obsTypeList[otL1].depend;
+         if((OTList[i].depend & test) && (kt->second.LL1 & 0x01))
+            jt->second.lli |= 1;
+         test = 0;
+         if(inL2 > -1) test=rhead.obsTypeList[inL2].depend;
+         else if(otL2 > -1) test=rhead.obsTypeList[otL2].depend;
+         if((OTList[i].depend & test) && (kt->second.LL2 & 0x01))
+            jt->second.lli |= 1;
+
+         //if(ok && Verbose) ;  // TD output here
+
+      }  // end loop over new output OTs
+
+      // --------------------------------------------------------
+      // delete this satellite if there is no good data in it
+      for(jt=it->second.begin(); jt != it->second.end(); jt++) {
+         if(jt->second.data != 0.0) break;
+      }
+      if(jt == it->second.end()) SVDelete.push_back(sat);
+
+   }  // end loop over sats
+
+      // delete satellites
+   for(int i=0; i<SVDelete.size(); i++) {
+      rod.obs.erase(RinexSatID(SVDelete[i].id,SatID::systemGPS));
+      rod.numSvs--;
+   }
+
+   if(Debug) {
+      logof << "Obs data after mods\n";
+      rod.dump(logof);
+   }
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+//------------------------------------------------------------------------------------//------------------------------------------------------------------------------------
+// NB reentrant, but ugly, function
+// NB PreProcessArgs pulls out --debug --verbose and the -f<f> and --file <f> options.
+// NB It also allow --ROThelp and --REChelp to have any case
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception)
+{
+try {
+   static bool found_cfg_file=false;
+
+   if(found_cfg_file || (arg[0]=='-' && arg[1]=='f')) {
+      string filename(arg);
+      if(!found_cfg_file) filename.erase(0,2); else found_cfg_file = false;
+      if(Debug) cout << "Found a file of options: " << filename << endl;
+      ifstream infile(filename.c_str());
+      if(!infile) {
+         cout << "Error: could not open options file " << filename << endl;
+         return;
+      }
+      bool again_cfg_file=false;
+      char c;
+      string buffer,word;
+      while(1) {
+         getline(infile,buffer);
+         stripTrailing(buffer,'\r');
+
+         while(!buffer.empty()) {
+            word = firstWord(buffer);
+            if(again_cfg_file) {
+               word = "-f" + word;
+               again_cfg_file = false;
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else if(word[0] == '#') {         // skip to end of line
+               buffer = "";
+            }
+            else if(word == "--file")
+               again_cfg_file = true;
+            else if(word[0] == '"') {
+               word = stripFirstWord(buffer,'"');
+               buffer = "dummy " + buffer;      // to be stripped below
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else
+               PreProcessArgs(word.c_str(),Args);
+
+            word = stripFirstWord(buffer);      // now remove it from buffer
+         }
+         // check this last b/c there can be a line at EOF without CRLF...
+         if(infile.eof() || !infile.good()) break;
+      }
+   }
+   else if(string(arg)==string("--verbose"))
+      Verbose = true;
+   else if(string(arg)==string("--debug"))
+      Debug = true;
+   else if(string(arg)==string("--file"))
+      found_cfg_file = true;
+   else if(lowerCase(string(arg)) == string("--rothelp"))
+      Args.push_back(string("--ROThelp"));
+   else if(lowerCase(string(arg)) == string("--rechelp"))
+      Args.push_back(string("--REChelp"));
+   else if(lowerCase(string(arg)) == string("--rxhelp"))
+      Args.push_back(string("--Rxhelp"));
+   else
+      Args.push_back(arg);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
+// define the bias limit, assign it to the invalid (-1,GPS) satellite
+int setBiasLimit(RinexObsHeader::RinexObsType& ot, double lim) throw(Exception)
+{
+try {
+   if(RinexObsHeader::convertObsType(ot)==string("UN") || lim <= 0.0) return -1;
+   RinexSatID p;          // invalid: -1,GPS ... let this hold the LIMIT in the map
+   map<RinexObsHeader::RinexObsType,map<RinexSatID,double> >::iterator it;
+   if( (it=AllBiases.find(ot)) == AllBiases.end()) {     // not found
+      map<RinexSatID,double> bm;
+      bm[p] = lim;
+      AllBiases[ot] = bm;
+      if(Verbose) logof << "Set bias for " << RinexObsHeader::convertObsType(ot)
+         << "," << p << " to " << fixed << setprecision(3) << lim << endl;
+   }
+   else {                                                // found
+      it->second[p] = lim;
+      if(Verbose) logof << "Re-Set bias for " << RinexObsHeader::convertObsType(ot)
+         << "," << p << " to " << fixed << setprecision(3) << lim << endl;
+   }
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// set bias, if necessary, and return raw-bias
+double removeBias(const RinexObsHeader::RinexObsType& ot, const RinexSatID& sv,
+   bool& rset, DayTime& tt, double raw) throw(Exception)
+{
+try {
+   rset = false;
+   // is the input valid?
+   if(RinexObsHeader::convertObsType(ot)==string("UN") || sv.id==-1) return raw;
+
+   // get the map<RinexSatID,double> for this OT
+   map<RinexObsHeader::RinexObsType,map<RinexSatID,double> >::iterator it;
+   if( (it=AllBiases.find(ot)) == AllBiases.end()) return raw; // did not find OT
+   // it->second is the right map<RinexSatID,double>
+
+   // get the limit
+   RinexSatID p;
+   map<RinexSatID,double>::iterator jt;
+   jt = it->second.find(p);                  // p is (-1,GPS) here, so bias=limit
+   if(jt == it->second.end()) return raw;    // should never happen - throw?
+   double limit=jt->second;
+
+   // now find the current bias for the input satellite
+   double bias;
+   if( (jt=it->second.find(sv)) == it->second.end()) {   // sat not found, define bias
+      bias = it->second[sv] = raw-0.001;
+      if(Verbose) logof << "Did not find a bias for "
+         << RinexObsHeader::convertObsType(ot) << "," << sv
+         << " at time " << tt.printf("%4F %10.3g = %4Y/%02m/%02d %02H:%02M:%02S")
+         << ", set it to " << fixed << setprecision(3) << bias << endl;
+      rset = true;
+   }
+   else {                                                      // found the sat
+      bias = jt->second;
+      // logof << "Found bias for " << RinexObsHeader::convertObsType(ot)
+      // << "," << sv << " = " << fixed << setprecision(3) << bias << endl;
+      if(fabs(raw-jt->second) > limit) {
+         if(Verbose) logof << "Bias limit for " << RinexObsHeader::convertObsType(ot)
+            << "," << sv << " was exceeded at time "
+            << tt.printf("%4F %10.3g = %4Y/%02m/%02d %02H:%02M:%02S")
+            << " (" << fixed << setprecision(3) << raw-jt->second
+            << " > " << setprecision(3) << limit
+            << "), set it to " << fixed << setprecision(3) << raw-0.001 << endl;
+         bias = it->second[sv] = raw-0.001;
+         rset = true;
+      }
+   }
+
+   return raw-bias;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
diff --git a/dev/apps/Rinextools/RinSum.cpp b/dev/apps/Rinextools/RinSum.cpp
new file mode 100644
index 0000000..8697c16
--- /dev/null
+++ b/dev/apps/Rinextools/RinSum.cpp
@@ -0,0 +1,852 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file RinSum.cpp
+ * Read and summarize Rinex observation files, optionally fill header in-place.
+ */
+
+#include "MathBase.hpp"
+#include "RinexObsBase.hpp"
+#include "RinexObsData.hpp"
+#include "RinexObsHeader.hpp"
+#include "RinexObsStream.hpp"
+#include "RinexNavBase.hpp"
+#include "RinexNavHeader.hpp"
+#include "RinexNavData.hpp"
+#include "RinexNavStream.hpp"
+#include "DayTime.hpp"
+#include "SatID.hpp"
+#include "RinexSatID.hpp"
+#include "CommandOptionParser.hpp"
+#include "CommandOption.hpp"
+#include "CommandOptionWithTimeArg.hpp"
+#include "icd_200_constants.hpp"
+#include "RinexUtilities.hpp"
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+#include <time.h>
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+string version("2.4 1/22/07");
+
+// data input from command line
+vector<string> InputFiles;
+string InputDirectory;
+string OutputFile;
+ostream* pout;
+DayTime BegTime, EndTime;
+bool ReplaceHeader=false;
+bool TimeSortTable=false;
+bool GPSTimeOutput=false;
+bool debug=false;
+bool brief=false;
+
+//------------------------------------------------------------------------------------
+// data used for computation
+const int ndtmax=15;
+double dt,bestdt[ndtmax];
+int ndt[ndtmax]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+int nepochs,ncommentblocks;
+
+//------------------------------------------------------------------------------------
+// class used to store SAT/Obs table
+class TableData {
+public:
+   RinexSatID sat;
+   vector<int> nobs;
+   double prevC1,prevP1,prevL1;
+   DayTime begin,end;
+   TableData(const SatID& p, const int& n)
+      { sat=RinexSatID(p); nobs=vector<int>(n); prevC1=prevP1=prevL1=0; };
+      // needed for find()
+   inline bool operator==(const TableData& d) {return d.sat == sat;}
+};
+   // for sort()
+class TableSATLessThan  {      
+public:
+   bool operator()(const TableData& d1, const TableData& d2)
+      { return d1.sat < d2.sat; }
+};
+class TableBegLessThan  {
+public:
+   bool operator()(const TableData& d1, const TableData& d2)
+      { return d1.begin < d2.begin; }
+};
+
+//------------------------------------------------------------------------------------
+// prototypes
+int GetCommandLine(int argc, char **argv) throw(Exception);
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception);
+
+//------------------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+try {
+   int iret,i,j,k,n,ifile,nsats,nclkjumps,L1lli;
+   double C1,L1,P1,clkjumpave,clkjumpvar;
+   DayTime last,prev,ftime;
+   vector<DayTime> clkjumpTimes;
+   vector<double> clkjumpMillsecs,clkjumpUncertainty;
+   vector<int> clkjumpAgree;
+
+   BegTime = DayTime::BEGINNING_OF_TIME;
+   EndTime = DayTime::END_OF_TIME;
+
+      // Title and description
+   string Title;
+   Title = "RINSUM, part of the GPS ToolKit, Ver " + version + ", Run ";
+   time_t timer;
+   struct tm *tblock;
+   timer = time(NULL);
+   tblock = localtime(&timer);
+   last.setYMDHMS(1900+tblock->tm_year,1+tblock->tm_mon,
+               tblock->tm_mday,tblock->tm_hour,tblock->tm_min,tblock->tm_sec);
+   Title += last.printf("%04Y/%02m/%02d %02H:%02M:%02S\n");
+   cout << Title;
+
+   iret=GetCommandLine(argc, argv);
+   if(iret) return iret;
+
+   iret = RegisterARLUTExtendedTypes();
+   if(iret) return iret;
+
+      // open the output file and write to it
+   if(!OutputFile.empty()) {
+      pout = new ofstream(OutputFile.c_str(),ios::out);
+      if(pout->fail()) {
+         cerr << "Could not open output file " << OutputFile << endl;
+         pout = &cout;
+      }
+      else {
+         pout->exceptions(ios::failbit);
+         *pout << Title;
+         cout << "Writing summary to file " << OutputFile << endl;
+      }
+   }
+   else pout = &cout;
+
+      // add path to input file names
+   if(!InputDirectory.empty()) for(ifile=0; ifile<InputFiles.size(); ifile++) {
+      InputFiles[ifile] = InputDirectory + "/" + InputFiles[ifile];
+   }
+      // sort the input file names on header first time
+   if(InputFiles.size() > 1) sortRinexObsFiles(InputFiles);
+
+      // now open the input files, read the headers and data
+   RinexObsHeader rheader;
+   RinexObsData robs;
+   for(ifile=0; ifile<InputFiles.size(); ifile++) {
+      string filename;
+      if(!InputDirectory.empty()) filename = InputDirectory + "/";
+      filename += InputFiles[ifile];
+      RinexObsStream InStream(filename.c_str());
+      if(!InStream) {
+         *pout << "File " << filename << " could not be opened.\n";
+         continue;
+      }
+      InStream.exceptions(ios::failbit);
+      if(!isRinexObsFile(filename)) {
+         *pout << "File " << filename << " is not a Rinex observation file\n";
+         if(isRinexNavFile(filename))
+            *pout << "This file is a Rinex navigation file - try NavMerge\n";
+         continue;
+      }
+
+      prev = DayTime::BEGINNING_OF_TIME;
+      ftime = DayTime::BEGINNING_OF_TIME;
+
+      if(!brief) *pout << "+++++++++++++ RinSum summary of Rinex obs file "
+         << filename << " +++++++++++++\n";
+      else *pout << "\nFile name: " << filename << endl;
+      
+         // input header
+      try {
+         InStream >> rheader;
+      }
+      catch(gpstk::FFStreamError& e) {
+         cerr << "Caught an FFStreamError while reading header: "
+            << e.getText(0) << endl;
+      }
+      catch(gpstk::Exception& e) {
+         cerr << "Caught a gpstk exception while reading header: "
+            << e.getText(0) << endl;
+      }
+
+      if(!brief) {
+         *pout << "Rinex header:\n";
+         rheader.dump(*pout);
+      }
+      else *pout << "Position (XYZ,m) : " << fixed << setprecision(4)
+         << rheader.antennaPosition << ".\n";
+
+      if(!rheader.isValid()) {
+         *pout << "Abort: header is invalid\n";
+         if(!brief) *pout << "\n+++++++++++++ End of RinSum summary of "
+            << filename << " +++++++++++++\n";
+         continue;
+      }
+
+      //RinexObsStream out(argv[2], ios::out);
+      //out << rheader;
+
+      nepochs = ncommentblocks = 0;
+      n = rheader.obsTypeList.size();
+      vector<TableData> table;
+      vector<int> totals(n);
+
+      if(pout == &cout) *pout << "Reading the observation data..." << endl;
+
+         // input obs
+      while(InStream >> robs)
+      {
+         if(debug) *pout << "Epoch: " << robs.time
+            << ", Flag " << robs.epochFlag
+            << ", Nsat " << robs.obs.size()
+            << ", clk " << fixed << robs.clockOffset << endl;
+
+          // is this a comment?
+         if(robs.epochFlag > 1) {
+            ncommentblocks++;
+            //*pout << "inline header info:\n";
+            //robs.auxHeader.dump(*pout);
+            continue;
+         }
+
+         // update first and last time seen, check time limits, count epochs
+         last = robs.time;
+         if(last < BegTime) continue;
+         if(last > EndTime) break;
+         if(ftime == DayTime::BEGINNING_OF_TIME) ftime=last;
+         nepochs++;
+         nsats = nclkjumps = 0;  // count sats and signs clock jumps have occurred
+         clkjumpave = clkjumpvar = 0.0;
+
+         // loop over satellites
+         RinexObsData::RinexSatMap::const_iterator it;
+         RinexObsData::RinexObsTypeMap::const_iterator jt;
+         for(it=robs.obs.begin(); it != robs.obs.end(); ++it) {
+            // update the table
+            vector<TableData>::iterator ptab;
+            ptab = find(table.begin(),table.end(),TableData(it->first,n));
+            if(ptab == table.end()) {        // sat not found in table - create one
+               table.push_back(TableData(it->first,n));
+               ptab = find(table.begin(),table.end(),TableData(it->first,n));
+               ptab->begin = last;
+            }
+            // update end time for this sat
+            ptab->end = last;
+            if(debug) *pout << "Sat " << setw(2) << RinexSatID(it->first);
+
+            // loop over obs types
+            C1 = P1 = L1 = 0;
+            for(jt=it->second.begin(); jt!=it->second.end(); jt++) {
+               // find the index for this obs type
+               for(k=0; k<n; k++) if(rheader.obsTypeList[k] == jt->first) break;
+               // count this obs
+               if(jt->second.data != 0) {
+                  ptab->nobs[k]++;      // per obs
+                  totals[k]++;
+               }
+               // save L1 range and phase for clk jump test below
+               if(jt->first==RinexObsHeader::C1) C1 = jt->second.data*1000.0/C_GPS_M;
+               if(jt->first==RinexObsHeader::P1) P1 = jt->second.data*1000.0/C_GPS_M;
+               if(jt->first == RinexObsHeader::L1) {
+                  L1 = jt->second.data * 1000.0/C_GPS_M;
+                  L1lli = jt->second.lli;
+               }
+               // dump this data
+               if(debug) *pout << " " << RinexObsHeader::convertObsType(jt->first)
+                  << " " << setw(13) << setprecision(3) << jt->second.data << " "
+                  << jt->second.lli << " " << jt->second.ssi;
+            }  // end loop over obs types
+            if(debug) *pout << endl;
+
+            // test for millisecond clock adjusts -
+            // sometimes they are applied to range but not phase or vice-versa
+            if(prev != DayTime::BEGINNING_OF_TIME && L1 != 0 && ptab->prevL1 != 0) {
+               int nms;
+               double test;
+               nsats++;
+               if(P1 != 0 && ptab->prevP1 != 0)
+                  test = P1-L1_WAVELENGTH*L1
+                     - (ptab->prevP1-L1_WAVELENGTH*ptab->prevL1);
+               else if(C1 != 0 && ptab->prevC1 != 0)
+                  test = C1-L1_WAVELENGTH*L1
+                     - (ptab->prevC1-L1_WAVELENGTH*ptab->prevL1);
+               else
+                  test = 0.0;
+               if(fabs(test) > 0.5) {      // test must be > 150 km =~ 1/2 millisecond
+                  // is it nearly an even multiple of 1 millisecond?
+                  //test *= 1000.0/C_GPS_M;
+                  if(debug) *pout << "possible clock jump: test = "
+                                 << setprecision(9) << test;
+                  nms = long(test + (test > 0 ? 0.5 : -0.5));
+                  if(fabs(test - double(nms)) < 0.001) {
+                     if(debug) *pout << " -> " << setprecision(9)
+                              << fabs(test - double(nms));
+                     // keep clkjumpave = sequential average nms, clkjumpvar=variance
+                     if(test < 0) nms *= -1;
+                     nclkjumps++;
+                     clkjumpave += (double(nms)-clkjumpave)/double(nclkjumps);
+                     if(nclkjumps > 1)
+                        clkjumpvar = (clkjumpvar*(nclkjumps-2)
+                         + nclkjumps*(double(nms)-clkjumpave)*(double(nms)-clkjumpave)
+                            /(nclkjumps-1))/(nclkjumps-1);
+                  }
+                  else if(debug) *pout << " - failed.";
+                  if(debug && L1lli != 0) { *pout << " LLI is set"; }
+                  if(debug) *pout << " " << RinexSatID(it->first)
+                     << " " << last.printf("%4F %.3g") << endl;
+               }
+            }
+            // save C1,L1,P1 for this sat for next time
+            ptab->prevC1 = C1;
+            ptab->prevL1 = L1;
+            ptab->prevP1 = P1;
+
+         }  // end loop over sats
+
+         //out << robs;
+
+         // if more than half the sats saw a clk jump, call it
+         if(nclkjumps > nsats/2) {
+            if(debug) *pout << "test nclkjumps is " << nclkjumps
+               << " and nsats is " << nsats
+               << ", ave is " << fixed << setprecision(3) << clkjumpave
+               << " and stddev is " << setprecision(3) << sqrt(clkjumpvar)
+               << endl;
+            clkjumpTimes.push_back(last);
+            clkjumpMillsecs.push_back(clkjumpave);
+            clkjumpAgree.push_back(nsats-nclkjumps);
+            clkjumpUncertainty.push_back(sqrt(clkjumpvar));
+         }
+
+         if(prev != DayTime::BEGINNING_OF_TIME) {
+            dt = last-prev;
+            if(dt > 0.0) {
+               for(i=0; i<ndtmax; i++) {
+                  if(ndt[i] <= 0) { bestdt[i]=dt; ndt[i]=1; break; }
+                  if(fabs(dt-bestdt[i]) < 0.0001) { ndt[i]++; break; }
+                  if(i == ndtmax-1) {
+                     k = 0;
+                     int nleast=ndt[k];
+                     for(j=1; j<ndtmax; j++) if(ndt[j] <= nleast) {
+                        k=j; nleast=ndt[j];
+                     }
+                     ndt[k]=1; bestdt[k]=dt;
+                  }
+               }
+            }
+            else {
+               cerr << " WARNING time tags out of order: "
+                  //<< " prev > curr : "
+                  << prev.printf("%F/%.0g = %04Y/%02m/%02d %02H:%02M:%02S")
+                  << " > "
+                  << last.printf("%F/%.0g = %04Y/%02m/%02d %02H:%02M:%02S")
+                  << endl;
+            }
+         }
+         prev = last;
+
+      }  // end loop over epochs in the file
+      InStream.close();
+
+         // check that we found some data
+      if(nepochs <= 0) {
+         *pout << "File " << filename << " : no data found. Are time limits wrong?\n";
+         continue;
+      }
+
+         // compute interval
+      for(i=1,j=0; i<ndtmax; i++) if(ndt[i]>ndt[j]) j=i;
+      dt = bestdt[j];
+
+         // summary info
+      *pout << "Computed interval "
+         << fixed << setw(5) << setprecision(2) << dt << " seconds." << endl;
+      *pout << "Computed first epoch: " << ftime.printf("%4F %14.7g") << " = "
+            << ftime.printf("%04Y/%02m/%02d %02H:%02M:%010.7f") << endl;
+      *pout << "Computed last  epoch: " << last.printf("%4F %14.7g") << " = "
+            << last.printf("%04Y/%02m/%02d %02H:%02M:%010.7f") << endl;
+
+      *pout << "Computed time span:";
+      double secs=last-ftime;
+      int iday = int(secs/86400.0);
+      if(iday > 0) *pout << " " << iday << "d";
+      DayTime delta;
+      delta.setSecOfDay(secs - iday*86400);
+      *pout << " " << delta.hour() << "h "
+         << delta.minute() << "m "
+         << delta.second() << "s = "
+         << secs << " seconds." << endl;
+
+      i = 1+int(0.5+(last-ftime)/dt);
+      if(!brief) *pout << "There were " << nepochs << " epochs ("
+         << setprecision(2) << double(nepochs*100)/i
+         << "% of " << i << " possible epochs in this timespan) and "
+         << ncommentblocks << " inline header blocks.\n";
+
+         // sort table
+      sort(table.begin(),table.end(),TableSATLessThan());
+      if(TimeSortTable) sort(table.begin(),table.end(),TableBegLessThan());
+
+         // output table
+         // header
+      vector<TableData>::iterator tit;
+      if(table.size() > 0) table.begin()->sat.setfill('0');
+      if(!brief) {
+         *pout << "\n          Summary of data available in this file: "
+            << "(Totals are based on times and interval)\n";
+         *pout << "Sat  OT:";
+         for(k=0; k<n; k++)
+            *pout << setw(7) << rheader.obsTypeList[k].type;
+         *pout << "  Total             Begin time - End time\n";
+            // loop
+         for(tit=table.begin(); tit!=table.end(); ++tit) {
+            *pout << "Sat " << tit->sat << " ";
+            for(k=0; k<n; k++) *pout << setw(7) << tit->nobs[k];
+            // compute total based on times
+            *pout << setw(7) << 1+int(0.5+(tit->end-tit->begin)/dt);
+            if(GPSTimeOutput) {
+               *pout << "  " << tit->begin.printf("%4F %10.3g")
+                  << " - " << tit->end.printf("%4F %10.3g") << endl;
+            }
+            else {
+               *pout
+                  << "  " << tit->begin.printf("%04Y/%02m/%02d %02H:%02M:%04.1f")
+                  << " - " << tit->end.printf("%04Y/%02m/%02d %02H:%02M:%04.1f")
+                  << endl;
+            }
+         }
+         *pout << "TOTAL   "; for(k=0; k<n; k++) *pout << setw(7) << totals[k];
+         *pout << endl;
+      }
+      else {
+         *pout << "SATs(" << table.size() << "):";
+         for(tit=table.begin(); tit!=table.end(); ++tit)
+            *pout << " " << tit->sat;
+         *pout << endl;
+
+         *pout << "Obs types(" << rheader.obsTypeList.size() << "): ";
+         for(i=0; i<rheader.obsTypeList.size(); i++)
+            *pout << " " << rheader.obsTypeList[i].type;
+         *pout << endl;
+      }
+
+         // warnings
+      if((rheader.valid & RinexObsHeader::intervalValid)
+            && fabs(dt-rheader.interval) > 1.e-3)
+         *pout << " WARNING: Computed interval is " << setprecision(2)
+            << dt << " sec, while input header has " << setprecision(2)
+            << rheader.interval << " sec.\n";
+      if(fabs(ftime-rheader.firstObs) > 1.e-8)
+         *pout << " WARNING: Computed first time does not agree with header\n";
+      if((rheader.valid & RinexObsHeader::lastTimeValid)
+            && fabs(last-rheader.lastObs) > 1.e-8)
+         *pout << " WARNING: Computed last time does not agree with header\n";
+
+      if(clkjumpTimes.size() > 0) {
+         *pout << " WARNING: millisecond clock adjusts at these times:\n";
+         for(i=0; i<clkjumpTimes.size(); i++) {
+            *pout << "   "
+             << clkjumpTimes[i].printf("%4F %10.3g = %04Y/%02m/%02d %02H:%02M:%06.3f")
+             << " " << setprecision(2) << clkjumpMillsecs[i] << " ms_clock_adjust";
+             if(clkjumpAgree[i] > 0 || clkjumpUncertainty[i] > 0.01)
+               *pout << " (low quality determination; data may be irredeemable)";
+            *pout << endl;
+         }
+      }
+         // look for 'empty' obs types
+      for(k=0; k<n; k++) {
+         if(totals[k] <= 0) *pout << " WARNING: ObsType "
+            << rheader.obsTypeList[k].type
+            << " should be deleted from header.\n";
+      }
+
+      if(ReplaceHeader) {
+            // modify the header
+         rheader.version = 2.1; rheader.valid |= RinexObsHeader::versionValid;
+         rheader.interval = dt; rheader.valid |= RinexObsHeader::intervalValid;
+         rheader.lastObs = last; rheader.valid |= RinexObsHeader::lastTimeValid;
+            // now the table
+         rheader.numSVs = table.size(); rheader.valid |= RinexObsHeader::numSatsValid;
+         rheader.numObsForSat.clear();
+         for(tit=table.begin(); tit!=table.end(); ++tit) {      // tit defined above
+            rheader.numObsForSat.insert(
+               map<SatID, vector<int> >::value_type(tit->sat,tit->nobs) );
+         }
+         rheader.valid |= RinexObsHeader::prnObsValid;
+         //*pout << "\nNew header\n";
+         //rheader.dump(*pout);
+
+            // now re-open the file and replace the header
+#ifdef _MSC_VER
+         char newname[L_tmpnam];
+         if(!tmpnam(newname)) {
+            cerr << "Could not create temporary file name - abort\n";
+            return -1;
+         }
+#else
+         char newname[]="RinSumTemp.XXXXXX";
+         if(mkstemp(newname)==-1) {
+            cerr << "Could not create temporary file name - abort\n";
+            return -1;
+         }
+#endif
+         remove(newname);
+
+         RinexObsHeader rhjunk;
+         RinexObsStream ROutStr(newname, ios::out);
+         RinexObsStream InAgain(filename.c_str());
+         InAgain.exceptions(ios::failbit);
+
+         InAgain >> rhjunk;
+         ROutStr << rheader;
+         while(InAgain >> robs) {
+            last = robs.time;
+            if(last < BegTime) continue;
+            if(last > EndTime) break;
+            ROutStr << robs;
+         }
+         InAgain.close();
+         ROutStr.close();
+            // delete original file and rename the temporary
+         iret = remove(filename.c_str());
+         if(iret) *pout << "RinSum: Error: Could not remove existing file: "
+            << filename << endl;
+         else {
+            iret = rename(newname,filename.c_str());
+            if(iret) *pout << "RinSum: Error: Could not rename new file " << newname
+               << " using old name " << filename << endl;
+            else *pout << "\nRinSum: Replaced original header with complete one,"
+               << " using temporary file name "
+               << newname << endl;
+         }
+      }
+
+      if(!brief) *pout << "\n+++++++++++++ End of RinSum summary of " << filename
+         << " +++++++++++++\n";
+   }
+
+   if(pout != &cout) {
+      ((ofstream *)pout)->close();
+      delete pout;
+   }
+
+   return 0;
+}
+catch(gpstk::FFStreamError& e) { cerr << "FFStreamError: " << e; }
+catch(gpstk::Exception& e) { cerr << "Exception: " << e; }
+catch (...) { cerr << "Unknown exception.  Abort." << endl; }
+   return 1;
+}   // end main()
+
+//------------------------------------------------------------------------------------
+int GetCommandLine(int argc, char **argv) throw(Exception)
+{
+try {
+   bool help=false;
+   int j;
+      // required options
+
+      // optional
+   CommandOption dashi(CommandOption::hasArgument, CommandOption::stdType,
+      'i',"input"," [-i|--input] <file>  Input RINEX observation file name(s)");
+   //dashi.setMaxCount(1);
+
+      // optional options
+      // this only so it will show up in help page...
+   CommandOption dashf(CommandOption::hasArgument, CommandOption::stdType,
+      'f',""," [-f|--file] <file>   file containing more options");
+
+   CommandOption dasho(CommandOption::hasArgument, CommandOption::stdType,
+      'o',"output"," [-o|--output] <file> Output the summary to a file named <file>");
+   dasho.setMaxCount(1);
+   
+   CommandOption dashp(CommandOption::hasArgument, CommandOption::stdType,
+      'p',"path"," [-p|--path] <path>   Find the input file(s) in this directory");
+   dashp.setMaxCount(1);
+
+   CommandOptionNoArg dashr('R', "Replace",
+      " [-R|--Replace]       Replace input file header with a full one, in place.");
+   dashr.setMaxCount(1);
+
+   CommandOptionNoArg dashs('s', "sort",
+      " [-s|--sort]          Sort the SAT/Obs table on begin time.");
+
+   CommandOptionNoArg dashg('g', "gps",
+      " [-g|--gps]           Print times in the SAT/Obs table as GPS times.");
+
+   // time
+   // times - don't use CommandOptionWithTimeArg
+   CommandOption dashbt(CommandOption::hasArgument, CommandOption::stdType,
+      0,"start", " --start <time>       Start time: <time> is 'GPSweek,sow' OR "
+      "'YYYY,MM,DD,HH,Min,Sec'");
+   dashbt.setMaxCount(1);
+
+   CommandOption dashet(CommandOption::hasArgument, CommandOption::stdType,
+      0,"stop", " --stop <time>        Stop time: <time> is 'GPSweek,sow' OR "
+      "'YYYY,MM,DD,HH,Min,Sec'");
+   dashet.setMaxCount(1);
+
+   CommandOptionNoArg dashb('b', "brief",
+      " [-b|--brief]         produce a brief (6-line) summary.");
+
+   // help and debug
+   CommandOptionNoArg dashh('h', "help",
+      " [-h|--help]          print this help page and quit.");
+   CommandOptionNoArg dashd('d', "debug",
+      " [-d|--debug]         print debugging info.");
+
+   // ... other options
+   CommandOptionRest Rest("<filename(s)>");
+
+   CommandOptionParser Par(
+      "Prgm RINSUM reads a Rinex file and summarizes it content.\n"
+      " It can optionally fill the header of the input file.\n"
+      " [either <filenames> or --input required; put <filenames> after options].\n"
+      );
+
+   // allow user to put all options in a file
+   // could also scan for debug here
+   vector<string> Args;
+   for(j=1; j<argc; j++) PreProcessArgs(argv[j],Args);
+
+   if(Args.size()==0)
+      Args.push_back(string("-h"));
+
+   argc = Args.size()+1;
+   char **CArgs;
+   CArgs = new char * [argc];
+   if(!CArgs) { cerr << "Failed to allocate CArgs\n"; return -1; }
+   CArgs[0] = argv[0];
+   for(j=1; j<argc; j++) {
+      CArgs[j] = new char[Args[j-1].size()+1];
+      if(!CArgs[j]) { cerr << "Failed to allocate CArgs[j]\n"; return -1; }
+      strcpy(CArgs[j],Args[j-1].c_str());
+   }
+
+   Par.parseOptions(argc, CArgs);
+   delete[] CArgs;
+
+      // get help option first
+   if(dashh.getCount() > 0) {
+      Par.displayUsage(cout,false);
+      help = true;   //return 1;
+   }
+
+   if (Par.hasErrors())
+   {
+      cerr << "\nErrors found in command line input:\n";
+      Par.dumpErrors(cerr);
+      cerr << "...end of Errors\n\n";
+      Par.displayUsage(cout,false);
+      help = true; // return -1;
+   }
+   
+      // get values found on command line
+   string msg;
+   vector<string> values,field;
+
+      // f never appears because we intercept it above
+   //if(dashf.getCount()) { cout << "Option f "; dashf.dumpValue(cout); }
+
+   if(dashi.getCount()) {
+      InputFiles = dashi.getValue();
+      if(help) {
+         cout << "Input: input files (--input) are:\n";
+         for(int i=0; i<InputFiles.size(); i++)
+            cout << "   " << InputFiles[i] << endl;
+      }
+   }
+   if(dasho.getCount()) {
+      values = dasho.getValue();
+      OutputFile = values[0];
+      if(help) cout << "Input: output file is " << OutputFile << endl;
+   }
+   if(dashp.getCount()) {
+      values = dashp.getValue();
+      InputDirectory = values[0];
+      if(help) cout << "Input: set path to " << InputDirectory << endl;
+   }
+
+   if(dashr.getCount()) {
+      ReplaceHeader=true;
+      if(help) cout << "Input: replace header in output" << endl;
+   }
+   if(dashs.getCount()) {
+      TimeSortTable=true;
+      if(help) cout << "Input: sort the SAT/Obs table" << endl;
+   }
+   if(dashg.getCount()) {
+      GPSTimeOutput=true;
+      if(help) cout << "Input: output in GPS time" << endl;
+   }
+   // times
+   // TD put try  {} around setToString and catch invalid formats...
+   if(dashbt.getCount()) {
+      values = dashbt.getValue();
+      msg = values[0];
+      field.clear();
+      while(msg.size() > 0)
+         field.push_back(stripFirstWord(msg,','));
+      if(field.size() == 2)
+         BegTime.setToString(field[0]+","+field[1], "%F,%g");
+      else if(field.size() == 6)
+         BegTime.setToString(field[0]+","+field[1]+","+field[2]+","+field[3]+","
+            +field[4]+","+field[5], "%Y,%m,%d,%H,%M,%S");
+      else {
+         cerr << "Error: invalid --start input: " << values[0] << endl;
+      }
+      if(help) cout << " Input: begin time " << values[0] << " = "
+         << BegTime.printf("%Y/%02m/%02d %2H:%02M:%06.3f = %F/%10.3g") << endl;
+   }
+   if(dashet.getCount()) {
+      values = dashet.getValue();
+      msg = values[0];
+      field.clear();
+      while(msg.size() > 0)
+         field.push_back(stripFirstWord(msg,','));
+      if(field.size() == 2)
+         EndTime.setToString(field[0]+","+field[1], "%F,%g");
+      else if(field.size() == 6)
+         EndTime.setToString(field[0]+","+field[1]+","+field[2]+","+field[3]+","
+            +field[4]+","+field[5], "%Y,%m,%d,%H,%M,%S");
+      else {
+         cerr << "Error: invalid --stop input: " << values[0] << endl;
+      }
+      if(help) cout << " Input: end time " << values[0] << " = "
+         << EndTime.printf("%Y/%02m/%02d %2H:%02M:%06.3f = %F/%10.3g") << endl;
+   }
+
+   if(dashb.getCount()) {
+      brief = true;
+      if(help) cout << "Input: found the brief flag" << endl;
+   }
+
+   if(dashd.getCount()) {
+      debug = true;
+      if(help) cout << "Input: found the debug flag" << endl;
+   }
+
+   if(Rest.getCount())
+   {
+      values = Rest.getValue();
+      if(help) cout << "Input: input files are:\n";
+      for (int i=0; i<values.size(); i++) {
+         if(help) cout << "  " << values[i] << endl;
+         InputFiles.push_back(values[i]);
+      }
+   }
+
+   if(debug && help) {
+      cout << "\nTokens on command line (" << Args.size() << ") are:" << endl;
+      for(j=0; j<Args.size(); j++) cout << Args[j] << endl;
+   }
+   if(help) return 1;
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+// Pull out -f<f> and --file <f> and deprecated options
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception)
+{
+try {
+   static bool found_cfg_file=false;
+
+   if(found_cfg_file || (arg[0]=='-' && arg[1]=='f')) {
+      string filename(arg);
+      if(!found_cfg_file) filename.erase(0,2); else found_cfg_file = false;
+      ifstream infile(filename.c_str());
+      if(!infile) {
+         cout << "Error: could not open options file " << filename << endl;
+         return;
+      }
+
+      bool again_cfg_file=false;
+      char c;
+      string buffer,word;
+      while(1) {
+         getline(infile,buffer);
+         stripTrailing(buffer,'\r');
+
+         // process the buffer before checking eof or bad b/c there can be
+         // a line at EOF that has no CRLF...
+         while(!buffer.empty()) {
+            word = firstWord(buffer);
+            if(again_cfg_file) {
+               word = "-f" + word;
+               again_cfg_file = false;
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else if(word[0] == '#') { // skip to end of line
+               buffer = "";
+            }
+            else if(word == "--file" || word == "-f")
+               again_cfg_file = true;
+            else if(word[0] == '"') {
+               word = stripFirstWord(buffer,'"');
+               buffer = "dummy " + buffer;            // to be stripped later
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else
+               PreProcessArgs(word.c_str(),Args);
+
+            word = stripFirstWord(buffer);      // now remove it from buffer
+         }
+         if(infile.eof() || !infile.good()) break;
+      }
+   }
+   else if(string(arg) == "--file" || string(arg) == "-f")
+      found_cfg_file = true;
+   // old versions of args -- deprecated
+   else if(string(arg)==string("--EpochBeg")) { Args.push_back("--start"); }
+   else if(string(arg)==string("--GPSBeg")) { Args.push_back("--start"); }
+   else if(string(arg)==string("--EpochEnd")) { Args.push_back("--stop"); }
+   else if(string(arg)==string("--GPSEnd")) { Args.push_back("--stop"); }
+   // regular arg
+   else Args.push_back(arg);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
diff --git a/dev/apps/Rinextools/RinexDump.cpp b/dev/apps/Rinextools/RinexDump.cpp
new file mode 100644
index 0000000..0a62e6b
--- /dev/null
+++ b/dev/apps/Rinextools/RinexDump.cpp
@@ -0,0 +1,369 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file RinexDump.cpp
+ * Dump Rinex observation data to a flat file.
+ * Read a RINEX file and dump the data for the given satellite(s).
+ * Any number of obstypes may appear in the command; if none appear, all are dumped.
+ * Any number of satellite ID (e.g. G27) may appear; if none appears, all are dumped.
+ * The output file is ASCII column-delimited with time, satellite ID and then three
+ * columns 'observation LLI SSI' for each observation type.
+ */
+
+#include "RinexObsData.hpp"
+#include "RinexObsHeader.hpp"
+#include "RinexObsStream.hpp"
+#include "DayTime.hpp"
+#include "RinexSatID.hpp"
+#include "StringUtils.hpp"
+#include "RinexUtilities.hpp"
+
+#include <vector>
+#include <string>
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+// find the index of first occurance of item t (of type T) in vector<T> v;
+// i.e. v[index]=t  Return -1 if t is not found.
+template<class T> int index(const std::vector<T> v, const T& t) 
+{
+   for(int i=0; i<v.size(); i++) {
+      if(v[i] == t) return i;
+   }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+// Returns 0 on success.  Input and output files should diff without error.
+int main(int argc, char *argv[])
+{
+try {
+   bool debug=false;
+   bool AllNumeric=false, DumpPos=false, help=false;
+   bool DumpAll=false,DumpAllObs=false,DumpAllSat=false,ok;
+   int i,j;
+   string X,Y,Z,T,rms,pdop,gdop,N,outputFormat=string("%4F %10.3g");
+   RinexObsHeader::RinexObsType ot;
+   RinexSatID sat;
+   string line, word, filename, leftpad=string(""), rightpad=string("");
+   vector<string> filenames;
+   vector<RinexObsHeader::RinexObsType> otlist;
+   vector<RinexSatID> satlist;
+   RinexObsHeader header;
+   RinexObsData obsdata;
+
+   sat.setfill('0');
+   RegisterARLUTExtendedTypes();
+   //cout << "Registered Obs types are:\n";
+   //for(j=0; j<RinexObsHeader::RegisteredRinexObsTypes.size(); j++)
+   //   cout << "ROT[" << j << "] = " << RinexObsHeader::RegisteredRinexObsTypes[j]
+   //   << endl;
+
+   // parse command line input
+   for(i=1; i<argc; i++) {
+      word = string(argv[i]);
+      if(debug) cout << "arg = " << word << endl;
+      if(word == "pos") DumpPos = true;
+      else if(word == "-h" || word == "--help") help = true;
+      else if(word == "-f" || word == "-file" || word == "--file") {
+         filename = string(argv[++i]);
+         filenames.push_back(filename);
+      }
+      else if(word == "-n" || word == "--num") AllNumeric = true;
+      else if(word == "--format") outputFormat = string(argv[++i]);
+      else if(word == "-sat" || word == "--sat") {
+         sat.fromString(string(argv[++i]));
+         if(!sat.isValid()) cout << "Error: input argument " << argv[i]
+               << " is not a valid satellite id" << endl;
+         else
+            satlist.push_back(sat);
+      }
+      else if(word == "-obs" || word == "--obs") {
+         ot = RinexObsHeader::convertObsType(argv[++i]);
+         if(RinexObsHeader::convertObsType(ot) == string("UN"))
+            cout << "Error: input argument " << argv[i]
+               << " is not a valid obs type" << endl;
+         else 
+            otlist.push_back(ot);
+      }
+      else {         // try to figure out what it is...
+         if(debug) cout << " try making it a RINEX obs file: " << word << endl;
+         if(isRinexObsFile(word)) {
+            filenames.push_back(word);
+            continue;
+         }
+
+         if(debug) cout << " try making it an obs type: " << word << endl;
+         ot = RinexObsHeader::convertObsType(argv[i]);
+         if(RinexObsHeader::convertObsType(ot) != string("UN")) {
+            otlist.push_back(ot);
+            continue;
+         }
+
+         if(debug) cout << " try making it a sat : " << word << endl;
+         try {
+            sat.fromString(word);
+            if(sat.isValid()) {
+               satlist.push_back(sat);
+               continue;
+            }
+         }
+         catch(Exception& e) { ; }
+
+         cout << "Unknown argument, ignore: " << word << endl;
+      }
+   }
+
+   if(argc < 2 || help) {
+      cout << 
+"Read RINEX file(s) and dump the given observation types in columns.\n"
+"Output is to the screen, with one time tag and one satellite per line.\n"
+" Usage: RinexDump [options] file obs sat [pos]\n"
+"  If no satellites are given, all are output; likewise for observation types.\n"
+"  Output begins with header lines (starting with #) identifying input and columns.\n"
+" Options are:\n"
+"    pos           output only positions from aux headers; sat and obs are ignored.\n"
+"    --num or -n   make output purely numeric (no header, no system char on sats)\n"
+"    --format <f>  output times in (DayTime) format (default " << outputFormat << ")\n"
+"    --file <file> file is a RINEX observation file; this option may be repreated.\n"
+"    --obs <obs>   obs is a RINEX observation type (e.g. P1) found in the file header.\n"
+"    --sat <sat>   sat is a RINEX satellite id (e.g. G31 for GPS PRN 31)\n"
+"      [--file, --obs and --sat are optional but may be needed to remove ambiguity.]\n"
+"    --help or -h  print this and quit.\n"
+" E.g. RinexDump test2820.06o L1 L2 G17\n";
+      return -1;
+   }
+
+   if(otlist.size() == 0) DumpAllObs = true;
+   if(satlist.size() == 0) DumpAllSat = true;
+   if(DumpAllObs && DumpAllSat) DumpAll = true;
+
+   vector<RinexObsHeader::RinexObsType>::iterator it;
+
+   if(filenames.size() == 0) cerr << "Error - no file names specified.\n";
+   if(debug || filenames.size() == 0) {
+      cout << "RinexDump read the following from the command line:\n";
+      for(i=0; i<filenames.size(); i++)
+         cout << " File: " << filenames[i] << endl;
+      cout << " Observation types:";
+      if(otlist.size() == 0) cout << " all";
+      else for(i=0; i<otlist.size(); i++)
+         cout << " " << RinexObsHeader::convertObsType(otlist[i]);
+      cout << endl;
+      cout << " Satellites:";
+      if(satlist.size() == 0) cout << " all";
+      else for(i=0; i<satlist.size(); i++) cout << " " << satlist[i];
+      cout << endl;
+   }
+
+   if(filenames.size() == 0) return -1;
+
+   // sort the file names on the begin time in the header
+   if(filenames.size() > 1) sortRinexObsFiles(filenames);
+
+   // loop over input files
+   for(int nfile=0; nfile < filenames.size(); nfile++) {
+      filename = filenames[nfile];
+
+      // does the file exist?
+      RinexObsStream RinFile(filename.c_str());
+      if(filename.empty() || !RinFile) {
+         cerr << "Error: input file " << filename << " does not exist.\n";
+         continue; //return -1;
+      }
+      RinFile.exceptions(fstream::failbit);
+
+      // is it a Rinex Obs file? ... read the header
+      try { RinFile >> header; }
+      catch(Exception& e) {
+         cerr << "Error: input file " << filename << " is not a Rinex obs file\n";
+         continue; //return -2;
+      }
+      //cout << "Rinex header:\n";
+      //header.dump(cout);
+
+      // check that obs types are in header
+      for(it=otlist.begin(); it !=otlist.end(); ) {
+         ok = false;
+         for(j=0; j<header.obsTypeList.size(); j++) {
+            if(*it == header.obsTypeList[j]) { ok = true; break; }
+         }
+         if(!ok) {
+            cout << "Warning: " << *it << " not found in header of file "
+               << filename << endl;
+            it = otlist.erase(it);
+         }
+         else it++;
+      }
+   
+      if(DumpAllObs) {
+         otlist.clear();
+         for(j=0; j<header.obsTypeList.size(); j++)
+            otlist.push_back(header.obsTypeList[j]);
+      }
+
+      // echo input
+      if(!AllNumeric) {
+         cout << "# Rinexdump File: " << filename;
+         if(DumpPos) cout << " Positions (in auxiliary header comments)";
+         else {
+            cout << "   Satellites:";
+            if(satlist.size() > 0)
+               for(j=0; j<satlist.size(); j++) { cout << " " << satlist[j]; }
+            else cout << " ALL";
+            cout << "   Observations:";
+            if(!DumpAllObs) for(j=0; j<otlist.size(); j++)
+               cout << " " << RinexObsHeader::convertObsType(otlist[j]);
+            else cout << " ALL";
+         }
+         cout << endl;
+      }
+
+      if(otlist.size() == 0) {
+         cout << " Nothing to do.\n";
+         continue; //return -1;
+      }
+
+      // dump the column headers
+      if(!AllNumeric) {
+         // figure out widths
+         DayTime Now;
+         string ts;
+         ts = "# Time (" + outputFormat + ")";
+         int n = ts.size() - Now.printf(outputFormat).size();
+         if(n < 0) rightpad = leftJustify(string(""),-n-1);
+         else leftpad = leftJustify(string(""),n);
+         cout << ts;
+         
+         if(DumpPos) cout << " NSVs        X(m)          Y(m)          Z(m)"
+               << "        Clk(m)   PDOP  GDOP   RMS(m)";
+         else {
+            cout << " Sat";
+            for(j=0; j<otlist.size(); j++) cout << "            "
+               << RinexObsHeader::convertObsType(otlist[j]) << " L S";
+         }
+         cout << endl;
+      }
+   
+      cout << fixed;
+      while(RinFile >> obsdata) {
+         RinexObsData::RinexSatMap::const_iterator it;
+         RinexObsData::RinexObsTypeMap::const_iterator jt;
+
+         // if dumping regular data, skip auxiliary header, etc
+         if(!DumpPos && obsdata.epochFlag != 0 && obsdata.epochFlag != 1)
+            continue;
+
+         // dump position data
+         if(DumpPos && obsdata.epochFlag == 4) {
+            // loop over comments in the header data
+            X=Y=Z=T=pdop=gdop=rms=N=string();
+            for(j=0,i=0; i<obsdata.auxHeader.commentList.size(); i++) {
+               line = stripTrailing(obsdata.auxHeader.commentList[i],
+                                    string("COMMENT"),1);
+               word = stripFirstWord(line);
+               if(word == "XYZT") {
+                  X = stripFirstWord(line);
+                  Y = stripFirstWord(line);
+                  Z = stripFirstWord(line);
+                  T = stripFirstWord(line);
+                  j++;
+               }
+               else if(word == "DIAG") {
+                  N = stripFirstWord(line);
+                  pdop = stripFirstWord(line);
+                  gdop = stripFirstWord(line);
+                  rms = stripFirstWord(line);
+                  j++;
+               }
+               else { // ignore
+               }
+            }
+   
+            // print it
+            if(j==2) cout << leftpad << obsdata.time.printf(outputFormat) << rightpad
+               << setw(4) << N
+               << setprecision(3)
+               << " " << setw(13) << X
+               << " " << setw(13) << Y
+               << " " << setw(13) << Z
+               << " " << setw(13) << T
+               << " " << setw(5) << pdop
+               << " " << setw(5) << gdop
+               << " " << setw(9) << rms
+               << endl;
+
+         } // end if dumping position data
+
+         if(DumpPos) continue;
+
+         // loop over satellites
+         for(it=obsdata.obs.begin(); it != obsdata.obs.end(); ++it) {
+            // convert to RinexSatID to get the fill character
+            RinexSatID sat = it->first;
+            if(!DumpAll && !DumpAllSat && index(satlist, sat) == -1) continue;
+            // loop over obs
+            ok = false;            // set true only when data exists to output
+            for(j=0; j<otlist.size(); j++) {
+               if((jt=it->second.find(otlist[j])) == it->second.end()) {
+                  cout << " " << setw(13) << setprecision(3)
+                     << 0.0 << " " << 0 << " " << 0;
+               }
+               else {
+                  if(!ok) {       // output a line
+                     // time tag
+                     cout << leftpad << obsdata.time.printf(outputFormat) << rightpad;
+                     // satellite
+                     cout << " ";
+                     if(AllNumeric)
+                        cout << setw(3) << sat.id;
+                     else
+                        cout << sat;
+                     ok = true;
+                  }
+                  cout << " " << setw(13) << setprecision(3) << jt->second.data
+                     << " " << jt->second.lli << " " << jt->second.ssi;
+               }
+            }
+            if(ok) cout << endl;
+         } // end loop over satellites
+      } // end loop over obs data in file
+
+      RinFile.close();
+   } // end loop over input files
+
+   return 0;
+}
+catch(FFStreamError& e) { cout << e; }
+catch(Exception& e) { cout << e; }
+catch (...) { cout << "unknown error.  Done." << endl; }
+   return -1;
+} // main()
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
diff --git a/dev/apps/Rinextools/RinexEditor.cpp b/dev/apps/Rinextools/RinexEditor.cpp
new file mode 100644
index 0000000..7c1108f
--- /dev/null
+++ b/dev/apps/Rinextools/RinexEditor.cpp
@@ -0,0 +1,1467 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file RinexEditor.cpp
+ * Edit Rinex observation files.
+ * class REditCmd encapsulates commands passed to the Rinex Editor
+ */
+
+//------------------------------------------------------------------------------------
+// TD Do better at catching exceptions
+
+//------------------------------------------------------------------------------------
+#include <vector>
+#include <algorithm>
+#include <time.h>
+#include <stdlib.h>  // for mkstemp
+#include <iostream>
+
+#include "RinexEditor.hpp"
+
+#include "MathBase.hpp"
+#include "StringUtils.hpp"
+#include "RinexObsStream.hpp"
+#include "RinexUtilities.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+//------------------------------------------------------------------------------------
+string RinexEditVersion;         // see below in Initialize()
+std::map<REditCmd::TYPE, std::string> REditCmd::typeLabel;
+
+//------------------------------------------------------------------------------------
+// Initialize
+REditCmd::Initialize REditCmdInitializer;
+
+REditCmd::Initialize::Initialize()
+{
+   RinexEditVersion = string("3.5 6/21/2007");
+
+   typeLabel[INVALID] = string("INVALID");
+   typeLabel[IF] = string("IF");
+   typeLabel[OF] = string("OF");
+   typeLabel[ID] = string("ID");
+   typeLabel[OD] = string("OD");
+   typeLabel[HD] = string("HD");
+   typeLabel[TN] = string("TN");
+   typeLabel[TB] = string("TB");
+   typeLabel[TE] = string("TE");
+   typeLabel[TT] = string("TT");
+   typeLabel[AO] = string("AO");
+   typeLabel[DA] = string("DA");
+   typeLabel[DO] = string("DO");
+   typeLabel[DS] = string("DS");
+   typeLabel[DD] = string("DD");
+   typeLabel[SD] = string("SD");
+   typeLabel[SS] = string("SS");
+   typeLabel[SL] = string("SL");
+   typeLabel[BD] = string("BD");
+   typeLabel[BS] = string("BS");
+   typeLabel[BL] = string("BL");
+   typeLabel[BZ] = string("BZ");
+}
+
+//------------------------------------------------------------------------------------
+// find the index of first occurance of item t (of type T) in vector<T> v;
+// i.e. v[index]=t  Return -1 if t is not found.
+template<class T> int index(const std::vector<T> v, const T& t) 
+{
+   for(int i=0; i<v.size(); i++) {
+      if(v[i] == t) return i;
+   }
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+string RinexEditor::getRinexEditVersion(void) { return RinexEditVersion; }
+
+//------------------------------------------------------------------------------------
+// REditCmd member functions
+//------------------------------------------------------------------------------------
+// constructor from a string, pass by value to avoid changing original
+REditCmd::REditCmd(string s, ostream *oflog) throw(Exception)
+{
+try {
+   type = INVALID;
+
+      // ignore leading '-'s
+   while(s.size() && (s[0]=='-' || (s[0]==' '||s[0]=='\t'))) s.erase(0,1);
+   if(s.size() < 2) return;
+
+      // separate type and the rest
+   string tag=s.substr(0,2);
+   field = s.substr(2,s.size()-2);
+
+      // first identify the type
+   map<TYPE,string>::const_iterator it;
+   for(it=typeLabel.begin(); it != typeLabel.end(); it++) {
+      if(tag == it->second) { type = it->first; break; }
+   }
+
+      // defaults
+   bias = -99.99;
+   SV = RinexSatID(33,SatID::systemGPS);
+   sign = 0;
+   inOT = -1;
+   time = DayTime::BEGINNING_OF_TIME;
+
+      // bail if invalid
+   if(type==INVALID) return;
+
+      // BZ needs nothing more
+   if(type==BZ) return;
+
+      // break field into subfields
+   if(field.size() == 0) { type = INVALID; return; }
+   vector<string> subfield;
+   string::size_type pos;
+   while(field.size() > 0) {
+      pos = field.find(",");
+      if(pos==string::npos) pos=field.size();
+      if(pos==0) subfield.push_back(" ");
+      else subfield.push_back(field.substr(0,pos));
+      if(pos >= field.size()) break;
+      field.erase(0,pos+1);
+   };
+
+      // TN just needs time spacing
+   if(type==TN) {
+      bias = asDouble(subfield[0]);
+      // validate?
+      return;
+   }
+
+      // TT just needs delta time
+   if(type==TT) {
+      bias = asDouble(subfield[0]);
+      // validate?
+      return;
+   }
+
+      // get (optional) sign
+   if(type==DA || type==DS || type==DD || type==SL || type==BD) {
+      if(subfield[0][0]=='+') { sign=+1; subfield[0].erase(0,1); }
+      if(subfield[0][0]=='-') { sign=-1; subfield[0].erase(0,1); }
+   }
+ 
+      // field = filename, OT, or header info
+   if(type==IF || type==OF || type==ID || type==OD || type==HD
+         || type==AO || type==DO) {
+      field = subfield[0];
+      if(type==HD) {            // inOT = int(first character)
+         char c=field[0];
+         inOT = int(toupper(c));
+         if(inOT!='F' && inOT!='P' && inOT!='R' && inOT!='O' && inOT!='A' &&
+            inOT!='M' && inOT!='N' && inOT!='C' && inOT!='D' && inOT!='X')
+               { type=INVALID; return; }
+         if(inOT == 'X') {
+            if(subfield.size() < 3) { type=INVALID; return; }
+            field += ";" + subfield[1] + ";" + subfield[2];
+         }
+         field.erase(0,1);
+      }
+      if(type!=OF || subfield.size()==1) return;
+      subfield.erase(subfield.begin());
+   }
+   else field = string(" ");
+
+      // get an SV
+   if(type >= DS) {
+      SV.fromString(subfield[0]);
+      //if(REDebug) *oflog << "REC: PRN is " << SV << endl;
+
+      // allow all commands from DS on to have SV = (system,-1)
+      // where id==-1 means 'all SV of this system'
+      //if((type==DS || type==SL) && SV.id == -1) ;   // ok
+      //else if(SV.system == SatID::systemGPS && (SV.id<=0 || SV.id>32))
+      //   { type=INVALID; return; }
+      if(type==DS && subfield.size()==1) return;
+      subfield.erase(subfield.begin());
+   }
+
+      // get an OT
+   if(type >= DD) {
+      field = subfield[0];
+         // TD have a bool valid(string) function or bool valid(RinexObsType)
+      RinexObsHeader::RinexObsType rot=RinexObsHeader::convertObsType(field);
+      if(rot.type==string("UN")) { type=INVALID; return; }
+      //if(REDebug) *oflog << "REC: processed OT is " << rot.type << endl;
+      subfield.erase(subfield.begin());
+   }
+
+      // get a time
+   if(subfield.size()==2 || subfield.size()==3) {
+      time.setGPSfullweek(asInt(subfield[0]), asDouble(subfield[1]));
+   }
+   if(subfield.size()==6 || subfield.size()==7) {
+      time.setYMDHMS(asInt(subfield[0]), asInt(subfield[1]),
+         asInt(subfield[2]), asInt(subfield[3]),
+         asInt(subfield[4]), asDouble(subfield[5]));
+   }
+   //if(REDebug) *oflog << "REC: time is "
+   //<< time.printf("%4Y/%2m/%2d %2H:%2M:%.4f") << endl;
+   // test validity?
+
+      // bias
+   if(type >= SD) {
+      //if(REDebug) *oflog << "REC: bias field is " << subfield.back() << endl;
+      bias = asDouble(subfield.back().c_str());
+      //if(REDebug) *oflog << "REC: bias is " << bias << endl;
+   }
+
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}   // end REditCmd::REditCmd(string)
+
+//------------------------------------------------------------------------------------
+REditCmd::~REditCmd(void)
+{
+}
+
+//------------------------------------------------------------------------------------
+void REditCmd::Dump(ostream& os, string msg) throw(Exception)
+{
+try {
+   if(msg.size()) os << msg;
+   os << " type=" << typeLabel[type] << ", sign=" << sign << ", SV="
+      << SV.toString()
+      << ", inOT=" << inOT
+      << ", field=" << field
+      << ", bias=" << fixed << setprecision(3) << bias
+      << ", time = " << time.printf("%4Y/%2m/%2d %2H:%2M:%.4f") << endl;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// RinexEditor member functions
+//------------------------------------------------------------------------------------
+RinexEditor::RinexEditor(void)
+{
+   Decimate = 0.0;
+   TimeTol = 0.001;
+   BegTime = DayTime::BEGINNING_OF_TIME;
+   EndTime = DayTime::END_OF_TIME;
+   REVerbose = REDebug = BiasZeroData = FillOptionalHeader = HDDeleteOldComments
+      = false;
+   Skip = false;
+   IVLast = IVInterval = IVTable = false;
+   for(int i=0; i<9; i++) ndt[i]=-1;
+   oflog = &cout;
+}
+
+//------------------------------------------------------------------------------------
+RinexEditor::~RinexEditor(void)
+{
+   Cmds.erase(Cmds.begin(),Cmds.end());
+   OneTimeCmds.erase(OneTimeCmds.begin(),OneTimeCmds.end());
+   CurrentCmds.erase(CurrentCmds.begin(),CurrentCmds.end());
+}
+
+//------------------------------------------------------------------------------------
+// Return 0 ok, -1 no input file name, -2 no output file name
+int RinexEditor::ParseCommands(void) throw(Exception)
+{
+try {
+   bool flag;
+   int i,iret=0;
+      // first scan command list for BZ,HDf,TN,TT,TB,TE,IF,OF,ID,OD
+   for(i=0; i<Cmds.size(); i++) {
+      if(REDebug) Cmds[i].Dump(*oflog,string("parse this command"));
+      switch(Cmds[i].type) {
+         case REditCmd::TN:
+            Decimate = Cmds[i].bias;
+            IVInterval = true;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set TN with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::TT:
+            TimeTol = Cmds[i].bias;
+            if(REDebug) Cmds[i].Dump(*oflog,string("set TT with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::TB:
+            BegTime = Cmds[i].time;
+            IVTable = true;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set TB with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::TE:
+            EndTime = Cmds[i].time;
+            IVLast = IVTable = true;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set TE with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::IF:
+            //InputFile = Cmds[i].field;
+            Inputfiles.push_back(Cmds[i].field);
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set IF with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::OF:
+            if(Cmds[i].time == DayTime::BEGINNING_OF_TIME) {
+               OutputFile = Cmds[i].field;
+               //if(REDebug) Cmds[i].Dump(*oflog,string("set OF with this cmd"));
+               Cmds[i].type = REditCmd::INVALID;
+            }
+            break;
+         case REditCmd::ID:
+            InputDir = Cmds[i].field;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set ID with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::OD:
+            OutputDir = Cmds[i].field;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set OD with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::BZ:
+            BiasZeroData = true;
+            //if(REDebug) Cmds[i].Dump(*oflog,string("set BZ with this cmd"));
+            Cmds[i].type = REditCmd::INVALID;
+            break;
+         case REditCmd::HD:
+            flag = true;
+            switch(Cmds[i].inOT) {
+               case int('F'): FillOptionalHeader=true; break;
+               case int('D'): HDDeleteOldComments=true; break;
+               case int('P'): HDProgram=Cmds[i].field; break;
+               case int('X'): HDPosition=Cmds[i].field; break;
+               case int('R'): HDRunBy=Cmds[i].field; break;
+               case int('O'): HDObserver=Cmds[i].field; break;
+               case int('A'): HDAgency=Cmds[i].field; break;
+               case int('M'): HDMarker=Cmds[i].field; break;
+               case int('N'): HDNumber=Cmds[i].field; break;
+               case int('C'): HDComments.push_back(Cmds[i].field); break;
+               default: flag=false; break;
+            }
+            if(flag) {
+               if(REDebug) Cmds[i].Dump(*oflog,string("set HD rec with this cmd"));
+               Cmds[i].type = REditCmd::INVALID;
+            }
+            break;
+         default: break;
+      }
+   }
+
+      // require an input file name
+   if(Inputfiles.size() == 0) iret -= 1;
+      // sort on begin time (header) and add path
+   else {
+      if(Inputfiles.size() > 1)
+         sortRinexObsFiles(Inputfiles);
+      if(!InputDir.empty()) {
+         for(i=0; i<Inputfiles.size(); i++) {
+            InputFile = InputDir + string("/") + Inputfiles[i];
+            Inputfiles[i] = InputFile;
+         }
+      }
+   }
+   
+      // now iterate over the list in reverse, deleting INVALID commands.
+   deque<REditCmd>::iterator jt,it=Cmds.begin();
+   while(it != Cmds.end()) {
+      if(it->type == REditCmd::INVALID) {
+         //if(REDebug) it->Dump(*oflog,string("Erase this INVALID command:"));
+         it = Cmds.erase(it);
+      }
+      else it++;
+   }
+
+      // sort on time
+   sort(Cmds.begin(),Cmds.end(),REditCmdLessThan());
+
+      // iterate over the command list, make sure first OF command has no time tag
+   it = Cmds.begin();
+   if(OutputFile.empty()) {
+      while(it != Cmds.end()) {
+         if(it->type==REditCmd::OF) {
+            if(OutputFile.empty()) {
+               OutputFile = it->field;
+            //if(REDebug) it->Dump(*oflog,string("Let this command set begin time"));
+               BegTime = it->time;
+               it->time = DayTime::BEGINNING_OF_TIME;
+            }
+         }
+         else { IVLast=true; break; }
+         it++;
+      }
+   }
+   if(OutputFile.empty()) {   // error
+      iret -= 2;
+   }
+   else if(!OutputDir.empty()) OutputFile = OutputDir + string("/") + OutputFile;
+
+   if(iret) return iret;
+
+      // iterate again, ensure that - commands have corresponding +
+   deque<REditCmd> newCmds;
+   it = Cmds.begin();
+   while(it != Cmds.end()) {
+      if(it->sign == -1) {
+         if(REDebug) it->Dump(*oflog,string("This one needs a +"));
+         flag=false;
+         if(it != Cmds.begin()) {
+            jt = it;
+            bool last=((--jt)==Cmds.begin());
+            while(1) {
+               if(jt->type==it->type && jt->SV==it->SV && jt->field==it->field) {
+                  if(REDebug) jt->Dump(*oflog,string("Is this the one ?"));
+                  flag = true;
+                  break;
+               }
+               if(last) break;
+               last = (--jt==Cmds.begin());
+            }
+         }
+         if(!flag) {
+            REditCmd re(*it);
+            re.sign = 1;
+            re.time = BegTime;
+            newCmds.push_back(re);
+            if(REDebug) re.Dump(*oflog,string("Add this new command:"));
+         }
+      }
+      it++;
+   }
+
+      // add new commands and sort again
+   it = newCmds.begin();
+   while(it != newCmds.end()) {
+      if(REDebug) it->Dump(*oflog,string("this is a new command:"));
+      Cmds.push_back(*it);
+      it++;
+   }
+   sort(Cmds.begin(),Cmds.end(),REditCmdLessThan());
+
+   if(REDebug)
+      for(it=Cmds.begin(); it != Cmds.end(); it++)
+         it->Dump(*oflog,string("final"));
+
+      // have to set the IVTable flag...
+   if(!IVTable) for(it=Cmds.begin(); it != Cmds.end(); it++) {
+      if(it->type==REditCmd::DS || it->type==REditCmd::DA || it->type==REditCmd::DS ||
+         it->type==REditCmd::DO || it->type==REditCmd::AO || it->type==REditCmd::DD)
+            { IVTable = true; break; }
+   }
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// leading -'s are ok
+void RinexEditor::AddCommand(string cmd) throw(Exception)
+{
+try {
+   REditCmd r(cmd,oflog);
+   if(r.valid()) Cmds.push_back(r);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// Adds valid commands to C and removes from args; leading -'s are ok
+void RinexEditor::AddCommandLine(vector<string>& args) throw(Exception)
+{
+try {
+   if(args.size()==0) return;
+   //if(REDebug) *oflog << "\nBefore stripping RE cmds, there are (" << args.size()
+   //<< ") tokens." << endl;
+
+   // ver 3.3 preprocess args to allow new-style input, e.g. --IF <file>
+   // e.g. --HD f; --HDp <program> or --HD p<program>; --DS +...; or --DS+ ...
+   static vector<string> CommandLineLabels;
+   if(CommandLineLabels.size() == 0) {
+      for(map<REditCmd::TYPE,string>::iterator jt=REditCmd::typeLabel.begin();
+         jt!=REditCmd::typeLabel.end();
+         jt++)
+            CommandLineLabels.push_back("--" + jt->second);
+      CommandLineLabels.push_back("--HDp");
+      CommandLineLabels.push_back("--HDr");
+      CommandLineLabels.push_back("--HDo");
+      CommandLineLabels.push_back("--HDa");
+      CommandLineLabels.push_back("--HDx");
+      CommandLineLabels.push_back("--HDm");
+      CommandLineLabels.push_back("--HDn");
+      CommandLineLabels.push_back("--HDc");
+      CommandLineLabels.push_back("--DA+");
+      CommandLineLabels.push_back("--DA-");
+      CommandLineLabels.push_back("--DS+");
+      CommandLineLabels.push_back("--DS-");
+      CommandLineLabels.push_back("--DD+");
+      CommandLineLabels.push_back("--DD-");
+      CommandLineLabels.push_back("--SL+");
+      CommandLineLabels.push_back("--SL-");
+      CommandLineLabels.push_back("--BD+");
+      CommandLineLabels.push_back("--BD-");
+   }
+
+   vector<string>::iterator it=args.begin();
+   while(it != args.end()) {
+      string str(*it);
+      if(str == string("--HDf")) *it = "-HDf";
+      else if(str == string("--HDdc")) *it = "-HDdc";
+      else if(str == string("--BZ")) *it = "-BZ";
+      else if(index(CommandLineLabels,str) != -1) {
+         it = args.erase(it);
+         if(it == args.end()) break;
+         str.erase(0,1);
+         str += *it;
+         *it = str;
+      }
+      it++;
+   }
+
+   // process args
+   it = args.begin();
+   while(it != args.end()) {
+      REditCmd r(*it,oflog);
+      if(r.valid()) {
+         Cmds.push_back(r); //if(REDebug) *oflog << "Erase command " << *it << endl;
+         it = args.erase(it);
+      }
+      else {
+         //if(REDebug) *oflog << "Its not an RE command: " << *it << endl;
+         it++;
+      }
+   }
+   //if(REDebug) *oflog << "\nAfter stripping RE cmds, tokens (" << args.size()
+   //<< ") are:" << endl;
+   //if(REDebug) for(unsigned int j=0; j<args.size(); j++) *oflog << args[j] << endl;
+   //if(REDebug) *oflog << "End of RE cmds, tokens" << endl;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// NB does not fill optional records, even when -HDf (EditObs will).
+int RinexEditor::EditHeader(RinexObsHeader& RHInput, RinexObsHeader& RHOutput)
+   throw(Exception)
+{
+try {
+      // save the input header
+   RHIn = RHOutput = RHInput;
+      // get the obstypes
+   ObsTypes = RHInput.obsTypeList;
+      // iterate over the list (in reverse), applying, then deleting, AO, DO
+      // and DS<SV> commands
+   deque<REditCmd>::iterator it=Cmds.begin();
+   while(it != Cmds.end()) {
+      if(it->type==REditCmd::AO || it->type==REditCmd::DO) {
+         //if(REDebug) it->Dump(*oflog,string("Apply and Erase this AO/DO command:"));
+         RinexObsHeader::RinexObsType rot=RinexObsHeader::convertObsType(it->field);
+         vector<RinexObsHeader::RinexObsType>::iterator jt;
+         jt = find(ObsTypes.begin(),ObsTypes.end(),rot);
+         if(jt != ObsTypes.end() && it->type==REditCmd::DO) {
+            ObsTypes.erase(jt);
+         }
+         if(jt == ObsTypes.end() && it->type==REditCmd::AO) {
+            ObsTypes.push_back(rot);
+         }
+         it = Cmds.erase(it);
+      }
+      else if(it->type==REditCmd::DS
+            && it->time==DayTime::BEGINNING_OF_TIME) {
+         //if(REDebug) it->Dump(*oflog,string("Apply and Erase this DS command:"));
+         if(index(DelSV,it->SV) == -1) DelSV.push_back(it->SV);
+         it = Cmds.erase(it);
+      }
+      else it++;
+   }
+
+   RHOutput.obsTypeList = ObsTypes;
+
+      // fill records in output header
+   DayTime currtime;
+   time_t timer;
+   struct tm *tblock;
+   timer = time(NULL);
+   tblock = localtime(&timer);
+   currtime.setYMDHMS(1900+tblock->tm_year,1+tblock->tm_mon,
+      tblock->tm_mday,tblock->tm_hour,tblock->tm_min,tblock->tm_sec);
+   RHOutput.date = currtime.printf("%04Y/%02m/%02d %02H:%02M:%02S");
+   { // figure out system -- anything else will be up to caller
+      bool gps=true,glo=true,tra=true,geo=true;
+      if(find(DelSV.begin(),DelSV.end(),RinexSatID(-1,SatID::systemGPS))
+                  != DelSV.end()) gps=false;
+      if(find(DelSV.begin(),DelSV.end(),RinexSatID(-1,SatID::systemGlonass))
+                  != DelSV.end()) glo=false;
+      if(find(DelSV.begin(),DelSV.end(),RinexSatID(-1,SatID::systemTransit))
+                  != DelSV.end()) tra=false;
+      if(find(DelSV.begin(),DelSV.end(),RinexSatID(-1,SatID::systemGeosync))
+                  != DelSV.end()) geo=false;
+      if(!glo && !tra && !geo) RHOutput.system.system = RinexSatID::systemGPS;
+      if(!gps && !tra && !geo) RHOutput.system.system = RinexSatID::systemGlonass;
+      if(!gps && !glo && !geo) RHOutput.system.system = RinexSatID::systemTransit;
+      if(!gps && !glo && !tra) RHOutput.system.system = RinexSatID::systemGeosync;
+   }
+   if(HDDeleteOldComments) {
+      RHOutput.commentList.clear();
+      RHOutput.valid ^= RinexObsHeader::commentValid;
+   }
+   if(!HDProgram.empty()) RHOutput.fileProgram = HDProgram;
+   if(!HDPosition.empty() && numWords(HDPosition,';') >= 3) {
+      double x = asDouble(stripFirstWord(HDPosition,';'));
+      double y = asDouble(stripFirstWord(HDPosition,';'));
+      double z = asDouble(stripFirstWord(HDPosition,';'));
+      RHOutput.antennaPosition = Triple(x,y,z);
+   }
+   if(!HDRunBy.empty()) RHOutput.fileAgency = HDRunBy;
+   if(!HDObserver.empty()) RHOutput.observer = HDObserver;
+   if(!HDAgency.empty()) RHOutput.agency = HDAgency;
+   if(!HDMarker.empty()) RHOutput.markerName = HDMarker;
+   if(!HDNumber.empty()) {
+      RHOutput.markerNumber = HDNumber;
+      RHOutput.valid |= RinexObsHeader::markerNumberValid;
+   }
+   if(HDComments.size()) RHOutput.commentList.insert(RHOutput.commentList.end(),
+      HDComments.begin(),HDComments.end());
+   RHOutput.commentList.push_back(string("Edited by GPSTK Rinex Editor ver ") +
+      RinexEditVersion+string(" on ") + RHOutput.date);
+   RHOutput.valid |= RinexObsHeader::commentValid;
+
+      // invalidate header records
+   if(IVTable && (RHOutput.valid & RinexObsHeader::numSatsValid))
+      RHOutput.valid ^= RinexObsHeader::numSatsValid;
+   if(IVTable && (RHOutput.valid & RinexObsHeader::prnObsValid))
+      RHOutput.valid ^= RinexObsHeader::prnObsValid;
+   if(IVLast && (RHOutput.valid & RinexObsHeader::lastTimeValid))
+      RHOutput.valid ^= RinexObsHeader::lastTimeValid;
+   if(IVInterval && (RHOutput.valid & RinexObsHeader::intervalValid))
+      RHOutput.valid ^= RinexObsHeader::intervalValid;
+
+   RHOut = RHOutput;  // save this header; if(FillOptionalHeader) mod RHOut in EditObs
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// will fill header (after writing) when -HDf found.
+// Return -2 error
+//        -1 time limit exceeded
+//         0 DO NOT write the output obs ROOut
+//         1 DO NOT write the output obs ROOut, but close and re-open the output file
+//         2 DO write the output obs ROOut
+//         3 DO write the output obs ROOut, but first close and re-open output file
+int RinexEditor::EditObs(RinexObsData& ROIn, RinexObsData& ROOut) throw(Exception)
+{
+try {
+      // check that stored input header is valid...but do only once!
+   //if(!RHIn.valid() || !RHOut.valid()) return -2;
+   bool NewFile=false;
+
+      // test time limits
+      // TD some comment blocks have blank epochs...
+   if(ROIn.time-BegTime < -TimeTol) return 0;
+   if(ROIn.time-EndTime >  TimeTol) return -1;
+
+      // when embedded comments found, just copy and go on
+   if(ROIn.epochFlag != 0 && ROIn.epochFlag != 1) {
+      ROOut = ROIn;
+      return 2;
+   }
+
+      // decimate the data
+   if(Decimate > 0.0) {
+         // if BegTime is unset, make it the first of the week
+      if(BegTime == DayTime::BEGINNING_OF_TIME)
+         BegTime.setGPSfullweek(ROIn.time.GPSfullweek(),0.0);
+      double dt=fabs(ROIn.time - BegTime);
+      dt -= Decimate*long(0.5+dt/Decimate);
+      if(fabs(dt) > TimeTol) return 0;
+   }
+
+      // scan command list, updating current, onetime command lists,
+      // delete-SV list, Skip, NewFile
+      // delete the command after processing it
+   double dt;
+   while(Cmds.size() > 0) {
+      dt = Cmds[0].time - ROIn.time;
+      if(dt < -TimeTol || fabs(dt) < TimeTol) {  // commands in present and past
+         if(REDebug) Cmds[0].Dump(*oflog,
+               Cmds[0].time.printf("%4Y/%2m/%2d %2H:%2M:%.4f")
+               + string(": Process (now) : "));
+         switch(Cmds[0].type) {
+            case REditCmd::DA:
+               if(Cmds[0].sign > 0) Skip=true;
+               if(Cmds[0].sign < 0) Skip=false;
+               break;
+            case REditCmd::OF:
+               OutputFile = Cmds[0].field;
+               if(!OutputDir.empty()) OutputFile = OutputDir + string("/")
+                  + OutputFile;
+               NewFile = true;
+               break;
+            case REditCmd::DS:
+               if(Cmds[0].sign > 0 && index(DelSV,Cmds[0].SV) == -1)
+                  DelSV.push_back(Cmds[0].SV);
+               if(Cmds[0].sign < 0) {
+                  if(find(DelSV.begin(),DelSV.end(),Cmds[0].SV) != DelSV.end())
+                     DelSV.erase(find(DelSV.begin(),DelSV.end(),Cmds[0].SV));
+               }
+               if(Cmds[0].sign == 0 && fabs(dt) < TimeTol)
+                  OneTimeCmds.push_back(Cmds[0]);
+               if(Cmds[0].sign != 0 && REDebug) {
+                  *oflog << "DS: DelSV is";
+                  for(int itemp=0; itemp<DelSV.size(); itemp++)
+                     *oflog << " " << DelSV[itemp];
+                  *oflog << endl;
+               }
+               break;
+            case REditCmd::DD:
+            case REditCmd::SS:
+            case REditCmd::SL:
+            case REditCmd::SD:
+            case REditCmd::BD:
+            case REditCmd::BS:
+            case REditCmd::BL:
+               if(Cmds[0].sign > 0)
+                  CurrentCmds.push_back(Cmds[0]);
+               if(Cmds[0].sign < 0) {
+                  vector<REditCmd>::iterator it;
+                  it = find(CurrentCmds.begin(), CurrentCmds.end(),Cmds[0]);
+                  if(it != CurrentCmds.end()) CurrentCmds.erase(it);
+               }
+               if(Cmds[0].sign == 0 && fabs(dt) < TimeTol)
+                  OneTimeCmds.push_back(Cmds[0]);
+               break;
+            default:
+               if(REDebug) Cmds[0].Dump(*oflog,
+                     Cmds[0].time.printf("%4Y/%2m/%2d %2H:%2M:%.4f")
+                     + string(": This command not implemented! : "));
+               break;
+         }   // end switch(type)
+
+            // delete this command
+         if(REDebug) Cmds[0].Dump(*oflog,
+               Cmds[0].time.printf("%4Y/%2m/%2d %2H:%2M:%.4f")
+               + string(": Delete (old) : "));
+         Cmds.pop_front();
+      }
+      else break;              // this command (and all others) is in future
+   }
+
+      // clear out anything old
+   ROOut.obs.clear();
+
+      // if not writing out, return here
+   if(Skip && !NewFile) return 0;
+   if(Skip && NewFile) return 1;
+
+      // copy data over to new obs structure
+   RinexObsData::RinexDatum datum;                       // place holder and zero
+   datum.data = 0.0;
+   datum.lli = datum.ssi = 0;
+   RinexObsData::RinexObsTypeMap otmap;           // place holder for ROOut.obs.second
+
+   for(int j=0; j<RHOut.obsTypeList.size(); j++)  // loop over obstypes (out) in otmap
+      otmap.insert(std::map<RinexObsHeader::RinexObsType,
+         RinexObsData::RinexDatum>::value_type(RHOut.obsTypeList[j],datum) );
+
+      // loop over prns, create otmap and then insert it with the correct sat
+   int nsvs=0;
+   RinexObsData::RinexSatMap::iterator it;
+   RinexObsData::RinexObsTypeMap::iterator jt,kt;
+   for(it=ROIn.obs.begin(); it != ROIn.obs.end(); ++it) {
+      // loop over prn=it->first, ObsTypeMap=it->second
+      if(find(DelSV.begin(),DelSV.end(),it->first) != DelSV.end()) {
+         if(REDebug) *oflog << "Deleted sat " << it->first
+            << " at " << ROIn.time << endl;
+         continue;
+      }
+      RinexSatID p(-1,it->first.system);
+      if(find(DelSV.begin(),DelSV.end(),p) != DelSV.end()) continue;
+      for(int j=0; j<RHOut.obsTypeList.size(); j++) { // loop over obstypes
+         jt = otmap.find(RHOut.obsTypeList[j]);  // jt points to ObsTypeMap output
+         kt = it->second.find(RHOut.obsTypeList[j]);  // kt points to ObsTypeMap input
+         if(kt==it->second.end())                        // not found
+            jt->second = datum;
+         else
+            jt->second = kt->second;
+      }
+      // TD should test for all zero data -> delete this SV.
+      ROOut.obs.insert(std::map<SatID,
+         RinexObsData::RinexObsTypeMap>::value_type(it->first,otmap) );
+   }  // end loop over sats
+
+   ROOut.time = ROIn.time;
+   if(!NewFile) {
+      PrevEpoch = CurrEpoch;
+      CurrEpoch = ROOut.time;
+   }
+   ROOut.clockOffset = ROIn.clockOffset;
+   ROOut.epochFlag = ROIn.epochFlag;
+
+      // apply current commands
+   vector<REditCmd>::iterator cit;                    // iterator for commands
+   for(cit=CurrentCmds.begin(); cit != CurrentCmds.end(); cit++) {
+      if(REDebug) cit->Dump(*oflog,string("Current : "));
+         // for SV=system only, start at beginning, else start with command SV
+      for(it = ROOut.obs.begin(); it != ROOut.obs.end(); it++) {
+            // skip if the sat is not a match
+         if(cit->SV.system != it->first.system ||
+            (cit->SV.id > -1 && cit->SV.id != it->first.id)) continue;
+            // find the command obs type in the data
+         jt = it->second.find(RinexObsHeader::convertObsType(cit->field));
+            // if its there, edit it
+         if(jt != it->second.end()) {
+            if(cit->type == REditCmd::DD) jt->second.data = 0.0;
+            if(cit->type == REditCmd::SS) jt->second.ssi =
+               (int(cit->bias) < 0 ? 0 : (int(cit->bias) > 9 ? 9 : int(cit->bias)));
+            if(cit->type == REditCmd::SL) jt->second.lli = int(cit->bias);
+               (int(cit->bias) < 0 ? 0 : (int(cit->bias) > 9 ? 9 : int(cit->bias)));
+            if(cit->type == REditCmd::BD) {
+               if(BiasZeroData || fabs(jt->second.data) > 0.001)
+                  jt->second.data += cit->bias;
+            }
+            if(cit->type == REditCmd::BS) {
+               jt->second.ssi += int(cit->bias);
+               if(jt->second.ssi < 0) jt->second.ssi = 0;
+               if(jt->second.ssi > 9) jt->second.ssi = 9;
+            }
+            if(cit->type == REditCmd::BL) {
+               jt->second.lli += int(cit->bias);
+               if(jt->second.lli < 0) jt->second.lli = 0;
+               if(jt->second.lli > 9) jt->second.lli = 9;
+            }
+         }
+      }  // end loop over satellites
+   }  // end loop over current commands
+
+      // apply one-time commands .. iterate in reverse so you can erase as you go
+   vector<REditCmd>::reverse_iterator irt;
+   RinexObsData::RinexSatMap::reverse_iterator roit;  // reverse iterator for obs data
+   for(irt=OneTimeCmds.rbegin(); irt != OneTimeCmds.rend(); irt++) {
+      if(REDebug) irt->Dump(*oflog,string("1-time : "));
+         // for SV=system only, start at beginning, else start with command SV
+      for(roit = ROOut.obs.rbegin(); roit != ROOut.obs.rend(); ) {
+            // skip if not a match
+         if(irt->SV.system != roit->first.system ||
+            (irt->SV.id > -1 && irt->SV.id != roit->first.id)) { roit++; continue; }
+            // DS : delete SV altogether
+         if(irt->type == REditCmd::DS) ROOut.obs.erase(roit->first);
+         else {
+               // find the command obs type in the data
+            jt=roit->second.find(RinexObsHeader::convertObsType(irt->field));
+            if(jt != roit->second.end()) {
+               if(irt->type == REditCmd::DD) jt->second.data = 0.0;
+               if(irt->type == REditCmd::SD) jt->second.data = irt->bias;
+               if(irt->type == REditCmd::SS)
+                  (int(irt->bias) < 0 ? 0 : (int(irt->bias) > 9 ? 9 : int(irt->bias)));
+               if(irt->type == REditCmd::SL)
+                  (int(irt->bias) < 0 ? 0 : (int(irt->bias) > 9 ? 9 : int(irt->bias)));
+               if(irt->type == REditCmd::BD) {
+                  if(BiasZeroData || fabs(jt->second.data) > 0.001)
+                     jt->second.data += irt->bias;
+               }
+               if(irt->type == REditCmd::BS) {
+                  jt->second.ssi += int(irt->bias);
+                  if(jt->second.ssi < 0) jt->second.ssi = 0;
+                  if(jt->second.ssi > 9) jt->second.ssi = 9;
+               }
+               if(irt->type == REditCmd::BL) {
+                  jt->second.lli += int(irt->bias);
+                  if(jt->second.lli < 0) jt->second.lli = 0;
+                  if(jt->second.lli > 9) jt->second.lli = 9;
+               }
+            }
+            roit++;
+         }
+      }  // end loop over satellites
+
+         // delete this command
+      OneTimeCmds.pop_back();
+   }  // end loop over one-time commands
+
+   ROOut.numSvs = ROOut.obs.size();
+
+      // update estimate of dt
+   if(FillOptionalHeader) {
+      if(PrevEpoch.year() != 1) {
+         dt = CurrEpoch-PrevEpoch;
+         for(int i=0; i<9; i++) {
+            if(ndt[i] <= 0) { bestdt[i]=dt; ndt[i]=1; break; }
+            if(fabs(dt-bestdt[i]) < 0.0001) { ndt[i]++; break; }
+            if(i == 8) {
+               int k = 0;
+               int nleast=ndt[k];
+               for(int j=1; j<9; j++) if(ndt[j] <= nleast) {
+                  k=j; nleast=ndt[j];
+               }
+               ndt[k]=1; bestdt[k]=dt;
+            }
+         }
+      }
+   }
+
+   if(NewFile) return 3;
+   return 2;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// use remove(newname) to delete it
+string GetTempFileName(void) throw(Exception)
+{
+try {
+#ifdef _MSC_VER
+   char newname[L_tmpnam];
+   if(!tmpnam(newname)) {
+#else
+   char newname[]="RETemp.XXXXXX";
+   if(mkstemp(newname)==-1) {
+#endif
+      return string("");
+   }
+   return string(newname);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// assumes TempFile has been written with RHOut, and info is in config
+// Return 0 or -1 if could not open/delete files
+int RinexEditor::FillHeaderAndReplaceFile(string& TempFile, string& TrueOutputFile)
+   throw(Exception)
+{
+try {
+   int i,j;
+      // compute interval
+   for(i=1,j=0; i<9; i++) if(ndt[i]>ndt[j]) j=i;
+   double dt = bestdt[j];
+      // modify the header
+   RHOut.version = 2.1; RHOut.valid |= RinexObsHeader::versionValid;
+   RHOut.interval = dt; RHOut.valid |= RinexObsHeader::intervalValid;
+   RHOut.lastObs = CurrEpoch; RHOut.valid |= RinexObsHeader::lastTimeValid;
+      // now the table
+   RHOut.numSVs = table.size(); RHOut.valid |= RinexObsHeader::numSatsValid;
+   RHOut.numObsForSat.clear();
+   vector<TableData>::iterator tit;
+   for(tit=table.begin(); tit!=table.end(); ++tit) {
+      RHOut.numObsForSat.insert(map<SatID,
+            vector<int> >::value_type(tit->prn,tit->nobs));
+   }
+   RHOut.valid |= RinexObsHeader::prnObsValid;
+
+      // callback
+   i = BeforeWritingFilledHeader(RHOut);
+   if(i) return -2;
+
+      // here you need to validate the RHOut header
+
+      // now re-open the file and replace the header
+   RinexObsHeader rhjunk;
+   RinexObsStream ROutStr(TrueOutputFile.c_str(), ios::out);
+   RinexObsStream InAgain(TempFile.c_str());
+   InAgain.exceptions(ios::failbit);
+
+   InAgain >> rhjunk;
+   ROutStr << RHOut;
+
+   RinexObsData robs;
+   while(InAgain >> robs) {
+      if(robs.time < BegTime) continue;
+      if(robs.time > EndTime) break;
+      ROutStr << robs;
+   }
+   InAgain.close();
+   ROutStr.close();
+
+      // delete the temporary
+   if(remove(TempFile.c_str()) != 0) {
+      *oflog << "Error: Could not remove existing temp file: " << TempFile << endl;
+      return -1;
+   }
+   else if(REVerbose) *oflog << "Removed temporary file " << TempFile << endl;
+   
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
+// Return -1 failed to open file
+//        -2 failed to read input file correctly (includes file not an obs file)
+//        -4 failed to fill header and replace original file
+//        -5 could not create temporary file
+//        -6 callback to BeforeEditHeader returned error
+//        -7 callback to AfterEditHeader returned error
+//        -8 callback to BeforeEditObs returned error
+//        -9 callback to BeforeWritingHeader returned error
+//       -10 callback to BeforeWritingObs returned error
+// will replace header after filling using temp file
+int RinexEditor::EditFile(void) throw(Exception)
+{
+try {
+   int iret,Noutput;
+   RinexObsHeader rhin,rhout;
+   RinexObsData roin,roout;
+   string TrueOutputFile,TempFile;
+   RinexObsStream ROFin;
+   RinexObsStream ROFout;
+
+   if(REVerbose) *oflog << "EditFile: Reading " << Inputfiles.size()
+      << " input files, and Writing " << OutputFile << endl;
+
+      // --------------------------------------------------------------
+      // loop over input files
+   for(int nfile=0; nfile < Inputfiles.size(); nfile++) {
+   InputFile = Inputfiles[nfile];
+
+      // --------------------------------------------------------------
+      // open input file
+   ROFin.open(InputFile.c_str(), ios::in);
+   if(!ROFin) {
+      if(REVerbose) *oflog << "RinexEditor::EditFile could not open input file "
+         << InputFile << endl;
+      cerr << "RinexEditor::EditFile could not open input file " << InputFile << endl;
+      if(REVerbose) *oflog << "RinexEditor::EditFile could not open input file "
+         << InputFile << endl;
+      return -1;
+   }
+   ROFin.exceptions(ios::failbit);
+   if(REDebug) *oflog << "Opened input file " << InputFile << endl;
+
+      // --------------------------------------------------------------
+      // read header
+   try {
+      ROFin >> rhin;
+   }
+   catch(gpstk::FFStreamError& e) {
+      cerr << "Caught an FFStreamError while reading header:\n" << e.getText(0)
+         << endl;
+      if(REVerbose) *oflog << "Caught an FFStreamError while reading header:\n"
+         << e.getText(0) << endl;
+      return -2;
+   }
+   catch(gpstk::Exception& e) {
+      cerr << "Caught an exception while reading header:\n" << e.getText(0) << endl;
+      if(REVerbose) *oflog << "Caught an exception while reading header:\n"
+         << e.getText(0) << endl;
+      return -2;
+   }
+   if(REDebug) *oflog << "Read input header" << endl;
+
+      // dump header
+   if(REVerbose) {
+      *oflog << "Input header:\n";
+      rhin.dump(*oflog);
+   }
+
+      // --------------------------------------------------------------
+      // Edit header and open output file - do this only once
+   if(nfile == 0) {
+         // callback before editing input header
+      iret = BeforeEditHeader(rhin);
+      if(iret) return -6;
+
+         // edit header
+      EditHeader(rhin,rhout);
+      if(REVerbose) *oflog << "Edit header done" << endl;
+
+         // callback after calling EditHeader (pass output header)
+      iret = AfterEditHeader(rhout);
+      if(iret) return -7;
+
+         // -----------------------------------------------------------
+         // if header is to be filled, write to a temporary file
+      TrueOutputFile = OutputFile;
+      if(FillOptionalHeader) {
+         OutputFile = GetTempFileName();
+         if(OutputFile.empty()) {
+            cerr << "Could not create temporary file name - abort\n";
+            if(REVerbose) *oflog << "Could not create temporary file name - abort\n";
+            return -5;
+         }
+         // some OSs create the file when you get the name...
+         remove(OutputFile.c_str());
+         if(!OutputDir.empty()) OutputFile = OutputDir + string("/") + OutputFile;
+         TempFile = OutputFile;
+      }
+
+         // -----------------------------------------------------------
+         // open output file
+      ROFout.open(OutputFile.c_str(), ios::out);
+      if(!ROFout) {
+         cerr << "RinexEditor::EditFile could not open output file "
+            << OutputFile << endl;
+         if(REVerbose) *oflog << "RinexEditor::EditFile could not open output file "
+            << OutputFile << endl;
+         return -1;
+      }
+
+      ROFout.exceptions(ios::failbit);
+      Noutput = 0;
+
+   } // end if this is the first input file
+
+      // --------------------------------------------------------------
+      // loop over epochs, reading input and writing to output
+   while (1) {
+
+         // read next observation epoch
+      try {
+         ROFin >> roin;
+      }
+      catch(gpstk::FFStreamError& e) {
+         cerr << "RinexEditor::EditFile caught an FFStreamError while reading obs:\n"
+            << e << endl;
+         if(REVerbose) *oflog
+            << "RinexEditor::EditFile caught an FFStreamError while reading obs:\n"
+               << e << endl;
+         return -2;
+      }
+      catch(gpstk::Exception& e) {
+         cerr << "RinexEditor::EditFile caught an exception while reading obs:\n"
+            << e << endl;
+         if(REVerbose) *oflog
+            << "RinexEditor::EditFile caught an exception while reading obs:\n"
+               << e << endl;
+         return -2;
+      }
+
+         // was read successful?
+      if(!ROFin) {                     // no
+         if(REVerbose) *oflog << "Reached EOF on " << InputFile << endl;
+         if(nfile == Inputfiles.size()-1) iret = -1;
+         else break;
+      }
+      else {                           // yes, edit the obs data
+         if(REDebug) {
+            *oflog << "Epoch: " << roin.time << ", Flag " << roin.epochFlag
+               << ", clk " << roin.clockOffset << endl;
+            roin.dump(*oflog);
+         }
+
+         // callback after reading input obs
+         // and before calling EditObs (pass input obs)
+         iret = BeforeEditObs(roin);
+         if(iret) { iret=-8; break; }
+
+         iret = EditObs(roin,roout);
+         // Return -2 error
+         //        -1 time limit reached
+         //         0 DO NOT write the output obs ROOut
+         //         1 DO NOT write the output obs ROOut,
+         //            but close and re-open the output file
+         //         2 DO write the output obs ROOut
+         //         3 DO write the output obs ROOut,
+         //            but first close and re-open the output file
+         if(REDebug) {
+            *oflog << "EditObs returned " << iret << endl;
+            roout.dump(*oflog);
+         }
+      }
+
+      if(iret == -2) break;                           // error => abort
+
+      if(iret == -1 || iret == 1 || iret == 3) {      // new output file
+            // close this output file
+         ROFout.close();
+            // fill the optional header records
+         if(FillOptionalHeader) {
+            if(Noutput > 0) {
+               if(FillHeaderAndReplaceFile(TempFile,TrueOutputFile) != 0) {
+                  cerr << "Failed to fill header and replace file - abort\n";
+                  if(REVerbose)
+                     *oflog << "Failed to fill header and replace file - abort\n";
+                  return -4;
+               }
+               else if(REVerbose) *oflog << "Added header to " << TempFile
+                  << " and put in " << TrueOutputFile << endl;
+            }
+
+            if(iret != -1) {        // not EOF => going on to another file
+               TrueOutputFile = OutputFile;
+               OutputFile = GetTempFileName();
+               if(OutputFile.empty()) {
+                  cerr << "Could not create temporary file name - abort\n";
+                  if(REVerbose)
+                     *oflog << "Could not create temporary file name - abort\n";
+                  return -5;
+               }
+               // some OSs create the file when you get the name...
+               remove(OutputFile.c_str());
+               if(!OutputDir.empty())
+                  OutputFile = OutputDir + string("/") + OutputFile;
+               TempFile = OutputFile;
+               if(REVerbose) *oflog << "New temp file is " << TempFile
+                  << ", and true output file is " << TrueOutputFile << endl;
+            }
+
+         }  // end if FillOptionalHeader
+         else {
+            TrueOutputFile = OutputFile;
+         }
+
+         if(iret == -1) {                  // quit
+            if(REVerbose)
+               *oflog << "Finished processing obs file " << InputFile << endl;
+            iret = 0;
+            break;
+         }
+
+            // open the new output file
+         ROFout.open(OutputFile.c_str(), ios::out);
+         Noutput = 0;
+         if(REVerbose) *oflog << "New output file " << TrueOutputFile
+            << " (really " << OutputFile << ") at time " << roin.time << endl;
+
+      }  // end if new output file
+
+         // write to output
+      if(iret > 1) {                // not EOF nor error
+         if(Noutput == 0) {
+            rhout.firstObs = roout.time;
+            // callback before writing out header (pass output header)
+            iret =  BeforeWritingHeader(rhout);
+            if(iret) return -9;
+
+            ROFout << rhout;
+            if(REVerbose) {
+               *oflog << "Dump output header (iret is " << iret << "):\n";
+               rhout.dump(*oflog);
+            }
+               // prepare for next file
+            RHOut = rhout;
+            table.clear();
+            for(int i=0; i<9; i++) ndt[i]=-1;
+         }
+   
+         // callback just before writing output obs (pass reference to output obs)
+         // return value of BeforeWritingObs determines what is written:
+         // if return <0 abort
+         //            0 write nothing
+         //            1 write the obs data structure (note that the caller may set
+         //               roout.epochFlag to determine what is output : 0,1 are data,
+         //               while 2,3,4 or 5, is for in-line header auxHeader only)
+         //           >1 write BOTH header data (in auxHeader, setting
+         //               epochFlag=return) AND obs data
+         roout.auxHeader.clear();
+         iret = BeforeWritingObs(roout);
+         if(iret < 0) return -10;
+         if(iret > 1) {             // write auxiliary header info first
+            int flag=roout.epochFlag, nsvs=roout.numSvs;
+            roout.epochFlag = iret;
+            roout.numSvs = roout.auxHeader.NumberHeaderRecordsToBeWritten();
+               // write out the header records
+            ROFout << roout;
+            Noutput++;
+               // prepare to write obs
+            roout.epochFlag = flag;
+            roout.numSvs = nsvs;
+         }
+
+            // add count of valid obs to table for header
+            // -- have to do it here b/c BeforeWritingObs has just filled it
+         if(FillOptionalHeader) {
+            int k,n=RHOut.obsTypeList.size();
+            RinexObsData::RinexSatMap::const_iterator pit;
+            RinexObsData::RinexObsTypeMap::const_iterator pjt;
+            for(pit=roout.obs.begin(); pit != roout.obs.end(); ++pit) {
+               vector<TableData>::iterator ptab;
+               ptab = find(table.begin(),table.end(),TableData(pit->first,n));
+               if(ptab == table.end()) {
+                  table.push_back(TableData(pit->first,n));
+                  ptab = find(table.begin(),table.end(),TableData(pit->first,n));
+               }
+               for(pjt=pit->second.begin(); pjt!=pit->second.end(); pjt++) {
+                  for(k=0; k<n; k++) if(RHOut.obsTypeList[k] == pjt->first) break;
+                  if(pjt->second.data != 0.0) ptab->nobs[k]++;
+               }
+            }
+         }
+
+         // now write out the obs
+         if(REDebug) {
+            *oflog << "Write this obs to output:\n";
+            roout.dump(*oflog);
+         }
+         ROFout << roout;
+         Noutput++;
+      }
+
+   }   // end while loop over epochs
+
+   if(REDebug) *oflog << "Close input file" << endl;
+   ROFin.clear();
+   ROFin.close();
+
+   }   // end loop over input file names
+
+   return iret;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+vector<string> RinexEditor::CommandList(void) throw(Exception)
+{
+try {
+   string str,comma(",");
+   vector<string> strs;
+   deque<REditCmd>::iterator jt;
+
+   for(jt=Cmds.begin(); jt != Cmds.end(); jt++) {
+      str = REditCmd::typeLabel[jt->type]
+            + comma + (jt->sign < 0 ? string("-1") :
+                      (jt->sign > 0 ? string("1") : string("0")))
+            + comma + jt->SV.toString()
+            + comma + asString(jt->inOT)
+            + comma + jt->field
+            + comma + asString(jt->bias,3)
+            + comma + jt->time.printf("%4Y/%02m/%02d,%02H:%02M:%.4f")
+            ;
+      strs.push_back(str);
+   }
+
+   return strs;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+void DisplayRinexEditUsage(ostream& os) throw()
+{
+   os <<
+" Rinex Editor commands:\n"
+" ===================================================================================\n"
+" Commands consist of an identifier and a comma-delimited data field; they may be\n"
+" separated by space(s) '--id <data>' (two minuses) or not '-id<data>' (one minus).\n"
+" Examples are '--IF myFile' or '-IFmyFile'; '--HDc msg' or '--HD cmsg' or '-HDcmsg';\n"
+" --BZ or -BZ; '--DD +<SV,OT,t>' or '--DD+ <SV,OT,t>' or '-DD+<SV,OT,t>'.\n"
+" The data field contains no whitespace and sub-fields are comma-delimited.\n"
+" <SV> is a RINEX 'system & id' identifier, e.g. G27 (= GPS PRN 27);\n"
+"   satellite system alone denotes 'all satellites this system', e.g. 'R' (GLONASS).\n"
+" <OT> is a RINEX observation type, e.g. L1 or P2, and is case sensitive.\n"
+" <time> is either <GPSweek,GPSsecOfWeek> or <year,mon,day,hour,min,second>.\n"
+"\n"
+" File I/O:\n"
+" ---------\n"
+" -IF<file>       Input RINEX observation file name [may be repeated] (required)\n"
+" -ID<dir>        Directory in which to find input file\n"
+" -OF<file>       Output RINEX file name (required, or -OF<file>,<time>)\n"
+" -OF<f>,<time>   At RINEX epoch <time>, close output file and open another named <f>\n"
+" -OD<dir>        Directory in which to put output file(s)\n"
+"\n"
+" Output RINEX header:\n"
+" --------------------\n"
+" -HDf            If present, fill optional records in the output RINEX header\n"
+//"                   (NB EditObs() and EditFile() will do this, but NOT EditHeader().)\n"
+" -HDp<program>   Set output RINEX header 'program' field\n"
+" -HDr<run_by>    Set output RINEX header 'run by' field\n"
+" -HDo<observer>  Set output RINEX header 'observer' field\n"
+" -HDa<agency>    Set output RINEX header 'agency' field\n"
+" -HDx<x,y,z>     Set output RINEX header 'position' field to ECEF position (x,y,z)\n"
+" -HDm<marker>    Set output RINEX header 'marker' field\n"
+" -HDn<number>    Set output RINEX header 'number' field\n"
+" -HDc<comment>   Add comment to output RINEX header (more than one allowed).\n"
+" -HDdc           Delete all comments in output RINEX header\n"
+"           (NB -HDdc cannot delete comments created by *subsequent* -HDc commands)\n"
+"\n"
+" Output RINEX observation types (also see 'Specific edit commands' below):\n"
+" -------------------------------------------------------------------------\n"
+" -AO<OT>         Add observation type OT to header and observation data\n"
+" -DO<OT>         Delete observation type OT entirely (including in header)\n"
+"\n"
+" Time-related edit commands:\n"
+" ---------------------------\n"
+" -TB<time>       Begin time: reject data before this time (also used for decimation)\n"
+" -TE<time>       End   time: reject data after this time\n"
+" -TT<dt>         Tolerance in comparing times, in seconds (default=1ms)\n"
+" -TN<dt>         Decimate data to epochs = Begin + integer*dt (within tolerance)\n"
+"\n"
+" Specific edit commands:\n"
+" -----------------------\n"
+" (Generally each '+' command (e.g DA+<time>) has a corresponding '-' command,\n"
+"     and vice-versa; if not, end-of-file or beginning-of-file is assumed.\n"
+"  Note that one-time commands are applied AFTER other commands of the same type.)\n"
+"\n"
+"     Delete commands:\n"
+" -DA+<time>      Delete all data beginning at this time\n"
+" -DA-<time>      Stop deleting data at this time\n"
+" -DO<OT>         Delete observation type OT entirely (including in header)\n"
+" -DS<SV>         Delete all data for satellite SV entirely (SV may be system only)\n"
+" -DS<SV>,<time>  Delete all data for satellite SV at this single time only\n"
+" -DS+<SV>,<time> Delete all data for satellite SV beginning at this time\n"
+" -DS-<SV>,<time> Stop deleting all data for satellite SV at this time\n"
+" -DD<SV,OT,t>    Delete a single RINEX datum(SV,OT,t) at time <t>\n"
+" -DD+<SV,OT,t>   Delete all (SV,OT) data, beginning at time <t>\n"
+" -DD-<SV,OT,t>   Stop deleting all (SV,OT) data at time <t>\n"
+"     (NB deleting data for one OT means setting it to zero - as RINEX requires)\n"
+"\n"
+"     Set commands:\n"
+" -SD<SV,OT,t,d>  Set data(SV,OT,t) to <d> at time <t>\n"
+" -SS<SV,OT,t,s>  Set ssi(SV,OT,t) to <s> at time <t>\n"
+" -SL+<SV,OT,t,l> Set all lli(SV,OT,t) to <l> at time <t>\n"
+" -SL-<SV,OT,t,l> Stop setting lli(SV,OT,t) to <l> at time <t> (',<l>' is optional)\n"
+" -SL<SV,OT,t,l>  Set lli(SV,OT,t) to <l> at the single time <t> only\n"
+"\n"
+"     Bias commands:\n"
+"   (NB. BD commands apply only when data is non-zero, unless -BZ appears)\n"
+" -BZ             Apply BD commands even when data is zero (i.e. 'missing')\n"
+" -BD<SV,OT,t,d>  Add the value of <d> to data(SV,OT,t) at time <t>\n"
+" -BD+<SV,OT,t,d> Add value <d> to data(SV,OT) beginning at time <t>\n"
+" -BD-<SV,OT,t,d> Stop adding <d> to data(SV,OT) at time <t> (',<d>' optional)\n"
+" -BS<SV,OT,t,s>  Add the value of <s> to ssi(SV,OT,t) at time <t>\n"
+" -BL<SV,OT,t,l>  Add the value of <l> to lli(SV,OT,t) at time <t>\n"
+"\n End of Rinex Editor commands.\n"
+" ===================================================================================\n"
+   ;
+   os << endl;
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
diff --git a/dev/apps/Rinextools/RinexEditor.hpp b/dev/apps/Rinextools/RinexEditor.hpp
new file mode 100644
index 0000000..b92a96d
--- /dev/null
+++ b/dev/apps/Rinextools/RinexEditor.hpp
@@ -0,0 +1,317 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file RinexEditor.hpp
+ * Edit Rinex observation files.
+ */
+
+#ifndef RINEX_EDITING_COMMANDS_INCLUDE
+#define RINEX_EDITING_COMMANDS_INCLUDE
+
+//------------------------------------------------------------------------------------
+#include "Exception.hpp"
+#include "RinexObsBase.hpp"
+#include "RinexObsData.hpp"
+#include "RinexObsHeader.hpp"
+#include "DayTime.hpp"
+#include "RinexSatID.hpp"
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <deque>
+
+//------------------------------------------------------------------------------------
+namespace gpstk {
+
+   /** @addtogroup rinexutils */
+   //@{
+
+   // forward declarations
+class RinexEditor;
+class REditCmdLessThan;
+class TableData;
+class TablePRNLessThan;
+
+//------------------------------------------------------------------------------------
+/// Class REditCmd encapsulates commands passed to the Rinex Editor
+/// (class RinexEditor).
+class REditCmd {
+   friend class RinexEditor;
+   friend class REditCmdLessThan;
+   friend bool operator==(const REditCmd& x, const REditCmd& y);
+private:
+   /// The command types: input file, output file, etc.
+   enum TYPE {
+         //(order matters)
+      INVALID=0,IF,OF,ID,OD,HD,TN,TB,TE,TT,AO,DA,DO,DS,DD,SD,SS,SL,BD,BS,BL,BZ
+   };
+   /// The type of this command
+   TYPE type;
+   /// Satellite identifier for data to which this command will apply.
+   RinexSatID SV;
+   /// Time associated with this command.
+   DayTime time;
+   /// Sign associated with this command.
+   int sign;
+   /// String associated with this command.
+   std::string field;
+   /// integer associated with this command.
+   int inOT;
+   /// bias associated with this command.
+   double bias;
+   /// map giving label as a string for each command type
+   static std::map<TYPE, std::string> typeLabel;
+
+public:
+   /// Default constructor (type is set INVALID).
+   REditCmd(void) { type=INVALID; }
+   /// Destructor
+   ~REditCmd(void);
+   /// Constructor from a string which contains the editing command.
+   REditCmd(std::string s, std::ostream *oflog=&std::cout) throw(Exception);
+   /// Is this a valid command?
+   inline bool valid(void) throw() { return (type!=INVALID); }
+   /// Print the command on an ostream, with an optional message.
+   void Dump(std::ostream& os, std::string msg) throw(Exception);
+
+   class Initialize {
+      public: Initialize();
+   };
+   static Initialize REditCmdInitializer;
+};
+
+   /// operator==(REditCmd), defined so algorithm find() can be used to find
+   /// the "-" companion to "+" commands.
+inline bool operator==(const REditCmd& x, const REditCmd& y)
+   { return (x.type == y.type &&
+             x.SV == y.SV &&
+             //x.time == y.time &&
+             x.sign == -y.sign &&         // note the -
+             x.field == y.field &&
+             //x.bias == y.bias &&
+             x.inOT == y.inOT); }
+
+/// class REditCmdLessThan, for use with algorithm sort().
+class REditCmdLessThan {      
+public:
+   /// return true if c1 is less than c2.
+   bool operator()(const REditCmd& c1, const REditCmd& c2)
+      { return c1.time < c2.time; }
+};
+
+//------------------------------------------------------------------------------------
+/// class RinexEditor encapsulates the process of editing a Rinex observation dataset
+/// (header and observations), including the editing commands that have been input
+/// from the user.
+class RinexEditor {
+private:
+   /// all input files
+   std::vector<std::string> Inputfiles;
+   /// input and output file names.
+   std::string InputFile,OutputFile;
+   /// directories for the input and output files.
+   std::string InputDir,OutputDir;
+   /// start and stop times, for windowing the data.
+   DayTime BegTime,EndTime;
+   /// tolerance to be used in comparing time tags.
+   double TimeTol;
+   /// time step interval if the data is to be decimated.
+   double Decimate;
+   /// flag to indicate how to handle data with value 'zero'.
+   bool BiasZeroData;
+   /// flag used when data is omitted from output
+   bool Skip;
+   /// vector of Rinex observation types in header.
+   std::vector<RinexObsHeader::RinexObsType> ObsTypes;
+   /// vector of satellites to be deleted.
+   std::vector<RinexSatID> DelSV;
+   /// input and output Rinex headers.
+   RinexObsHeader RHIn,RHOut;
+   /// flag for editing the Rinex header.
+   bool FillOptionalHeader,HDDeleteOldComments;
+   /// flags indicating validity of optional records in the input header.
+   bool IVLast,IVInterval,IVTable;
+   /// content of the PROGRAM header record.
+   std::string HDProgram;
+   /// content of the RUN BY header record.
+   std::string HDRunBy;
+   /// content of the OBSERVER header record.
+   std::string HDObserver;
+   /// content of the AGENCY header record.
+   std::string HDAgency;
+   /// content of the POSITION header record, in the form 'x,y,z'
+   std::string HDPosition;
+   /// content of the MARKER header record.
+   std::string HDMarker;
+   /// content of the NUMBER header record.
+   std::string HDNumber;
+   /// comments in the Rinex header.
+   std::vector<std::string> HDComments;
+   /// times for computing start and stop times, to go in the Rinex header.
+   DayTime CurrEpoch,PrevEpoch;
+   /// an integer array for computing the time interval, to go in the Rinex header.
+   int ndt[9];
+   /// a double array for computing the time interval, to go in the Rinex header.
+   double bestdt[9];
+   /// storage for the PRN/OBS table, to go in the Rinex header.
+   std::vector<TableData> table;
+
+      /// Rinex Editing commands that will have to be saved.
+   std::deque<REditCmd> Cmds;
+      /// Rinex Editing commands for use during processing.
+   std::vector<REditCmd> OneTimeCmds;
+      /// Rinex Editing commands for use in the current timestep.
+   std::vector<REditCmd> CurrentCmds;
+
+public:
+      /// flag to control debugging and analysis output.
+   bool REVerbose,REDebug;
+      /// the output log file stream.
+   std::ostream *oflog;
+
+      /// Default constructor. NB. Do not instantiate a RinexEditor outside of
+      /// main(), as static initialization order on some OSs (Solaris) mean that
+      /// DayTime::END_OF_TIME may not be defined at that point.
+   RinexEditor(void);
+      /// Destructor
+   virtual ~RinexEditor(void);
+      /// return string giving the editor version
+   std::string getRinexEditVersion(void);
+      /// pretty print configuration
+   std::ostream& operator<<(const std::ostream& os);
+      /// Add the Rinex Editing command structures to the user's command line.
+   void AddCommandLine(std::vector<std::string>& args) throw(Exception);
+      /// Add a Rinex Editing command to this Editor.
+   void AddCommand(std::string cmd) throw(Exception);
+      /// Parse the command line for Rinex Editing commands.
+   int ParseCommands(void) throw(Exception);
+      /// Edit the input header to produce the output header.
+   int EditHeader(RinexObsHeader& RHIn, RinexObsHeader& RHOut) throw(Exception);
+      /// Edit the input observation to produce the output observation.
+   int EditObs(RinexObsData& ROIn, RinexObsData& ROOut) throw(Exception);
+      /// Edit a Rinex observation file, using the stored Rinex Editing commands.
+   int EditFile(void) throw(Exception);
+      /// used to add optional records to the header.
+   int FillHeaderAndReplaceFile(std::string& TempFile,std::string& TrueOutputFile)
+      throw(Exception);
+
+   /// This function is called after reading input header and before
+   /// calling EditHeader (pass input header).
+   virtual int BeforeEditHeader(const RinexObsHeader& rhin) throw(Exception)
+      { return 0; }
+
+   /// This function is called after calling EditHeader (pass it the output header).
+   virtual int AfterEditHeader(const RinexObsHeader& rhout) throw(Exception)
+      { return 0; }
+
+   /// This function is called after reading the input observation and before
+   /// calling EditObs (pass it the input observation).
+   virtual int BeforeEditObs(const RinexObsData& roin) throw(Exception)
+      { return 0; }
+
+   /// This function is called before writing out the header (pass it
+   /// the output header).
+   virtual int BeforeWritingHeader(RinexObsHeader& rhout) throw(Exception)
+      { return 0; }
+
+   /// This function is called before writing out the header that has been
+   /// filled with optional records
+   virtual int BeforeWritingFilledHeader(RinexObsHeader& rhout) throw(Exception)
+      { return 0; }
+
+   /** Callback, just before writing output obs (pass output obs)
+   * Return value of BeforeWritingObs determines what is written:
+   * if return <0 write nothing and abort
+   *            0 write nothing
+   *            1 write the obs data roout ONLY (note that the caller may set
+   *                roout.epochFlag to determine what is output : 0,1 are data,
+   *                while 2,3,4 or 5, are for in-line header (roout.auxHeader)
+   *                only -- see the Rinex or RinexObsData documentation)
+   *           >1 write BOTH header data (in roout.auxHeader), first setting
+   *                roout.epochFlag = the return value), AND the obs data
+   *                in roout, using the original value of roout.epochFlag
+   */
+   virtual int BeforeWritingObs(RinexObsData& roout) throw(Exception)
+      { return 0; }
+
+   /// member access of the decimation time interval.
+   double Decimation(void) { return Decimate; }
+   /// member access of the time comparison tolerance.
+   double Tolerance(void) { return TimeTol; }
+   /// member access of the start time.
+   DayTime BeginTimeLimit(void) { return BegTime; }
+   /// member access of the end time.
+   DayTime EndTimeLimit(void) { return EndTime; }
+   /// member access of the input file name.
+   std::string InputFileName(void) { return InputFile; }
+   /// member access of the output file name.
+   std::string OutputFileName(void) { return OutputFile; }
+   /// member access input directory.
+   std::string InputDirectory(void) { return InputDir; }
+   /// member access output directory.
+   std::string OutputDirectory(void) { return OutputDir; }
+   /// member access command list, return vector of strings, each with
+   /// comma-delimited fields: type,sign,sat,inOT,field,bias,time
+   /// e.g. AO,0,G33,-1,SZ,-99.990,-4713/01/01,00:00:0.0000
+   ///      IF,0,G33,-1,usno2930.06o.df,-99.990,-4713/01/01,00:00:0.0000
+   ///      HD,0,G33,80,ResCor v.3.7 ,-99.990,-4713/01/01,00:00:0.0000
+   std::vector<std::string> CommandList(void) throw(Exception);
+
+}; // end class RinexEditor
+
+//------------------------------------------------------------------------------------
+/// class TableData is used to store the information in the PRN/Obs table in the
+/// Rinex observation header.
+class TableData {                      // class used to store PRN/Obs table
+public:
+   /// satellite identifier.
+   RinexSatID prn;
+   /// vector of the number of observations, parallel to the obs types in the header.
+   std::vector<int> nobs;
+   /// constructor, given a satellite id and the number of observation types.
+   TableData(const RinexSatID& p, const int& n) { prn=p; nobs=std::vector<int>(n); };
+   /// operator==(), needed for find() (compares prn only).
+   inline bool operator==(const TableData& d) {return d.prn == prn;}
+};
+
+/// class define for use with sort(TableData).
+class TablePRNLessThan  {
+public:
+   /// return true is d1 is less than d2 (compares prn only).
+   bool operator()(const TableData& d1, const TableData& d2)
+      { return d1.prn < d2.prn; }
+};
+
+   //@}
+
+}  // end namespace gpstk
+
+//------------------------------------------------------------------------------------
+/// Pretty print the Rinex Editing command syntax, for use by the calling program.
+void DisplayRinexEditUsage(std::ostream& os) throw();
+
+//------------------------------------------------------------------------------------
+#endif   // nothing below this
diff --git a/trunk/apps/bindings/DayTime.i b/dev/apps/bindings/DayTime.i
similarity index 100%
rename from trunk/apps/bindings/DayTime.i
rename to dev/apps/bindings/DayTime.i
diff --git a/trunk/apps/bindings/Exception.i b/dev/apps/bindings/Exception.i
similarity index 100%
rename from trunk/apps/bindings/Exception.i
rename to dev/apps/bindings/Exception.i
diff --git a/trunk/apps/bindings/FFTextStream.i b/dev/apps/bindings/FFTextStream.i
similarity index 100%
rename from trunk/apps/bindings/FFTextStream.i
rename to dev/apps/bindings/FFTextStream.i
diff --git a/trunk/apps/bindings/GPSZcount.i b/dev/apps/bindings/GPSZcount.i
similarity index 100%
rename from trunk/apps/bindings/GPSZcount.i
rename to dev/apps/bindings/GPSZcount.i
diff --git a/trunk/apps/bindings/README b/dev/apps/bindings/README
similarity index 100%
rename from trunk/apps/bindings/README
rename to dev/apps/bindings/README
diff --git a/trunk/apps/bindings/RinexObsStream.i b/dev/apps/bindings/RinexObsStream.i
similarity index 100%
rename from trunk/apps/bindings/RinexObsStream.i
rename to dev/apps/bindings/RinexObsStream.i
diff --git a/trunk/apps/bindings/common.i b/dev/apps/bindings/common.i
similarity index 100%
rename from trunk/apps/bindings/common.i
rename to dev/apps/bindings/common.i
diff --git a/trunk/apps/bindings/gpstk.i b/dev/apps/bindings/gpstk.i
similarity index 100%
rename from trunk/apps/bindings/gpstk.i
rename to dev/apps/bindings/gpstk.i
diff --git a/dev/apps/bindings/java/Makefile b/dev/apps/bindings/java/Makefile
new file mode 100644
index 0000000..8930ca2
--- /dev/null
+++ b/dev/apps/bindings/java/Makefile
@@ -0,0 +1,51 @@
+#
+# $Id$
+#
+# This simple makefile builds a Java interface to the GPSTk from code
+# generated the SWIG utility (http://www.swig.org/). 
+#
+#
+# What you do need:
+#  - A C/C++ compiler.
+#  - Development kit for Java
+#  - The sed utility
+#  - the swig utility
+# 
+# Not all GPSTk functionality is present in these bindings.
+# Two reasons: First, this is a work in progress and should be considered
+# incomplete. Second, not all of the GPSTk's functionality _can_ be mapped
+# into other languages such as Java.
+#
+# Examples of usage of this interface are in the examples subdirectory.
+#
+# Reminder for later: $@ is target, $< is rhs, $^ is rhs
+
+all :	libgpstk.so 
+
+gpstk_wrap.cxx: ../gpstk.i ../common.i ../Exception.i ../GPSZcount.i \
+	../DayTime.i
+	swig -java -c++ -noexcept -I.. gpstk.i
+
+gpstk_wrap_mod.cxx: gpstk_wrap.cxx
+	sed 's/DayTime.hpp\"/DayTime.hpp\"\nusing namespace gpstk;/' gpstk_wrap.cxx > gpstk_wrap_mod.cxx
+
+gpstk_wrap_mod.o: gpstk_wrap_mod.cxx
+	g++ -fPIC -c gpstk_wrap_mod.cxx -I.. \
+	-I/usr/local/jdk1.5.0_01/include \
+	-I/usr/local/jdk1.5.0_01/include/linux
+
+libgpstk.so: gpstk_wrap_mod.o
+	g++ -shared gpstk_wrap_mod.o -o libgpstk.so -lgpstk
+
+clean:
+	rm *.class
+	rm *.java
+	rm *_wrap*
+	rm *gpstk*.so
+
+
+
+
+
+
+
diff --git a/trunk/apps/bindings/java/examples/example1.java b/dev/apps/bindings/java/examples/example1.java
similarity index 100%
rename from trunk/apps/bindings/java/examples/example1.java
rename to dev/apps/bindings/java/examples/example1.java
diff --git a/dev/apps/bindings/octave/Makefile b/dev/apps/bindings/octave/Makefile
new file mode 100644
index 0000000..180ffd3
--- /dev/null
+++ b/dev/apps/bindings/octave/Makefile
@@ -0,0 +1,29 @@
+#
+# $Id$
+#
+# To use these routines the GPSTk shared library either needs to be
+# installed as a system shared library, or the environment variable
+# LD_LIBRARY_PATH needs to set to the directory where the shared 
+# library file resides
+#
+
+all :	readRinexObsFast.oct readRinexObsGeom.oct \
+	calculatePosition.oct
+
+readRinexObsFast.oct: readRinexObsFast.cpp
+	$(make_oct)
+
+readRinexObsGeom.oct: readRinexObsGeom.cpp
+	$(make_oct)
+
+calculatePosition.oct: calculatePosition.cpp
+	$(make_oct)
+
+define make_oct
+	@echo "making oct file: $@"
+	@echo "mkoctfile $(CXXFLAGS) -o $@ $^ $(LDLIBS)"
+	mkoctfile -I../src -L../src -lgpstk -o $@ $^ $(LDLIBS)
+	chmod 755 $@
+	for file in $^ ; do $(RM) $$file.o ; done  
+endef
+
diff --git a/trunk/apps/bindings/octave/calculatePosition.cpp b/dev/apps/bindings/octave/calculatePosition.cpp
similarity index 100%
rename from trunk/apps/bindings/octave/calculatePosition.cpp
rename to dev/apps/bindings/octave/calculatePosition.cpp
diff --git a/trunk/apps/bindings/octave/readRinexObsFast.cpp b/dev/apps/bindings/octave/readRinexObsFast.cpp
similarity index 100%
rename from trunk/apps/bindings/octave/readRinexObsFast.cpp
rename to dev/apps/bindings/octave/readRinexObsFast.cpp
diff --git a/trunk/apps/bindings/octave/readRinexObsGeom.cpp b/dev/apps/bindings/octave/readRinexObsGeom.cpp
similarity index 100%
rename from trunk/apps/bindings/octave/readRinexObsGeom.cpp
rename to dev/apps/bindings/octave/readRinexObsGeom.cpp
diff --git a/dev/apps/bindings/perl/Makefile b/dev/apps/bindings/perl/Makefile
new file mode 100644
index 0000000..5b73d37
--- /dev/null
+++ b/dev/apps/bindings/perl/Makefile
@@ -0,0 +1,50 @@
+#
+# $Id$
+#
+# This simple makefile builds a Perl interface to the GPSTk from code
+# generated the SWIG utility (http://www.swig.org/). 
+#
+# You shouldn't need to install SWIG to compile this interface as
+# distributed, unless you change the interface definition (.i files).
+#
+# What you do need:
+#  - A C/C++ compiler.
+#  - Development headers of Perl
+#  - Knowledge of which directory contains "perl.h", "Extern.h" and "XSUB.h"
+#  - The sed utility
+# 
+# Not all GPSTk functionality is present in these bindings.
+# Two reasons: First, this is a work in progress and should be considered
+# incomplete. Second, not all of the GPSTk's functionality _can_ be mapped
+# into other languages such as perl.
+#
+# Examples of usage of this interface are in the examples subdirectory.
+#
+# Reminder for later: $@ is target, $< is rhs, $^ is rhs
+
+all :	gpstk-perl.so 
+
+gpstk_wrap.cxx: ../gpstk.i ../common.i ../Exception.i ../GPSZcount.i \
+	../DayTime.i ../FFTextStream.i ../RinexObsStream.i
+	swig -perl5 -c++ -I.. \
+	gpstk.i
+
+gpstk_wrap_mod.cxx: gpstk_wrap.cxx
+	sed 's/DayTime.hpp\"/DayTime.hpp\"\nusing namespace gpstk;/' gpstk_wrap.cxx > gpstk_wrap_mod.cxx
+
+gpstk_wrap_mod.o: gpstk_wrap_mod.cxx
+	g++ -fpic -c gpstk_wrap_mod.cxx -I.. -I /usr/lib/perl/5.8.4/CORE \
+		-Dbool=char `perl -e 'use Config; print $Config{ccflags}'`
+
+gpstk-perl.so: gpstk_wrap_mod.o
+	g++ -shared gpstk_wrap_mod.o -o gpstk-perl.so -lgpstk
+
+clean:
+	rm *_wrap*
+	rm *gpstk*.so
+
+
+
+
+
+
diff --git a/trunk/apps/bindings/python/DayTimeException.i b/dev/apps/bindings/python/DayTimeException.i
similarity index 100%
rename from trunk/apps/bindings/python/DayTimeException.i
rename to dev/apps/bindings/python/DayTimeException.i
diff --git a/trunk/apps/bindings/python/Developer.txt b/dev/apps/bindings/python/Developer.txt
similarity index 100%
rename from trunk/apps/bindings/python/Developer.txt
rename to dev/apps/bindings/python/Developer.txt
diff --git a/trunk/apps/bindings/python/ExtraWaveFact.i b/dev/apps/bindings/python/ExtraWaveFact.i
similarity index 100%
rename from trunk/apps/bindings/python/ExtraWaveFact.i
rename to dev/apps/bindings/python/ExtraWaveFact.i
diff --git a/trunk/apps/bindings/python/Makefile b/dev/apps/bindings/python/Makefile
similarity index 100%
rename from trunk/apps/bindings/python/Makefile
rename to dev/apps/bindings/python/Makefile
diff --git a/trunk/apps/bindings/python/RinexDatum.i b/dev/apps/bindings/python/RinexDatum.i
similarity index 100%
rename from trunk/apps/bindings/python/RinexDatum.i
rename to dev/apps/bindings/python/RinexDatum.i
diff --git a/trunk/apps/bindings/python/RinexObsType.i b/dev/apps/bindings/python/RinexObsType.i
similarity index 100%
rename from trunk/apps/bindings/python/RinexObsType.i
rename to dev/apps/bindings/python/RinexObsType.i
diff --git a/trunk/apps/bindings/python/common.i b/dev/apps/bindings/python/common.i
similarity index 100%
rename from trunk/apps/bindings/python/common.i
rename to dev/apps/bindings/python/common.i
diff --git a/trunk/apps/bindings/python/examples/example1.html b/dev/apps/bindings/python/examples/example1.html
similarity index 100%
rename from trunk/apps/bindings/python/examples/example1.html
rename to dev/apps/bindings/python/examples/example1.html
diff --git a/trunk/apps/bindings/python/examples/example1.py b/dev/apps/bindings/python/examples/example1.py
similarity index 100%
rename from trunk/apps/bindings/python/examples/example1.py
rename to dev/apps/bindings/python/examples/example1.py
diff --git a/trunk/apps/bindings/python/examples/example2.html b/dev/apps/bindings/python/examples/example2.html
similarity index 100%
rename from trunk/apps/bindings/python/examples/example2.html
rename to dev/apps/bindings/python/examples/example2.html
diff --git a/trunk/apps/bindings/python/examples/example2.py b/dev/apps/bindings/python/examples/example2.py
similarity index 100%
rename from trunk/apps/bindings/python/examples/example2.py
rename to dev/apps/bindings/python/examples/example2.py
diff --git a/trunk/apps/bindings/python/examples/example3.html b/dev/apps/bindings/python/examples/example3.html
similarity index 100%
rename from trunk/apps/bindings/python/examples/example3.html
rename to dev/apps/bindings/python/examples/example3.html
diff --git a/trunk/apps/bindings/python/examples/example3.py b/dev/apps/bindings/python/examples/example3.py
similarity index 100%
rename from trunk/apps/bindings/python/examples/example3.py
rename to dev/apps/bindings/python/examples/example3.py
diff --git a/trunk/apps/bindings/python/gpstkPython.html b/dev/apps/bindings/python/gpstkPython.html
similarity index 100%
rename from trunk/apps/bindings/python/gpstkPython.html
rename to dev/apps/bindings/python/gpstkPython.html
diff --git a/trunk/apps/bindings/python/gpstkPython.i b/dev/apps/bindings/python/gpstkPython.i
similarity index 100%
rename from trunk/apps/bindings/python/gpstkPython.i
rename to dev/apps/bindings/python/gpstkPython.i
diff --git a/trunk/apps/bindings/python/gpstkPythonUtils.cpp b/dev/apps/bindings/python/gpstkPythonUtils.cpp
similarity index 100%
rename from trunk/apps/bindings/python/gpstkPythonUtils.cpp
rename to dev/apps/bindings/python/gpstkPythonUtils.cpp
diff --git a/trunk/apps/bindings/python/gpstkPythonUtils.i b/dev/apps/bindings/python/gpstkPythonUtils.i
similarity index 100%
rename from trunk/apps/bindings/python/gpstkPythonUtils.i
rename to dev/apps/bindings/python/gpstkPythonUtils.i
diff --git a/trunk/apps/bindings/python/gpstkPython_wrap.cxx b/dev/apps/bindings/python/gpstkPython_wrap.cxx
similarity index 100%
rename from trunk/apps/bindings/python/gpstkPython_wrap.cxx
rename to dev/apps/bindings/python/gpstkPython_wrap.cxx
diff --git a/trunk/apps/bindings/python/pydoc.py b/dev/apps/bindings/python/pydoc.py
similarity index 100%
rename from trunk/apps/bindings/python/pydoc.py
rename to dev/apps/bindings/python/pydoc.py
diff --git a/trunk/apps/bindings/python/sensorType.i b/dev/apps/bindings/python/sensorType.i
similarity index 100%
rename from trunk/apps/bindings/python/sensorType.i
rename to dev/apps/bindings/python/sensorType.i
diff --git a/trunk/apps/bindings/python/streamRead.cpp b/dev/apps/bindings/python/streamRead.cpp
similarity index 100%
rename from trunk/apps/bindings/python/streamRead.cpp
rename to dev/apps/bindings/python/streamRead.cpp
diff --git a/trunk/apps/bindings/python/streamRead.i b/dev/apps/bindings/python/streamRead.i
similarity index 100%
rename from trunk/apps/bindings/python/streamRead.i
rename to dev/apps/bindings/python/streamRead.i
diff --git a/dev/apps/bindings/tcl/Makefile b/dev/apps/bindings/tcl/Makefile
new file mode 100644
index 0000000..4a4a270
--- /dev/null
+++ b/dev/apps/bindings/tcl/Makefile
@@ -0,0 +1,49 @@
+#
+# $Id$
+#
+# This simple makefile builds a Tcl/Tk interface to the GPSTk from code
+# generated the SWIG utility (http://www.swig.org/). 
+#
+# You shouldn't need to install SWIG to compile this interface as
+# distributed, unless you change the interface definition (.i files).
+#
+# What you do need:
+#  - A C/C++ compiler.
+#  - Development headers of Tcl/Tk
+#  - Knowledge where the header tcl.h is installed
+#  - The sed utility
+# 
+# Not all GPSTk functionality is present in these Tcl/Tk bindings.
+# Two reasons: First, this is a work in progress and should be considered
+# incomplete. Second, not all of the GPSTk's functionality _can_ be mapped
+# into other languages such as Tcl/Tk.
+#
+# Examples of usage of this interface are in the examples subdirectory.
+#
+# Reminder for later: $@ is target, $< is rhs, $^ is rhs
+
+all :	gpstk-tcl.so 
+
+gpstk_wrap.cxx: ../gpstk.i ../common.i ../Exception.i ../GPSZcount.i \
+	../DayTime.i ../FFTextStream.i ../RinexObsStream.i
+	swig -tcl -c++ -importall -ignoremissing -I.. \
+	gpstk.i
+
+gpstk_wrap_mod.cxx: gpstk_wrap.cxx
+	sed 's/DayTime.hpp\"/DayTime.hpp\"\nusing namespace gpstk;/' gpstk_wrap.cxx > gpstk_wrap_mod.cxx
+
+gpstk_wrap_mod.o: gpstk_wrap_mod.cxx
+	g++ -fpic -c gpstk_wrap_mod.cxx -I.. -I /usr/include/tcl8.4
+
+gpstk-tcl.so: gpstk_wrap_mod.o
+	g++ -shared gpstk_wrap_mod.o -o gpstk-tcl.so -lgpstk
+
+clean:
+	rm *_wrap*
+	rm *gpstk*.so
+
+
+
+
+
+
diff --git a/trunk/apps/bindings/tcl/examples/example1.tcl b/dev/apps/bindings/tcl/examples/example1.tcl
similarity index 100%
rename from trunk/apps/bindings/tcl/examples/example1.tcl
rename to dev/apps/bindings/tcl/examples/example1.tcl
diff --git a/trunk/apps/bindings/tcl/gpstk_wrap_mod.cxx b/dev/apps/bindings/tcl/gpstk_wrap_mod.cxx
similarity index 100%
rename from trunk/apps/bindings/tcl/gpstk_wrap_mod.cxx
rename to dev/apps/bindings/tcl/gpstk_wrap_mod.cxx
diff --git a/dev/apps/checktools/CheckFrame.hpp b/dev/apps/checktools/CheckFrame.hpp
new file mode 100644
index 0000000..364a69f
--- /dev/null
+++ b/dev/apps/checktools/CheckFrame.hpp
@@ -0,0 +1,121 @@
+#pragma ident "$Id$"
+
+
+#ifndef CHECKFRAME_HPP
+#define CHECKFRAME_HPP
+
+#include "CommandOptionWithTimeArg.hpp"
+#include "FileFilterFrame.hpp"
+#include "BasicFramework.hpp"
+
+template <class FileData>
+struct NullTimeFilter : public std::unary_function<FileData, bool>
+{
+public:
+   NullTimeFilter(const gpstk::DayTime& startTime,
+                  const gpstk::DayTime& endTime)
+   {}
+
+   bool operator() (const FileData& l) const
+   {
+      return false;
+   }
+};
+ 
+
+template <class FileStream, class FileData, class FilterTimeOperator = NullTimeFilter<FileData> >
+class CheckFrame : public gpstk::BasicFramework
+{
+public:
+   CheckFrame(char* arg0, std::string fileType) :
+         gpstk::BasicFramework(arg0,
+                               "Reads given input " + fileType + 
+                               " files and check for errors. This will only"
+                               " report the first error found in each file. "
+                               " The entire file is always checked, regardless"
+                               " of time options."),
+         timeOption('t', "time", "Time of first record to count (default ="
+                    " \"beginning of time\")"),
+         eTimeOption('e', "end-time", "End of time range to compare (default"
+                     " = \"end of time\")"),
+         inputFileOption("Each input file is checked for errors.", true),
+         startTime(gpstk::DayTime::BEGINNING_OF_TIME),
+         endTime(gpstk::DayTime::END_OF_TIME)
+   {
+      timeOption.setMaxCount(1);
+      eTimeOption.setMaxCount(1);
+      timeOptions.addOption(&timeOption);
+      timeOptions.addOption(&eTimeOption);
+   }
+   
+   virtual bool initialize(int argc, char* argv[]) throw()
+   {
+      if (!gpstk::BasicFramework::initialize(argc, argv))
+         return false;
+      if (timeOption.getCount())
+         startTime = timeOption.getTime()[0];
+      if (eTimeOption.getCount())
+         endTime = eTimeOption.getTime()[0];
+      if (startTime > endTime)
+      {
+         std::cerr << "End time can't precede start time." << std::endl;
+         return false;
+      }
+      return true;
+   }
+   
+protected:
+   virtual void process()
+   {
+      std::vector<std::string> inputFiles = inputFileOption.getValue();
+      std::vector<std::string>::iterator itr = inputFiles.begin();
+      FilterTimeOperator timeFilt(startTime, endTime);
+      while (itr != inputFiles.end())
+      {
+         std::cout << "Checking " << *itr << std::endl;
+         unsigned long recCount = 0;
+         try
+         {
+            FileStream f((*itr).c_str());
+            f.exceptions(std::ios::failbit);
+            
+            FileData temp;
+            while (f >> temp)
+            {
+               if (!timeFilt(temp))
+                  recCount++;
+            }
+            
+            std::cout << "Read " << recCount << " records." 
+                      << std::endl << std::endl;
+         }
+         catch (gpstk::Exception& e)
+         {
+            std::cout << e << std::endl << std::endl;
+         }
+         catch (std::exception& e)
+         {
+            std::cout << e.what() << std::endl;
+         }
+         catch (...)
+         {
+            std::cout << "unknown exception caught" << std::endl;
+         }
+         
+         itr++;
+      }
+   }
+   
+      /// start time for record counting
+   gpstk::CommandOptionWithSimpleTimeArg timeOption;
+      /// end time for record counting
+   gpstk::CommandOptionWithSimpleTimeArg eTimeOption;
+      /// if either of the time options are set
+   gpstk::CommandOptionGroupOr timeOptions;
+   gpstk::CommandOptionRest inputFileOption;
+   
+   gpstk::DayTime startTime, endTime;
+   
+};
+
+#endif
diff --git a/dev/apps/checktools/Jamfile b/dev/apps/checktools/Jamfile
new file mode 100644
index 0000000..ec8980c
--- /dev/null
+++ b/dev/apps/checktools/Jamfile
@@ -0,0 +1,12 @@
+# $Id$
+
+SubDir TOP apps checktools ;
+
+GPSLinkLibraries rowcheck rmwcheck rnwcheck ficcheck ficacheck : gpstk ;
+BonkForte ; # bleah.
+
+GPSMain rowcheck : rowcheck.cpp ;
+GPSMain rmwcheck : rmwcheck.cpp ;
+GPSMain rnwcheck : rnwcheck.cpp ;
+GPSMain ficcheck : ficcheck.cpp ;
+GPSMain ficacheck : ficacheck.cpp ;
diff --git a/dev/apps/checktools/Makefile.am b/dev/apps/checktools/Makefile.am
new file mode 100644
index 0000000..12f415b
--- /dev/null
+++ b/dev/apps/checktools/Makefile.am
@@ -0,0 +1,11 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ../../src/libgpstk.la
+
+bin_PROGRAMS = rowcheck rmwcheck rnwcheck ficcheck ficacheck
+
+rowcheck_SOURCES = rowcheck.cpp
+rmwcheck_SOURCES = rmwcheck.cpp
+rnwcheck_SOURCES = rnwcheck.cpp
+ficcheck_SOURCES = ficcheck.cpp
+ficacheck_SOURCES = ficacheck.cpp
diff --git a/dev/apps/checktools/ficacheck.cpp b/dev/apps/checktools/ficacheck.cpp
new file mode 100644
index 0000000..79d7525
--- /dev/null
+++ b/dev/apps/checktools/ficacheck.cpp
@@ -0,0 +1,39 @@
+#pragma ident "$Id$"
+
+
+#include "CheckFrame.hpp"
+
+#include "FICAStream.hpp"
+#include "FICData.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      CheckFrame<FICAStream, FICData> cf(argv[0],
+                                         std::string("FIC ASCII"));
+      
+      if (!cf.initialize(argc, argv))
+         return 0;
+      if (!cf.run())
+         return 1;
+      
+      return 0;   
+   }
+   catch(gpstk::Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/checktools/ficcheck.cpp b/dev/apps/checktools/ficcheck.cpp
new file mode 100644
index 0000000..dd5a9d7
--- /dev/null
+++ b/dev/apps/checktools/ficcheck.cpp
@@ -0,0 +1,39 @@
+#pragma ident "$Id$"
+
+
+#include "CheckFrame.hpp"
+
+#include "FICStream.hpp"
+#include "FICData.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      CheckFrame<FICStream, FICData> cf(argv[0],
+                                        std::string("FIC"));
+      
+      if (!cf.initialize(argc, argv))
+         return 0;
+      if (!cf.run())
+         return 1;
+      
+      return 0;   
+   }
+   catch(gpstk::Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/checktools/rmwcheck.cpp b/dev/apps/checktools/rmwcheck.cpp
new file mode 100644
index 0000000..3e4dbf3
--- /dev/null
+++ b/dev/apps/checktools/rmwcheck.cpp
@@ -0,0 +1,40 @@
+#pragma ident "$Id$"
+
+
+#include "CheckFrame.hpp"
+
+#include "RinexMetStream.hpp"
+#include "RinexMetData.hpp"
+#include "RinexMetFilterOperators.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      CheckFrame<RinexMetStream, RinexMetData, RinexMetDataFilterTime>
+         cf(argv[0], "Rinex Met");
+      
+      if (!cf.initialize(argc, argv))
+         return 0;
+      if (!cf.run())
+         return 1;
+      
+      return 0;   
+   }
+   catch(gpstk::Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/checktools/rnwcheck.cpp b/dev/apps/checktools/rnwcheck.cpp
new file mode 100644
index 0000000..79daf99
--- /dev/null
+++ b/dev/apps/checktools/rnwcheck.cpp
@@ -0,0 +1,39 @@
+#pragma ident "$Id$"
+
+
+#include "CheckFrame.hpp"
+
+#include "RinexNavStream.hpp"
+#include "RinexNavData.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      CheckFrame<RinexNavStream, RinexNavData> cf(argv[0],
+                                                  std::string("Rinex Nav"));
+      
+      if (!cf.initialize(argc, argv))
+         return 0;
+      if (!cf.run())
+         return 1;
+      
+      return 0;   
+   }
+   catch(gpstk::Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/checktools/rowcheck.cpp b/dev/apps/checktools/rowcheck.cpp
new file mode 100644
index 0000000..ed1d3b5
--- /dev/null
+++ b/dev/apps/checktools/rowcheck.cpp
@@ -0,0 +1,39 @@
+#pragma ident "$Id$"
+
+
+#include "CheckFrame.hpp"
+
+#include "RinexObsStream.hpp"
+#include "RinexObsData.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      CheckFrame<RinexObsStream, RinexObsData> cf(argv[0],
+                                                  std::string("Rinex Obs"));
+      
+      if (!cf.initialize(argc, argv))
+         return 0;
+      if (!cf.run())
+         return 1;
+      
+      return 0;   
+   }
+   catch(gpstk::Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/converters/Jamfile b/dev/apps/converters/Jamfile
new file mode 100644
index 0000000..7daf81e
--- /dev/null
+++ b/dev/apps/converters/Jamfile
@@ -0,0 +1,9 @@
+#
+# $Id$
+#
+
+SubDir TOP apps converters ;
+
+GPSLinkLibraries novaRinex : gpstk ;
+
+GPSMain novaRinex : novaRinex.cpp NovatelData.cpp ;
diff --git a/dev/apps/converters/Makefile.am b/dev/apps/converters/Makefile.am
new file mode 100644
index 0000000..a900d92
--- /dev/null
+++ b/dev/apps/converters/Makefile.am
@@ -0,0 +1,7 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ../../src/libgpstk.la
+
+bin_PROGRAMS = novaRinex
+
+novaRinex_SOURCES = novaRinex.cpp NovatelData.cpp
diff --git a/dev/apps/converters/NovatelData.cpp b/dev/apps/converters/NovatelData.cpp
new file mode 100644
index 0000000..a90152a
--- /dev/null
+++ b/dev/apps/converters/NovatelData.cpp
@@ -0,0 +1,1123 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/**
+ * @file NovatelData.cpp
+ * gpstk::NovatelData - container for Novatel data, with methods to convert
+ * binary data files to Rinex
+ */
+
+#include "BinUtils.hpp"
+#include "StringUtils.hpp"
+#include "DayTime.hpp"
+#include "EngEphemeris.hpp"
+#include "RinexObsHeader.hpp"
+#include "icd_200_constants.hpp"
+#include "NovatelData.hpp"
+
+using namespace std;
+using namespace gpstk::BinUtils;
+using namespace gpstk::StringUtils;
+
+static bool debug=false;
+
+namespace gpstk
+{
+
+   // --------------------------------------------------------------------------------
+   const double CFF=C_GPS_M/OSC_FREQ;
+   const double wl1=CFF/L1_MULT;
+   const double wl2=CFF/L2_MULT;
+   const double PhaseRollover=8388608;
+
+   // --------------------------------------------------------------------------------
+   const string NovatelData::RecNames[] = {
+         string("Unknown"),
+         string("RGEB obs"),
+         string("RGEC obs"),
+         string("POSB pos"),
+         string("REPB nav"),
+         string("RCSB sts"),
+         string("RANGE obs"),
+         string("RANGECMP obs"),
+         string("RAWEPHEM nav")
+      };
+
+   // --------------------------------------------------------------------------------
+   bool NovatelData::isNav(void) const
+   {
+      switch(rectype) {
+         case POSB:
+         case RCSB:
+            return false;
+         case REPB:
+         case RAWEPHEM:
+            return true;
+         case RGEB:
+         case RGEC:
+         case RANGE:
+         case RANGECMP:
+            return false;
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+   bool NovatelData::isObs(void) const
+   {
+      switch(rectype) {
+         case POSB:
+         case RCSB:
+            return false;
+         case REPB:
+         case RAWEPHEM:
+            return false;
+         case RGEB:
+         case RGEC:
+         case RANGE:
+         case RANGECMP:
+            return true;
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+   bool NovatelData::isAux(void) const
+   {
+      switch(rectype) {
+         case POSB:
+         case RCSB:
+            return true;
+         case REPB:
+         case RAWEPHEM:
+            return false;
+         case RGEB:
+         case RGEC:
+         case RANGE:
+         case RANGECMP:
+            return false;
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+      // True if this record belongs to OEM2 receivers
+   bool NovatelData::isOEM2(void) const
+   {
+      switch(rectype) {
+         case POSB:
+         case RCSB:
+         case REPB:
+         case RGEB:
+         case RGEC:
+            return true;
+         case RAWEPHEM:
+         case RANGE:
+         case RANGECMP:
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+      // True if this record belongs to OEM4 receivers
+   bool NovatelData::isOEM4(void) const
+   {
+      switch(rectype) {
+         case RAWEPHEM:
+         case RANGE:
+         case RANGECMP:
+            return true;
+         case POSB:
+         case RCSB:
+         case REPB:
+         case RGEB:
+         case RGEC:
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+   bool NovatelData::isValid(void) const
+   {
+      switch(rectype) {
+         case POSB:
+         case RCSB:
+         case REPB:
+         case RAWEPHEM:
+         case RGEB:
+         case RGEC:
+         case RANGE:
+         case RANGECMP:
+            if(datasize == 0 || headersize==0) return false;
+            return true;
+         case Unknown:
+         default:
+            return false;
+      }
+   }
+
+   // --------------------------------------------------------------------------------
+   void NovatelData::dump(ostream& str) const
+   {
+      str << "Record type is " << rectype << endl;
+   }
+
+   // --------------------------------------------------------------------------------
+   void NovatelData::reallyPutRecord(FFStream& s) const 
+      throw(exception, StringUtils::StringException, FFStreamError)
+   {
+      FFStreamError e("Novatel::reallyPutRecord() is not implemented");
+      GPSTK_THROW(e);
+   }
+
+
+   // --------------------------------------------------------------------------------
+   void NovatelData::reallyGetRecord(FFStream& ffs)
+      throw(exception, StringUtils::StringException, FFStreamError)
+   {
+   try {
+      if(dynamic_cast<NovatelStream*>(&ffs)) {
+
+         NovatelStream& strm = dynamic_cast<NovatelStream&>(ffs);
+
+         unsigned char *p0 = &buffer[0];
+         unsigned char *p1 = &buffer[1];
+         unsigned char *p2 = &buffer[2];
+         unsigned char *p3 = &buffer[3];
+         unsigned char *p4 = &buffer[4];
+         int i,j,k,failure;
+         long filepos;
+
+         if(debug) cout << "Entered NovatelData::reallyGetRecord()" << endl;
+            // read loop
+         do {
+
+            // move data down by 1 byte
+            *p0 = *p1;
+            *p1 = *p2;
+
+            // get another character
+            try {
+               strm.read((char *)p2, 1);
+            }
+            catch(exception& e) {
+               if(debug) cout << "read 1 threw std exception: " << e.what() << endl;
+               //FFStreamError fe(string("std exception: ")+e.what());
+               //GPSTK_THROW(fe);
+            }
+
+            if(strm.bad()) {
+               FFStreamError fe("Read error");
+               GPSTK_THROW(fe);
+            }
+            if(strm.eof()) {
+               if(debug) cout << "Reached EOF" << endl;
+               break;
+            }
+
+            if(debug) cout << "got char 0x" << hex << uppercase << int(buffer[2])
+               << dec << endl;
+
+            // look for sync bytes
+            if(*p0==0xAA && *p1==0x44 && *p2==0x11) {
+               // -------------------------------------------------- OEM2
+               if(debug) cout << "Found OEM2 sync" << endl;
+
+                  // save position in case of failure
+               filepos = strm.tellg();
+               if(debug) cout << "File position " << filepos << endl;
+
+                  // read 9 more characters into buffer, giving total of 12
+               strm.read((char *)p3,9);
+               if(strm.bad()) {
+                  FFStreamError fe("Read error");
+                  GPSTK_THROW(fe);
+               }
+               if(strm.eof()) {
+                  if(debug) cout << "Reached EOF" << endl;
+                  break;
+               }
+
+                  // read the record ID
+                    if(*p4==0x20) rectype = RGEB;
+               else if(*p4==0x21) rectype = RGEC;
+               else if(*p4==0x01) rectype = POSB;
+               else if(*p4==0x0E) rectype = REPB;
+               else if(*p4==0x0D) rectype = RCSB;
+               else               rectype = Unknown;
+               recnum = int(*p4);
+               intelToHost(recnum);
+
+                  // read the rest of the record
+               failure = 0;
+               if(rectype != Unknown) {
+
+                     // get the size of the record
+                  memmove(&datasize, &(buffer[8]), 4);
+                  intelToHost(datasize);
+
+                     // read the rest of the record
+                  if(datasize-12 >= 1024) {
+                     //FFStreamError fe("Read error - buffer overflow");
+                     //GPSTK_THROW(fe);
+                     failure = 1;
+                  }
+                  else {
+                     strm.read((char *)&buffer[12],datasize-12);
+                     if(strm.bad()) {
+                        FFStreamError fe("Read error");
+                        GPSTK_THROW(fe);
+                     }
+                     if(strm.eof()) {
+                        if(debug) cout << "Reached EOF" << endl;
+                        break;
+                     }
+                     headersize = 3;             // just the sync bytes
+
+                        // compute the checksum
+                        // Ref OEM2 manual
+                     unsigned char checksum = 0;
+                     checksum ^= buffer[0];
+                     checksum ^= buffer[1];
+                     checksum ^= buffer[2];
+                     for(i=4; i<datasize; i++) checksum ^= buffer[i];
+
+                     if(checksum == buffer[3]) break;    // sucess
+                     failure = 2;
+
+                  }  // end if datasize fits into buffer
+               }  // end if record type != unknown
+
+                  // failure - either type unknown, buffer overflow or failed checksum
+               if(debug) {
+                  cout << "Failure - ";
+                  if(failure == 0) cout << "type unknown";
+                  else if(failure == 1) cout << "buffer overflow";
+                  else if(failure == 2) cout << "failed checksum";
+                  cout << " for recnum " << recnum
+                     << " with headersize " << headersize
+                     << " and message size " << datasize << endl;
+               }
+
+               strm.seekg(filepos);          // rewind to just after the sync bytes
+               datasize = headersize = 0;
+
+            }  // end if OEM2 sync
+
+            else if(*p0==0xAA && *p1==0x44 && *p2==0x12) {
+               // -------------------------------------------------- OEM4
+               // Ref OEM4 Manual pg 15
+
+               if(debug) cout << "Found OEM4 sync" << endl;
+
+                  // save position in case of failure
+               filepos = strm.tellg();
+               if(debug) cout << "File position " << filepos << endl;
+
+                  // ---------------------------------------
+                  // read header, 25 characters, into buffer
+               strm.read((char *)p3,25);
+               if(strm.bad()) {
+                  FFStreamError fe("Read error");
+                  GPSTK_THROW(fe);
+               }
+               if(strm.eof()) {
+                  if(debug) cout << "Reached EOF" << endl;
+                  break;
+               }
+
+                  // parse the header
+                  // Ref OEM4 Manual pg 16
+                  // (only need some of the data here - cast to Rinex functions
+                  // will parse the whole thing)
+               unsigned char headerLength;
+               memmove(&headerLength, &(buffer[3]), 1);  intelToHost(headerLength);
+               short messageID;
+               memmove(&messageID, &(buffer[4]), 2);     intelToHost(messageID);
+               //char messageType;
+               //memmove(&messageType, &(buffer[6]), 1);   intelToHost(messageType);
+               //char portAddress;
+               //memmove(&portAddress, &(buffer[7]), 1);   intelToHost(portAddress);
+               short messageLength;
+               memmove(&messageLength, &(buffer[8]), 2); intelToHost(messageLength);
+               //short sequence;
+               //memmove(&sequence, &(buffer[10]), 2);     intelToHost(sequence);
+               //char idleTime;
+               //memmove(&idleTime, &(buffer[12]), 1);     intelToHost(idleTime);
+               //char timeStatus;
+               //memmove(&timeStatus, &(buffer[13]), 1);   intelToHost(timeStatus);
+               //short week;
+               //memmove(&week, &(buffer[14]), 2);         intelToHost(week);
+               //long msecOfWeek;
+               //memmove(&msecOfWeek, &(buffer[16]), 4);   intelToHost(msecOfWeek);
+               //long rxStatus;
+               //memmove(&rxStatus, &(buffer[20]), 4);     intelToHost(rxStatus);
+               //short reserved;
+               //memmove(&reserved, &(buffer[24]), 2);     intelToHost(reserved);
+               //short rxSWVersion;
+               //memmove(&rxSWVersion, &(buffer[26]), 2);  intelToHost(rxSWVersion);
+               
+               datasize = messageLength;
+               headersize = int(headerLength);
+               recnum = messageID;
+
+               if(headersize != 28) {   // manual warns that changes may be made
+                  Exception e("Header size : expected 28 but found "
+                     + StringUtils::asString(headersize) + " for record ID "
+                     + StringUtils::asString(recnum));
+                  GPSTK_THROW(e);
+               }
+
+               if(debug) cout << "hL " << int(headerLength)
+                     << " ID " << messageID
+                     << " mL " << messageLength
+                     //<< " seq " << sequence
+                     //<< " week " << week
+                     //<< " msow " << msecOfWeek
+                     //<< " rxver " << rxSWVersion
+                     << endl;
+
+               if(     recnum ==  43) rectype = RANGE;
+               else if(recnum == 140) rectype = RANGECMP;
+               else if(recnum ==  41) rectype = RAWEPHEM;
+               else                   rectype = Unknown;
+
+               failure=0;
+               if(rectype != Unknown) {
+
+                     // ---------------------------------------
+                     // read the data message, but don't overwrite the header
+                     // first check against buffer overflow
+                  if(datasize-28 >= 1024) {
+                     //FFStreamError fe("Read error - buffer overflow");
+                     //GPSTK_THROW(fe);
+                     failure = 1;
+                  }
+                  else {
+                     strm.read((char *)&(buffer[28]),datasize);
+                     if(strm.bad()) {
+                        FFStreamError fe("Read error");
+                        GPSTK_THROW(fe);
+                     }
+                     if(strm.eof()) {
+                        datasize = 0;         // mark a bad record
+                        if(debug) cout << "Reached EOF" << endl;
+                        break;
+                     }
+                     if(debug) cout << "Successfully read message" << endl;
+
+                        // ---------------------------------------
+                        // validate with 32-bit CRC
+                        // cf. Ref OEM4 manual pg 21.
+
+                        // get the checksum at the end
+                     unsigned int checksum =
+                        intelToHost(strm.getData<unsigned int>());
+
+                        // calculate the checksum of the header(even sync)+data
+                     unsigned int check=0,ultemp1,ultemp2;
+                     for(i=0; i<datasize+28; i++) {
+                        ultemp1 = (check >> 8) & 0x00FFFFFFL;
+                        j = ((int)check ^ buffer[i]) & 0xFF;
+                        ultemp2 = j;
+                        for(k=8; k>0; k--) {
+                           if(ultemp2 & 1)
+                              ultemp2 = (ultemp2 >> 1) ^ 0xEDB88320L;
+                           else
+                              ultemp2 >>= 1;
+                        }
+                        check = ultemp1 ^ ultemp2;
+                     }
+
+                     if(check == checksum) {
+                        if(debug) cout << "checksum ok" << endl;
+                        break;
+                     }
+                     else failure = 2;
+
+                  }  // end if datasize-28 < buffersize
+               }  // end if rectype != Unknown
+
+                  // failure - either type unknown, buffer overflow or failed checksum
+               if(debug) {
+                  cout << "Failure - ";
+                  if(failure == 0) cout << "type unknown";
+                  else if(failure == 1) cout << "buffer overflow";
+                  else if(failure == 2) cout << "failed checksum";
+                  cout << " for recnum " << recnum
+                     << " with headersize " << headersize
+                     << " and message size " << datasize << endl;
+               }
+               strm.seekg(filepos);
+               datasize = headersize = 0;               // marks an invalid object
+
+            }  // end if OEM4 sync
+
+            else {                                       // skip these bytes
+                  // print only if sync is not underway
+               if(debug && !(*p1==0xAA && *p2==0x44) && !(*p2==0xAA) )
+                  cout << "Skip a byte " << hex << uppercase << setfill('0')
+                     << setw(2) << *p0 << dec << setfill(' ') << endl;
+            }
+
+         } while(1);   // end read loop
+      }
+      else {
+         FFStreamError e("NovatelData tried to read from a non-Novatel file");
+         GPSTK_THROW(e);
+      }
+
+      if(!isValid()) {
+         FFStreamError e("Read an invalid Novatel record");
+         GPSTK_THROW(e);
+      }
+
+   }
+   catch(Exception e) {
+      if(debug) cout << "reallyGetRecord caught GPSTK exception " << e << endl;
+   }
+   catch(exception e) {
+      if(debug) cout << "reallyGetRecord caught std exception " << e.what() << endl;
+   }
+   catch(...) {
+      if(debug) cout << "reallyGetRecord caught an unknown exception" << endl;
+   }
+
+   }  // end NovatelData::reallyGetRecord
+
+
+   // --------------------------------------------------------------------------------
+   NovatelData::operator RinexNavData()
+      throw(Exception)
+   {
+      if(!isValid() || !isNav()) {
+         Exception e("Invalid or non-Nav record");
+         GPSTK_THROW(e);
+      }
+
+      int i,j,k;
+      long templ;
+      EngEphemeris eeph;
+
+      if(rectype == RAWEPHEM) {                    // OEM4
+
+            // parse header
+            // Ref OEM4 Manual pg 16
+         unsigned char headerLength;
+         memmove(&headerLength,  &(buffer[3]), 1); intelToHost(headerLength);
+         short messageID;
+         memmove(&messageID,     &(buffer[4]), 2); intelToHost(messageID);
+         char messageType;
+         memmove(&messageType,   &(buffer[6]), 1); intelToHost(messageType);
+         char portAddress;
+         memmove(&portAddress,   &(buffer[7]), 1); intelToHost(portAddress);
+         short messageLength;
+         memmove(&messageLength, &(buffer[8]), 2); intelToHost(messageLength);
+         short sequence;
+         memmove(&sequence,     &(buffer[10]), 2); intelToHost(sequence);
+         char idleTime;
+         memmove(&idleTime,     &(buffer[12]), 1); intelToHost(idleTime);
+         char timeStatus;
+         memmove(&timeStatus,   &(buffer[13]), 1); intelToHost(timeStatus);
+         short week;
+         memmove(&week,         &(buffer[14]), 2); intelToHost(week);
+         long msecOfWeek;
+         memmove(&msecOfWeek,   &(buffer[16]), 4); intelToHost(msecOfWeek);
+         long rxStatus;
+         memmove(&rxStatus,     &(buffer[20]), 4); intelToHost(rxStatus);
+         short reserved;
+         memmove(&reserved,     &(buffer[24]), 2); intelToHost(reserved);
+         short rxSWVersion;
+         memmove(&rxSWVersion,  &(buffer[26]), 2); intelToHost(rxSWVersion);
+               
+            // parse data
+            // Ref OEM4 Manual pg 206
+         short prn,track=1;
+         long gpsSOW;
+
+            // get PRN and timetag 
+         memmove(&templ, &(buffer[28]), 4);
+         intelToHost(templ);
+         prn = short(templ);
+         memmove(&gpsWeek, &(buffer[32]), 4);      // long gpsWeek is member data
+         intelToHost(gpsWeek);
+         memmove(&gpsSOW, &(buffer[36]), 4);
+         intelToHost(gpsSOW);
+      
+            // convert the 3 subframes and create EngEphemeris
+         long subframe[10];
+         for(j=0; j<3; j++) {
+            k = 40 + j*30;
+
+            if(debug) {
+               cout << "Subframe " << setfill('0') << j+1;
+               for(i=0; i<30; i++)
+                  cout << " " << hex << uppercase << setw(2) << int(buffer[k+i]);
+               cout << dec << setfill(' ') << endl;
+            }
+
+            for(i=0; i<10; i++) {
+               subframe[i] = (buffer[k] << 22)+(buffer[k+1] << 14)+(buffer[k+2] << 6);
+               k += 3;
+            }
+            if(!eeph.addSubframe(subframe,gpsWeek,prn,track)){
+               if(debug) cout << "Failed to convert RAWEPH subframe " << j+1
+                  << ", prn " << prn << " at time " << gpsWeek << " " << gpsSOW
+                  << endl;
+            }
+         }
+      }  // end RAWEPH record
+
+      else if(rectype == REPB) {                   // OEM2
+
+         long prn;
+         short track=1;
+
+         // get PRN
+         memmove(&prn,&(buffer[12]), 4);
+         intelToHost(prn);
+
+         // be sure week is defined
+         if(gpsWeek == -1) {
+            DayTime sysTime;
+            gpsWeek = long(sysTime.GPSfullweek());
+         }
+
+            // convert the 3 subframes and create EngEphemeris
+         long subframe[10];
+         for(j=0; j<3; j++) {
+            k = 16 + j*30;
+
+            if(debug) {
+               cout << "Subframe " << setfill('0') << j+1;
+               for(i=0; i<30; i++)
+                  cout << " " << hex << uppercase << setw(2) << int(buffer[k+i]);
+               cout << dec << setfill(' ') << endl;
+            }
+
+            for(i=0; i<10; i++) {
+               subframe[i] = (buffer[k] << 22)+(buffer[k+1] << 14)+(buffer[k+2] << 6);
+               k += 3;
+            }
+            if(!eeph.addSubframe(subframe,gpsWeek,short(prn),track)){
+               if(debug) cout << "Failed to convert REPB subframe " << j+1
+                     << ", prn " << prn << endl;
+            }
+         }
+         
+      }  // end REPB record
+
+      // convert it to Rinex
+      RinexNavData rnd(eeph);
+
+      return rnd;
+
+   }  // end NovatelData::operator RinexNavData()
+
+
+   // --------------------------------------------------------------------------------
+   NovatelData::operator RinexObsData()
+      throw(Exception)
+   {
+      if(!isValid() || !isObs()) {
+         Exception e("Invalid or non-Obs record");
+         GPSTK_THROW(e);
+      }
+
+      int i,j;
+      short temps;
+      long nobs;            // number of observation records (may be 2/PRN: L1 and L2)
+      SatID sat;
+      RinexObsData rod;     // this will be returned
+      RinexObsData::RinexDatum rd;
+      RinexObsData::RinexSatMap::iterator satit;
+      RinexObsData::RinexObsTypeMap::iterator obsit;
+
+      if(     rectype == RGEB) {             // OEM2
+
+
+      }  // end RGEB record
+
+      else if(rectype == RGEC) {             // OEM2
+            // Ref OEM2 Manual pg 97
+
+         if(debug) {
+            cout << "Header " << setfill('0') << hex << uppercase;
+            for(i=0; i<24; i++) cout << " " << setw(2) << int(buffer[i]);
+            cout << dec << setfill(' ') << endl;
+         }
+
+            // number of observation records to follow
+         memmove(&temps, &(buffer[12]), 2);
+         intelToHost(temps);
+         nobs = long(temps);
+
+            // GPS week (long gpsWeek is member data)
+         memmove(&temps, &(buffer[14]), 2);
+         intelToHost(temps);
+
+            // resolve the week number ambiguity
+         if(gpsWeek == -1) {
+            DayTime sysTime;
+            gpsWeek = long(sysTime.GPSfullweek());
+         }
+         gpsWeek = long(temps) + 1024*(gpsWeek/1024);
+
+            // seconds of week * 100
+         long gpsSOW;
+         memmove(&gpsSOW, &(buffer[16]), 4);
+         intelToHost(gpsSOW);
+
+            // receiver status
+         long rxStatus;
+         memmove(&rxStatus, &(buffer[20]), 4);
+         
+            // put timetag into rod
+         rod.time = DayTime(gpsWeek,gpsSOW/100.);
+         rod.epochFlag = 0;
+         rod.clockOffset = 0.0;     // don't have it ?
+         rod.numSvs = 0;
+
+            // loop over observation records
+         for(i=0; i<nobs; i++) {
+            unsigned long data[5];
+            for(j=0; j<5; j++)
+               memmove(&data[j], &(buffer[24+i*20+j*4]), 4);
+
+            int prn         =     int(data[0] & 0x0000003FL);
+
+            double SNR      = double((data[0] & 0x000007C0L) >>  6);
+
+            double locktime = double((data[0] & 0xFFFFF800L) >> 11);
+
+            double Ph;
+            if(data[1] & 0x80000000L)     // 2s complement
+               Ph =  double(data[1] ^ 0x7FFFFFFFL + 1);
+            else
+               Ph =  double(data[1]);
+
+            double Doppler  = double((data[2] & 0xFFFFFFF0L) >> 4);
+            if(data[2] & 0x80000000L)     // 2s complement
+               Doppler = -double((((data[2] & 0xFFFFFFF0L) ^ 0xFFFFFFF0L) >> 4)+1);
+   
+            //                                               this is 0xFFFFFFFF + 1
+            double Pr       =  double(data[2] & 0x0000000FL) * 4294967296.
+                             + double(data[3]);
+            // could the pseudorange ever be negative?
+            if(data[2] & 0x00000008L)     // 2s complement
+               Pr = -double((data[2] & 0x0000000FL) ^ 0x0000000FL) * 4294967296.
+                    - double(data[3]                ^ 0xFFFFFFFFL  + 1);
+
+            double SdPh     =     int(data[4] & 0x0000000FL);
+
+            double SdPr     = double((data[4] & 0x000000F0L) >>  4);
+
+            long TrackStatus =  long((data[4] & 0xFFFFFF00L) >>  8);
+            // the rest are reserved
+
+            // swap bytes
+            intelToHost(prn);
+            intelToHost(SNR);
+            intelToHost(locktime);
+            intelToHost(Ph);
+            intelToHost(Doppler);
+            intelToHost(Pr);
+            intelToHost(SdPr);
+            intelToHost(SdPh);
+
+            // convert to physical units
+            SNR += 20.;             // dB-Hz, but 51 means >=51, and 20 means <=20.
+            locktime /= 32.;        // sec
+            Doppler /= 256.;        // Hz
+            Pr /= 128.;             // m
+            Ph /= 256.;             // cycles
+            SdPr = (SdPr + 1.)/16.; // m
+            SdPh = (SdPh + 1)/512.; // cycles
+
+            // break out the TrackStatus
+            // cf. Table 5-6, pg 95 of OEM2 manual
+            int TrackState   = int( TrackStatus & 0x0000000FL);
+            int Channel      = int((TrackStatus & 0x000001F0L) >>  4);
+            bool PhaseLock   = bool(TrackStatus & 0x00000200L);
+            bool ParityKnown = bool(TrackStatus & 0x00000400L);
+            bool CodeLock    = bool(TrackStatus & 0x00000800L);
+            int Frequency    = int((TrackStatus & 0x00100000L) >> 20); // 0:L1 1:L2
+            // CodeType is 0: CA 1: P 2: Pcodeless
+            int CodeType   = int((TrackStatus & 0x00600000L) >> 21);
+
+            if(!PhaseLock || !CodeLock) continue;
+
+            // correct the phase for rollovers
+            // ref. OEM2 manual pg 97
+            double ADRrolls = ((-Pr/(Frequency==0 ? wl1 : wl2))-Ph)/PhaseRollover;
+            Ph += long(ADRrolls + (ADRrolls > 0 ? 0.5 : -0.5)) * PhaseRollover;
+
+            //apparently the Novatel convert utility ignores this too
+            //ignore if(!ParityKnown) Ph = 0.0;
+
+            // fill RinexObsData rod
+            sat = SatID(prn,SatID::systemGPS);
+            satit = rod.obs.find(sat);          // find the sat
+            if(satit == rod.obs.end()) {        // not there - add this sat
+               RinexObsData::RinexObsTypeMap rotm;
+               rod.obs[sat] = rotm;
+               rod.numSvs++;
+               satit = rod.obs.find(sat);       // now find it
+            }
+
+            // for convenience, reference the obs data map
+            RinexObsData::RinexObsTypeMap& obs = satit->second;
+            if(Frequency == 0) {       // frequency = L1
+               rd.ssi = rd.lli = 0; rd.data = -Ph;
+               obs[RinexObsHeader::L1] = rd;                         // L1
+
+               rd.ssi = rd.lli = 0; rd.data = Pr;
+               if(CodeType == 0) obs[RinexObsHeader::C1] = rd;       // C1
+               else              obs[RinexObsHeader::P1] = rd;       // P1
+
+               rd.ssi = rd.lli = 0; rd.data = -Doppler;
+               obs[RinexObsHeader::D1] = rd;                         // D1
+
+               rd.ssi = rd.lli = 0; rd.data = SNR;                   // S1
+               obs[RinexObsHeader::S1] = rd;
+            }
+            else {
+               rd.ssi = rd.lli = 0; rd.data = Ph;
+               obs[RinexObsHeader::L2] = rd;                         // L2
+
+               rd.ssi = rd.lli = 0; rd.data = Pr;
+               obs[RinexObsHeader::P2] = rd;                         // P2
+
+               rd.ssi = rd.lli = 0; rd.data = -Doppler;
+               obs[RinexObsHeader::D2] = rd;                         // D2
+
+               rd.ssi = rd.lli = 0; rd.data = SNR;
+               obs[RinexObsHeader::S2] = rd;                         // S2
+            }
+
+         }
+
+      }  // end RGEC record
+
+      else {                                 // all OEM4 obs records
+
+            // header
+            // Ref OEM4 Manual pg 16
+         unsigned char headerLength;
+         memmove(&headerLength, &(buffer[3]), 1);  intelToHost(headerLength);
+         short messageID;
+         memmove(&messageID, &(buffer[4]), 2);     intelToHost(messageID);
+         char messageType;
+         memmove(&messageType, &(buffer[6]), 1);   intelToHost(messageType);
+         char portAddress;
+         memmove(&portAddress, &(buffer[7]), 1);   intelToHost(portAddress);
+         short messageLength;
+         memmove(&messageLength, &(buffer[8]), 2); intelToHost(messageLength);
+         short sequence;
+         memmove(&sequence, &(buffer[10]), 2);     intelToHost(sequence);
+         char idleTime;
+         memmove(&idleTime, &(buffer[12]), 1);     intelToHost(idleTime);
+         char timeStatus;
+         memmove(&timeStatus, &(buffer[13]), 1);   intelToHost(timeStatus);
+         short week;
+         memmove(&week, &(buffer[14]), 2);         intelToHost(week);
+         long msecOfWeek;
+         memmove(&msecOfWeek, &(buffer[16]), 4);   intelToHost(msecOfWeek);
+         long rxStatus;
+         memmove(&rxStatus, &(buffer[20]), 4);     intelToHost(rxStatus);
+         short reserved;
+         memmove(&reserved, &(buffer[24]), 2);     intelToHost(reserved);
+         short rxSWVersion;
+         memmove(&rxSWVersion, &(buffer[26]), 2);  intelToHost(rxSWVersion);
+               
+            // put timetag into rod
+         rod.time = DayTime(week,double(msecOfWeek)/1000.);
+         rod.epochFlag = 0;
+         rod.clockOffset = 0.0;     // don't have it ?
+
+         if(     rectype == RANGE) {
+            // Ref OEM4 Manual pg 198-201
+
+            nobs = 0;
+            memmove(&nobs, &(buffer[28]), 4);
+            intelToHost(nobs);
+
+            rod.numSvs = 0;
+            for(i=0; i<nobs; i++) {
+               unsigned short prn,reserved;
+               unsigned long TrackStatus;
+               float PrStd,PhStd,Doppler,SNR,locktime;
+               double Pr,Ph;
+
+               memmove(&prn,         &(buffer[32+i*44]), 2);
+               intelToHost(prn);
+               memmove(&reserved,    &(buffer[34+i*44]), 2);
+               intelToHost(reserved);
+               memmove(&Pr,          &(buffer[36+i*44]), 8);
+               intelToHost(Pr);
+               memmove(&PrStd,       &(buffer[44+i*44]), 4);
+               intelToHost(PrStd);
+               memmove(&Ph,          &(buffer[48+i*44]), 8);
+               intelToHost(Ph);
+               memmove(&PhStd,       &(buffer[56+i*44]), 4);
+               intelToHost(PhStd);
+               memmove(&Doppler,     &(buffer[60+i*44]), 4);
+               intelToHost(Doppler);
+               memmove(&SNR,         &(buffer[64+i*44]), 4);
+               intelToHost(SNR);
+               memmove(&locktime,    &(buffer[68+i*44]), 4);
+               intelToHost(locktime);
+               memmove(&TrackStatus, &(buffer[72+i*44]), 4);
+               intelToHost(TrackStatus);
+
+               // break out the TrackStatus
+               // cf. Table 56, pg 199 of OEM4 manual
+               int TrackState = int( TrackStatus & 0x0000001FL);
+               int Channel    = int((TrackStatus & 0x000003E0L) >>  5);
+               bool PhaseLock = bool(TrackStatus & 0x00000400L);
+               bool CodeLock  = bool(TrackStatus & 0x00001000L);
+               int Frequency  = int((TrackStatus & 0x00600000L) >> 21); // 0:L1 1:L2
+               // CodeType is 0CA 1P 2Pcodeless
+               int CodeType   = int((TrackStatus & 0x03800000L) >> 23);
+               bool HalfCycle = bool(TrackStatus & 0x10000000L);
+
+               if(!PhaseLock || !CodeLock) continue;        // data is not reliable
+
+               // fill RinexObsData rod
+               sat = SatID(prn,SatID::systemGPS);
+               satit = rod.obs.find(sat);          // find the sat
+               if(satit == rod.obs.end()) {        // not there - add this sat
+                  RinexObsData::RinexObsTypeMap rotm;
+                  rod.obs[sat] = rotm;
+                  rod.numSvs++;
+                  satit = rod.obs.find(sat);       // now find it
+               }
+
+               // for convenience, reference the obs data map inside rod
+               RinexObsData::RinexObsTypeMap& obs = satit->second;
+               if(Frequency == 0) {       // frequency = L1
+                  rd.ssi = rd.lli = 0; rd.data = -Ph;
+                  obs[RinexObsHeader::L1] = rd;                      // L1
+
+                  rd.ssi = rd.lli = 0; rd.data = Pr;
+                  if(CodeType == 0) obs[RinexObsHeader::C1] = rd;    // C1
+                  else              obs[RinexObsHeader::P1] = rd;    // P1
+
+                  rd.ssi = rd.lli = 0; rd.data = Doppler;
+                  obs[RinexObsHeader::D1] = rd;                      // D1
+
+                  rd.ssi = rd.lli = 0; rd.data = SNR;
+                  obs[RinexObsHeader::S1] = rd;                      // S1
+               }
+               else {
+                  rd.ssi = rd.lli = 0; rd.data = -Ph;
+                  obs[RinexObsHeader::L2] = rd;                      // L2
+
+                  rd.ssi = rd.lli = 0; rd.data = Pr;
+                  obs[RinexObsHeader::P2] = rd;                      // P2
+
+                  rd.ssi = rd.lli = 0; rd.data = Doppler;
+                  obs[RinexObsHeader::D2] = rd;                      // D2
+
+                  rd.ssi = rd.lli = 0; rd.data = SNR;
+                  obs[RinexObsHeader::S2] = rd;                      // S2
+               }
+
+            }
+
+         }  // end RANGE record
+
+         else if(rectype == RANGECMP) {
+            // Ref OEM4 Manual pg 202-203
+
+            nobs = 0;
+            memmove(&nobs, &(buffer[28]), 4);
+            intelToHost(nobs);
+
+            rod.numSvs = 0;
+            for(i=0; i<nobs; i++) {
+               unsigned long data[6];
+               for(j=0; j<6; j++)
+                  memmove(&data[j], &(buffer[32+i*24+j*4]), 4);
+
+               long TrackStatus =        data[0];
+               // this is what is in the manual - its wrong
+               //double Doppler =   double(data[1] & 0x0FFFFFFFL);
+               // this is not documented in the manual...
+               //double Doppler =   double(data[1] & 0x000FFFFFL);
+               //if(data[1] & 0x0FF00000L == 0x0FF00000L) Doppler = -Doppler;
+               // try this - cf the OEM2 manual and implementation above
+               double Doppler  = double((data[1] & 0x0FFFFFFFL));
+               if(data[1] & 0x08000000L)     // 2s complement
+                  Doppler = -double(((data[1] & 0x0FFFFFFFL) ^ 0x0FFFFFFFL) + 1);
+               double Pr =       double((data[1] & 0xF0000000L) >> 28)
+                                + double(data[2]) * 16.;
+               double Ph =        double(data[3]);
+               int SdPrCode =        int(data[4] & 0x0000000FL);
+               double SdPh =     double((data[4] & 0x000000F0L) >>  4);
+               int prn =            int((data[4] & 0x0000FF00L) >>  8);
+               double locktime = double((data[4] & 0xFFFF0000L) >> 16)
+                               +  double(data[5] & 0x0000001FL);
+               double SNR     =  double((data[5] & 0x000003E0L) >>  5);
+               // the rest are reserved
+ 
+               // swap bytes
+               intelToHost(Doppler);
+               intelToHost(Pr);
+               intelToHost(Ph);
+               intelToHost(SdPrCode);  // code - see pg 203 of OEM4 manual
+               intelToHost(SdPh);
+               intelToHost(prn);
+               intelToHost(locktime);
+               intelToHost(SNR);
+
+               // convert to physical units
+               Doppler /= 256.;        // Hz
+               Pr /= 128.;             // m
+               Ph /= 256.;             // cycles
+               double SdPr;
+               switch(SdPrCode) {      // this is just a code
+                  // ref table on pg 203 of OEM4 manual
+                  case  0: SdPr =   0.050; break; // m
+                  case  1: SdPr =   0.075; break; // m
+                  case  2: SdPr =   0.113; break; // m
+                  case  3: SdPr =   0.169; break; // m
+                  case  4: SdPr =   0.253; break; // m
+                  case  5: SdPr =   0.380; break; // m
+                  case  6: SdPr =   0.570; break; // m
+                  case  7: SdPr =   0.854; break; // m
+                  case  8: SdPr =   1.281; break; // m
+                  case  9: SdPr =   2.375; break; // m
+                  case 10: SdPr =   4.750; break; // m
+                  case 11: SdPr =   9.500; break; // m
+                  case 12: SdPr =  19.000; break; // m
+                  case 13: SdPr =  38.000; break; // m
+                  case 14: SdPr =  76.000; break; // m
+                  case 15: SdPr = 152.000; break; // m
+                  default: SdPr =    0.00; break;
+               }
+               SdPh = (SdPh + 1)/512.; // cycles
+               locktime /= 32.;        // seconds
+               SNR += 20.;             // dB-Hz
+               // NB SNR 51 means >=51, and 20 means <=20.
+
+               // break out the TrackStatus
+               // cf. Table 56, pg 199 of OEM4 manual
+               int TrackState = int( TrackStatus & 0x0000001FL);
+               int Channel    = int((TrackStatus & 0x000003E0L) >>  5);
+               bool PhaseLock = bool(TrackStatus & 0x00000400L);
+               bool CodeLock  = bool(TrackStatus & 0x00001000L);
+               int Frequency  = int((TrackStatus & 0x00600000L) >> 21); // 0:L1 1:L2
+               // CodeType is 0CA 1P 2Pcodeless
+               int CodeType   = int((TrackStatus & 0x03800000L) >> 23);
+               bool HalfCycle = bool(TrackStatus & 0x10000000L);
+
+               if(!PhaseLock || !CodeLock) continue;        // data is not reliable
+
+               // correct the phase for rollovers
+               // cf. OEM4 manual pg 203
+               double ADRrolls = ((Pr/(Frequency==0 ? wl1 : wl2)) + Ph)/PhaseRollover;
+               Ph -= long(ADRrolls + (ADRrolls > 0 ? 0.5 : -0.5)) * PhaseRollover;
+
+               // consider debiasing the phase
+
+               // use track status flags to set lli on the phase
+
+               // what to do with HalfCycle?
+
+               // fill RinexObsData rod
+               sat = SatID(prn,SatID::systemGPS);
+               satit = rod.obs.find(sat);          // find the sat
+               if(satit == rod.obs.end()) {        // not there - add this sat
+                  RinexObsData::RinexObsTypeMap rotm;
+                  rod.obs[sat] = rotm;
+                  rod.numSvs++;
+                  satit = rod.obs.find(sat);       // now find it
+               }
+
+               // for convenience, reference the obs data map inside rod
+               RinexObsData::RinexObsTypeMap& obs = satit->second;
+               if(Frequency == 0) {       // frequency = L1
+                  rd.ssi = rd.lli = 0; rd.data = -Ph;
+                  obs[RinexObsHeader::L1] = rd;                      // L1
+
+                  rd.ssi = rd.lli = 0; rd.data = Pr;
+                  if(CodeType == 0) obs[RinexObsHeader::C1] = rd;    // C1
+                  else              obs[RinexObsHeader::P1] = rd;    // P1
+
+                  rd.ssi = rd.lli = 0; rd.data = Doppler;
+                  obs[RinexObsHeader::D1] = rd;                      // D1
+
+                  rd.ssi = rd.lli = 0; rd.data = SNR;
+                  obs[RinexObsHeader::S1] = rd;                      // S1
+               }
+               else {
+                  rd.ssi = rd.lli = 0; rd.data = -Ph;
+                  obs[RinexObsHeader::L2] = rd;                      // L2
+
+                  rd.ssi = rd.lli = 0; rd.data = Pr;
+                  obs[RinexObsHeader::P2] = rd;                      // P2
+
+                  rd.ssi = rd.lli = 0; rd.data = Doppler;
+                  obs[RinexObsHeader::D2] = rd;                      // D2
+
+                  rd.ssi = rd.lli = 0; rd.data = SNR;
+                  obs[RinexObsHeader::S2] = rd;                      // S2
+               }
+
+            }  // end loop over obs
+
+         }  // end RANGECMP record
+
+      }  // end all OEM4 obs records
+
+      return rod;
+
+   }  // end NovatelData::operator RinexObsData()
+
+}  // end namespace gpstk
diff --git a/dev/apps/converters/NovatelData.hpp b/dev/apps/converters/NovatelData.hpp
new file mode 100644
index 0000000..6cab0bf
--- /dev/null
+++ b/dev/apps/converters/NovatelData.hpp
@@ -0,0 +1,184 @@
+#pragma ident "$Id$"
+
+
+/**
+ * @file NovatelData.hpp
+ * gpstk::NovatelData - container for Novatel data, with methods to convert to Rinex
+ */
+
+#ifndef GPSTK_NOVATEL_DATA_HPP
+#define GPSTK_NOVATEL_DATA_HPP
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include <ostream>
+#include "Exception.hpp"
+#include "FFStream.hpp"
+#include "RinexNavData.hpp"
+#include "RinexObsData.hpp"
+#include "NovatelStream.hpp"
+
+namespace gpstk
+{
+   /** @addtogroup NovatelGroup */
+   //@{
+
+   /// Read Novatel binary records and parse into Rinex. Currently supports
+   /// the basic data records of OEM2 and OEM4 files only; others are yet to be
+   /// implemented.
+   /// Ref. (OEM2) 'GPSCard Command Descriptions' Manual (Rev 3)
+   /// Ref. 'OEM4 Family of Receivers,' Users Manual Volume 2 (OM-20000047 Rev 12)
+   class NovatelData : public FFData
+   {
+   public:
+         /// block types implemented here
+      enum RecType
+      {
+         Unknown=0,  ///< unknown block type
+            // OEM2 records
+         RGEB,       ///< observations -- not implemented
+         RGEC,       ///< observations
+         POSB,       ///< position solution -- not implemented
+         REPB,       ///< ephemeris
+         RCSB,       ///< receiver stats -- not implemented
+            // OEM4 records
+         RANGE,      ///< range and phase data (synchronous)
+         RANGECMP,   ///< range and phase data, compressed (synchronous)
+         RAWEPHEM    ///< ephemeris data (asynchronous)
+      };
+
+         /// Names of the record types : RecNames[rectype]
+      static const std::string RecNames[];
+
+         /// Default constructor
+      NovatelData(void) : rectype(Unknown), datasize(0), headersize(0), gpsWeek(-1)
+         {}
+
+         /// Destructor
+      virtual ~NovatelData(void) {}
+
+         /// Return true if this is a valid Novatel record.
+         /// Test the validity of the record with this before further processing.
+      bool isValid(void) const;
+
+         /// This class is not header
+      virtual bool isHeader(void) const { return false; }
+
+         /// This class is data
+      virtual bool isData(void) const { return true; }
+
+         /// True if this record is an ephemeris record
+         /// Test the identity of the record with this before casting into Rinex.
+      bool isNav(void) const;
+
+         /// True if this record is observation data
+         /// Test the identity of the record with this before casting into Rinex.
+      bool isObs(void) const;
+
+         /// True if this record is auxiliary data (not Nav, not Obs)
+         /// (only one of isNav(), isObs() and isAux() is true
+      bool isAux(void) const;
+
+         /// True if this record belongs to OEM2 receivers
+      bool isOEM2(void) const;
+
+         /// True if this record belongs to OEM4 receivers
+      bool isOEM4(void) const;
+
+         /// Dump the contents of the record to the ostream \c str.
+      virtual void dump(std::ostream& str) const;
+
+         /// set the week number of the data, this is required for
+         /// OEM2 nav records that are processed before any obs records
+      void setWeek(long& gpsweek) { gpsWeek = gpsweek; }
+
+         /// cast *this into an gpstk::RinexNavData.
+         /// @throw if the record is invalid or not an ephemeris (isNav()==false)
+      operator RinexNavData() throw(gpstk::Exception);
+
+         /// cast *this into a gpstk::RinexObsData
+         /// @throw if the record is invalid or not an observation (isObs()==false)
+      operator RinexObsData() throw(gpstk::Exception);
+
+         /// public data members
+      RecType rectype;              ///< record type (cf. enum RecType)
+      int recnum;                   ///< record number (byte 4 of record)
+      long datasize;                ///< size of data in bytes
+      int headersize;               ///< size of header in bytes (=3 for OEM2)
+
+   protected:
+         /// Write this record to the stream \a s.
+      virtual void reallyPutRecord(FFStream& s) const 
+         throw(std::exception, gpstk::StringUtils::StringException, 
+               gpstk::FFStreamError);
+
+         /**
+          * Read a NovatelData record from the FFStream \c s. 
+          * If an error is encountered, the function will 
+          * return the stream to its original state and mark its fail-bit.
+          * @throws FFStreamError when exceptions(failbit) is set and
+          *  a read or formatting error occurs.  This also resets the
+          *  stream to its pre-read position.
+          */
+      virtual void reallyGetRecord(FFStream& s)
+         throw(std::exception, gpstk::StringUtils::StringException, 
+               gpstk::FFStreamError);
+
+   private:
+         /// private data members
+      unsigned char buffer[1024];   ///< buffer for raw data
+
+         /// Reference GPS week, for OEM2, where the nav records require a GPS week,
+         /// but only the obs records have one, and then it is 10-bit.
+         /// This epoch will be used to remove the ambiguity in the 10-bit week
+         /// number of the obs records, and then will provide a week number for
+         /// for the nav records. If not set by the user, it will be set by the
+         /// system time, and then by the first obs record.
+      long gpsWeek;
+      
+   }; // end class NovatelData
+
+   //@}
+
+}  // end namespace gpstk
+
+#endif
diff --git a/dev/apps/converters/NovatelStream.hpp b/dev/apps/converters/NovatelStream.hpp
new file mode 100644
index 0000000..c4bc919
--- /dev/null
+++ b/dev/apps/converters/NovatelStream.hpp
@@ -0,0 +1,97 @@
+#pragma ident "$Id$"
+
+
+/**
+ * @file NovatelStream.hpp
+ * gpstk::NovatelStream - binary Novatel file stream container.
+ */
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#ifndef GPSTK_NOVATELSTREAM_HPP
+#define GPSTK_NOVATELSTREAM_HPP
+
+#include <vector>
+#include <map>
+
+#include "FFBinaryStream.hpp"
+
+namespace gpstk
+{
+      /** @defgroup NovatelGroup Novatel receiver utilities */
+      //@{
+
+      /** 
+       * The stream used to obtain data from a binary Novatel File.
+       * \sa NovatelData
+       */
+   class NovatelStream : public FFBinaryStream
+   {
+   public:
+         /// default constructor
+      NovatelStream() {}
+
+         /**
+          * Constructor
+          * @param fn the name of the Novatel file to be opened
+          * @param mode the ios::openmode to be used on \a fn
+          */
+      NovatelStream(const char* fn,
+                    std::ios::openmode mode=std::ios::in|std::ios::binary)
+            : FFBinaryStream(fn, mode)
+         {}
+
+         /// destructor per the coding standards
+      virtual ~NovatelStream() {}
+
+         /// overrides open
+      virtual void open(const char* fn,
+                        std::ios::openmode mode=std::ios::in|std::ios::binary)
+         { FFBinaryStream::open(fn, mode); }
+
+   }; // class NovatelStream
+
+   //@}
+
+} // namespace gpstk
+
+#endif
diff --git a/dev/apps/converters/novaRinex.cpp b/dev/apps/converters/novaRinex.cpp
new file mode 100644
index 0000000..05f6873
--- /dev/null
+++ b/dev/apps/converters/novaRinex.cpp
@@ -0,0 +1,1151 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+/**
+ * @file novaRinex.cpp
+ * gpstk::novaRinex - convert Novatel binary data files to RINEX format
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <time.h>
+#include <string>
+#include <map>
+
+// GPSTk
+#include "StringUtils.hpp"
+#include "DayTime.hpp"
+#include "CommandOption.hpp"
+#include "CommandOptionWithTimeArg.hpp"
+#include "CommandOptionParser.hpp"
+#include "NovatelStream.hpp"
+#include "NovatelData.hpp"
+#include "RinexObsStream.hpp"
+#include "RinexNavStream.hpp"
+#include "RinexObsHeader.hpp"
+#include "Triple.hpp"
+//#include "RinexBinex.hpp"
+
+using namespace std;
+using namespace gpstk;
+using namespace StringUtils;
+
+// -----------------------------------------------------------------------------------
+string Prgm("novaRinex");                 // name of this program
+string Vers("v2.0 4/07");                 // version - keep to 10 char
+// 1.0 8/05
+// 1.1 2/06 process obs only when datasize > 4 - empty records were setting FirstEpoch
+// 1.2 6/06 catch exceptions, and allow blanks on cmd line and in input file
+// 1.3 7/06 correctly open files in other than current directory, don't print input
+//          errors when help is the only option
+// 1.4 7/06 handle exceptions
+// 1.5 7/06 correct handling of header inputs
+// 2.0 4/07 added Binex output -- commented out
+
+// -----------------------------------------------------------------------------------
+// global data, mostly to save information to go in the final RINEX header
+// for computing the data time interval
+int ndt[9];
+double bestdt[9];
+// epochs
+DayTime CurrEpoch,PrevEpoch,FirstEpoch;
+// table of PRN/#obs
+map<SatID,vector<int> > table;
+vector<int> totals;
+// Command line input
+bool help,Debug,verbose;
+DayTime BegTime,EndTime;
+string NovatelFile, RinexObsFile, RinexNavFile; //, BinexFile;
+string InputDirectory;
+// header fields
+bool FillOptionalHeader;
+Triple HDAntPos,HDAntOffset;      // TD
+vector<string> HDcomments;
+vector<RinexObsHeader::RinexObsType> OutputTypes;
+long gpsWeek;
+bool debias;
+
+//------------------------------------------------------------------------------------
+// other global data
+string TempFileName;       // initial output is here, before header is filled
+NovatelStream instr;
+RinexObsStream rostr;
+RinexNavStream rnstr;
+RinexObsHeader roh;        // used in CommandLine
+// indexes for the std obs types in the header
+int inC1,inP1,inL1,inD1,inS1,inP2,inL2,inD2,inS2;
+
+// -----------------------------------------------------------------------------------
+// command line input
+int GetCommandInput(int argc, char **argv) throw(Exception);
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception);
+void DumpCommandLine(ostream& ofs = cout) throw(Exception);
+
+// open input and output files
+int OpenFiles(void) throw(Exception);
+
+// fill header initially
+void InitializeHeaders(RinexObsHeader& roh, RinexNavHeader& rnh) throw(Exception);
+
+// update saved information for revised header
+void UpdateInformation(RinexObsData& rod) throw(Exception);
+
+// final header update, and write out
+int UpdateHeader(string& TempFile, string& OutputFile, RinexObsHeader& rh)
+   throw(Exception);
+
+// final obs output - modify header and write to the real output file name
+string GetTempFileName(void) throw(Exception);
+int FillHeaderAndReplaceFile(string& TempFile,string& OutputFile,RinexObsHeader& rh)
+   throw(Exception);
+
+// -----------------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+   try {
+
+      int i,n,nobs,nnav;
+      double dt;
+
+      // get the current system time
+      time_t timer;
+      struct tm *tblock;
+      timer = time(NULL);
+      tblock = localtime(&timer);
+      CurrEpoch.setLocalTime();
+
+      i = GetCommandInput(argc, argv);
+      if(i) return 0;
+      if(verbose) {
+         cout << Prgm << " version " << Vers << " run " << CurrEpoch << endl;
+         DumpCommandLine();
+      }
+
+      i = OpenFiles();
+      if(i) return i;
+
+      // declare data objects used for I/O
+      long bytesread=0;  // at the end, bytesread should equal the Novatel file size.
+      NovatelData novad;
+      novad.setWeek(gpsWeek);
+
+      RinexNavHeader rnh;
+      RinexNavData rnd;
+      RinexObsData rod;
+
+      // initialize the headers (indexes inC1,etc defined here)
+      InitializeHeaders(roh, rnh);
+
+      // write headers
+      rostr << roh;
+      rnstr << rnh;
+
+      // prep for the I/O loop
+      FirstEpoch = DayTime::BEGINNING_OF_TIME;
+      for(i=0; i<9; i++) ndt[i] = -1;
+
+      // show a counter
+      nobs = nnav = n = 0;
+      // loop over data in the Novatel file
+      try{
+         while(instr >> novad) {
+            if(Debug) cout << "Read " << NovatelData::RecNames[novad.rectype]
+               << " size " << novad.headersize << " + " << novad.datasize
+               << " number " << novad.recnum;
+
+            if(novad.isOEM2()) {
+               if(roh.recVers == string("OEM2/4")) roh.recVers = "OEM2";
+               if(Debug) cout << " OEM2";
+            }
+
+            if(novad.isOEM4()) {
+               if(Debug) cout << " OEM4";
+               if(roh.recVers == string("OEM2/4")) roh.recVers = "OEM4";
+            }
+
+            if(Debug) {
+               if(novad.isObs()) cout << " obs";
+               if(novad.isNav()) cout << " nav";
+               if(novad.isAux()) cout << " aux";
+               cout << endl;
+            }
+
+            bytesread += novad.datasize + novad.headersize;
+            if(novad.isOEM2()) bytesread += 1;      // CRC byte
+            if(novad.isOEM4()) bytesread += 4;      // CRC bytes
+
+            if(novad.isObs() && novad.datasize > 4) {   // obs only, with data
+               rod = RinexObsData(novad);    // convert
+               if(rod.time < BegTime) continue;
+               if(rod.time > EndTime) break;
+               if(Debug) rod.dump(cout);     // dump
+
+               rostr << rod;                 // write out
+               nobs++;
+
+               UpdateInformation(rod);
+            }
+            else if(novad.isNav()) {                                 // nav only
+               rnd = RinexNavData(novad);    // convert
+               if(Debug) rnd.dump(cout);     // dump
+               rnstr << rnd;                 // write out
+               nnav++;
+            }
+
+            n++;
+            if(verbose && !Debug) {
+               if(n == 100) cout << "Reading Novatel records: (100 per .)\n";
+               if(!(n % 100)) { cout << "."; cout.flush(); }
+               if(!(n % 8000)) cout << endl;
+            }
+
+         }  // end while loop over data
+      }
+      catch(Exception& e) { GPSTK_RETHROW(e); }
+
+      if(verbose && !Debug) cout << "\n";
+
+      //instr.clear();
+      instr.close();
+      //rostr.clear();
+      rostr.close();
+      //rnstr.clear();
+      rnstr.close();
+
+      // now update the header and (re)write it to the file
+      i = UpdateHeader(TempFileName, RinexObsFile, roh);
+
+      if(verbose) {
+         cout << "novaRinex read " << n
+            << " records, and wrote " << nobs
+            << " observations and " << nnav << " ephemerides\n";
+         cout << "Total bytes read = " << bytesread << endl;
+      }
+
+      return i;
+   }
+   catch(Exception& e) { cerr << "Caught exception\n" << e << endl; }
+   catch(...) { cerr << "Unknown error." << endl; }
+
+   return -1;
+}
+
+//------------------------------------------------------------------------------------
+int OpenFiles(void) throw(Exception)
+{
+try {
+   string filename;
+   if(InputDirectory.empty())
+      filename = NovatelFile;
+   else
+      filename = InputDirectory + string("/") + NovatelFile;
+   instr.open(filename.c_str(),ios::in | ios::binary);
+   if(!instr) {
+      cerr << "Failed to open input file " << NovatelFile << endl;
+      return -1;
+   }
+   if(verbose) cout << "Opened input file " << NovatelFile << endl;
+   instr.exceptions(fstream::failbit);
+
+   TempFileName = GetTempFileName();
+   rostr.open(TempFileName.c_str(),ios::out);
+   if(!rostr) {
+      cerr << "Failed to open temporary output file " << TempFileName << endl;
+      return -2;
+   }
+   rostr.exceptions(fstream::failbit);
+
+   rnstr.open(RinexNavFile.c_str(),ios::out);
+   if(!rnstr) {
+      cerr << "Failed to open output nav file " << RinexNavFile << endl;
+      return -3;
+   }
+   if(verbose) cout << "Opened output nav file " << RinexNavFile << endl;
+   rnstr.exceptions(fstream::failbit);
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+void InitializeHeaders(RinexObsHeader& roh, RinexNavHeader& rnh) throw(Exception)
+{
+try {
+   int i;
+   // observation header
+   roh.version = 2.1;
+   roh.fileType = "Observation";
+   roh.system = RinexSatID();
+   // use same format as writer in RinexObsHeader.cpp uses
+      // old "%04Y/%02m/%02d %02H:%02M:%02S");
+   roh.date = CurrEpoch.printf("%02m/%02d/%04Y %02H:%02M:%02S");
+   roh.antennaPosition = Triple(0.0,0.0,0.0);
+   roh.antennaOffset = Triple(0.0,0.0,0.0);
+   roh.wavelengthFactor[0] = 1;
+   roh.wavelengthFactor[1] = 1;
+
+   // must keep track of indexes - for use in table
+   if(Debug) cout << "Output obs types and indexes:";
+   inC1 = inP1 = inL1 = inD1 = inS1 = inP2 = inL2 = inD2 = inS2 = -1;
+   for(i=0; i<OutputTypes.size(); i++) {
+      if(OutputTypes[i] == RinexObsHeader::C1) inC1=i;
+      if(OutputTypes[i] == RinexObsHeader::P1) inP1=i;
+      if(OutputTypes[i] == RinexObsHeader::L1) inL1=i;
+      if(OutputTypes[i] == RinexObsHeader::D1) inD1=i;
+      if(OutputTypes[i] == RinexObsHeader::S1) inS1=i;
+      if(OutputTypes[i] == RinexObsHeader::P2) inP2=i;
+      if(OutputTypes[i] == RinexObsHeader::L2) inL2=i;
+      if(OutputTypes[i] == RinexObsHeader::D2) inD2=i;
+      if(OutputTypes[i] == RinexObsHeader::S2) inS2=i;
+      if(Debug)
+         cout << " " << RinexObsHeader::convertObsType(OutputTypes[i]) << ":" << i;
+   }
+   if(Debug) cout << endl;
+   roh.obsTypeList = OutputTypes;
+
+   roh.interval = 10.; // defined later by data
+   roh.firstObs = CurrEpoch; // defined later by data
+   roh.firstSystem = RinexSatID();
+   roh.lastObs = CurrEpoch; // defined later by data
+   roh.commentList.push_back("Created by GPSTK program " + Prgm + " " + Vers
+      + CurrEpoch.printf("%04Y/%02m/%02d %02H:%02M:%02S"));
+   for(i=0; i<HDcomments.size(); i++)
+      roh.commentList.push_back(HDcomments[i]);
+
+   roh.valid = RinexObsHeader::allValid21;
+   roh.valid |= RinexObsHeader::commentValid;
+
+   // navigation header
+   rnh.version = 2.1;
+   rnh.fileType = "Observation";
+   rnh.fileProgram = roh.fileProgram;
+   rnh.fileAgency = roh.fileAgency;
+   rnh.date = CurrEpoch.printf("%04Y/%02m/%02d %02H:%02M:%02S");
+   rnh.commentList.push_back("Created by GPSTK program " + Prgm + " " + Vers
+      + CurrEpoch.printf("%04Y/%02m/%02d %02H:%02M:%02S"));
+   for(i=0; i<HDcomments.size(); i++)
+      rnh.commentList.push_back(HDcomments[i]);
+
+   rnh.valid = RinexNavHeader::allValid21;
+   rnh.valid |= RinexNavHeader::commentValid;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+void UpdateInformation(RinexObsData& rod) throw(Exception)
+{
+try {
+   int i,j,k;
+   double dt;
+   SatID sat;
+
+   if(fabs(FirstEpoch - DayTime::BEGINNING_OF_TIME) < 1)  {
+      PrevEpoch = FirstEpoch = rod.time;
+      if(verbose) cout << "Set First Epoch to "
+         << rod.time.printf("%Y/%m/%d %H:%02M:%6.3f = %F/%10.3g") << endl;
+   }
+   else
+      PrevEpoch = CurrEpoch;
+   CurrEpoch = rod.time;
+
+      // compute the most likely value of dt, the time spacing of the data
+   dt=CurrEpoch-PrevEpoch;
+   if(dt > 0.0) {
+      for(i=0; i<9; i++) {
+         if(ndt[i] <= 0) { bestdt[i]=dt; ndt[i]=1; break; }
+         if(fabs(dt-bestdt[i]) < 0.0001) { ndt[i]++; break; }
+         if(i == 8) {
+            k = 0;
+            int nleast=ndt[k];
+            for(j=1; j<9; j++) if(ndt[j] <= nleast) {
+               k=j; nleast=ndt[j];
+            }
+            ndt[k]=1; bestdt[k]=dt;
+         }
+      }
+   }
+   else if(dt < 0.0)
+      cout << "Warning! observation records out of time order (previous > current) : "
+         << PrevEpoch.printf("%F %.3g") << " > " << CurrEpoch.printf("%F %.3g")
+         << endl;
+
+   RinexObsData::RinexSatMap::iterator jt;
+   map<SatID,vector<int> >::iterator it;
+   for(jt=rod.obs.begin(); jt != rod.obs.end(); jt++) {
+      // find this satellite in the table
+      sat = jt->first;
+      if((it=table.find(sat)) == table.end()) {
+         table[sat] = vector<int>(OutputTypes.size(),0);
+         it = table.find(sat);
+      }
+
+      // increment counter for each obstype found
+      if(inC1 >= 0 &&
+         rod.obs[sat][RinexObsHeader::C1].data != 0.0) {
+         table[sat][inC1]++;
+         totals[inC1]++;
+      }
+      if(inP1 >= 0 &&
+         rod.obs[sat][RinexObsHeader::P1].data != 0.0) {
+         table[sat][inP1]++;
+         totals[inP1]++;
+      }
+      if(inL1 >= 0 &&
+         rod.obs[sat][RinexObsHeader::L1].data != 0.0) {
+         table[sat][inL1]++;
+         totals[inL1]++;
+      }
+      if(inD1 >= 0 &&
+         rod.obs[sat][RinexObsHeader::D1].data != 0.0) {
+         table[sat][inD1]++;
+         totals[inD1]++;
+      }
+      if(inS1 >= 0 &&
+         rod.obs[sat][RinexObsHeader::S1].data != 0.0) {
+         table[sat][inS1]++;
+         totals[inS1]++;
+      }
+      if(inP2 >= 0 &&
+         rod.obs[sat][RinexObsHeader::P2].data != 0.0) {
+         table[sat][inP2]++;
+         totals[inP2]++;
+      }
+      if(inL2 >= 0 &&
+         rod.obs[sat][RinexObsHeader::L2].data != 0.0) {
+         table[sat][inL2]++;
+         totals[inL2]++;
+      }
+      if(inD2 >= 0 &&
+         rod.obs[sat][RinexObsHeader::D2].data != 0.0) {
+         table[sat][inD2]++;
+         totals[inD2]++;
+      }
+      if(inS2 >= 0 &&
+         rod.obs[sat][RinexObsHeader::S2].data != 0.0) {
+         table[sat][inS2]++;
+         totals[inS2]++;
+      }
+   }
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+string GetTempFileName(void) throw(Exception)
+{
+try {
+#ifdef _MSC_VER
+   char newname[L_tmpnam];
+   if(!tmpnam(newname)) {
+#else
+   char newname[]="TempnovaRinex.XXXXXX";
+   if(mkstemp(newname)==-1) {
+#endif
+      return string("");
+   }
+   remove(newname);
+   return string(newname);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+int UpdateHeader(string& TempFile, string& OutputFile, RinexObsHeader& rh)
+   throw(Exception)
+{
+try {
+   int i,j;
+
+   // update header
+   if(FillOptionalHeader) {
+      for(i=1,j=0; i<9; i++) if(ndt[i]>ndt[j]) j=i;
+      rh.interval = bestdt[j];
+      rh.valid |= RinexObsHeader::intervalValid;
+      rh.firstObs = FirstEpoch;
+      rh.lastObs = CurrEpoch;
+      rh.valid |= RinexObsHeader::lastTimeValid;
+   }
+
+   // edit out obs types that have no data
+   vector<RinexObsHeader::RinexObsType>::iterator it;
+   vector<int> indexes;    // indexes is a list of 'good' indexes into table
+   for(i=0, it=rh.obsTypeList.begin(); it != rh.obsTypeList.end(); i++) {
+      if(totals[i] <= 0) {
+         // no data for this obs type
+         if(Debug) cout << " Obs type " << RinexObsHeader::convertObsType(*it)
+            << " had no data - delete" << endl;
+         // delete from header
+         rh.obsTypeList.erase(it);
+      }
+      else {
+         indexes.push_back(i);      // this is an index with data
+         it++;
+      }
+   }
+   // now edit the table
+   map<SatID,vector<int> >::iterator jt;
+   for(jt=table.begin(); jt != table.end(); jt++) {      // for each sat..
+      for(j=0,i=0; i<indexes.size(); i++,j++)
+         if(j != indexes[i]) jt->second[j] = jt->second[indexes[i]];
+      jt->second.resize(indexes.size());
+   }
+   
+
+   // add the PRN/obs table
+   if(FillOptionalHeader && table.size() > 0) {
+      rh.numSVs = table.size();
+      rh.valid |= RinexObsHeader::numSatsValid;
+      rh.numObsForSat.clear();
+      rh.numObsForSat = table;
+      rh.valid |= RinexObsHeader::prnObsValid;
+   }
+
+      // re-open the obs file for reading, and replace the header
+   RinexObsStream InAgain(TempFile.c_str());
+   if(!InAgain) {
+      cerr << "Failed to re-open temp output Rinex obs file " << TempFile << endl;
+      return -3;
+   }
+   InAgain.exceptions(fstream::failbit);
+
+      // open the true output obs file for writing
+   RinexObsStream ROutStr(OutputFile.c_str(), ios::out);
+   if(!ROutStr) {
+      cerr << "Failed to open output Rinex obs file " << OutputFile << endl;
+      return -3;
+   }
+   if(verbose) cout << "Opened file " << OutputFile << " for RINEX output." << endl;
+   ROutStr.exceptions(fstream::failbit);
+
+   //   // open a BINEX stream
+   //BinexStream BinexOut;
+   //if(!BinexFile.empty()) {
+   //   BinexOut.open(BinexFile.c_str(), std::ios::out | std::ios::binary);
+   //   if(!BinexOut) {
+   //      cerr << "Failed to open output BINEX file " << BinexFile << endl;
+   //      return -3;
+   //   }
+   //   BinexOut.exceptions(ios_base::failbit | ios_base::badbit);
+   //   if(verbose) cout << "Opened file " << BinexFile << " for BINEX output." << endl;
+   //}
+
+      // read preliminary header, ...
+   RinexObsHeader rhjunk;
+   InAgain >> rhjunk;
+      // ...write out the full one
+   ROutStr << rh;
+      // write header to BINEX, also all the nav information
+   //if(!BinexFile.empty()) {
+   //   writeBinex(BinexOut, rh, char(4));     // 4 means 'from native receiver format'
+   //      // open nav file RinexNavFile, read it all and write it all to Binex
+   //   RinexNavStream rns(RinexNavFile.c_str(),ios::in);
+   //   if(!rns) {
+   //      cerr << "Failed to re-open output nav file " << RinexNavFile << endl;
+   //      return -3;
+   //   }
+   //   rns.exceptions(fstream::failbit);
+   //      // ignore the header
+   //   RinexNavHeader rnh;
+   //   rns >> rnh;
+   //   RinexNavData rnd;
+   //   while(rns >> rnd) writeBinex(BinexOut, rnd);
+   //   rns.close();
+   //}
+
+   RinexObsData robs;
+   while(InAgain >> robs) {
+      ROutStr << robs;
+      //if(!BinexFile.empty()) writeBinex(BinexOut, robs);
+   }
+
+   InAgain.close();
+   ROutStr.close();
+   //if(!BinexFile.empty()) BinexOut.close();
+
+      // delete the temporary
+   if(remove(TempFile.c_str()) != 0) {
+      cerr << "Error: Could not remove existing temp file: " << TempFile << endl;
+      return -1;
+   }
+   else if(Debug) cout << "Deleted temporary file " << TempFile << endl;
+
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
+int GetCommandInput(int argc, char **argv) throw(Exception)
+{
+try {
+   int i,j;
+   vector<string> values;
+
+   // --------------------------------------------------------------------------------
+   // set all the defaults
+   Debug = help = verbose = false;
+   BegTime = DayTime::BEGINNING_OF_TIME;
+   EndTime = DayTime::END_OF_TIME;
+   //NovatelFile,
+   RinexObsFile = string("RnovaRinex.obs");
+   RinexNavFile = string("RnovaRinex.nav");
+   //BinexFile = string();
+   InputDirectory = string("");
+   // header fields
+   FillOptionalHeader = true;
+   roh.fileProgram = Prgm+" "+Vers;
+   roh.fileAgency = string("ARL:UT/GPSTk");
+   roh.observer = string(" ");
+   roh.agency = string("ARL:UT/GPSTk");
+   roh.markerName = string(" ");
+   roh.markerNumber = string(" ");
+   roh.recNo = " ";
+   roh.recType = "Novatel";
+   roh.recVers = "OEM2/4"; // defined later by data
+   roh.antNo = " ";
+   roh.antType = " ";
+   //string HDRxNo,HDRxType,HDRxVer,HDAntNo,HDAntType;     // TD
+   //vector<string> HDcomments;        // none
+   //vector<RinexObsHeader::RinexObsType> OutputTypes;  // define later
+   gpsWeek = -1;
+   debias = false;
+
+   // --------------------------------------------------------------------------------
+   // Define the options
+
+   // required options:
+   RequiredOption dashinput(CommandOption::hasArgument, CommandOption::stdType,
+      0,"input"," --input <file>    Novatel binary input file");
+   dashinput.setMaxCount(1);
+
+   // optional arguments:
+   // this is here only so it will show up in the help msg...
+   CommandOption dashf(CommandOption::hasArgument, CommandOption::stdType,
+      'f',""," [-f|--file] <fn>  Name of file containing more options"
+      " (ignores '#' to EOL)");
+
+   CommandOption dashdir(CommandOption::hasArgument, CommandOption::stdType,0,"dir",
+      " --dir <dir>       Directory in which to find input file (defaults to ./)");
+   dashdir.setMaxCount(1);
+
+   CommandOption dashobs(CommandOption::hasArgument, CommandOption::stdType,0,"obs",
+      " --obs <file>      RINEX observation output file (RnovaRinex.obs)");
+   dashobs.setMaxCount(1);
+
+   CommandOption dashnav(CommandOption::hasArgument, CommandOption::stdType,0,"nav",
+      " --nav <file>      RINEX navigation output file (RnovaRinex.nav)");
+   dashnav.setMaxCount(1);
+
+   //CommandOption dashbin(CommandOption::hasArgument, CommandOption::stdType,0,"bin",
+   //   " --bin <file>      BINEX (binary) output file (RnovaRinex.bnx)");
+   //dashbin.setMaxCount(1);
+
+   CommandOption dashNHF(CommandOption::hasArgument, CommandOption::stdType,0,
+      "noHDopt", "\nOutput RINEX header fields:\n --noHDopt         If present, "
+      "do not fill optional records in the output RINEX header");
+   dashNHF.setMaxCount(1);
+
+   CommandOption dashHDp(CommandOption::hasArgument, CommandOption::stdType,0,"HDp",
+      " --HDp <program>   Set output RINEX header 'program' field ('"
+      + roh.fileProgram + "')");
+   dashHDp.setMaxCount(1);
+
+   CommandOption dashHDr(CommandOption::hasArgument, CommandOption::stdType,0,"HDr",
+      " --HDr <run_by>    Set output RINEX header 'run by' field ('"
+      + roh.fileAgency + "')");
+   dashHDr.setMaxCount(1);
+
+   CommandOption dashHDo(CommandOption::hasArgument, CommandOption::stdType,0,"HDo",
+      " --HDo <obser>     Set output RINEX header 'observer' field ('"
+      + roh.observer + "')");
+   dashHDo.setMaxCount(1);
+
+   CommandOption dashHDa(CommandOption::hasArgument, CommandOption::stdType,0,"HDa",
+      " --HDa <agency>    Set output RINEX header 'agency' field ('"
+      + roh.agency + "')");
+   dashHDa.setMaxCount(1);
+
+   CommandOption dashHDm(CommandOption::hasArgument, CommandOption::stdType,0,"HDm",
+      " --HDm <marker>    Set output RINEX header 'marker' field ('"
+      + roh.markerName + "')");
+   dashHDm.setMaxCount(1);
+
+   CommandOption dashHDn(CommandOption::hasArgument, CommandOption::stdType,0,"HDn",
+      " --HDn <number>    Set output RINEX header 'number' field ('"
+      + roh.markerNumber + "')");
+   dashHDn.setMaxCount(1);
+
+   CommandOption dashHDrn(CommandOption::hasArgument, CommandOption::stdType,0,"HDrn",
+      " --HDrn <number>   Set output RINEX header 'Rx number' field ('"
+      + roh.recNo + "')");
+   dashHDrn.setMaxCount(1);
+
+   CommandOption dashHDrt(CommandOption::hasArgument, CommandOption::stdType,0,"HDrt",
+      " --HDrt <type>     Set output RINEX header 'Rx type' field ('"
+      + roh.recType + "')");
+   dashHDrt.setMaxCount(1);
+
+   CommandOption dashHDrv(CommandOption::hasArgument, CommandOption::stdType,0,"HDrv",
+      " --HDrv <vers>     Set output RINEX header 'Rx version' field ('"
+      + roh.recVers + "')");
+   dashHDrv.setMaxCount(1);
+
+   CommandOption dashHDan(CommandOption::hasArgument, CommandOption::stdType,0,"HDan",
+      " --HDan <number>   Set output RINEX header 'antenna number' field ('"
+      + roh.antNo + "')");
+   dashHDan.setMaxCount(1);
+
+   CommandOption dashHDat(CommandOption::hasArgument, CommandOption::stdType,0,"HDat",
+      " --HDat <type>     Set output RINEX header 'antenna type' field ('"
+      + roh.antType + "')");
+   dashHDat.setMaxCount(1);
+
+   CommandOption dashHDc(CommandOption::hasArgument, CommandOption::stdType,0,"HDc",
+      " --HDc <comment>   Add comment to output RINEX headers (>1 allowed).");
+   //dashHDc.setMaxCount(1);
+
+   CommandOption dashobstype(CommandOption::hasArgument, CommandOption::stdType,
+   0,"obstype","\nOutput RINEX observation data:\n"
+   " --obstype <OT>    Output this RINEX (standard) obs type (i.e. <OT> is one of\n"
+   "                     L1,L2,C1,P1,P2,D1,D2,S1,or S2); repeat for each type.\n"
+   "                     NB default is ALL std. types that have data.");
+   //dashobstype.setMaxCount(1);
+
+
+   // times
+   CommandOptionWithTimeArg dasheb(0,"begin","%Y,%m,%d,%H,%M,%f",
+      "\nOutput configuration:\n --begin <arg>     Start time, arg is of the form "
+      "YYYY,MM,DD,HH,Min,Sec");
+   CommandOptionWithTimeArg dashgb(0,"beginGPS","%F,%g",
+      " --beginGPS <arg>  Start time, arg is of the form GPSweek,GPSsow");
+
+   CommandOptionWithTimeArg dashee(0,"end","%Y,%m,%d,%H,%M,%f",
+      " --end <arg>       End time, arg is of the form YYYY,MM,DD,HH,Min,Sec");
+   CommandOptionWithTimeArg dashge(0,"endGPS","%F,%g",
+      " --endGPS <arg>    End time, arg is of the form GPSweek,GPSsow");
+
+   // allow ONLY one start time (use startmutex(true) if one is required)
+   CommandOptionMutex startmutex(false);
+   startmutex.addOption(&dasheb);
+   startmutex.addOption(&dashgb);
+   CommandOptionMutex stopmutex(false);
+   stopmutex.addOption(&dashee);
+   stopmutex.addOption(&dashge);
+
+   CommandOption dashweek(CommandOption::hasArgument, CommandOption::stdType,0,"week",
+   " --week <week>     GPS Week number of this data, NB: this is for OEM2;\n"
+   "                     this command serves two functions, resolving the ambiguity\n"
+   "                     in the 10-bit week (default uses --begin, --end, or the\n"
+   "                     current system time) and ensuring that ephemeris records\n"
+   "                     that precede any obs records are not lost.");
+   dashweek.setMaxCount(1);
+
+   CommandOptionNoArg dashdebias(0,"debias",
+      " --debias          Remove an initial bias from the phase");
+   dashdebias.setMaxCount(1);
+
+   CommandOptionNoArg dashhelp('h',"help",
+      " [-h|--help]       print this message and quit");
+   dashhelp.setMaxCount(1);
+
+   CommandOptionNoArg dashVerbose('v', "verbose",
+      " --verbose         print more information");
+   dashVerbose.setMaxCount(1);
+
+   CommandOptionNoArg dashDebug('d',"debug",
+      " [-d|--debug]      print much more information");
+   dashDebug.setMaxCount(1);
+
+   // ... other options
+   CommandOptionRest Rest("");
+
+   // --------------------------------------------------------------------------------
+   // Define the parser here, after the options -- this is the 'prgm description'
+   CommandOptionParser Par(
+" Prgm " + Prgm + " (" + Vers + ") will open and read a binary Novatel file\n"
+"  (OEM2 and OEM4 receivers are supported), and convert the data to RINEX format\n"
+"  observation and navigation files. The RINEX header is filled using user input\n"
+"  (see below), and optional records are filled. Input is on the command line,\n"
+"  or of the same format in a file (--file <file>).\n");
+
+   // parse the command line
+   // allow user to put all options in a file
+   // PreProcessArgs pulls out help and Debug
+   vector<string> Args;
+   for(j=1; j<argc; j++) PreProcessArgs(argv[j],Args);
+
+   if(Args.size()==0) help = true;
+
+      // pass the rest
+   argc = Args.size()+1;
+   char **CArgs=new char*[argc];
+   if(!CArgs) { cerr << "Failed to allocate CArgs\n"; return -1; }
+   CArgs[0] = argv[0];
+   for(j=1; j<argc; j++) {
+      CArgs[j] = new char[Args[j-1].size()+1];
+      if(!CArgs[j]) { cerr << "Failed to allocate CArgs[j]\n"; return -1; }
+      strcpy(CArgs[j],Args[j-1].c_str());
+   }
+
+   if(Debug) {
+      cout << "Argument list passed to parser:\n";
+      for(j=0; j<argc; j++) cout << j << " " << CArgs[j] << endl;
+   }
+
+   Par.parseOptions(argc, CArgs);
+   for(j=1; j<argc; j++) delete[] CArgs[j];
+   delete[] CArgs;
+
+   // if help, print usage
+   if(help) {
+      Par.displayUsage(cout,false);
+      cout << endl;
+      if(argc <= 2) return 1;
+   }
+
+   // check for errors on the command line
+   if (Par.hasErrors() || Rest.getCount()) {
+      cerr << "\nErrors found in command line input:\n";
+      if(Par.hasErrors()) Par.dumpErrors(cerr);
+      if(Rest.getCount()) {
+         cerr << "The following command line fields were not recognized:\n";
+         values = Rest.getValue();
+         for(i=0; i<values.size(); i++)
+            cerr << "  " << values[i] << endl;
+      }
+      cerr << "...end of Errors. Abort.\n";
+      help = true;
+   }
+
+   if(help && argc > 1)
+      cout << endl << "--------- parsed input:" << endl;
+
+   // --------------------------------------------------------------------------------
+   // pull out the parsed input
+
+   if(dashinput.getCount()) {
+      values = dashinput.getValue();
+      if(help) cout << " Input Novatel file name " << values[0] << endl;
+      NovatelFile = values[0];
+   }
+   if(dashdir.getCount()) {
+      values = dashdir.getValue();
+      if(help) cout << " Input Novatel file directory " << values[0] << endl;
+      InputDirectory = values[0];
+   }
+   if(dashobs.getCount()) {
+      values = dashobs.getValue();
+      if(help) cout << " Input RINEX obs file name " << values[0] << endl;
+      RinexObsFile = values[0];
+   }
+   if(dashnav.getCount()) {
+      values = dashnav.getValue();
+      if(help) cout << " Input RINEX nav file name " << values[0] << endl;
+      RinexNavFile = values[0];
+   }
+   //if(dashbin.getCount()) {
+   //   values = dashbin.getValue();
+   //   if(help) cout << " Input BINEX file name " << values[0] << endl;
+   //   BinexFile = values[0];
+   //}
+   if(dashNHF.getCount()) {
+      values = dashNHF.getValue();
+      if(help) cout << " Turn off filling of optional header" << endl;
+      FillOptionalHeader = false;
+   }
+   if(dashHDp.getCount()) {
+      values = dashHDp.getValue();
+      if(help) cout << " Input header program name " << values[0] << endl;
+      roh.fileProgram = values[0];
+   }
+   if(dashHDr.getCount()) {
+      values = dashHDr.getValue();
+      if(help) cout << " Input header 'run by' field " << values[0] << endl;
+      roh.fileAgency = values[0];
+   }
+   if(dashHDo.getCount()) {
+      values = dashHDo.getValue();
+      if(help) cout << " Input header observer field " << values[0] << endl;
+      roh.observer = values[0];
+   }
+   if(dashHDa.getCount()) {
+      values = dashHDa.getValue();
+      if(help) cout << " Input header agency field " << values[0] << endl;
+      roh.agency = values[0];
+   }
+   if(dashHDm.getCount()) {
+      values = dashHDm.getValue();
+      if(help) cout << " Input header marker field " << values[0] << endl;
+      roh.markerName = values[0];
+   }
+   if(dashHDn.getCount()) {
+      values = dashHDn.getValue();
+      if(help) cout << " Input header marker name " << values[0] << endl;
+      roh.markerNumber = values[0];
+   }
+   if(dashHDrn.getCount()) {
+      values = dashHDrn.getValue();
+      if(help) cout << " Input header receiver number " << values[0] << endl;
+      roh.recNo = values[0];
+   }
+   if(dashHDrt.getCount()) {
+      values = dashHDrt.getValue();
+      if(help) cout << " Input header receiver type " << values[0] << endl;
+      roh.recType = values[0];
+   }
+   if(dashHDrv.getCount()) {
+      values = dashHDrv.getValue();
+      if(help) cout << " Input header receiver version " << values[0] << endl;
+      roh.recVers = values[0];
+   }
+   if(dashHDan.getCount()) {
+      values = dashHDan.getValue();
+      if(help) cout << " Input header antenna number " << values[0] << endl;
+      roh.antNo = values[0];
+   }
+   if(dashHDat.getCount()) {
+      values = dashHDat.getValue();
+      if(help) cout << " Input header antenna type " << values[0] << endl;
+      roh.antType = values[0];
+   }
+   if(dashHDc.getCount()) {
+      values = dashHDc.getValue();
+      for(i=0; i<values.size(); i++) {
+         if(help) cout << " Input comment for headers " << values[i] << endl;
+         HDcomments.push_back(values[i]);
+      }
+   }
+   if(dashobstype.getCount()) {
+      values = dashobstype.getValue();
+      for(i=0; i<values.size(); i++) {
+         RinexObsHeader::RinexObsType rot;
+         rot = RinexObsHeader::convertObsType(values[i]);
+         OutputTypes.push_back(rot);
+         if(help) cout << " Input output RINEX obs type " << values[i] << endl;
+      }
+   }
+   if(dasheb.getCount()) {
+      values = dasheb.getValue();
+      BegTime.setToString(values[0], "%Y,%m,%d,%H,%M,%S");
+      if(help) cout << " Input begin time " << values[0] << " = " << BegTime << endl;
+      if(gpsWeek == -1) gpsWeek = BegTime.GPSfullweek();
+   }
+   if(dashgb.getCount()) {
+      values = dashgb.getValue();
+      BegTime.setToString(values[0], "%F,%g");
+      if(help) cout << " Input begin time " << values[0] << " = " << BegTime << endl;
+      if(gpsWeek == -1) gpsWeek = BegTime.GPSfullweek();
+   }
+   if(dashee.getCount()) {
+      values = dashee.getValue();
+      EndTime.setToString(values[0], "%Y,%m,%d,%H,%M,%S");
+      if(help) cout << " Input end time " << values[0] << " = " << EndTime << endl;
+      if(gpsWeek == -1) gpsWeek = EndTime.GPSfullweek();
+   }
+   if(dashge.getCount()) {
+      values = dashge.getValue();
+      EndTime.setToString(values[0], "%F,%g");
+      if(help) cout << " Input end time " << values[0] << " = " << EndTime << endl;
+      if(gpsWeek == -1) gpsWeek = EndTime.GPSfullweek();
+   }
+   if(dashweek.getCount()) {
+      values = dashweek.getValue();
+      gpsWeek = StringUtils::asInt(values[0]);
+   }
+   if(dashdebias.getCount()) {
+      if(help) cout << " Turn on debiasing of the phase " << endl;
+      debias = true;
+   }
+   // help and Debug are pulled out by PreProcessArgs
+   //if(dashhelp.getCount()) {
+   //   help = true;
+   //}
+   //if(dashDebug.getCount()) {
+   //   Debug = true;
+   //}
+   if(dashVerbose.getCount() || Debug) verbose = true;
+
+
+   // process input
+   if(gpsWeek == -1) gpsWeek = CurrEpoch.GPSfullweek();
+   if(OutputTypes.size() == 0) {                     // fill with the standard types
+      OutputTypes.push_back(RinexObsHeader::C1);
+      OutputTypes.push_back(RinexObsHeader::P1);
+      OutputTypes.push_back(RinexObsHeader::L1);
+      OutputTypes.push_back(RinexObsHeader::D1);
+      OutputTypes.push_back(RinexObsHeader::S1);
+      OutputTypes.push_back(RinexObsHeader::P2);
+      OutputTypes.push_back(RinexObsHeader::L2);
+      OutputTypes.push_back(RinexObsHeader::D2);
+      OutputTypes.push_back(RinexObsHeader::S2);
+   }
+   // table will be initialized inside the loop
+   totals = vector<int>(OutputTypes.size(),0);
+
+   if(help) return 1;
+   return 0;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
+// Pull out --debug --help and --file
+void PreProcessArgs(const char *arg, vector<string>& Args) throw(Exception)
+{
+try {
+   static bool found_cfg_file=false;
+
+   if(found_cfg_file || (arg[0]=='-' && arg[1]=='f')) {
+      string filename(arg);
+      if(!found_cfg_file) filename.erase(0,2); else found_cfg_file = false;
+      if(Debug) cout << "Found a file of options: " << filename << endl;
+      ifstream infile(filename.c_str());
+      if(!infile) {
+         cout << "Error: could not open options file " << filename << endl;
+         return;
+      }
+
+      bool again_cfg_file=false;
+      char c;
+      string buffer,word;
+      while(1) {
+         getline(infile,buffer);
+         stripTrailing(buffer,'\r');
+
+         // process the buffer before checking eof or bad b/c there can be
+         // a line at EOF that has no CRLF...
+         while(!buffer.empty()) {
+            word = firstWord(buffer);
+            if(again_cfg_file) {
+               word = "-f" + word;
+               again_cfg_file = false;
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else if(word[0] == '#') { // skip to end of line
+               buffer.clear();
+            }
+            else if(word == "--file" || word == "-f")
+               again_cfg_file = true;
+            else if(word[0] == '"') {
+               word = stripFirstWord(buffer,'"');
+               buffer = "dummy " + buffer;            // to be stripped later
+               PreProcessArgs(word.c_str(),Args);
+            }
+            else
+               PreProcessArgs(word.c_str(),Args);
+
+            word = stripFirstWord(buffer);      // now remove it from buffer
+         }
+         if(infile.eof() || !infile.good()) break;
+      }
+   }
+   else if((arg[0]=='-' && arg[1]=='d') || string(arg)==string("--debug"))
+      Debug = true;
+   else if((arg[0]=='-' && arg[1]=='h') || string(arg)==string("--help"))
+      help = true;
+   else if(string(arg) == "--file" || string(arg) == "-f")
+      found_cfg_file = true;
+   else Args.push_back(arg);
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+
+//------------------------------------------------------------------------------------
+void DumpCommandLine(ostream& ofs) throw(Exception)
+{
+try {
+   int i;
+
+   ofs << "Summary of command line input:" << endl;
+   ofs << " Debug is " << (Debug ? "on":"off") << endl;
+   ofs << " Verbose is " << (verbose ? "on":"off") << endl;
+   if(!InputDirectory.empty()) ofs << " Path for input Novatel file is "
+      << InputDirectory << endl;
+   ofs << " Input Novatel file is: " << NovatelFile << endl;
+   ofs << " Output RINEX obs file is: " << RinexObsFile << endl;
+   ofs << " Output RINEX nav file is: " << RinexNavFile << endl;
+   //ofs << " Output BINEX (obs/nav) file is: " << BinexFile << endl;
+   ofs << " --------- Header information:\n";
+   if(!FillOptionalHeader) ofs << " Do not";
+   ofs << " Fill optional records in header" << endl;
+   ofs << " Header program: " << roh.fileProgram << endl;
+   ofs << " Header run by: " << roh.fileAgency << endl;
+   ofs << " Header observer: " << roh.observer << endl;
+   ofs << " Header agency: " << roh.agency << endl;
+   ofs << " Header marker name: " << roh.markerName << endl;
+   ofs << " Header marker number: " << roh.markerNumber << endl;
+   if(HDcomments.size() > 0) {
+      ofs << " Header comments:\n";
+      for(i=0; i<HDcomments.size(); i++) ofs << HDcomments[i] << endl;
+   }
+   ofs << " Output RINEX observation types (if found in the data):\n";
+   for(i=0; i<OutputTypes.size(); i++)
+      ofs << " " << RinexObsHeader::convertObsType(OutputTypes[i]);
+   ofs << endl;
+   if(BegTime > DayTime::BEGINNING_OF_TIME) ofs << " Begin time is "
+      << BegTime.printf("%Y/%m/%d %H:%02M:%6.3f = %F/%10.3g") << endl;
+   if(EndTime < DayTime::END_OF_TIME) ofs << " End   time is "
+      << EndTime.printf("%Y/%m/%d %H:%02M:%6.3f = %F/%10.3g") << endl;
+   ofs << " Debiasing of phase is turned " << (debias ? "on" : "off") << endl;
+
+   ofs << "End of command line input summary." << endl;
+}
+catch(Exception& e) { GPSTK_RETHROW(e); }
+catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
+catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
+}
+
+//------------------------------------------------------------------------------------
diff --git a/trunk/apps/differential/Jamfile b/dev/apps/differential/Jamfile
similarity index 100%
rename from trunk/apps/differential/Jamfile
rename to dev/apps/differential/Jamfile
diff --git a/dev/apps/differential/Makefile.am b/dev/apps/differential/Makefile.am
new file mode 100644
index 0000000..8bdd5b9
--- /dev/null
+++ b/dev/apps/differential/Makefile.am
@@ -0,0 +1,7 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ../../src/libgpstk.la
+
+bin_PROGRAMS = vecsol
+
+vecsol_SOURCES = vecsol.cpp
diff --git a/dev/apps/differential/vecsol.1 b/dev/apps/differential/vecsol.1
new file mode 100644
index 0000000..953284b
--- /dev/null
+++ b/dev/apps/differential/vecsol.1
@@ -0,0 +1,118 @@
+.TH VECSOL "1" "September 2005" "vecsol (GPStk) 0.9" "User Commands"
+.SH NAME
+vecsol \- GPS Vector Solver, computes a 3D vector from RINEX input
+.SH SYNOPSIS
+.B vecsol
+[\fIRINEX obs file 1\fR] [\fIRINEX obs file 2\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Computes a 3D vector solution using dual-frequency carrier phases. A double
+difference algorithm is applied with properly computed weights
+(elevation sine weighting) and correlations. The program iterates to
+convergence and attempts to resolve ambiguities to integer values if
+close enough.  Crude outlier rejection is provided based on a
+triple-difference test. Ephemeris used are either broadcast or precise
+(SP3). Alternatively, also P code processing is provided.
+.PP
+The solution is computed using the ionosphere-free linear combination.
+The ionospheric model included in broadcast ephemeris may be used. A
+standard tropospheric correction is applied, or tropospheric parameters
+(zenith delays) may be estimated.
+.SH FILES
+.TP
+\fBRINEX obs files 1 and 2\fR
+contain the observations collected at the two end points 1 and 2 of the
+baseline. They \fImust\fR contain a sufficient set of simultaneous
+observations to the same satellites.
+.TP
+\fBvecsol.conf\fR
+contains the input options for the program, one per line.
+.PP
+.SS 	Options
+.TP 10
+	\fBphase\fR [1/0] 
+Process carrier phase data (instead of P code data)
+.TP
+	\fBtruecov\fR [1/0] 
+If 1, use true double difference covariances. If 0, ignore any possible
+correlations
+.TP
+	\fBprecise\fR [1/0] 
+If 1, use precise ephemeris, if 0, use broadcast ephemeris
+.TP
+	\fBiono\fR [1/0] 
+If 1, use the 8-parameter ionospheric model that comes with the
+broadcast ephemeris (.nav) files
+.TP
+	\fBtropo\fR [1/0] 
+Estimate troposphere parameters (zenith delays relative to the standard
+value, which is always applied)
+.TP
+	\fBvecmode\fR [1/0] 
+If 1, solve the \fIvector\fR, i.e. the three co-ordinate differences
+between the baseline end points. If 0, solve for the absolute
+co-ordinates of both end points
+.TP
+	\fBdebug\fR [1/0] 
+If 1, produce lots of gory debugging output. See the source for what it
+all means
+.TP
+	\fBrefsat_elev\fR 
+Minimum elevation of the reference satellite used for computing
+inter-satellite differences.  Good initial choice: 30.0
+.TP
+	\fBcutoff_elev\fR 
+cut-off elevation. Good initial choice: 10.0 \- 20.0
+.TP
+	\fBPTDRej, CTDRej\fR 
+Rejection limits for phase and code observations on triple
+difference level. Good choice: 0.001 0.1
+.TP
+	\fBreduce\fR 
+Reduce out dependencies between DD biases
+
+.PP
+.TP
+\fBvecsol.nav\fR 
+contains the names of the navigation RINEX files ("nav files", extension .yyN) to be used, one per line.
+.PP
+Good navigation RINEX files that are globally valid can be found from the CORS website at 
+.B http://www.ngs.noaa.gov/CORS/
+.
+.TP
+\fBvecsol.eph\fR 
+contains the names of the precise ephemeris SP3 files (extension .sp3)
+to be used. These should cover the time span of the observations, with
+time to spare on both ends. 
+.PP
+Note that the date in the filenames of the SP3 files is given as GPS
+week + weekday, not year + day of year, as in the observation and nav
+files.
+.PP
+In the .nav and .eph files, comment lines have # in the first position.
+
+.SH AUTHOR
+Written by Martin Vermeer and others.
+.SH BUGS
+Doesn't currently recover \fIat all\fR from cycle slips, so the RINEX
+observation files used have to be fairly clean already.
+.PP
+Report bugs to <gpstk-devel at lists.sourceforge.net>.
+.SH COPYRIGHT
+Copyright \(co 2005 The Authors.
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+The full documentation for the 
+.B GPStk
+package of which 
+.B vecsol
+is a part can be found on the website of
+.B GPStk
+at
+.IP
+.B http://gpstk.sourceforge.net
+.
+
diff --git a/dev/apps/differential/vecsol.conf b/dev/apps/differential/vecsol.conf
new file mode 100644
index 0000000..cbded09
--- /dev/null
+++ b/dev/apps/differential/vecsol.conf
@@ -0,0 +1,11 @@
+1    obsmode
+1    true covariances
+1    precise ephemeris
+0    nav file ionosphere
+1    tropo estimation
+1    vector mode
+1    debug
+30.0 ref sat cut-off
+20.0 cut-off angle
+0.1 0.1 triple diff rejection limits (phase, code)
+1    reduce
diff --git a/dev/apps/differential/vecsol.cpp b/dev/apps/differential/vecsol.cpp
new file mode 100644
index 0000000..1758c24
--- /dev/null
+++ b/dev/apps/differential/vecsol.cpp
@@ -0,0 +1,1303 @@
+/* vim: set cindent shiftwidth=4: */
+#include <string>
+#include <vector>
+#include <map>
+
+#include <limits.h>
+#include <float.h>
+
+#include "geometry.hpp"
+
+#include "Matrix.hpp"
+#include "MatrixOperators.hpp"
+
+#include "RinexObsBase.hpp"
+#include "RinexObsHeader.hpp"
+#include "RinexObsData.hpp"
+#include "RinexObsStream.hpp"
+
+#include "RinexNavBase.hpp"
+#include "RinexNavHeader.hpp"
+#include "RinexNavData.hpp"
+#include "RinexNavStream.hpp"
+
+#include "SP3Base.hpp"
+#include "SP3Header.hpp"
+#include "SP3Data.hpp"
+#include "SP3Stream.hpp"
+
+#include "TropModel.hpp"
+#include "WGS84Geoid.hpp"
+#include "IonoModelStore.hpp"
+
+#include "BCEphemerisStore.hpp"
+#include "SP3EphemerisStore.hpp"
+#include "EphemerisRange.hpp"
+#include "icd_200_constants.hpp"
+
+
+/**
+ * @file vecsol.cpp 
+ * Vector solution using dual-frequency carrier phases. Double difference
+ * algorithm with proper weights, iteration with attempted ambiguity
+ * resolution, crude outlier rejection, elevation sine weighting. 
+ * Alternatively also code processing provided.
+ * The configuration file is 'vecsol.conf'; broadcast or precise ephemeris
+ * are in files 'vecsol.nav' and 'vecsol.eph', respectively.
+
+ * LGPL (see COPYING). No furry animals were harmed in the coding 
+ * of this software.
+
+ * (c) 2005-2006 Martin Vermeer
+
+
+GPS data in RINEX format, as well as precise orbits, can be downloaded
+from the SOPAC site: http://sopac.ucsd.edu/cgi-bin/dbDataByDate.cgi
+
+Site co-ordinates for testing can be had from:
+http://sopac.ucsd.edu/sites/getSiteInfo.html
+
+
+ To Do:
+ - Use of ssi and lli bits?
+ - Remove limitation that unknowns must be same across iterations
+ - Ionosphere models: use IONEX
+ - Satellite center-of-mass correction 
+ - Receiver antenna phase delay patterns
+ - Earth tides
+ - Output of vectors to be read by other software
+ 
+ */
+
+
+using namespace std;
+using namespace gpstk;
+using gpstk::StringUtils::asString;
+using gpstk::transpose;
+
+    void
+stationData(RinexObsData const & rod, bool const phase, 
+	vector < SatID > &prnVec,
+	vector < double >&rangeVecL1, vector < double >&rangeVecL2)
+{
+    RinexObsData::RinexSatMap::const_iterator it;
+    for (it = rod.obs.begin(); it != rod.obs.end(); it++) {
+	RinexObsData::RinexObsTypeMap otmap;
+	RinexObsData::RinexObsTypeMap::const_iterator itL1, itL2;
+	otmap = (*it).second;
+	if (phase) {
+	    itL1 = otmap.find(RinexObsHeader::L1);
+	    itL2 = otmap.find(RinexObsHeader::L2);
+	} else {
+	    itL1 = otmap.find(RinexObsHeader::P1);
+	    itL2 = otmap.find(RinexObsHeader::P2);
+	}
+	if (itL1 != otmap.end() && itL2 != otmap.end()) {
+	    double /* const */ rangeL1 = (*itL1).second.data;
+	    double /* const */ rangeL2 = (*itL2).second.data;
+
+	    // here we would have Loss of Lock Indicator
+	    // and Signal Strength Indicator: =0, >5 are OK
+	    // cout << (*itL2).second.lli << endl;
+	    // cout << (*itL2).second.ssi << endl;
+
+	    // Discard empty data
+	    if (rangeL1 != 0.0 && rangeL2 != 0.0) {
+		prnVec.push_back((*it).first);
+		rangeVecL1.push_back(rangeL1);
+		rangeVecL2.push_back(rangeL2);
+	    }
+	}
+    }
+}
+
+
+enum FixType {
+    FIX_NONE,
+    FIX_WIDELANE,
+    FIX_BOTH
+};
+
+
+FixType phaseCycles(vector <double> & DDobs,
+	double const lambda1, double const lambda2, double const wt1, 
+	double const wt2)
+{
+#define WITHIN_HALF_CYCLE(Lx)  Lx = Lx - int(Lx); \
+    (Lx < -0.5 ? Lx += 1.0 : (Lx > 0.5 ? Lx -= 1.0 : Lx = Lx))
+
+    double L1 = DDobs[1] / lambda1; 
+    double L2 = DDobs[2] / lambda2;
+    double L5 = L1 - L2;
+
+    WITHIN_HALF_CYCLE(L1);
+    WITHIN_HALF_CYCLE(L2);
+    WITHIN_HALF_CYCLE(L5);
+
+    cout << fixed << setprecision(8) << L1 << " " << L2 << " | " << L5;
+
+    bool const L1fixable = L1 <  0.1 && L1 >  -0.1;
+    bool const L2fixable = L2 <  0.1 && L2 >  -0.1;
+    bool const L5fixable = L5 < 0.07 && L5 > -0.07;
+    double const L3 = wt1 * lambda1 * L1 + wt2 * lambda2 * L2;
+    // Unit for L3: m
+    bool const L3fixable = L3 < 0.02 && L3 > -0.02;
+    
+    if (L1fixable && L2fixable && L5fixable) {
+	DDobs[1] -= L1 * lambda1;
+	DDobs[2] -= L2 * lambda2;
+	cout << " FIX" << endl;
+	return FIX_BOTH;
+    }
+
+    if (L5fixable && L3fixable) {
+	DDobs[1] -= L1 * lambda1;
+	DDobs[2] -= L2 * lambda2;
+	cout << " QIF" << endl;
+	return FIX_BOTH;
+    }
+
+    if (L5fixable) {
+	DDobs[1] -= 0.5 * L5 * lambda1;
+	DDobs[2] += 0.5 * L5 * lambda2;
+	cout << " Widelane" << endl;
+	return FIX_WIDELANE;
+    }
+
+    cout << endl;
+    return FIX_NONE;
+}
+
+
+enum SolveType {
+    SOLVE_AMBS,
+    SOLVE_COORDS
+};
+
+
+void Solve(gpstk::Matrix <double> N, gpstk::Matrix <double> const b, 
+	gpstk::Matrix <double> & N2, gpstk::Matrix <double> & b2, 
+	SolveType const solveType, 
+	gpstk::Vector <FixType> const fixed, 
+	int const MaxUnkn, int const unknowns, bool const tropo)
+{
+    if (solveType == SOLVE_COORDS) {
+	// Fix "fixed" unknowns:
+	for (int k = MaxUnkn; k < unknowns; k++) {
+	    if (fixed[k] == FIX_BOTH)
+		N(k,k) += 1.0E8; // absolutely fixed
+	    // Note: we store separately the bias unknowns (ambiuguities) for L1 and
+	    // L2. However, in the normal matrix we have them jointly. This limits
+	    // what we can do here with weighting in case of a widelane fix.
+	    // Obviously it would be better to have them separately in the normal 
+	    // matrix also, but then the solution effort would be 8x as expensive
+	    // numerically.
+	    if (fixed[k] == FIX_WIDELANE)
+		N(k,k) *= 1.25; // upgraded
+	}
+    } else { // SOLVE_AMBS
+	int const endCoords = ( tropo ? MaxUnkn - 2 : MaxUnkn );
+	for (int k = 0; k < endCoords; k++) {
+	    N(k,k) += 1.0E8; // Keep only coords fixed (to earlier solved values)
+	}
+    }
+    // Soft-constrain tropo params. This is tricky, as this has to be scaled
+    // with the variance of unit weight for the GPS observations. Use
+    // conservative values:
+    if (tropo) {
+	N(MaxUnkn - 1, MaxUnkn - 1) += 0.01;
+	N(MaxUnkn - 2, MaxUnkn - 2) += 0.01;
+    }
+
+    // Copy over to correctly sized matrices
+    gpstk::Matrix <double> N1(unknowns, unknowns);
+    gpstk::Matrix <double> bb(unknowns, 3);
+    for (int k = 0; k < unknowns; k++) {
+	for (int m = 0; m < unknowns; m++)
+	    N1(k,m) = N(k,m);
+	// Suppress unused unknowns, if any
+	if (N1(k,k) == 0.0)
+	    N1(k,k) = 1.0;
+	for (int ot = 0; ot < 3; ot++)
+	    bb(k,ot) = b(k,ot);
+    }
+
+    N2 = gpstk::inverse(N1);
+    double big, small;
+    cout << setprecision(10) << "Condition number(" << solveType << "):" 
+	<< condNum(N1, big, small) << endl;
+
+    cout << "Largest, smallest eigenvalue:" << " [" << big << " > " 
+	<< small << ']' << endl;
+    // Is it my imagination, or does SVD sometimes fail to order the
+    // eigenvalues properly?  -- MV 01.03.2006
+    gpstk::SVD<double> svd;
+    svd(N1);
+    double big2 = 0;
+    for (int k = 0; k < unknowns; k++)
+	if (svd.S(k) > big2)
+	   big2 = svd.S(k);
+    if (big != big2) { 
+	// List all the eigenvalues; this shouldn't happen
+	for (int k = 0; k < unknowns; k++)
+	    cout << k << ':' << svd.S(k) << ' ';
+	cout << endl;
+    }
+    
+    for (int k = 0; k < unknowns; k++) {
+	if (N2(k,k) < 0.0)
+	    cout << "Negative diagonal element " 
+		<< k << ": " << N2(k,k) << endl;
+    }
+    b2 = bb;
+}
+
+
+bool Reduce(gpstk::Matrix <double> & N, gpstk::Matrix <double> & sol, 
+	Matrix <double> x0,
+	int const k, int const j, int const l, 
+	int const dir1, int const dir2, int const dir3, 
+	int const MaxUnkn, int const unknowns)
+{
+    // Here, the relationships ("closures") that may exist between different 
+    // DD real-valued amvbiguities (biases) are used as condition equations 
+    // to least-squares adjust (improve) them. 
+    // The condition equation coefficient matrix is B, the condition
+    // quantity ("zero variate") is y.
+    gpstk::Matrix <double> B(1, unknowns);
+    gpstk::Matrix <double> BT(unknowns, 1);
+    gpstk::Matrix <double> y, BN, NBT, BNBT, InvBNBT;
+    int m;
+
+    // dir1/2/3 contain the "directions" of Double Diffs used, 
+    // i.e., +1 or -1.
+    for (m = 0; m < unknowns; m++) {
+	if (m == k)
+	    B(0, m) = dir1;
+	else if (m == j)
+	    B(0, m) = dir2;
+	else if (m == l)
+	    B(0, m) = dir3;
+	else
+	    B(0, m) = 0.0;
+    }
+
+    // Least squares condition eqs. adjustment machinery
+    BT = transpose(B);
+    BN = B * N;
+    NBT = transpose(BN);
+    BNBT = BN * BT;
+    InvBNBT = BNBT; // Make sure size is always right
+    if (BNBT(0, 0) == 0.0) {
+	cout << "Warning: Zero Variance!" << endl;
+	InvBNBT(0, 0) = 0.0;
+    } else
+	InvBNBT(0, 0) = 1.0 / BNBT(0, 0);
+
+    // Create replacement of proper dimensions:
+    gpstk::Matrix <double> x0b(unknowns, 3);
+    for (int i = 0; i < unknowns; i++)
+	for (int m = 0; m < 3; m++)
+	x0b(i, m) = x0(i, m);
+
+    y = B * (sol + x0b);
+
+    if (abs(y(0, 0)) < 0.1) {
+        sol = sol - NBT * InvBNBT * y;
+        N = N - NBT * InvBNBT * BN;
+    	cout << "DD ambigs.: " << k << ' ' << j << ' ' << l << endl;
+    	cout << "Directions: " << dir1 << ' ' << dir2 << ' ' << dir3 << endl;
+        cout << "Closures:   " << y << endl;
+	cout << "(Previous): " << B * x0b << endl;
+	return true;
+    }
+    return false;
+}
+
+
+Triple Rotate(Triple const UEN, Triple const R)
+    {
+	// Rotates an Up-East-North antenna offset to geocentric XYZ
+	// (This should be part of the GPStk library)
+	double const rad = R.mag();
+	double const rho = sqrt(R[0] * R[0] + R[1] * R[1]);
+	double const sf = R[2] / rad;
+	double const cf = rho / rad;
+	double const sl = R[1] / rho;
+	double const cl = R[0] / rho;
+	Triple a;
+	a[0] = cl * cf * UEN[0] - sl * UEN[1] - cl * sf * UEN[2];
+	a[1] = sl * cf * UEN[0] + cl * UEN[1] - sl * sf * UEN[2];
+	a[2] =      sf * UEN[0]               +      cf * UEN[2];
+	return a;
+    }
+
+
+Triple permanentTide(double const phi)
+{
+    // Based on McCarthy (1996). Permanent part of the solid Earth tide,
+    // computed with conventional Love number, as has been the practice.
+    // Note that this is only meant to reduce measured GPS co-ordinates in a
+    // way that makes them comparable to ITRF published co-ordinates.
+    Triple disp;
+    double const c = cos(phi * DEG_TO_RAD);
+    disp [0] = 0.6026 * 0.19844 * (1.5 * c*c - 1.0);
+    disp [1] =  0.0;
+    disp [2] = 0.0831 * 0.19844 * 1.5 * sin(2.0 * phi);
+    return disp;
+}
+
+
+    int main(int argc, char *argv[])
+    {
+#define EPH_RANGE(C,T,X,S) ((precise ? \
+	    C.ComputeAtReceiveTime(T.time,X,S,sp3store) : \
+	    C.ComputeAtReceiveTime(T.time,X,S,bcestore)))
+
+	BCEphemerisStore bcestore;
+	SP3EphemerisStore sp3store;
+	WGS84Geoid geoid;
+	IonoModelStore ion;
+	CorrectedEphemerisRange CER1, CER2;
+	SimpleTropModel trop;
+
+	if (argc != 3) {
+	    cerr << "Usage:" << endl;
+	    cerr << "   " << argv[0] <<
+		" <RINEX Obs file 1> <RINEX Obs file 2>" <<
+		endl;
+	    cerr << "Edit vecsol.conf, vecsol.nav and vecsol.eph for input" <<
+		endl;
+	    exit(-1);
+	}
+
+	int obsMode;   // 0,2 = code, 1,3 = phase; 0,1 = iono free, 2,3 L1+L2
+	bool phase;    // Process carrier phase data (instead of P code data)
+	bool ionoFree; // Compute iono free (instead of L1 + L2)
+	bool truecov;  // Use true DD covariances (inst. of no correlations)
+	bool precise;  // Use precise ephemeris (inst. of broadcast)
+	bool iono;     // Use nav file iono model
+	bool tropo;    // Estimate troposphere parameters
+	bool vecmode;  // Solve vector (inst. of end point coords)
+	bool debug;
+	double refsat_elev; // Minimum elevation of the reference satellite.
+			    // Good value: 30.0
+	double cutoff_elev; // cut-off elevation. Good value: 10.0
+	int MaxUnkn;   // The number of std. unknowns. 3 for baseline est.,
+			    // 6 for two endpoint positions, 8 for tropo est. too.
+	// rejection criteria, m/s, m
+	double PTDrej, CTDrej, DDrej = 1.0; 
+	bool reduce;   // Reduce out dependencies between DD biases
+
+	char s[80];
+	std::ifstream conf;
+	conf.open("vecsol.conf", ios::in);
+	conf >> obsMode;	conf.getline(s, 80);
+	phase = obsMode == 1 || obsMode == 3;
+	ionoFree = obsMode == 0 || obsMode == 1;
+	conf >> truecov;	conf.getline(s, 80);
+	conf >> precise;	conf.getline(s, 80);
+	conf >> iono;		conf.getline(s, 80);
+	conf >> tropo;		conf.getline(s, 80);
+	conf >> vecmode;	conf.getline(s, 80);
+	conf >> debug;		conf.getline(s, 80);
+	conf >> refsat_elev;	conf.getline(s, 80);
+	conf >> cutoff_elev;	conf.getline(s, 80);
+	conf >> PTDrej >> CTDrej; 
+	conf.getline(s, 80);
+	conf >> reduce;		conf.getline(s, 80);
+	conf.close();
+
+	if (vecmode)
+	    MaxUnkn = 3;
+	else
+	    MaxUnkn = 6;
+	if (tropo)
+	    MaxUnkn += 2;
+
+	cout << endl;
+	cout << "Configuration data from vecsol.conf" << endl;
+	cout << "-----------------------------------" << endl;
+	cout << "Use carrier phases:             " << phase << endl;
+	cout << "Compute ionosphere-free:        " << ionoFree << endl;
+	cout << "Use true correlations:          " << truecov << endl;
+	cout << "Use precise ephemeris:          " << precise << endl;
+	cout << "Use broadcast iono model:       " << iono << endl;
+	cout << "Use tropospheric est.:          " << tropo << endl;
+	cout << "Vector mode:                    " << vecmode << endl;
+	cout << "Debugging mode:                 "  << debug << endl;
+	cout << "Ref sat elevation limit:        " << refsat_elev << endl;
+	cout << "Cut-off elevation:              " << cutoff_elev << endl;
+	cout << "TD rej. limits (phase, code):   " << PTDrej << " "
+						   << CTDrej << endl;
+	cout << "Reduce out DD dependencies:     " << reduce << endl;
+	cout << endl;
+
+	int const MaxDim(phase ? 1000 : MaxUnkn); // For reserving array space
+	int const MaxSats(30);    // Same
+
+	const double gamma((L1_FREQ / L2_FREQ) * (L1_FREQ / L2_FREQ));
+	const double L1_F2(L1_FREQ * L1_FREQ), L2_F2(L2_FREQ * L2_FREQ);
+	const double LDIF_F2(L1_F2 - L2_F2);
+	// Weights for adding L1 and L2 pseudo-ranges into metric iono free
+	const double wt1(L1_F2 / LDIF_F2);
+	const double wt2(-L2_F2 / LDIF_F2);
+	double lambda1, lambda2;
+	if (phase) {
+	    lambda1 = C_GPS_M / L1_FREQ;
+	    lambda2 = C_GPS_M / L2_FREQ;
+	} else {
+	    lambda1 = 1.0;		// Already in metres
+	    lambda2 = 1.0;
+	}
+
+	try {
+	    if (!precise) {
+		try {
+		    // Read nav file(s) and store unique list of ephemerides
+		    string filename;
+		    std::ifstream nav;
+		    nav.open("vecsol.nav", ios::in);
+		    while (nav >> filename) {
+			cout << "Nav file: " << filename;
+			// Comment:
+			if (filename[0] == '#') {
+			    cout << " skipped" << endl;
+			    nav.getline(s, 80);
+			} else {
+			    cout << endl;
+			    RinexNavStream rnffs(filename.c_str());
+			    rnffs.exceptions(ios::failbit);
+			    RinexNavData rne;
+			    RinexNavHeader hdr;
+
+			    rnffs >> hdr;
+			    if (iono)
+				ion.addIonoModel(DayTime::BEGINNING_OF_TIME, 
+					IonoModel(hdr.ionAlpha, hdr.ionBeta));
+			    while (rnffs >> rne)
+				bcestore.addEphemeris(rne);
+			}
+		    }
+		    bcestore.SearchNear();
+		}
+		catch(...) {
+		    cerr << "Something wrong with nav files." << endl << endl;
+		    exit(-1);
+		}
+	    } else {
+		try{
+		    // Precise ephemerides:
+		    string filename;
+		    std::ifstream eph;
+		    eph.open("vecsol.eph", ios::in);
+		    while (eph >> filename) {
+			cout << "Eph file: " << filename;
+			// Comment:
+			if (filename[0] == '#') {
+			    cout << " skipped" << endl;
+			    eph.getline(s, 80);
+			} else {
+			    cout << endl;
+			    sp3store.loadFile(filename.c_str());
+			}
+		    }
+		sp3store.dump(1, cout);
+		}
+		catch(...) {
+		    cerr << "Something wrong with SP3 files." << endl << endl;
+		    exit(-1);
+		}
+	    }
+	    
+	    gpstk::Matrix <double> x0(MaxDim, 3, 0.0);
+	    gpstk::Vector <FixType> fixed(MaxDim, FIX_NONE);
+	    gpstk::Vector <SatID> FromSat(MaxDim), ToSat(MaxDim);
+	    
+	    // Get station positions from RINEX headers:
+	    RinexObsHeader roh1, roh2;
+
+	    try {
+		RinexObsStream roffs1(argv[1]);
+		RinexObsStream roffs2(argv[2]);
+		roffs1.exceptions(ios::failbit);
+		roffs2.exceptions(ios::failbit);
+
+		roffs1 >> roh1;
+		roffs2 >> roh2;
+	    }
+	    catch(...) {
+		cerr << "Something wrong with obs files." << endl << endl;
+		exit(-1);
+	    }
+            // Here we should input pre-given coordinates (if any) that
+	    //  will override those from the RINEX header
+	    bool coords = true;
+	    string const name1(roh1.markerName);
+	    string const name2(roh2.markerName);
+	    Position XYZ1, XYZ2;
+	    std::ifstream coord1, coord2;
+	    try {
+		coord1.exceptions(ios::failbit);
+		coord2.exceptions(ios::failbit);
+		coord1.open((name1 + ".crd").c_str(), ios::in);
+		coord2.open((name2 + ".crd").c_str(), ios::in);
+	    }
+	    catch(...) {
+		cout << endl;
+		cout << "Did not find / cannot open coordinate files." << endl << endl;
+		coords = false;
+	    }
+	    if (coords) {
+		// Here the given coordinates (of BENCHMARK!) are used if available:
+		coord1 >> XYZ1[0] >> XYZ1[1] >> XYZ1[2];
+		coord2 >> XYZ2[0] >> XYZ2[1] >> XYZ2[2];
+		coord1.close();
+		coord2.close();
+		roh1.antennaPosition = XYZ1;
+		roh2.antennaPosition = XYZ2;
+		cout << "Positions       : " << setprecision(12) << Triple(XYZ1) << endl;
+		cout << "From files      : " << setprecision(12) << Triple(XYZ2) << endl << endl;
+	    }
+	    Triple AO1 = Rotate(roh1.antennaOffset, roh1.antennaPosition);
+	    Triple AO2 = Rotate(roh2.antennaOffset, roh2.antennaPosition);
+
+	    cout << "Geocentric      : " << AO1 << endl 
+		 << "antenna offsets : " << AO2 << endl << endl;
+
+	    // Receiver provided offset to be re-subtracted 
+	    bool apply_clockOffset1(roh1.receiverOffsetValid &&
+				    roh1.receiverOffset);
+	    bool apply_clockOffset2(roh2.receiverOffsetValid &&
+				    roh2.receiverOffset);
+	    // However, sometimes the header record is missing and yet the
+	    // data contains valid offsets (Huh? Ask Werner).
+	    // (WinPrism's Ashtech rinexer seems to need this, and
+	    // doesn't seem to harm otherwise)
+	    apply_clockOffset1 = true;
+	    apply_clockOffset2 = true;
+
+#if 0
+	    // Low hanging fruit
+	    Triple PT1 =
+		permanentTide(Position(roh1.antennaPosition).geodeticLatitude());
+	    Triple PT2 =
+		permanentTide(Position(roh2.antennaPosition).geodeticLatitude());
+	    cout << "Tides:" << PT1 << " " << PT2 << endl;
+#else
+	    Triple PT1, PT2;	    
+#endif
+	    // t1, t2 represent now antenna (ARP) positions.
+	    // (roh1/2.antennaPosition is named wrong, it is benchmark pos!)
+	    Position t1(roh1.antennaPosition + AO1, Position::Cartesian);
+	    Position t2(roh2.antennaPosition + AO2, Position::Cartesian);
+
+	    cout << "Data interval: " << roh1.interval << "," <<
+		roh2.interval << endl;
+	    cout << "Generated by:  " << 
+		roh1.fileProgram << ", " << roh2.fileProgram << endl;
+
+	    // How was this RINEX generated?
+	    bool javad1 = roh1.fileProgram.find("Pinnacle") != string::npos;
+	    bool javad2 = roh2.fileProgram.find("Pinnacle") != string::npos;
+	    // Note: we compute the reduction by comparing L1 with the
+	    // computed range. This is only valid if L1, L2 were 'gauged' to
+	    // pseudoranges, which is the case for Pinnacle RINEX. If not,
+	    // we would have to use one of the code observables instead. See
+	    // code further below.
+	    if (javad1 || javad2) {
+		cout << "RINEX file was not reduced for clock offset." << endl;
+		cout << "We do the reduction ourselves." << endl << endl;
+	    }
+	    
+	    Position const t10(t1);  // To keep unknowns invariant
+	    Position const t20(t2);
+	    Position Pos1, Pos2;
+
+	    double crit(1.0);
+	    double const limit = (phase ? 0.0001 : 0.001);
+	    for (int l = 0; (crit > limit) && l < 25; l++) {
+		// Iteration loop. Important! The unknowns are expected to
+		// remain _identical_ across iterations.
+		cout << "Iteration: " << l << endl;
+		
+		// Map pointing from PRN to obs. eq. element position
+		map <SatID, int> CommonSatsPrev;
+
+		SatID OldRefSat;
+
+		map <SatID, vector<double> > DDobsPrev;
+		map <SatID, double> SecsPrev;
+		
+		// Open and read the observation files one epoch at a time.
+		// Compute a contribution to normal matrix and right hand
+		// side
+		RinexObsStream roffs1(argv[1]);
+		RinexObsStream roffs2(argv[2]);
+		roffs1.exceptions(ios::failbit);
+		roffs2.exceptions(ios::failbit);
+
+		RinexObsHeader dummy1, dummy2;
+		RinexObsData rod1, rod2;
+
+		// Only skip over header this time:
+		roffs1 >> dummy1;
+		roffs2 >> dummy2;
+
+		// Improve antenna positions from previous iteration:
+		if (l > 0) {
+		    t1 = t1 + 0.5 * Pos1;
+		    if (!vecmode)
+			t2 = t2 + 0.5 * Pos2;
+		}
+		Geodetic g1(t1, &geoid);
+		Geodetic g2(t2, &geoid);
+
+		// Output bench mark (not: antenna) positions: (Published
+		// GPS positions are always reduced for solid Earth tides)
+		cout << name1 << ": " << Position(Triple(t1) - AO1 - PT1) << endl 
+		     << name2 << ": " << Position(Triple(t2) - AO2 - PT2) << endl << endl;
+		// Print also geographic coords:
+		Position t1g(Triple(t1) - AO1 - PT1);
+		Position t2g(Triple(t2) - AO2 - PT2);
+		cout << name1 << ": " << t1g.asGeodetic() << endl 
+		     << name2 << ": " << t2g.asGeodetic() << endl;
+
+		// Update these for output at program end:
+		XYZ1 = t1g.asECEF();
+		XYZ2 = t2g.asECEF();
+
+		gpstk::Matrix <double> N(MaxDim, MaxDim, 0.0);
+		gpstk::Matrix <double> b(MaxDim, 3, 0.0);
+		int observations(0), rejections(0), rej_DD(0);
+		double TD_RMS(0), DD_RMS(0), Iono_RMS(0);
+		// points to _after_ the last unknown
+		int unknowns = MaxUnkn;
+		
+		while (roffs1 >> rod1 && roffs2 >> rod2) { // Epoch loop
+		    // Make sure we have a common epoch:
+		    while (rod1.time > rod2.time + 0.1 && roffs2 >> rod2) { }
+		    while (rod1.time + 0.1 < rod2.time && roffs1 >> rod1) { }
+		    double sync_err = rod2.time.secOfDay() - rod1.time.secOfDay();
+		    if (abs(sync_err) > 0.001) {
+			cout << "Synchronization Error: " << 
+			    std::setprecision(6) << sync_err << " sec" << endl;
+		    }
+		    double Secs = rod1.time.secOfDay();
+
+		    // Experimental for WinPrism's RINEX
+		    if (apply_clockOffset1)
+			rod1.time -= rod1.clockOffset;
+		    if (apply_clockOffset2)
+			rod2.time -= rod2.clockOffset;
+
+		    if (rod1.epochFlag < 2 && rod2.epochFlag < 2)
+			// Observations are good
+		    {
+			map <SatID, int> CommonSats;
+			CommonSats.clear();
+			vector <SatID> prnVec_1, prnVec_2;
+			vector <double> rangeVecL1_1, rangeVecL2_1;
+			vector <double> rangeVecL1_2, rangeVecL2_2;
+
+			// First station
+			stationData(rod1, phase, prnVec_1, rangeVecL1_1,
+				    rangeVecL2_1);
+			// Second station
+			stationData(rod2, phase, prnVec_2, rangeVecL1_2,
+				    rangeVecL2_2);
+
+			/// Process station pairs
+
+			// for construction of double diffs
+			SatID RefSat;
+			bool hasRefSat(false);
+
+			double ref_rdiffL1, ref_rdiffL2;
+			vector <double> ref_A(MaxUnkn);
+			gpstk::Matrix <double> A(MaxDim, MaxSats, 0.0);
+			gpstk::Matrix <double> Obs(MaxSats, 3, 0.0);
+			// For var-cov modelling
+			vector <double> Q(MaxSats), 
+			       Elev10(MaxSats), Elev20(MaxSats);
+			double Qref;
+			int nObs(0);
+
+			// Find out the highest satellite:
+			double best(0.0);
+			int bestIdx(0);
+			bool stickWithOld = false;
+			for (int i = 0; i != prnVec_2.size(); i++)
+			    if (prnVec_2[i].id > 0) {
+
+				// Invariant over iterations! Uses t10, t20
+				double dummy = EPH_RANGE(CER2, rod2, t10,
+					prnVec_2[i]);
+				double const riseVel1 = CER2.svPosVel.v.dot(t10);
+				//double const riseVel1 =
+				//	-CER2.svPosVel.v.dot(CER2.svPosVel.x);
+				Elev10[i] = CER2.elevation;
+				bool const elev1OK = CER2.elevation > refsat_elev;
+
+				dummy = EPH_RANGE(CER2, rod2, t20,
+					prnVec_2[i]);
+				double const riseVel2 = CER2.svPosVel.v.dot(t20);
+				//double const riseVel2 =
+				//	-CER2.svPosVel.v.dot(CER2.svPosVel.x);
+				Elev20[i] = CER2.elevation;
+				bool const elev2OK = CER2.elevation > refsat_elev;
+
+				double const riseVel = 0.5 * (riseVel1 + riseVel2);
+				
+				if (elev1OK && elev2OK 
+					&& riseVel > best
+					&& !stickWithOld) {
+				    best = riseVel;
+				    bestIdx = i;
+				}
+				// Hang on to same ref sat if still high enough 
+				if (OldRefSat == prnVec_2[i] 
+				    && CER2.elevation > refsat_elev) {
+				    bestIdx = i;
+				    stickWithOld = true;
+				}
+			    }
+
+			for (int ii = 0; ii != prnVec_2.size(); ii++) {
+			    // Reshuffle... 
+			    int i = (ii + bestIdx) % prnVec_2.size();
+			    if (prnVec_2[i].id > 0 
+				    && Elev10[i] > cutoff_elev
+				    && Elev20[i] > cutoff_elev) {
+				double r2 = EPH_RANGE(CER2, rod2, t2,
+					prnVec_2[i]);
+				double trop2 =
+				    trop.correction(t2, CER2.svPosVel.x, rod2.time);
+				r2 += trop2;
+				
+				for (int j = 0; j != prnVec_1.size(); j++) {
+				    if (prnVec_1[j].id > 0
+					&& prnVec_1[j].id == prnVec_2[i].id)
+				    {
+					// This sat is visible from both
+					// stations
+					double r1 = EPH_RANGE(CER1, rod1, t1,
+					prnVec_1[j]);
+					double trop1 = trop.correction(t1,
+						    CER1.svPosVel.x, rod1.time);
+					r1 += trop1;
+					
+					// Between-station diffs to each satellite
+					double diffL1 = rangeVecL1_1[j] - rangeVecL1_2[i];
+					double diffL2 = rangeVecL2_1[j] - rangeVecL2_2[i];
+					// Subtract out approx values; this
+					// value now roughly reflects the
+					// inter-station bias difference and
+					// is thus nearly the same for all
+					// sats
+					double rdiffL1 = lambda1 * diffL1 - (r1 - r2);
+					double rdiffL2 = lambda2 * diffL2 - (r1 - r2);
+
+					// The clock offset problem for
+					// Javad / Pinnacle:
+					double rr1, rr2;
+					// Range rates:
+					if (javad1) 
+					    rr1 = CER1.svPosVel.v.dot(CER1.cosines);
+					if (javad2) 
+					    rr2 = CER2.svPosVel.v.dot(CER2.cosines);
+					// Clock corrections:
+					double cc1 = lambda1 * rangeVecL1_1[j] - r1;
+					double cc2 = lambda1 * rangeVecL1_2[i] - r2;
+					rdiffL1 -= (rr1 * cc1 - rr2 * cc2) / C_GPS_M;
+					rdiffL2 -= (rr1 * cc1 - rr2 * cc2) / C_GPS_M;
+
+					if (iono) {
+					    // Ionospheric corrections:
+					    double const 
+						ionoL1_1 = ion.getCorrection(
+						    rod1.time, g1, CER1.elevation,
+						    CER1.azimuth, IonoModel::L1);
+					    double const
+						ionoL2_1 = ion.getCorrection(
+						    rod1.time, g1, CER1.elevation,
+						    CER1.azimuth, IonoModel::L2);
+					    double const
+						ionoL1_2 = ion.getCorrection(
+						    rod2.time, g2, CER2.elevation,
+						    CER2.azimuth, IonoModel::L1);
+					    double const
+						ionoL2_2 = ion.getCorrection(
+						    rod2.time, g2, CER2.elevation,
+						    CER2.azimuth, IonoModel::L2);
+					    // Apply them:
+					    if (phase) {
+						rdiffL1 += ionoL1_1 - ionoL1_2;
+						rdiffL2 += ionoL2_1 - ionoL2_2;
+					    } else {
+						rdiffL1 -= ionoL1_1 - ionoL1_2;
+						rdiffL2 -= ionoL2_1 - ionoL2_2;
+					    }
+					}
+
+					vector <double> A_(MaxDim, 0.0);
+					// Store obs. coefficients for
+					// coordinates
+					for (int k = 0; k < 3; k++)
+					    A_[k] = CER1.cosines[k];
+					if (!vecmode) {
+					    for (int k = 3; k < 6; k++)
+						A_[k] = -CER2.cosines[k - 3];
+					}
+					// Tropo estimation
+					if (tropo) {
+					    A_[MaxUnkn - 2] = trop1;
+					    A_[MaxUnkn - 1] = -trop2;
+					}
+
+					// Weight coefficient of this obs:
+					double const q_ = 1.0 / 
+					    sin(DEG_TO_RAD * CER1.elevation) + 1.0 /
+					    sin(DEG_TO_RAD * CER2.elevation);
+					// Build obs. coefs for satellite
+					// ambiguities
+					if (!hasRefSat) {
+					    RefSat = prnVec_1[j];
+					    hasRefSat = true;
+
+					    // Ref sat change; invalidates
+					    // unknowns
+					    if (RefSat != OldRefSat) {
+						CommonSatsPrev.clear();
+						cout << "New ref sat:" <<
+						    RefSat << endl;
+					    }
+
+					    ref_rdiffL1 = rdiffL1;
+					    ref_rdiffL2 = rdiffL2;
+					    Qref = q_;
+					    for (int k = 0; k < MaxUnkn; k++)
+						ref_A[k] = A_[k];
+					} else {
+					    // Construct inter-sat diffs
+					    bool reject(false);
+					    FixType fix(FIX_NONE);
+					    vector <double> DDobs(3);
+					    DDobs[1] = rdiffL1 - ref_rdiffL1;
+					    DDobs[2] = rdiffL2 - ref_rdiffL2;
+					    
+					    // iono free observable, unit metres
+					    DDobs[0] =
+						wt1 * DDobs[1] + wt2 * DDobs[2];
+					    for (int k = 0; k < MaxUnkn; k++)
+						A_[k] -= ref_A[k];
+
+					    SatID ThisSat = prnVec_1[j];
+					    if (CommonSatsPrev.find(ThisSat) ==
+						CommonSatsPrev.end()) {
+						// New satellite
+						CommonSats[ThisSat] = unknowns;
+						if (phase) {
+						    FromSat[unknowns] = RefSat;
+						    ToSat [unknowns] = ThisSat;
+						    cout << endl << "New unknown " 
+							<< unknowns << " == " 
+							<< asString(FromSat[unknowns])
+							<< " -> "
+							<< asString(ToSat[unknowns])
+							<< endl;
+						    
+						    // create new DD ambiguity unknown
+						    A_[unknowns] = 1.0;
+						    // Initial approx. ambiguities
+						    if (l == 0)
+							for (int k = 0; k < 3; k++)
+							    x0(unknowns,k) = 
+								DDobs[k];
+
+						    for (int k = 0; k < 3; k++)
+							DDobs[k] -= 
+							    x0(unknowns,k);
+						    unknowns++;
+						} else {
+						    // Rough test double diffs
+						    reject =
+							(std::abs(DDobs[0]) > DDrej);
+						}
+						// Initialize previous obs for
+						// triple diff comp
+						DDobsPrev[ThisSat] = DDobs;
+						SecsPrev[ThisSat] = Secs;
+						reject = false;
+					    } else {
+
+						// Already known sat, copy forward
+						CommonSats[ThisSat] =
+						    CommonSatsPrev[ThisSat];
+						if (phase) {
+						    A_[CommonSats[ThisSat]] = 1.0;
+						    for (int k = 0; k < 3; k++)
+							DDobs[k] -= 
+							    x0(CommonSats[ThisSat],k);
+						}
+
+						// Triple difference testing
+
+						double timebase 
+						    = Secs - SecsPrev[ThisSat];
+						timebase = 
+						    (timebase > 10 * roh1.interval ?
+							0.000001 : roh1.interval);
+						double res 
+						    = (DDobs[0] -
+							    DDobsPrev[ThisSat][0]) / timebase;
+						// Cycle slips will show up here
+						// over 30 s as 0.0226 and
+						// 0.0131, respectively.
+						reject =
+						    (std::abs(res) *
+						     sqrt(2.0 / (Qref + q_)) >
+						    (phase ? PTDrej : CTDrej));
+
+						// Include rough DD test
+						// too: this occurs esp.
+						// with newly appearing sats
+						if (!reject && 
+						    std::abs(DDobs[0]) > DDrej) {
+						    if (debug)
+							cout << "DD rej:" 
+							     << DDobs[0];
+						    rej_DD++;
+						    reject = true;
+						}
+
+						if (!reject) {
+						    // Accumulate statistics
+						    TD_RMS += res * res;
+						    DD_RMS +=
+							DDobs[0] * DDobs[0];
+						    double Iono = 
+							(DDobs[2] - DDobs[1]) 
+							/ wt1;
+						    Iono_RMS += Iono * Iono;
+							
+						}
+						else {
+						    rejections++;
+						}
+						if (debug) {
+						    cout << ThisSat << ":";
+						    if (reject)
+							cout << "REJ [" <<
+							    Elev10[i] << ":" <<
+							    Elev20[i] << "] ";
+						    cout << setprecision(4) << res << " "; 
+						}
+						observations++;
+					    }
+
+					    if (!reject) {
+						// Update "last good" obs
+						DDobsPrev[ThisSat] = DDobs;
+						SecsPrev[ThisSat] = Secs;
+
+						// Collect per-obs quantities
+						// into per-epoch tables
+						for (int k = 0; k < unknowns; k++)
+						    A(k, nObs) = A_[k];
+						for (int k = 0; k < 3; k++)
+						    Obs(nObs, k) = DDobs[k];
+						Q[nObs] = q_;
+						nObs++;
+					    }
+
+					}	// end other-than-ref sats processing
+
+				    } // end sats visible from both stations
+				}
+			    }
+			}		// All obs for this epoch processed
+
+			if (nObs > 0) {
+			    gpstk::Matrix <double> Qmat(nObs, nObs, 0.0);
+			    for (int io = 0; io < nObs; io++) {
+				if (truecov)
+				    for (int jo = 0; jo < nObs; jo++)
+					Qmat(io, jo) = Qref;
+				Qmat(io, io) += Q[io];
+			    }
+			    Qmat = gpstk::inverse(Qmat);
+
+			    // Right hand side vector
+			    for (int k = 0; k < unknowns; k++)
+				for (int io = 0; io < nObs; io++) {
+				    for (int jo = 0; jo < nObs; jo++)
+					for (int ot = 0; ot < 3; ot++)
+					    b(k, ot) += A(k, io) * Obs(jo, ot)
+						* Qmat(io, jo);
+
+				}
+
+			    // Normal eqs fill-in with full weight matrix per-epoch
+			    for (int k = 0; k < unknowns; k++) {
+				for (int io = 0; io < nObs; io++) {
+				    if (A(k, io) != 0.0) { // Optimize
+					for (int m = 0; m < unknowns; m++) {
+					    for (int jo = 0; jo < nObs; jo++)
+						N(k, m) += A(k, io) * A(m, jo) *
+						    Qmat(io, jo);
+					}
+				    }
+				}
+			    }
+			} // Normals building
+
+			cout << endl;
+			cout << "epoch " << rod1.time;
+			cout << " unkn " << unknowns;
+			cout << " obs  " << nObs;
+			cout << " sats " << CommonSats.size() << endl;
+
+			CommonSatsPrev = CommonSats;
+			OldRefSat = RefSat;
+
+		    }		// End usable data
+
+		}			// End loop through each epoch
+
+		cout << endl;
+		cout << "Total unknowns:           " << unknowns << endl;
+		cout << "Observations:             " << observations << endl;
+		cout << "Rejected:                 " << rejections;
+		cout << " or " << 100.0 * rejections / observations << "%" << endl;
+		cout << " of which DD-based:       " << rej_DD;
+		cout << "  or " << 100.0 * rej_DD / observations << "%" << endl;
+		cout << "Triple-diff RMS [m/s]:    " << sqrt(TD_RMS /
+							 (observations -
+							  rejections)) << endl;
+		float const DDrms = sqrt(DD_RMS / (observations - rejections));
+		cout << "Double-diff RMS [m]:      " << DDrej << endl;
+		// Three-sigma criterion, generous:
+		DDrej = 2.0 * 3.0 * DDrms;
+		cout << "Iono RMS on L1 [m]:       " << sqrt(Iono_RMS /
+							 (observations -
+							  rejections)) <<
+								endl << endl;
+
+		if (debug) {
+		    cout << "Start of b vector:" << setprecision(10) << endl;
+		    cout << "b0: " << b(0,0) << " " << b(1,0) << " " << b(2,0)
+			<< endl;
+		    cout << "b1: " << b(0,1) << " " << b(1,1) << " " << b(2,1)
+			<< endl;
+		    cout << "b2: " << b(0,2) << " " << b(1,2) << " " << b(2,2)
+			<< endl << endl;
+		}
+
+		// Here we solve the normal equations and print solution
+		gpstk::Matrix <double> NN(unknowns, unknowns);
+		gpstk::Matrix <double> bb(unknowns, 3);
+		Solve(N, b, NN, bb, SOLVE_COORDS, fixed, MaxUnkn, unknowns,
+			tropo);
+
+		cout << "Weight coefficient matrix:" << endl;
+		for (int i = 0; i < MaxUnkn; i++) {
+		    for (int j = 0; j < MaxUnkn; j++) {
+			cout << setw(10) << NN(i, j) << " ";
+		    }
+		    cout << endl;
+		}
+		cout << endl;
+
+		// Solution:
+		gpstk::Matrix <double> sol = NN * bb;
+
+		cout << "Solution (correction to inter-station vector):" <<
+		    setprecision(5) << endl;
+		Triple PosCorr0 = Triple(sol(0,0), sol(1,0), sol(2,0));
+		Triple PosCorr1 = Triple(sol(0,1), sol(1,1), sol(2,1));
+		Triple PosCorr2 = Triple(sol(0,2), sol(1,2), sol(2,2));
+		if (ionoFree)
+		    Pos1 = PosCorr0;
+		else
+		    Pos1 = 0.5 * (PosCorr1 + PosCorr2);
+		if (!vecmode) {
+		    PosCorr0 = PosCorr0 - Triple(sol(3,0), sol(4,0), sol(5,0));
+		    PosCorr1 = PosCorr1 - Triple(sol(3,1), sol(4,1), sol(5,1));
+		    PosCorr2 = PosCorr2 - Triple(sol(3,2), sol(4,2), sol(5,2));
+		    if (ionoFree)
+			Pos2 = Pos1 - Position(PosCorr0);
+		    else
+			Pos2 = Pos1 + 0.5 * Position(PosCorr1 + PosCorr2);
+		}
+		cout << "Iono free: " << PosCorr0 << endl;
+		cout << "Freq. 1:   " << PosCorr1 << endl;
+		cout << "Freq. 2:   " << PosCorr2 << endl;
+		cout << endl;
+
+		// Manhattan distance for iteration stop (is there a
+		// std method for this?):
+		if (ionoFree)
+		    crit = std::abs(PosCorr0[0]) + std::abs(PosCorr0[1]) 
+			 + std::abs(PosCorr0[2]);
+		else
+		    crit = 0.5 * 
+			  (std::abs(PosCorr1[0]) + std::abs(PosCorr1[1]) 
+			 + std::abs(PosCorr1[2]) + std::abs(PosCorr2[0]) 
+			 + std::abs(PosCorr2[1]) + std::abs(PosCorr2[2]));
+
+		cout << "Standard deviations (unscaled):" << endl;
+		for (int k = 0; k < MaxUnkn; k++)
+		    cout << sqrt(NN(k, k)) << " ";
+		cout << endl;
+
+		// Again: published vectors must be conventionally 
+		// reduced for tide.
+		// And published vector must be inter-benchmark:
+		Position vec = Position(Triple(t1) - AO1 - PT1) 
+		    	     - Position(Triple(t2) - AO2 - PT2);
+		cout << "A priori vector:" << endl << vec << endl;
+		cout << "A posteriori vector:" << endl 
+		     << Position(Triple(vec) + PosCorr0)
+		     << " (Iono free)" << endl;
+		Triple PosCorrMean(PosCorr1 + PosCorr2);
+		PosCorrMean = 0.5 * PosCorrMean;
+		cout << Position(Triple(vec) + PosCorrMean) 
+		     << " (L1 + L2)" << endl;
+		cout << endl;
+
+		if (tropo) {
+		    cout <<
+			"Tropospheric corr. parameters (fraction of full effect):"
+			<< endl;
+
+		    cout << sol(MaxUnkn - 2, 0) << " " << sol(MaxUnkn - 1, 0) << endl;
+		    cout << "Standard deviations (unscaled):" << endl;
+		    cout << sqrt(NN(MaxUnkn - 2, MaxUnkn - 2)) << " "
+			 << sqrt(NN(MaxUnkn - 1, MaxUnkn - 1)) << endl <<
+			endl;
+		}
+
+		// Ambiguity fixing on the unknowns, keeping coords as known:
+		if (phase) {
+		    Solve(N, b, NN, bb, SOLVE_AMBS, fixed, MaxUnkn,
+			    unknowns, tropo);
+		    sol = NN * bb;
+
+		    // Here we use the relationships between DD ambiguities, 
+		    // e.g. (G18-G6) - (G26-G6) - (G26-G18) = 0
+		    // for a condition equation adjustment on NN, bb
+		    int k, j, l, dir1, dir2, dir3;
+		    int closures = 0;
+		    SatID Free1, Free2;
+		    for (k = MaxUnkn; k < unknowns; k++)
+			for (j = k + 1; j < unknowns; j++) {
+			    dir1 = 0;
+			    if (FromSat[k] == FromSat[j]) {
+				dir1 = 1; 
+				dir2 = -1;
+				Free1 = ToSat[k];
+				Free2 = ToSat[j];
+			    }
+			    if (ToSat[k] == ToSat[j]) {
+				dir1 = -1; 
+				dir2 = 1;
+				Free1 = FromSat[k];
+				Free2 = FromSat[j];
+			    }
+			    if (FromSat[k] == ToSat[j]) {
+				dir1 = 1; 
+				dir2 = 1;
+				Free1 = ToSat[k];
+				Free2 = FromSat[j];
+			    }
+			    if (ToSat[k] == FromSat[j]) {
+				dir1 = -1; 
+				dir2 = -1;
+				Free1 = FromSat[k];
+				Free2 = ToSat[j];
+			    }
+
+			    if (dir1 != 0)
+				for (l = j + 1; l < unknowns; l++) {
+			    	    dir3 = 0;
+				    if (Free1 == FromSat[l] && Free2 == ToSat[l])
+					dir3 = 1;
+				    if (Free1 == ToSat[l] && Free2 == FromSat[l])
+					dir3 = -1;
+				    if (reduce && dir3 != 0) {
+					if (Reduce(NN, sol, x0, k, j, l, 
+						dir1, dir2, dir3, 
+						MaxUnkn, unknowns))
+					    closures++;
+				    }
+				}
+			}
+
+		    cout << endl << "No. of closures: " << closures << endl << endl;
+		    int fixedunknowns = 0;
+		    int widelanes = 0;
+		    cout << "DD bias fixes (fractional cycles):" << endl;
+		    for (int k = MaxUnkn; k < unknowns; k++) {
+			cout << "[" << setprecision(8) << sqrt(NN(k,k)) << "] ";
+			cout << k << " (";
+			cout << asString(FromSat[k]) << " -> " 
+			     << asString(ToSat[k]) << "): ";
+			vector <double> x0vec(3);
+			x0vec[1] = x0(k, 1) + sol(k, 1);
+			x0vec[2] = x0(k, 2) + sol(k, 2);
+			FixType f = phaseCycles(x0vec, lambda1, lambda2, wt1, wt2);
+			// remember to correct iono-free too
+			x0(k, 0) = wt1 * x0vec[1] + wt2 * x0vec[2];
+			x0(k, 1) = x0vec[1];
+			x0(k, 2) = x0vec[2];
+			if (f == FIX_BOTH)
+			    fixedunknowns++;
+			if (f == FIX_WIDELANE) 
+			    widelanes++;
+			fixed[k] = f;
+		    }
+		    cout << endl;
+		    cout << "Fixed:      " << fixedunknowns << "  "
+			 << 100.0 * fixedunknowns / (unknowns - MaxUnkn) << "%" << endl;
+		    cout << "Widelanes:  " << widelanes << "  "
+			 << 100.0 * widelanes / (unknowns - MaxUnkn) << "%" << endl;
+		    cout << endl;
+		}
+
+	    } // iteration loop end
+
+	    cout << "Writing coordinate(s) to file(s)..." << endl;
+	    std::ofstream coord1o, coord2o;
+	    try {
+		coord1o.exceptions(ios::failbit);
+		coord1o.open((name1 + ".crd").c_str(), ios::out | ios::trunc);
+		coord1o << setprecision(12) << 
+		    XYZ1[0] << ' ' << XYZ1[1] << ' ' << XYZ1[2] << endl;
+		coord1o.close();
+		if (!vecmode || !coords) {
+		    coord2o.exceptions(ios::failbit);
+		    coord2o.open((name2 + ".crd").c_str(), ios::out | ios::trunc);
+		    coord2o << setprecision(12) << 
+			XYZ2[0] << ' ' << XYZ2[1] << ' ' << XYZ2[2] << endl;
+		    coord2o.close();
+		}
+	    }
+	    catch(...) {
+		cerr << "Exception writing coordinate file(s)" << endl;
+	    }
+	        
+	    cout << "Finished." << endl;
+    	}
+        catch(Exception & e) {
+    	    cerr << e << endl;
+	}
+	catch(...) {
+	    cerr << "Caught an unexpected exception." << endl;
+	}
+
+	exit(0);
+ 
+    }
+
+
diff --git a/dev/apps/differential/vecsol.eph b/dev/apps/differential/vecsol.eph
new file mode 100644
index 0000000..e8d2581
--- /dev/null
+++ b/dev/apps/differential/vecsol.eph
@@ -0,0 +1,15 @@
+# The sp3 precise ephemeris file. Use # in first pos for comment
+igs11172.sp3
+igs11173.sp3
+igs11174.sp3
+igs11175.sp3
+igs11176.sp3
+# Day 6 of GPS week 1323 is May 21, 2005
+igs13235.sp3
+igs13236.sp3
+#gfz13236.sp3
+igs13240.sp3
+# rapid orbits 7-8 sep 2005
+igr13393.sp3
+igr13394.sp3
+igs13250.sp3
diff --git a/dev/apps/differential/vecsol.nav b/dev/apps/differential/vecsol.nav
new file mode 100644
index 0000000..430ebe5
--- /dev/null
+++ b/dev/apps/differential/vecsol.nav
@@ -0,0 +1,15 @@
+# Broadcast ephemeris file. Use # in first pos for comment
+# day 141 of 2005 is May 21
+brdc1410.05n
+brdc2510.05n
+brdc2500.05n
+# Sep 7
+SAHA2501.05N
+# Sep 8
+KUMP2511.05N
+# Mauri
+0058275h.03N
+6146275g.03N
+#
+kiru1550.05n
+mas11550.05n
diff --git a/trunk/apps/differential/vecsol.pdf b/dev/apps/differential/vecsol.pdf
similarity index 100%
rename from trunk/apps/differential/vecsol.pdf
rename to dev/apps/differential/vecsol.pdf
diff --git a/dev/apps/difftools/DiffFrame.hpp b/dev/apps/difftools/DiffFrame.hpp
new file mode 100644
index 0000000..7caeb27
--- /dev/null
+++ b/dev/apps/difftools/DiffFrame.hpp
@@ -0,0 +1,85 @@
+#pragma ident "$Id$"
+
+
+#ifndef DIFFFRAME_HPP
+#define DIFFFRAME_HPP
+
+#include "CommandOptionWithTimeArg.hpp"
+#include "BasicFramework.hpp"
+
+class DiffFrame : public gpstk::BasicFramework
+{
+public:
+      /**
+       * arg0 is the name of the executable from argv[0].
+       * type is a string with the type of file (i.e. "RINEX Obs").
+       * message is an extra message that gets passed to the
+       * program description.
+       */
+   DiffFrame(char* arg0, 
+             const std::string& type, 
+             const std::string& message = std::string())
+         : gpstk::BasicFramework(arg0,
+                                 "Diffs input " + type + " files. " + message),
+           timeOption('t', "time", "Start of time range to compare (default ="
+                      " \"beginning of time\")"),
+           eTimeOption('e', "end-time", "End of time range to compare"
+                       " (default = \"end of time\")"),
+           inputFileOption("An input " + type + " file.", true),
+           startTime(gpstk::DayTime::BEGINNING_OF_TIME),
+           endTime(gpstk::DayTime::END_OF_TIME)
+   {
+      inputFileOption.setMaxCount(2);
+      timeOption.setMaxCount(1);
+      eTimeOption.setMaxCount(1);
+      timeOptions.addOption(&timeOption);
+      timeOptions.addOption(&eTimeOption);
+   }
+
+   virtual bool initialize(int argc, char* argv[]) throw()
+   {
+      if (!gpstk::BasicFramework::initialize(argc, argv))
+      {
+         return false;
+      }
+
+      if (inputFileOption.getCount() != 2)
+      {
+         std::cerr << "This program requires two input files." << std::endl;
+         return false;
+      }
+
+      if (timeOption.getCount())
+      {
+         startTime = timeOption.getTime()[0];
+      }
+
+      if (eTimeOption.getCount())
+      {
+         endTime = eTimeOption.getTime()[0];
+      }
+
+      if (startTime > endTime)
+      {
+            std::cerr << "End time can't precede start time." << std::endl;
+            return false;
+      }
+      return true;
+   }
+
+protected:
+   virtual void process() = 0;
+   
+      /// start time for file record differencing
+   gpstk::CommandOptionWithSimpleTimeArg timeOption;
+      /// end time for file record differencing
+   gpstk::CommandOptionWithSimpleTimeArg eTimeOption;
+      /// if either of the time options are set
+   gpstk::CommandOptionGroupOr timeOptions;
+   gpstk::CommandOptionRest inputFileOption;
+
+   gpstk::DayTime startTime, endTime;
+};
+
+
+#endif
diff --git a/dev/apps/difftools/Jamfile b/dev/apps/difftools/Jamfile
new file mode 100644
index 0000000..1d18d67
--- /dev/null
+++ b/dev/apps/difftools/Jamfile
@@ -0,0 +1,12 @@
+# $Id$
+
+SubDir TOP apps difftools ;
+
+GPSLinkLibraries rowdiff rnwdiff rmwdiff ephdiff ficdiff : gpstk ;
+BonkForte ; # bleah.
+
+GPSMain rowdiff : rowdiff.cpp ;
+GPSMain rnwdiff : rnwdiff.cpp ;
+GPSMain rmwdiff : rmwdiff.cpp ;
+GPSMain ephdiff : ephdiff.cpp ;
+GPSMain ficdiff : ficdiff.cpp ;
diff --git a/dev/apps/difftools/Makefile.am b/dev/apps/difftools/Makefile.am
new file mode 100644
index 0000000..8d63dba
--- /dev/null
+++ b/dev/apps/difftools/Makefile.am
@@ -0,0 +1,11 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ../../src/libgpstk.la
+
+bin_PROGRAMS = rowdiff rnwdiff rmwdiff ephdiff ficdiff
+
+rowdiff_SOURCES = rowdiff.cpp
+rnwdiff_SOURCES = rnwdiff.cpp
+rmwdiff_SOURCES = rmwdiff.cpp
+ephdiff_SOURCES = ephdiff.cpp
+ficdiff_SOURCES = ficdiff.cpp
diff --git a/dev/apps/difftools/ephdiff.cpp b/dev/apps/difftools/ephdiff.cpp
new file mode 100644
index 0000000..198895b
--- /dev/null
+++ b/dev/apps/difftools/ephdiff.cpp
@@ -0,0 +1,360 @@
+#pragma ident "$Id$"
+
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include "FICStream.hpp"
+#include "FICData.hpp"
+#include "RinexNavData.hpp"
+#include "RinexNavStream.hpp"
+#include "FileFilterFrame.hpp"
+
+#include "BasicFramework.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+struct EphDiffLessThan : 
+   public std::binary_function<EngEphemeris, EngEphemeris, bool>
+{
+public:
+   bool operator() (const EngEphemeris& l, const EngEphemeris& r) const
+      {
+         if (l.getPRNID() < r.getPRNID())
+            return true;
+         else if (l.getPRNID() > r.getPRNID())
+            return false;
+         else if (l.getFullWeek() < r.getFullWeek())
+            return true;
+         else if (l.getFullWeek() > r.getFullWeek())
+            return false;
+         else if (l.getIODC() < r.getIODC())
+            return true;
+         else if (l.getIODC() > r.getIODC())
+            return false;
+         else if (l.getASAlert(1) < r.getASAlert(1))
+            return true;
+         else if (l.getASAlert(1) > r.getASAlert(1))
+            return false;
+         else if (l.getASAlert(2) < r.getASAlert(2))
+            return true;
+         else if (l.getASAlert(2) > r.getASAlert(2))
+            return false;
+         else if (l.getASAlert(3) < r.getASAlert(3))
+            return true;
+         else if (l.getASAlert(3) > r.getASAlert(3))
+            return false;
+
+         return false;
+      }
+};
+
+struct EphDiffEquals : 
+   public std::binary_function<EngEphemeris, EngEphemeris, bool>
+{
+public:
+   bool operator() (const EngEphemeris& l, const EngEphemeris& r) const
+      {
+         if ( (l.getPRNID() == r.getPRNID()) && 
+              (l.getIODC() == r.getIODC()) )
+            return true;
+         return false;
+      }
+};
+
+struct EphDiffFinder : 
+   public std::unary_function<EngEphemeris, bool>
+{
+public:
+   EphDiffFinder(const EngEphemeris& e)
+         : PRN(e.getPRNID()), IODC(e.getIODC())
+      {}
+
+   bool operator() (const EngEphemeris& l) const
+      {
+         if ( (l.getPRNID() == PRN) && (l.getIODC() == IODC) )
+            return true;
+         return false;
+      }
+
+private:
+   short PRN;
+   double IODC;
+};
+
+class EphDiff : public BasicFramework
+{
+public:
+   EphDiff(char* arg0);
+   virtual bool initialize(int argc, char* argv[]) throw();
+
+protected:
+   virtual void process();
+
+   void fillFIC(FileFilterFrame<FICStream, FICData>& ff,
+                vector<EngEphemeris>& l);
+   void fillRINEX(FileFilterFrame<RinexNavStream, RinexNavData>& ff,
+                  vector<EngEphemeris>& l);
+
+private:
+   CommandOptionWithAnyArg ficFileOption;
+   CommandOptionWithAnyArg rinexFileOption;
+
+   string file1, file2;
+
+   vector<EngEphemeris> file1list, file2list;
+};
+
+EphDiff::EphDiff(char* arg0)
+      : BasicFramework(arg0, "Compares the contents of two files with ephemeris data - either file can be RINEX or FIC"),
+        ficFileOption('f',
+                      "fic",
+                      "Name of an input FIC file.",
+                      false),
+        rinexFileOption('r',
+                        "rinex",
+                        "Name of an input RINEX NAV file", 
+                        false)
+{
+}
+
+bool EphDiff::initialize(int argc, char* argv[]) throw()
+{
+   if(!BasicFramework::initialize(argc, argv))
+   {
+      return false;
+   }
+
+      // check the command options for 2 input files
+   int ficCount = ficFileOption.getCount();
+   int rinexCount = rinexFileOption.getCount();
+
+   if (  ((ficCount == 2) && (rinexCount != 0)) ||
+         ((ficCount == 1) && (rinexCount != 1)) ||
+         ((ficCount == 0) && (rinexCount != 2)) )
+   {
+      cout << "Exactly two input files must be specified on the command line" 
+           << endl
+           << "   ephdiff is ending..." << endl
+           << endl;
+      return false;
+   }
+
+      // open the files
+   
+      // in the case of 1 FIC and 1 rinex file, make sure the first file
+      // is chosen correctly...
+   if ( (ficCount == 1) && (rinexCount == 1) )
+   {
+      string ficname = ficFileOption.getValue()[0];
+      string rinexname = rinexFileOption.getValue()[0];
+
+      FileFilterFrame<FICStream, FICData> ficdata(ficname);
+      FileFilterFrame<RinexNavStream, RinexNavData> rinexdata(rinexname);
+
+      if (ficFileOption.getOrder() < rinexFileOption.getOrder())
+      {
+         file1 = ficname;
+         file2 = rinexname;
+         fillFIC(ficdata, file1list);
+         fillRINEX(rinexdata, file2list);
+      }
+      else
+      {
+         file1 = rinexname;
+         file2 = ficname;
+         fillRINEX(rinexdata, file1list);
+         fillFIC(ficdata, file2list);
+      }
+
+   }
+   else if (ficCount == 2)
+   {
+      file1 = ficFileOption.getValue()[0];
+      FileFilterFrame<FICStream, FICData> fic1(file1);
+      file2 = ficFileOption.getValue()[1];
+      FileFilterFrame<FICStream, FICData> fic2(file2);
+
+      fillFIC(fic1, file1list);
+      fillFIC(fic2, file2list);
+   }
+   else // if (rinexCount == 2)
+   {
+      file1 = rinexFileOption.getValue()[0];
+      FileFilterFrame<RinexNavStream, RinexNavData> rn1(file1);
+      file2 = rinexFileOption.getValue()[1];
+      FileFilterFrame<RinexNavStream, RinexNavData> rn2(file2);
+
+      fillRINEX(rn1, file1list);
+      fillRINEX(rn2, file2list);
+   }
+
+   return true;
+}
+
+void EphDiff::process()
+{
+      // first sort and filter the lists
+   stable_sort(file1list.begin(), file1list.end(), EphDiffLessThan());
+   stable_sort(file2list.begin(), file2list.end(), EphDiffLessThan());
+
+   vector<EngEphemeris>::iterator listitr;
+
+   listitr = unique(file1list.begin(), file1list.end(), EphDiffEquals());
+   file1list.erase(listitr, file1list.end());
+
+   listitr = unique(file2list.begin(), file2list.end(), EphDiffEquals());
+   file2list.erase(listitr, file2list.end());
+
+      // for each element in the first list, try to find a match in the
+      // second list.  if a match is found, compare the data. otherwise
+      // add to the unmatchedData list.
+   vector<EngEphemeris> unmatchedData;
+
+   while (!file1list.empty())
+   {
+      listitr = find_if(file2list.begin(), 
+                        file2list.end(), 
+                        EphDiffFinder(file1list[0]));
+                      
+      if (listitr != file2list.end())
+      {
+            // compare the data
+         
+            // delete the data so we don't search for it again
+         file2list.erase(listitr);
+      }
+      else
+         unmatchedData.push_back(file1list[0]);
+
+      file1list.erase(file1list.begin());
+   }
+
+      // that's all the processing... now just write the results
+   cout << "Data in " << file1 << " not found in " << file2 << ": " << endl;
+
+   if (!unmatchedData.empty())
+   {
+      listitr = unmatchedData.begin();
+      while (listitr != unmatchedData.end())
+      {
+         (*listitr).dump(cout << endl);
+         listitr++;
+      }
+   }
+   else
+      cout << "   All data in " << file1 << " was found in " << file2 << "."
+           << endl;
+   
+   cout << endl;
+   cout << "Data in " << file2 << " not found in " << file1 << ": " << endl;
+   
+   if (!file2list.empty())
+   {
+      listitr = file2list.begin();
+      while (listitr != file2list.end())
+      {
+         (*listitr).dump(cout << endl);
+         listitr++;
+      }
+   }
+   else
+      cout << "   All data in " << file2 << " was found in " << file1 << "."
+           << endl;
+
+}
+
+void EphDiff::fillFIC(FileFilterFrame<FICStream, FICData>& ff,
+                      vector<EngEphemeris>& l)
+{
+   list<FICData>& ficlist = ff.getData();
+   
+   list<FICData>::iterator itr = ficlist.begin();
+
+   while (itr != ficlist.end())
+   {
+      if ((*itr).blockNum == 9)
+         l.push_back(*itr);
+      itr++;
+   }
+}
+
+void EphDiff::fillRINEX(FileFilterFrame<RinexNavStream, RinexNavData>& ff,
+                        vector<EngEphemeris>& l)
+{
+   list<RinexNavData>& ficlist = ff.getData();
+   
+   list<RinexNavData>::iterator itr = ficlist.begin();
+
+   while (itr != ficlist.end())
+   {
+      l.push_back(EngEphemeris(*itr));
+      itr++;
+   }
+}
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      EphDiff ed(argv[0]);
+      if (!ed.initialize(argc, argv))
+         return 0;
+      if (!ed.run())
+         return 1;
+      
+      return 0;
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/difftools/ficdiff.cpp b/dev/apps/difftools/ficdiff.cpp
new file mode 100644
index 0000000..d567188
--- /dev/null
+++ b/dev/apps/difftools/ficdiff.cpp
@@ -0,0 +1,104 @@
+#pragma ident "$Id$"
+
+
+#include "FICFilterOperators.hpp"
+#include "FileFilterFrame.hpp"
+
+#include "DiffFrame.hpp"
+#include "FICData.hpp"
+#include "FICStream.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class FICDiff : public DiffFrame
+{
+public:
+   FICDiff(char* arg0)
+         : DiffFrame(arg0, 
+                      std::string("binary FIC"))
+      {}
+
+protected:
+   virtual void process();
+};
+
+void FICDiff::process()
+{
+   try
+   {
+      FileFilterFrame<FICStream, FICData> ff1(inputFileOption.getValue()[0]);
+      FileFilterFrame<FICStream, FICData> ff2(inputFileOption.getValue()[1]);
+
+      ff1.sort(FICDataOperatorLessThanFull());
+      ff2.sort(FICDataOperatorLessThanFull());
+
+      pair< list<FICData>, list<FICData> > difflist = 
+         ff1.diff(ff2, FICDataOperatorLessThanFull());
+
+      if (difflist.first.empty() && difflist.second.empty())
+         exit(0);
+
+      list<FICData>::iterator itr = difflist.first.begin();
+      while (itr != difflist.first.end())
+      {
+         (*itr).dump(cout << '<');
+         itr++;
+      }
+
+      cout << endl;
+
+      itr = difflist.second.begin();
+      while (itr != difflist.second.end())
+      {
+         (*itr).dump(cout << '>');
+         itr++;
+      }
+
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(...)
+   {
+      cout << "Unknown exception... terminating..." << endl;
+   }
+
+}
+
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      FICDiff m(argv[0]);
+      if (!m.initialize(argc, argv))
+         return 0;
+      if (!m.run())
+         return 1;
+      
+      return 0;
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 0;
+}
diff --git a/dev/apps/difftools/rmwdiff.cpp b/dev/apps/difftools/rmwdiff.cpp
new file mode 100644
index 0000000..1c32612
--- /dev/null
+++ b/dev/apps/difftools/rmwdiff.cpp
@@ -0,0 +1,222 @@
+#pragma ident "$Id$"
+
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include "FileFilterFrameWithHeader.hpp"
+
+#include "RinexMetData.hpp"
+#include "RinexMetStream.hpp"
+#include "RinexMetFilterOperators.hpp"
+
+#include "DiffFrame.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class RMWDiff : public DiffFrame
+{
+public:
+   RMWDiff(char* arg0)
+         : DiffFrame(arg0, 
+                     std::string("RINEX Met"))
+   {}
+
+protected:
+   virtual void process();
+};
+
+
+void RMWDiff::process()
+{
+   try
+   {
+      FileFilterFrameWithHeader<RinexMetStream, RinexMetData, RinexMetHeader>
+         ff1(inputFileOption.getValue()[0]), ff2(inputFileOption.getValue()[1]);
+
+         // find the obs data intersection
+      RinexMetHeaderTouchHeaderMerge merged;
+
+         // no data?  FIX make this program faster.. if one file
+         // doesn't exist, there's little point in reading any.
+      if (ff1.emptyHeader())
+         cerr << "No header information for " << inputFileOption.getValue()[0]
+              << endl;
+      if (ff2.emptyHeader())
+         cerr << "No header information for " << inputFileOption.getValue()[1]
+              << endl;
+      if (ff1.emptyHeader() || ff2.emptyHeader())
+      {
+         cerr << "Check that files exist." << endl;
+         cerr << "diff failed." << endl;
+         exit(1);
+      }
+
+      merged(ff1.frontHeader());
+      merged(ff2.frontHeader());
+
+      set<RinexMetHeader::RinexMetType> intersection = merged.obsSet;
+
+      cout << "Comparing the following fields (other header data is ignored):" << endl;
+      set<RinexMetHeader::RinexMetType>::iterator m = intersection.begin();
+      while (m != intersection.end())
+      {
+         cout << RinexMetHeader::convertObsType(*m) << ' ';
+         m++;
+      }
+      cout << endl;
+
+      if (timeOptions.getCount())
+      {
+         ff1.filter(RinexMetDataFilterTime(startTime, endTime));
+         ff2.filter(RinexMetDataFilterTime(startTime, endTime));
+      }
+
+      ff1.sort(RinexMetDataOperatorLessThanFull(intersection));
+      ff2.sort(RinexMetDataOperatorLessThanFull(intersection));
+
+      pair< list<RinexMetData>, list<RinexMetData> > difflist = 
+         ff1.diff(ff2, RinexMetDataOperatorLessThanFull(intersection));
+
+      if (difflist.first.empty() && difflist.second.empty())
+         exit(0);
+
+      list<RinexMetData>::iterator firstitr = difflist.first.begin();
+      while (firstitr != difflist.first.end())
+      {
+         bool matched = false;
+         list<RinexMetData>::iterator seconditr = difflist.second.begin();
+         while ((!matched) && (seconditr != difflist.second.end()))
+         {
+            if (firstitr->time == seconditr->time)
+            {
+               cout << setw(3) << firstitr->time.DOYday() << ' ' 
+                    << setw(10) << setprecision(0)
+                    << firstitr->time.DOYsecond() << ' ' 
+                    << ff1.frontHeader().markerName << ' '
+                    << ff2.frontHeader().markerName << ' ';
+
+               for (m = intersection.begin(); m != intersection.end(); m++)
+               {
+                  double diff = firstitr->data[*m];
+                  diff -= seconditr->data[*m];
+
+                  cout << setw(7) << setprecision(1) << fixed << diff << ' '
+                       << RinexMetHeader::convertObsType(*m) << ' ';
+
+               }
+               cout << endl;
+
+               firstitr = difflist.first.erase(firstitr);
+               seconditr = difflist.second.erase(seconditr);
+               matched = true;
+            }
+            else
+               seconditr++;
+         }
+
+         if (!matched)
+            firstitr++;
+      }
+
+      list<RinexMetData>::iterator itr = difflist.first.begin();
+      while (itr != difflist.first.end())
+      {
+         (*itr).dump(cout << '<');
+         itr++;
+      }
+
+      cout << endl;
+
+      itr = difflist.second.begin();
+      while (itr != difflist.second.end())
+      {
+         (*itr).dump(cout << '>');
+         itr++;
+      }
+
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(...)
+   {
+      cout << "Unknown exception... terminating..." << endl;
+   }
+}
+
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      RMWDiff m(argv[0]);
+      if (!m.initialize(argc, argv))
+         return 0;
+      if (!m.run())
+         return 1;
+      
+      return 0;
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/difftools/rnwdiff.cpp b/dev/apps/difftools/rnwdiff.cpp
new file mode 100644
index 0000000..294de81
--- /dev/null
+++ b/dev/apps/difftools/rnwdiff.cpp
@@ -0,0 +1,206 @@
+#pragma ident "$Id$"
+
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include "FileFilterFrame.hpp"
+
+#include "RinexNavData.hpp"
+#include "RinexNavStream.hpp"
+#include "RinexNavFilterOperators.hpp"
+
+#include "DiffFrame.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class RNWDiff : public DiffFrame
+{
+public:
+   RNWDiff(char* arg0)
+         : DiffFrame(arg0, 
+                     std::string("RINEX Nav"))
+   {}
+
+protected:
+   virtual void process();
+};
+
+
+void RNWDiff::process()
+{
+   try
+   {
+      FileFilterFrame<RinexNavStream, RinexNavData> ff1(inputFileOption.getValue()[0]);
+      FileFilterFrame<RinexNavStream, RinexNavData> ff2(inputFileOption.getValue()[1]);
+      
+      ff1.sort(RinexNavDataOperatorLessThanFull());
+      ff2.sort(RinexNavDataOperatorLessThanFull());
+      
+      pair< list<RinexNavData>, list<RinexNavData> > difflist = 
+         ff1.diff(ff2, RinexNavDataOperatorLessThanFull());
+      
+      if (difflist.first.empty() && difflist.second.empty())
+         exit(0);
+
+      list<RinexNavData>::iterator firstitr = difflist.first.begin();
+      while (firstitr != difflist.first.end())
+      {
+         bool matched = false;
+         list<RinexNavData>::iterator seconditr = difflist.second.begin();
+         while ((!matched) && (seconditr != difflist.second.end()))
+         {
+               // this will match the exact same nav message in both
+               // files, not just the same ephemeris broadcasted at
+               // different times.
+            if ((firstitr->time == seconditr->time) &&
+                (firstitr->PRNID == seconditr->PRNID) &&
+                (firstitr->HOWtime == seconditr->HOWtime) )
+            {
+               cout << fixed << setw(3) << firstitr->time.DOYday() << ' ' 
+                    << setw(10) << setprecision(0)
+                    << firstitr->time.DOYsecond() << ' ' 
+                    << setw(19) << setprecision(12) << scientific
+                    << (firstitr->af0      - seconditr->af0) << ' '
+                    << (firstitr->af1      - seconditr->af1) << ' '
+                    << (firstitr->af2      - seconditr->af2) << ' '
+                    << (firstitr->IODE     - seconditr->IODE) << ' '
+                    << (firstitr->Crs      - seconditr->Crs) << ' '
+                    << (firstitr->dn       - seconditr->dn) << ' '
+                    << (firstitr->M0       - seconditr->M0) << ' '
+                    << (firstitr->Cuc      - seconditr->Cuc) << ' '
+                    << (firstitr->ecc      - seconditr->ecc) << ' '
+                    << (firstitr->Cus      - seconditr->Cus) << ' '
+                    << (firstitr->Ahalf    - seconditr->Ahalf) << ' '
+                    << (firstitr->Toe      - seconditr->Toe) << ' '
+                    << (firstitr->Cic      - seconditr->Cic) << ' '
+                    << (firstitr->OMEGA0   - seconditr->OMEGA0) << ' '
+                    << (firstitr->Cis      - seconditr->Cis) << ' '
+                    << (firstitr->i0       - seconditr->i0) << ' '
+                    << (firstitr->Crc      - seconditr->Crc) << ' '
+                    << (firstitr->w        - seconditr->w) << ' '
+                    << (firstitr->OMEGAdot - seconditr->OMEGAdot) << ' '
+                    << (firstitr->idot     - seconditr->idot) << ' '
+                    << (firstitr->codeflgs - seconditr->codeflgs) << ' '
+                    << (firstitr->weeknum  - seconditr->weeknum) << ' '
+                    << (firstitr->L2Pdata  - seconditr->L2Pdata) << ' '
+                    << (firstitr->accuracy - seconditr->accuracy) << ' '
+                    << (firstitr->health   - seconditr->health) << ' '
+                    << (firstitr->Tgd      - seconditr->Tgd) << ' '
+                    << (firstitr->IODC     - seconditr->IODC) << ' '
+                    << (firstitr->HOWtime  - seconditr->HOWtime) << ' '
+                    << (firstitr->fitint   - seconditr->fitint)
+                    << endl;
+
+               firstitr = difflist.first.erase(firstitr);
+               seconditr = difflist.second.erase(seconditr);
+               matched = true;
+            }
+            else
+               seconditr++;
+         }
+         
+         if (!matched)
+            firstitr++;
+      }
+
+      list<RinexNavData>::iterator itr = difflist.first.begin();
+      while (itr != difflist.first.end())
+      {
+         (*itr).dump(cout << '<');
+         itr++;
+      }
+
+      cout << endl;
+
+      itr = difflist.second.begin();
+      while (itr != difflist.second.end())
+      {
+         (*itr).dump(cout << '>');
+         itr++;
+      }
+
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl
+           << endl
+           << "Terminating.." << endl;
+   }
+   catch(...)
+   {
+      cout << "Unknown exception... terminating..." << endl;
+   }
+}
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      RNWDiff m(argv[0]);
+      if (!m.initialize(argc, argv))
+         return 0;
+      if (!m.run())
+         return 1;
+      
+      return 0;
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
diff --git a/dev/apps/difftools/rowdiff.cpp b/dev/apps/difftools/rowdiff.cpp
new file mode 100644
index 0000000..2035c88
--- /dev/null
+++ b/dev/apps/difftools/rowdiff.cpp
@@ -0,0 +1,204 @@
+#pragma ident "$Id$"
+
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include "FileFilterFrameWithHeader.hpp"
+
+#include "RinexObsData.hpp"
+#include "RinexObsStream.hpp"
+#include "RinexObsFilterOperators.hpp"
+
+#include "DiffFrame.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+class ROWDiff : public DiffFrame
+{
+public:
+   ROWDiff(char* arg0)
+         : DiffFrame(arg0, 
+                      std::string("RINEX Obs"))
+      {}
+
+protected:
+   virtual void process();
+};
+
+void ROWDiff::process()
+{
+   gpstk::FileFilterFrameWithHeader<RinexObsStream, RinexObsData, RinexObsHeader>
+      ff1(inputFileOption.getValue()[0]), ff2(inputFileOption.getValue()[1]);
+
+      // find the obs data intersection
+   RinexObsHeaderTouchHeaderMerge merged;
+
+      // no data?  FIX make this program faster.. if one file
+      // doesn't exist, there's little point in reading any.
+   if (ff1.emptyHeader())
+      cerr << "No header information for " << inputFileOption.getValue()[0]
+           << endl;
+   if (ff2.emptyHeader())
+      cerr << "No header information for " << inputFileOption.getValue()[1]
+           << endl;
+   if (ff1.emptyHeader() || ff2.emptyHeader())
+   {
+      cerr << "Check that files exist." << endl;
+      cerr << "diff failed." << endl;
+      exit(1);
+   }
+
+   merged(ff1.frontHeader());
+   merged(ff2.frontHeader());
+
+   cout << "Comparing the following fields (other header data is ignored):" 
+        << endl;
+   set<RinexObsHeader::RinexObsType> intersection = merged.obsSet;
+   set<RinexObsHeader::RinexObsType>::iterator m = intersection.begin();
+   while (m != intersection.end())
+   {
+      cout << gpstk::RinexObsHeader::convertObsType(*m) << ' ';
+      m++;
+   }
+   cout << endl;
+
+   ff1.sort(RinexObsDataOperatorLessThanFull(intersection));
+   ff2.sort(RinexObsDataOperatorLessThanFull(intersection));
+
+   pair< list<RinexObsData>, list<RinexObsData> > difflist = 
+      ff1.diff(ff2, RinexObsDataOperatorLessThanFull(intersection));
+
+   if (difflist.first.empty() && difflist.second.empty())
+      exit(0);
+
+   list<RinexObsData>::iterator firstitr = difflist.first.begin();
+   while (firstitr != difflist.first.end())
+   {
+      bool matched = false;
+      list<RinexObsData>::iterator seconditr = difflist.second.begin();
+      while ((!matched) && (seconditr != difflist.second.end()))
+      {
+         if (firstitr->time == seconditr->time)
+         {
+            RinexObsData::RinexSatMap::iterator fpoi, spoi;
+            for (fpoi = firstitr->obs.begin(); fpoi != firstitr->obs.end();
+                 fpoi++)
+            {
+               cout << setw(3) << firstitr->time.DOYday() << ' ' 
+                    << setw(10) << setprecision(0)
+                    << firstitr->time.DOYsecond() << ' ' 
+                    << ff1.frontHeader().markerName << ' '
+                    << ff2.frontHeader().markerName << ' '
+                    << setw(2) << fpoi->first << ' ';
+               spoi = seconditr->obs.find(fpoi->first);
+               for (m = intersection.begin(); m != intersection.end(); m++)
+               {
+                     // no need to do a find, we're using the merged
+                     // set of obses which guarantees that we have the
+                     // obs in this record
+                  double diff = (fpoi->second[*m]).data;
+                  if (spoi != seconditr->obs.end())
+                     diff -= (spoi->second[*m]).data;
+
+                  cout << setw(14) << setprecision(3) << fixed << diff << ' '
+                       << RinexObsHeader::convertObsType(*m) << ' ';
+
+               }
+               cout << endl;
+            }
+            firstitr = difflist.first.erase(firstitr);
+            seconditr = difflist.second.erase(seconditr);
+            matched = true;
+         }
+         else
+            seconditr++;
+      }
+
+      if (!matched)
+         firstitr++;
+   }
+
+   list<RinexObsData>::iterator itr = difflist.first.begin();
+   while (itr != difflist.first.end())
+   {
+      (*itr).dump(cout << '<');
+      itr++;
+   }
+
+   cout << endl;
+
+   itr = difflist.second.begin();
+   while (itr != difflist.second.end())
+   {
+      (*itr).dump(cout << '>');
+      itr++;
+   }
+}
+
+int main(int argc, char* argv[])
+{
+   try
+   {
+      ROWDiff m(argv[0]);
+      if (!m.initialize(argc, argv))
+         return 0;
+      if (!m.run())
+         return 1;
+      
+      return 0;
+   }
+   catch(Exception& e)
+   {
+      cout << e << endl;
+   }
+   catch(exception& e)
+   {
+      cout << e.what() << endl;
+   }
+   catch(...)
+   {
+      cout << "unknown error" << endl;
+   }
+   return 1;
+}
+
diff --git a/dev/apps/filetools/Jamfile b/dev/apps/filetools/Jamfile
new file mode 100644
index 0000000..d93e8b3
--- /dev/null
+++ b/dev/apps/filetools/Jamfile
@@ -0,0 +1,17 @@
+#
+# $Id$
+#
+
+SubDir TOP apps filetools ;
+
+GPSLinkLibraries ficafic ficfica navdmp fic2rin rinexthin sp3version bc2sp3 : gpstk ;
+
+GPSMain navdmp : navdmp.cpp ;
+GPSMain ficfica : ficfica.cpp ;
+GPSMain ficafic : ficafic.cpp ;
+GPSMain fic2rin : fic2rin.cpp ;
+GPSMain rinexthin : RinexThin.cpp ;
+GPSMain sp3version : sp3version.cpp ;
+GPSMain bc2sp3 : bc2sp3.cpp ;
+#GPSMain sp32bc : sp32bc.cpp ;
+
diff --git a/dev/apps/filetools/Makefile.am b/dev/apps/filetools/Makefile.am
new file mode 100644
index 0000000..f9d2a46
--- /dev/null
+++ b/dev/apps/filetools/Makefile.am
@@ -0,0 +1,13 @@
+# $Id$
+INCLUDES = -I$(srcdir)/../../src
+LDADD = ../../src/libgpstk.la
+
+bin_PROGRAMS = ficafic ficfica navdmp fic2rin rinexthin sp3version bc2sp3
+
+ficfica_SOURCES = ficfica.cpp
+ficafic_SOURCES = ficafic.cpp
+fic2rin_SOURCES = fic2rin.cpp
+navdmp_SOURCES = navdmp.cpp
+rinexthin_SOURCES = RinexThin.cpp
+sp3version_SOURCES = sp3version.cpp
+bc2sp3_SOURCES = bc2sp3.cpp
diff --git a/trunk/apps/filetools/RinexThin.cpp b/dev/apps/filetools/RinexThin.cpp
similarity index 100%
rename from trunk/apps/filetools/RinexThin.cpp
rename to dev/apps/filetools/RinexThin.cpp
diff --git a/dev/apps/filetools/bc2sp3.cpp b/dev/apps/filetools/bc2sp3.cpp
new file mode 100644
index 0000000..b3f6150
--- /dev/null
+++ b/dev/apps/filetools/bc2sp3.cpp
@@ -0,0 +1,323 @@
+#pragma ident "$Id$"
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+/**
+ * @file bc2sp3.cpp
+ * Read RINEX format navigation file(s) and write the data out to an SP3 format file.
+ * Potential problems related to discontinuities at change of BCE are ignored.
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <vector>
+
+#include "RinexNavStream.hpp"
+#include "RinexNavHeader.hpp"
+#include "RinexNavData.hpp"
+#include "EphemerisStore.hpp"
+#include "BCEphemerisStore.hpp"
+#include "SP3Stream.hpp"
+#include "SP3Header.hpp"
+#include "SP3Data.hpp"
+#include "DayTime.hpp"
+#include "SatID.hpp"
+#include "StringUtils.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char *argv[])
+{
+   string Usage(
+      "Usage: bc2sp3 <RINEX nav file(s)> [options]\n"
+      " Read RINEX nav file(s) and write to SP3(a or c) file.\n"
+      " Options (defaults):\n"
+      "  --in <file>   Read the input file <file> (--in is optional, repeatable) ()\n"
+      "  --out <file>  Name the output file <file> (sp3.out)\n"
+      "  --tb <time>   Output beginning epoch; <time> = week,sec-of-week (earliest in input)\n"
+      "  --te <time>   Output ending epoch; <time> = week,sec-of-week (latest in input)\n"
+      "  --outputC     Output version c (no correlation) (otherwise a)\n"
+      "  --msg \"...\"   Add ... as a comment to the output header (repeatable)\n"
+      "  --verbose     Output to screen: dump headers, data, etc\n"
+      "  --help        Print this message and quit\n"
+      );
+   if(argc < 2) { cout << Usage; return -1; }
+
+   try
+   {
+      bool verbose=false;
+      char version_out='a';
+      int i,j,nrec,nfile;
+      string fileout("sp3.out");
+      vector<string> inputFiles;
+      vector<string> comments;
+      map<SatID,long> IODEmap;
+      DayTime begTime=DayTime::BEGINNING_OF_TIME;
+      DayTime endTime=DayTime::END_OF_TIME;
+      DayTime tt;
+      BCEphemerisStore BCEph;
+      SP3Header sp3header;
+      SP3Data sp3data;
+
+      for(i=1; i<argc; i++) {
+         if(argv[i][0] == '-') {
+            string arg(argv[i]);
+            if(arg == string("--outputC")) {
+               version_out = 'c';
+               if(verbose) cout << " Output version c\n";
+            }
+            else if(arg == string("--in")) {
+               inputFiles.push_back(string(argv[++i]));
+               if(verbose) cout << " Input file name "
+                  << inputFiles[inputFiles.size()-1] << endl;
+            }
+            else if(arg == string("--out")) {
+               fileout = string(argv[++i]);
+               if(verbose) cout << " Output file name " << fileout << endl;
+            }
+            else if(arg == string("--tb")) {
+               arg = string(argv[++i]);
+               int wk=StringUtils::asInt(StringUtils::stripFirstWord(arg,','));
+               double sow=StringUtils::asDouble(StringUtils::stripFirstWord(arg,','));
+               begTime.setGPSfullweek(wk,sow);
+               if(verbose) cout << " Begin time "
+                  << begTime.printf("%Y/%02m/%02d %2H:%02M:%06.3f = %F/%10.3g")
+                  << endl;
+            }
+            else if(arg == string("--te")) {
+               arg = string(argv[++i]);
+               int wk=StringUtils::asInt(StringUtils::stripFirstWord(arg,','));
+               double sow=StringUtils::asDouble(StringUtils::stripFirstWord(arg,','));
+               endTime.setGPSfullweek(wk,sow);
+               if(verbose) cout << " End time   "
+                  << endTime.printf("%Y/%02m/%02d %2H:%02M:%06.3f = %F/%10.3g")
+                  << endl;
+            }
+            else if(arg == string("--msg")) {
+               comments.push_back(string(argv[++i]));
+               if(verbose) cout << " Add comment " << comments[comments.size()-1]
+                  << endl;
+            }
+            else if(arg == string("--help")) {
+               cout << Usage;
+               return -1;
+            }
+            else if(arg == string("--verbose"))
+               verbose = true;
+            else
+               cout << "Ignore unknown option: " << arg << endl;
+         }
+         else {
+            inputFiles.push_back(string(argv[i]));
+            if(verbose) cout << " Input file name "
+               << inputFiles[inputFiles.size()-1] << endl;
+         }
+      }
+
+      if(inputFiles.size() == 0) {
+         cout << "Error - no input filename specified. Abort.\n";
+         return -1;
+      }
+
+      // open the output SP3 file
+      SP3Stream outstrm(fileout.c_str(),ios::out);
+      outstrm.exceptions(ifstream::failbit);
+
+      for(nfile=0; nfile<inputFiles.size(); nfile++) {
+         RinexNavHeader rnh;
+         RinexNavData rnd;
+
+         RinexNavStream rns(inputFiles[nfile].c_str());
+         if(!rns) {
+            cout << "Could not open input file " << inputFiles[nfile] << endl;
+            continue;
+         }
+         rns.exceptions(ifstream::failbit);
+
+         if(verbose) cout << "Reading file " << inputFiles[nfile] << endl;
+
+         rns >> rnh;
+         if(verbose) {
+            cout << "Input";
+            rnh.dump(cout);
+         }
+
+         while(rns >> rnd)
+            if(rnd.health == 0) BCEph.addEphemeris(rnd);
+   
+      }
+
+      // time limits, if not given by user
+      if(begTime == DayTime::BEGINNING_OF_TIME)
+         begTime = BCEph.getInitialTime();
+      if(endTime == DayTime::END_OF_TIME)
+         endTime = BCEph.getFinalTime();
+
+      // define the data version and the header info
+      if(version_out == 'c') {
+         // data and header must have the correct version
+         sp3data.version = sp3header.version = 'c';
+
+         sp3header.system = SP3SatID();
+         sp3header.timeSystem = SP3Header::timeGPS;
+         sp3header.basePV = 0.0;
+         sp3header.baseClk = 0.0;
+      }
+      else {
+         sp3data.version = sp3header.version = 'a';
+      }
+
+      // fill the header
+      sp3header.pvFlag = 'V';
+      sp3header.time = DayTime::END_OF_TIME;
+      sp3header.epochInterval = 900.0;          // hardcoded here only
+      sp3header.dataUsed = "BCE";
+      sp3header.coordSystem = "WGS84";
+      sp3header.orbitType = "   ";
+      sp3header.agency = "ARL";
+
+      // determine which SVs, with accuracy, start time, epoch interval,
+      // number of epochs, for header
+      // this is a pain....
+      sp3header.numberOfEpochs = 0;
+      tt = begTime;
+      while(tt < endTime) {
+         bool foundSome = false;
+         for(i=1; i<33; i++) {            // for each PRN ...
+            SatID sat(i,SatID::systemGPS);
+            try { EngEphemeris ee = BCEph.findEphemeris(sat, tt); }
+            catch(EphemerisStore::NoEphemerisFound& nef) { continue; }
+
+            if(sp3header.satList.find(sat) == sp3header.satList.end()) {
+               sp3header.satList[sat] = 0;        // sat accuracy = ?
+               IODEmap[sat] = -1;
+            }
+
+            if(!foundSome) {
+               sp3header.numberOfEpochs++;
+               foundSome = true;
+               if(tt < sp3header.time) sp3header.time = tt;
+            }
+         }
+         tt += sp3header.epochInterval;
+      }
+
+      // add comments
+      if(comments.size() > 0) {
+         // try to keep existing comments
+         for(i=0; i<comments.size(); i++) {
+            if(i > 3) {
+               cout << "Warning - only 4 comments are allowed in SP3 header.\n";
+               break;
+            }
+            sp3header.comments.push_back(comments[i]);
+         }
+      }
+
+      // dump the SP3 header
+      if(verbose) sp3header.dump(cout);
+
+      // write the header
+      outstrm << sp3header;
+
+      // sigmas to output (version c)
+      for(j=0; j<4; j++) sp3data.sig[j]=0;   // sigma = ?
+
+      tt = begTime;
+      while(tt < endTime) {
+         bool epochOut=false;
+
+         for(i=1; i<33; i++) {
+            long iode;
+            SatID sat(i,SatID::systemGPS);
+            Xvt xvt;
+            EngEphemeris ee;
+
+            try { ee = BCEph.findEphemeris(sat, tt); }
+            catch(EphemerisStore::NoEphemerisFound& nef) { continue; }
+
+            sp3data.sat = sat;
+            xvt = BCEph.getSatXvt(sat, tt);
+
+            // epoch
+            if(!epochOut) {
+               sp3data.time = tt;
+               sp3data.flag = '*';
+               outstrm << sp3data;
+               if(verbose) sp3data.dump(cout);
+               epochOut = true;
+            }
+
+            // Position
+            sp3data.flag = 'P';
+            for(j=0; j<3; j++) sp3data.x[j] = xvt.x[j]/1000.0;       // km
+            // must remove the relativity correction from Xvt::dtime
+            // see EngEphemeris::svXvt() - also convert to microsec
+            sp3data.clk = (xvt.dtime - ee.svRelativity(tt)) * 1000000.0;
+
+            //if(version_out == 'c') for(j=0; j<4; j++) sp3data.sig[j]=...
+            iode = ee.getIODE();
+            if(IODEmap[sat] == -1) IODEmap[sat] = iode;
+            if(IODEmap[sat] != iode) {
+               sp3data.orbitManeuverFlag = true;
+               IODEmap[sat] = iode;
+            }
+            else sp3data.orbitManeuverFlag = false;
+
+            outstrm << sp3data;
+            if(verbose) sp3data.dump(cout);
+
+            // Velocity
+            sp3data.flag = 'V';
+            for(j=0; j<3; j++) sp3data.x[j] = xvt.v[j]/10.0;         // dm/s
+            sp3data.clk = xvt.ddtime;                                // s/s
+            //if(version_out == 'c') for(j=0; j<4; j++) sp3data.sig[j]=...
+
+            outstrm << sp3data;
+            if(verbose) sp3data.dump(cout);
+         }
+
+         tt += sp3header.epochInterval;
+      }
+      // don't forget this
+      outstrm << "EOF" << endl;
+
+      outstrm.close();
+
+      if(verbose) cout << "Wrote " << sp3header.numberOfEpochs << " records" << endl;
+   }
+   catch (Exception& e)
+   {
+      cout << e;
+      return -1;
+   }
+   catch (...)
+   {
+      cout << "Caught an unknown exception" << endl;
+      return -1;
+   }
+
+   return 0;
+} 
diff --git a/dev/apps/filetools/fic2rin.cpp b/dev/apps/filetools/fic2rin.cpp
new file mode 100644
index 0000000..720546b
--- /dev/null
+++ b/dev/apps/filetools/fic2rin.cpp
@@ -0,0 +1,126 @@
+#pragma ident "$Id$"
+
+
+/**
+ * @file fic2rin.cpp Convert FIC files to RINEX.
+ */
+
+//============================================================================
+//
+//  This file is part of GPSTk, the GPS Toolkit.
+//
+//  The GPSTk 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.1 of the License, or
+//  any later version.
+//
+//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
+//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//  
+//  Copyright 2004, The University of Texas at Austin
+//
+//============================================================================
+
+//============================================================================
+//
+//This software developed by Applied Research Laboratories at the University of
+//Texas at Austin, under contract to an agency or agencies within the U.S. 
+//Department of Defense. The U.S. Government retains all rights to use,
+//duplicate, distribute, disclose, or release this software. 
+//
+//Pursuant to DoD Directive 523024 
+//
+// DISTRIBUTION STATEMENT A: This software has been approved for public 
+//                           release, distribution is unlimited.
+//
+//=============================================================================
+
+
+
+
+
+
+#include "FICStream.hpp"
+#include "FICHeader.hpp"
+#include "FICData.hpp"
+#include "RinexNavStream.hpp"
+#include "RinexNavData.hpp"
+#include "FICFilterOperators.hpp"
+#include "RinexNavFilterOperators.hpp"
+#include "FileFilterFrame.hpp"
+
+using namespace std;
+using namespace gpstk;
+
+int main(int argc, char* argv[])
+{
+   if (argc != 3)
+   {
+      cout << "fic2rin" << endl
+           << "  converts a binary FIC file to a Rinex Nav file" << endl
+           << endl
+           << "usage:" << endl
+           << "    fic2rin inputfile outputfile" << endl
+           << endl
+           << "where:" << endl
+           << "    inputfile: an input binary FIC file name" << endl
+           << "    outputfile: an output Rinex Nav file name" << endl;
+      return 0;
+   }
+   // What is up
+   FileFilterFrame<FICStream, FICData> input(argv[1]);
+   list<FICData> alist = input.getData();
+   
+      // write the header info
+   RinexNavStream rns(argv[2], ios::out|ios::trunc);
+   RinexNavHeader rnh;
+   rnh.fileType = "Navigation";
+   rnh.fileProgram = "fic2rin";
+   rnh.fileAgency = "";
+   ostringstream ostr;
+   ostr << DayTime();
+   rnh.date = ostr.str();
+   rnh.version = 2.1;
+   rnh.valid |= RinexNavHeader::versionValid;
+   rnh.valid |= RinexNavHeader::runByValid;
+   rnh.valid |= RinexNavHeader::endValid;
+   rns.header = rnh;
+   rnh.putRecord(rns);
+   rns.close();
+
+      // filter the FIC data for block 9
+   list<long> blockList;
+   blockList.push_back(9);
+   input.filter(FICDataFilterBlock(blockList));
+   input.sort(FICDataOperatorLessThanBlock9());
+   input.unique(FICDataUniqueBlock9());
+
+      // some hand waving for the data conversion
+   list<RinexNavData> rndList;
+   list<FICData>& ficList = input.getData();
+   list<FICData>::iterator itr = ficList.begin();
+   while (itr != ficList.end())
+   {
+         // use TOE and transmit week number to determine time
+      DayTime time;
+      time.setGPSfullweek(short((*itr).f[5]), (dou