def testNullConversion(self): """Test round trip conversion """ site = coordConv.Site(-105.822616, 32.780988, 2788) site.setPoleWander(1.1e-4, -0.5e-4) site.ut1_tai = -2e-8 site.refCoA = 1.2e-2 site.refCoB = -1.3e-5 maxRoundTripErr = 0 for cls in JulianSysList: for date in (1975, 2012): coordSys = cls(date) for equatAng in (100, -45): for polAng in (-20, 25, 89): for dist in (0, 0.01, 1): for pmRA in (0, -3): for pmDec in (0, 5): for radVel in (0, 7): fromCoord = coordConv.Coord( equatAng, polAng, dist, pmRA, pmDec, radVel) fk5Coord = coordSys.toFK5J2000( fromCoord, site) toCoord = coordSys.fromFK5J2000( fk5Coord, site) roundTripErr = toCoord.angularSeparation( fromCoord) maxRoundTripErr = max( roundTripErr, maxRoundTripErr) self.assertLess(roundTripErr, 1e-8) print "maxRoundTripErr for mean and app. geo. coordinate systems =", maxRoundTripErr, "deg" maxRoundTripErr = 0 for cls in AzAltSysList: for date in (4842765000, 4872765000): coordSys = cls(date) for equatAng in (100, -45): for polAng in (0, 25, 89): for dist in (0, 0.01, 1): fromCoord = coordConv.Coord(equatAng, polAng, dist) fk5Coord = coordSys.toFK5J2000(fromCoord, site) toCoord = coordSys.fromFK5J2000(fk5Coord, site) roundTripErr = toCoord.angularSeparation(fromCoord) maxRoundTripErr = max(roundTripErr, maxRoundTripErr) self.assertLess(roundTripErr, 1e-7) print "maxRoundTripErr for app. topo and observed coordinate systems =", maxRoundTripErr, "deg"
def testLunarVel(self): """Sanity-check lunar tracking velocity This checks for an issue we had with tracking close objects: position was right, but velocity was not. This was because Apparent Geocentric date was not being updated inside the PVTCoord version of CoordSys.convertFrom. """ tai = 4914602887 dt = 0.1 site = coordConv.Site(-105.822616, 32.780988, 2788) geoCoordSys = coordConv.AppGeoCoordSys() topoCoordSys = coordConv.AppTopoCoordSys() geoCoord = coordConv.Coord(0, 75, 82505922) topoPVTList = [] for evalTAI in (tai, tai + dt): geoPVTCoord = coordConv.PVTCoord(geoCoord, geoCoord, evalTAI, 0.01) topoPVTCoord = topoCoordSys.convertFrom(geoCoordSys, geoPVTCoord, site) topoPVTPair = [coordConv.PVT() for i in range(2)] topoPVTCoord.getSphPVT(topoPVTPair[0], topoPVTPair[1]) topoPVTList.append(topoPVTPair) for i in range(2): pvt0 = topoPVTList[0][i] pvt1 = topoPVTList[1][i] coordConv.assertPVTsAlmostEqual(pvt0.copy(pvt1.t), pvt1)
def testNoneAndOtherCoordSys(self): """Test that conversions to and from NoneCoordSys and OtherCoordSys fail """ for nullSys in ( coordConv.NoneCoordSys(), coordConv.OtherCoordSys("foo"), ): self.assertFalse(nullSys.canConvert()) site = coordConv.Site(-105.822616, 32.780988, 2788) fromCoord = coordConv.Coord(10, 30) for csysName in FullNameList: otherSys = coordConv.makeCoordSys(csysName, 2001) self.assertRaises(Exception, nullSys.convertFrom, otherSys, fromCoord, site) self.assertRaises(Exception, otherSys.convertFrom, nullSys, fromCoord, site)
def testConvertFromVel(self): """Test velocity of convertFrom """ taiDate = 4889900000.205 site = coordConv.Site(-105.822616, 32.780988, 2788) icrsCoordSys = coordConv.ICRSCoordSys() appTopoCoordSys = coordConv.AppTopoCoordSys() # find ICRS coordinate of a sidereal point on the equator along the meridion appTopoCoord = coordConv.Coord(0, 90 - site.meanLat) icrsCoord = icrsCoordSys.convertFrom(appTopoCoordSys, appTopoCoord, site, taiDate) icrsPVTCoord = coordConv.PVTCoord(icrsCoord, icrsCoord, taiDate, 0.001) appTopoPVTCoord = appTopoCoordSys.convertFrom(icrsCoordSys, icrsPVTCoord, site) equatPVT = coordConv.PVT() polarPVT = coordConv.PVT() appTopoPVTCoord.getSphPVT(equatPVT, polarPVT) self.assertEqual(equatPVT.t, taiDate) self.assertEqual(polarPVT.t, taiDate) equatSpaceVel = equatPVT.vel * coordConv.cosd(polarPVT.pos) self.assertAlmostEqual(polarPVT.vel, 0, places=3) self.assertAlmostEqual(equatSpaceVel, -1 / 240.0, places=3) # 360 deg/day # check round trip of scale and orientation for fromDir in (0, 45): for fromVel in (0, 0.01): fromDirPVT = coordConv.PVT(fromDir, fromVel, taiDate) toDirPVT = coordConv.PVT() fromDir2PVT = coordConv.PVT() at2PVTCoord, scaleChange = appTopoCoordSys.convertFrom( toDirPVT, icrsCoordSys, icrsPVTCoord, fromDirPVT, site) icrs2PVTCoord, scaleChange2 = icrsCoordSys.convertFrom( fromDir2PVT, appTopoCoordSys, at2PVTCoord, toDirPVT, site) self.assertAlmostEqual(scaleChange, 1.0 / scaleChange2, places=7) coordConv.assertPVTsAlmostEqual(fromDirPVT, fromDir2PVT, doWrap=True, velPlaces=6)
def testTwoCoordConstructor(self): """Test PVTCoord(coord0, coord1, tai, deltaT, defOrient) """ numRan = 0 for equatAng in (0, 71, -123.4): for polarAng in (0, -75, -89.999999, 89.999999): coord0 = coordConv.Coord(equatAng, polarAng) for orient in (0, -37.5, 90, 128): for tai in (500.5, 10001.3): for deltaT in (1e-7, 1e-5, 1e-3, 0.1, 10): # test that velocity is 0 when the coords are identical pvtCoordA = coordConv.PVTCoord( coord0, coord0, tai, deltaT) self.assertTrue(pvtCoordA.isfinite()) self.assertAlmostEqual(pvtCoordA.getTAI(), tai) self.assertTrue( numpy.allclose(pvtCoordA.getVel(), (0, 0, 0))) self.assertEqual(pvtCoordA.getCoord(), coord0) self.assertEqual(pvtCoordA.getCoord(tai), coord0) # test cases where coord1 may not equal coord0 for dist in (0, 0.001, 0.01, 0.1, 1, 10, 100, 179): predVel = dist / float(deltaT) if predVel > 100: continue numRan += 1 coord1, toOrient = coord0.offset(orient, dist) pvtCoordB = coordConv.PVTCoord( coord0, coord1, tai, deltaT) self.assertEqual(pvtCoordB.getCoord(), coord0) self.assertEqual(pvtCoordB.getCoord(tai), coord0) gotCoord1 = pvtCoordB.getCoord(tai + deltaT) self.assertTrue( numpy.allclose(coord1.getVecPos(), gotCoord1.getVecPos())) self.assertTrue( numpy.allclose(coord1.getVecPM(), gotCoord1.getVecPM())) self.assertTrue(numRan > 1000)
def timeAppTopoToFK5(alt, niter): """Time apparent topocentric to FK5 at the specified altitude @param[in] alt initial altitude (degrees) @param[in] niter number of iterations The coordinate conversion code presently does not have a special branch for zero proper motion, so no attempt is made to provide proper motion. Use the approximation that TAI = UTC, which is plenty close enough for timing. Increment TAI by 0.1 seconds per iteration, which is reasonable for a control loop. """ fk5Sys = coordConv.FK5CoordSys(1980) currTAI = utcFromPySec(time.time()) appTopoSys = coordConv.AppTopoCoordSys() appTopoCoord = coordConv.Coord(120, alt) site = makeSite() startTime = time.time() fromDir = 5.0 for i in range(niter): tai = currTAI + (i * 0.1) fk5Coord, toDir, scaleChange = fk5Sys.convertFrom( appTopoSys, appTopoCoord, fromDir, site, tai) duration = time.time() - startTime print "AppTopo To FK5 Coord with dir: %0.1f conversions/second (%d conversions in %0.2f sec) at alt=%0.1f" % ( niter / duration, niter, duration, alt) startTime = time.time() appTopoPVTCoord = coordConv.PVTCoord(appTopoCoord, appTopoCoord, currTAI, 0.01) toPVTDir = coordConv.PVT() fromPVTDir = coordConv.PVT(5, 0, tai) for i in range(niter): tai = currTAI + (i * 0.1) fk5PVTCoord, scaleChange = fk5Sys.convertFrom(toPVTDir, appTopoSys, appTopoPVTCoord, fromPVTDir, site) duration = time.time() - startTime print "AppTopo To FK5 PVTCoord with dir: %0.1f conversions/second (%d conversions in %0.2f sec) at alt=%0.1f" % ( niter / duration, niter, duration, alt)
def measureOrientationToErr(): """Measure error in Coord.orientationTo for tiny offsets For best results modify Coord::orientationTo to not short-circuit tiny values of sinVal, cosVal """ errDict = {} # dict of distArcSec: (max orient error, max(sinVal, cosVal)) for fromPolarAng in (-89, -72, -45.0, -30, 0.01, 12.5, 31, 47, 56, 68, 89): for fromEquatAng in (0, 41.0): # should not matter fromCoord = coordConv.Coord(fromEquatAng, fromPolarAng) for fromOrient in (-90, -72, -45.0, -30, 0.01, 0, 12.5, 31, 45, 56, 68, 89): # offset by such small distances that toOrient=fromOrient # by 1e-6 the error is starting to go up, indicating that the approximation is failing for distArcSec in (5e-5, 1e-4, 2e-4, 3e-4, 4e-4, 5e-4, 1e-3, 1e-2): toCoord, dumToOrient = fromCoord.offset( fromOrient, distArcSec / 3600) toOrient = coordConv.wrapCtr( 180 + toCoord.orientationTo(fromCoord)) # expect fromOrient = toOrient newErrArcSec = abs(toOrient - fromOrient) * 3600 oldErrArcSec = errDict.get(distArcSec, (0, 0))[0] if newErrArcSec > oldErrArcSec: toU = toCoord.getVecPos() toU /= numpy.linalg.norm(toU) fromU = fromCoord.getVecPos() fromU /= numpy.linalg.norm(fromU) sinVal = (toU[1] * fromU[0]) - (toU[0] * fromU[1]) cosVal = (toU[2] * ((fromU[0] * fromU[0]) + (fromU[1] * fromU[1]))) \ - (fromU[2] * ((toU[0] * fromU[0]) + (toU[1] * fromU[1]))) errDict[distArcSec] = (newErrArcSec, max(sinVal, cosVal)) distKeys = sorted(errDict.keys()) print "Note that by 0.01 arcsec the error creeps up, indicating that the approximation toOrient=fromOrient is failing" for distArcSec in distKeys: errArcSec, maxSinCos = errDict[distArcSec] print "distArcSec=%0.3g arcsec, maxErr=%0.3g arcsec, maxSin/Cos=%0.3g" % ( distArcSec, errArcSec, maxSinCos)
fromCoord = fromCoordSys.convertFrom(appGeoCoordSys, coord, site) angSep = max(refAppGeoCoord.angularSeparation(appGeoCoord), refFromCoord.angularSeparation(fromCoord)) dictKey = (dataAgeSec, ageMult) oldAngSep = errDict.get(dictKey) if oldAngSep is None or angSep > oldAngSep: errDict[dictKey] = angSep errDict = {} site = coordConv.Site(-105.822616, 32.780988, 2.788) for fromCoordSys in (coordConv.ICRSCoordSys(2010), ): for equatAng in (0, 45): for polarAng in (-85, 0, 30, 89): coord = coordConv.Coord(equatAng, polarAng) runOne(fromCoordSys, coord, site, errDict) print "Results (in arcsec) for fromCoordSys=", fromCoordSys.getName( ), fromCoordSys.getDate(), "; coord=", coord.getSphPos() for ageAndMult in sorted(errDict.keys()): print "%s: %0.4f" % (ageAndMult, errDict[ageAndMult] * 3600) # based on these results, 200 seconds gives an error < 0.001 arcseconds, # and it is not so sensitive that it needs to be a constructor parameter # localhost$ tests/checkAppGeoTime.py # Results (in arcsec) for fromCoordSys= icrs 2000.0 ; coord= [False, 44.99999999999999, 29.999999999999996] # (50, -1): 0.0002 # (50, 1): 0.0002 # (100, -1): 0.0004 # (100, 1): 0.0004
def testOneCoordConstructor(self): """Test PVTCoord(coord, vel, tai) constructor """ for equatAng in (0, 71, -123.4): for polarAng in (0, -75, -89.999999, 89.999999): coord = coordConv.Coord(equatAng, polarAng) for vel in ( (0, 0, 0), (10, -23, 1.23), ): vel = numpy.array(vel, dtype=numpy.float64) for tai in (500.5, 10001.3): pvtCoord = coordConv.PVTCoord(coord, vel, tai) self.assertEqual(coord, pvtCoord.getCoord()) self.assertEqual(coord, pvtCoord.getCoord(tai)) self.assertTrue(numpy.allclose(vel, pvtCoord.getVel())) self.assertAlmostEqual(pvtCoord.getTAI(), tai) self.assertTrue(pvtCoord.isfinite()) for parallax in (0, 0.012): for equatPM in (0, 0.11): for polarPM in (0, -0.12): for radVel in (0, 0.13): coordWithPM = coordConv.Coord( equatAng, polarAng, parallax, equatPM, polarPM, radVel) pvtCoordWithPM = coordConv.PVTCoord( coordWithPM, vel, tai) self.assertEqual( coordWithPM, pvtCoordWithPM.getCoord()) self.assertEqual( coordWithPM, pvtCoordWithPM.getCoord(tai)) self.assertTrue( numpy.allclose( vel, pvtCoordWithPM.getVel())) self.assertAlmostEqual( pvtCoordWithPM.getTAI(), tai) self.assertTrue( pvtCoordWithPM.isfinite()) self.assertAlmostEqual( pvtCoordWithPM.getCoord(). angularSeparation(coord), 0) self.assertTrue( pvtCoordWithPM.isfinite()) coordToCheck = pvtCoordWithPM.getCoord( ) self.assertAlmostEqual( radVel, coordToCheck.getRadVel()) atPole, checkEquatPM, checkPolarPM = coordToCheck.getPM( ) if not atPole: self.assertAlmostEqual( equatPM, checkEquatPM) self.assertAlmostEqual( polarPM, checkPolarPM) self.assertAlmostEqual( parallax, coordToCheck.getParallax())
def testFile(self): """Test file of coordinate conversions from TCC (data/masscc_out.dat) Known issues: - radVel does not match; the TCC seems to zero radVel if at infinity, but why? Also, the TCC seems to be able to round trip RadVel even if at infinity, but how, if it zeros it when at infinity? Once I resolve this, update the testCoord.py accordingly, as well as this code. - Other problems await at other coordinate systems. """ site = None numErrors = 0 with file(DataFile, "rU") as f: gotSiteData = False startTime = time.time() nTested = 0 for lineInd, line in enumerate(f): line = line.strip() if not line or line.startswith("#"): continue if not gotSiteData: meanLat, meanLong, elevation, ut1_tai, poleX, poleY = [ float(val) for val in line.split() ] site = coordConv.Site(meanLong, meanLat, elevation) site.setPoleWander(poleX, poleY) site.ut1_tai = ut1_tai gotSiteData = True continue dataList = line.split() fromSysCode, fromDate, fromPos1, fromPos2, fromPM1, fromPM2, fromParallax, fromRadVel, fromDir, refCoA, refCoB, \ toSysCode, toDate, refToPos1, refToPos2, refToPM1, refToPM2, refToParallax, refToRadVel, \ refToDir, refScaleChange, refAtInf, refAtPole, isOK, tai, last \ = [cnvFunc(val) for val, cnvFunc in itertools.izip(dataList, CnvList)] if not isOK: print "Skipping line %s: %s; isOK false" % (lineInd + 1, line) if (fromSysCode == 1) and (fromRadVel != 0) and ( fromPM1 == 0) and (fromPM2 == 0): print "Skipping line %s; FK4 with zero PM and nonzero radVel" % ( lineInd + 1, ) continue nTested += 1 fromCoord = coordConv.Coord(fromPos1, fromPos2, fromParallax, fromPM1, fromPM2, fromRadVel) fromPVTCoord = coordConv.PVTCoord(fromCoord, fromCoord, tai, 0.01) fromPVTDir = coordConv.PVT(fromDir, 0, tai) fromCoordSys = getCoordSys(fromSysCode, fromDate, tai) toCoordSys = getCoordSys(toSysCode, toDate, tai) site.refCoA = refCoA site.refCoB = refCoB try: toCoord, toDir, scaleChange = toCoordSys.convertFrom( fromCoordSys, fromCoord, fromDir, site) toPVTDir = coordConv.PVT() toPVTCoord, scaleChange2 = toCoordSys.convertFrom( toPVTDir, fromCoordSys, fromPVTCoord, fromPVTDir, site) except Exception: print "Failed on line %s: %s\n" % (lineInd + 1, line) raise atPole, toPos1, toPos2 = toCoord.getSphPos() toParallax = toCoord.getParallax() atPole, toPM1, toPM2 = toCoord.getPM() toRadVel = toCoord.getRadVel() if toCoord.atInfinity( ): # emulate something the TCC does that I don't think my code can do toRadVel = fromRadVel predList = (toParallax, toPM1, toPM2, toRadVel) refList = (refToParallax, refToPM1, refToPM2, refToRadVel) refToCoord = coordConv.Coord(refToPos1, refToPos2, refToParallax, refToPM1, refToPM2, refToRadVel) try: self.assertEqual(toCoord.atPole(), refAtPole) self.assertEqual(toCoord.atInfinity(), refAtInf) if (fromSysCode > 0) and (toSysCode > 0): atol = 1e-7 elif (fromSysCode < -1) and (toSysCode < -1): atol = 1e-7 else: # the sla_Mappa in the old TCC is giving slightly different answers # thatn the latest slaMappa and that appears to explain a small discrepancy # when converting to/from apparent geocentric coordinates; # the error is most noticeable for the precession/nutation matrix. atol = 1e-3 self.assertLess(toCoord.angularSeparation(refToCoord), atol) self.assertLess( toPVTCoord.getCoord(tai).angularSeparation(refToCoord), atol) maxPxDelta = refToParallax * 1000.0 self.assertAlmostEqual(toParallax, refToParallax, delta=maxPxDelta) self.assertTrue( numpy.allclose(predList[1:], refList[1:], atol=atol)) self.assertAlmostEqual(refToDir, coordConv.wrapNear(toDir, refToDir), places=2) self.assertAlmostEqual(refToDir, coordConv.wrapNear( toPVTDir.getPos(tai), refToDir), places=2) # scale change bears very little resemblance between old and new. # I believe this is a bug in the old TCC, since mean->mean should be 1.0 # and the new code is significantly closer to 1.0 than the old code. # self.assertAlmostEqual(refScaleChange, scaleChange, places=5) self.assertAlmostEqual(scaleChange, scaleChange2, places=5) if (fromSysCode > 0) and (toSysCode > 0): self.assertAlmostEqual(scaleChange, 1.0, places=5) if toCoordSys.getDateType() == coordConv.DateType_TAI: # "to" system uses tai as its time; try various strategies that remove proper motion to the given tai date # test the removePM function (which removes proper motion and radial velocity, but not parallax) zpmFromCoord = fromCoordSys.removePM(fromCoord, tai) if fromCoordSys.getName() != "fk4": # FK4 coordinates have fictitious space motion zpmFromAtPole, zpmFromPM1, zpmFromPM2 = zpmFromCoord.getPM( ) self.assertEqual(fromCoord.atPole(), zpmFromAtPole) self.assertEqual(zpmFromPM1, 0) self.assertEqual(zpmFromPM2, 0) zpmFromRadVel = zpmFromCoord.getRadVel() self.assertEqual(zpmFromRadVel, 0) # zpmFromAtPole, zpmFromPM1, zpmFromPM2 = zpmFromCoord.getPM() # self.assertEqual(fromCoord.atPole(), zpmFromAtPole) # zpmFromRadVel = zpmFromCoord.getRadVel() # self.assertEqual(zpmFromPM1, 0) # self.assertEqual(zpmFromPM2, 0) # self.assertEqual(zpmFromRadVel, 0) zpmToCoord, zpmToDir, zpmScaleChange = toCoordSys.convertFrom( fromCoordSys, zpmFromCoord, fromDir, site) zpmToAtPole, zpmToPos1, zpmToPos2 = zpmToCoord.getSphPos( ) self.assertEqual(atPole, zpmToAtPole) zpmToAtPole, zpmToPM1, zpmToPM2 = zpmToCoord.getPM() self.assertEqual(atPole, zpmToAtPole) zpmToRadVel = zpmToCoord.getRadVel() self.assertAlmostEqual(toDir, zpmToDir, places=2) # why so poor? self.assertAlmostEqual(scaleChange, zpmScaleChange, places=6) self.assertLess(toCoord.angularSeparation(zpmToCoord), 1e-7) self.assertEqual(zpmToPM1, 0) self.assertEqual(zpmToPM2, 0) self.assertEqual(zpmToRadVel, 0) except Exception as e: if ContinueOnError: print print str(e) print "Failed on line %s: %s" % (lineInd + 1, line) print "fromCoordSys=(%s, %s); toCoordSys=(%s, %s)" % ( fromCoordSys.getName(), fromCoordSys.getDate(), toCoordSys.getName(), toCoordSys.getDate()) print "toSphPos= ", toPos1, toPos2 print "refToSphPos=", refToPos1, refToPos2 print "angular sep=", toCoord.angularSeparation( refToCoord) * 3600.0, "arcsec" print "pred parallax, PM and radVel=", predList print "ref parallax, PM and radVel=", refList print "from parallax, PM and radVel=", (fromParallax, fromPM1, fromPM2, fromRadVel) print "from vec pos, vel=", fromCoord.getVecPos( ), fromCoord.getVecPM() print "to vec pos, vel=", toCoord.getVecPos( ), toCoord.getVecPM() if not ContinueOnError: raise numErrors += 1 duration = time.time() - startTime print "Tested %d conversions in %0.2f seconds: %0.0f conversions/second" % \ (nTested, duration, nTested/duration) self.assertEqual(numErrors, 0, "%s errors" % (numErrors, ))