From 5a8daba7d5314549304ab4be2c5ab68b2fc2734b Mon Sep 17 00:00:00 2001 From: Don Cross Date: Thu, 15 Apr 2021 20:54:37 -0400 Subject: [PATCH] Added Python demo of calculating Jupiter's moons. The demo shows how to correct for light travel time to render Jupiter's moons as they appear from the Earth. Created an addition operator for the Vector class in the Python code, because it is handy. Corrected a bug in the string representation of the Python StateVector class. --- demo/python/README.md | 17 ++++++ demo/python/astronomy.py | 5 +- demo/python/jupiter_moons.py | 62 ++++++++++++++++++++++ demo/python/test/.gitignore | 1 + demo/python/test/jupiter_moons_correct.txt | 10 ++++ demo/python/test/test | 6 ++- generate/template/astronomy.py | 5 +- source/python/astronomy.py | 5 +- 8 files changed, 107 insertions(+), 4 deletions(-) create mode 100755 demo/python/jupiter_moons.py create mode 100644 demo/python/test/jupiter_moons_correct.txt diff --git a/demo/python/README.md b/demo/python/README.md index 3d68b127..a60ddfd1 100644 --- a/demo/python/README.md +++ b/demo/python/README.md @@ -2,6 +2,16 @@ --- +### [Camera](camera.py) +Suppose you want to photograph the Moon, +and you want to know what it will look like in the photo. +Given a location on the Earth, and a date/time, +this program calculates the orientation of the sunlit +side of the Moon with respect to the top of your +photo image. It assumes the camera faces directly +toward the Moon's azimuth and tilts upward to its +altitude angle above the horizon. + ### [Culmination](culminate.py) Finds when the Sun, Moon, and planets reach their highest position in the sky on a given date, as seen by an observer at a specified location on the Earth. @@ -14,6 +24,13 @@ This is a more advanced example. It shows how to use coordinate transforms to find where the ecliptic intersects with an observer's horizon at a given date and time. +### [Jupiter's Moons](jupiter_moons.py) +Calculates the coordinates of Jupiter and its four major moons +(Io, Europa, Ganymede, and Callisto) as seen from the Earth +at a given date and time. This program illustrates how to correct +for the delay caused by the time it takes for light to reach +the Earth from the Jupiter system. + ### [Lunar Eclipse](lunar_eclipse.py) Calculates details about the first 10 partial/total lunar eclipses after the given date and time. diff --git a/demo/python/astronomy.py b/demo/python/astronomy.py index 25bf4976..33931b07 100644 --- a/demo/python/astronomy.py +++ b/demo/python/astronomy.py @@ -162,6 +162,9 @@ class Vector: """Returns the length of the vector in AU.""" return math.sqrt(self.x**2 + self.y**2 + self.z**2) + def __add__(self, other): + return Vector(self.x + other.x, self.y + other.y, self.z + other.z, self.t) + class StateVector: """A combination of a position vector, a velocity vector, and a time. @@ -197,7 +200,7 @@ class StateVector: self.t = t def __repr__(self): - return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t{}]'.format( + return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t={}]'.format( self.x, self.y, self.z, self.vx, self.vy, self.vz, str(self.t)) diff --git a/demo/python/jupiter_moons.py b/demo/python/jupiter_moons.py new file mode 100755 index 00000000..b390593b --- /dev/null +++ b/demo/python/jupiter_moons.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# jupiter_moons.py - by Don Cross - 2021-04-15 +# +# Example Python program for Astronomy Engine: +# https://github.com/cosinekitty/astronomy +# +# Calculates the coordinates of Jupiter and its four major moons +# (Io, Europa, Ganymede, and Callisto) as seen from the Earth +# at a given date and time. This program illustrates how to correct +# for the delay caused by the time it takes for light to reach +# the Earth from the Jupiter system. +# +import sys +from astronomy import Time, JupiterMoons, GeoVector, EquatorFromVector, Body, C_AUDAY + +def PrintBody(name, geovec): + # Convert the geocentric vector into equatorial coordinates. + equ = EquatorFromVector(geovec) + print('{:<8s} RA {:10.6f} DEC {:10.6f} {:10.6f} AU'.format(name, equ.ra, equ.dec, equ.dist)) + + +if __name__ == '__main__': + # If date/time is provided on the command line, use it. + # Otherwise, use the current date and time. + if len(sys.argv) == 2: + time = Time.Parse(sys.argv[1]) + else: + time = Time.Now() + print('Calculations for:', time) + + # Call GeoVector to calculate the geocentric position of Jupiter. + # GeoVector corrects for light travel time. + # That means it returns a vector to where Jupiter appears to be + # in the sky, when the light left Jupiter to travel toward the + # Earth to arrive here at the specified time. This is different from + # where Jupiter is at that time. + + jv = GeoVector(Body.Jupiter, time, True) + + # Calculate the amount of time it took light to reach the Earth from Jupiter. + # The distance to Jupiter (AU) divided by the speed of light (AU/day) = time in days. + lt_days = jv.Length() / C_AUDAY + print() + print('It took light {:0.2f} minutes to reach the Earth from Jupiter.'.format(lt_days * 24.0 * 60.0)) + print() + + # The JupiterMoons function calculates positions of Jupiter's moons without + # correcting for light travel time. Correct for light travel by backdating + # by the given amount of light travel time. + backdate = time.AddDays(-lt_days) + + jm = JupiterMoons(backdate) + + PrintBody('Jupiter', jv) + PrintBody('Io', jv + jm.moon[0]) + PrintBody('Europa', jv + jm.moon[1]) + PrintBody('Ganymede', jv + jm.moon[2]) + PrintBody('Callisto', jv + jm.moon[3]) + print() + + sys.exit(0) diff --git a/demo/python/test/.gitignore b/demo/python/test/.gitignore index 4529900c..63675b78 100644 --- a/demo/python/test/.gitignore +++ b/demo/python/test/.gitignore @@ -6,3 +6,4 @@ seasons.txt culminate.txt horizon.txt lunar_eclipse.txt +jupiter_moons.txt diff --git a/demo/python/test/jupiter_moons_correct.txt b/demo/python/test/jupiter_moons_correct.txt new file mode 100644 index 00000000..9fb17962 --- /dev/null +++ b/demo/python/test/jupiter_moons_correct.txt @@ -0,0 +1,10 @@ +Calculations for: 2021-04-16T00:26:18.000Z + +It took light 45.61 minutes to reach the Earth from Jupiter. + +Jupiter RA 21.880180 DEC -13.606457 5.484363 AU +Io RA 21.882006 DEC -13.595692 5.484937 AU +Europa RA 21.883169 DEC -13.588544 5.484757 AU +Ganymede RA 21.880320 DEC -13.605789 5.477195 AU +Callisto RA 21.888368 DEC -13.558804 5.486620 AU + diff --git a/demo/python/test/test b/demo/python/test/test index c85ae2b0..43058d09 100755 --- a/demo/python/test/test +++ b/demo/python/test/test @@ -5,7 +5,11 @@ Fail() exit 1 } -rm -f test/{camera,moonphase,positions,riseset,seasons,culminate,horizon,lunar_eclipse}.txt +rm -f test/{jupiter_moons,camera,moonphase,positions,riseset,seasons,culminate,horizon,lunar_eclipse}.txt + +echo "Testing example: jupiter_moons.py" +./jupiter_moons.py 2021-04-16T00:26:18Z > test/jupiter_moons.txt || Fail "Error testing jupiter_moons.py." +diff test/jupiter_moons.txt test/jupiter_moons_correct.txt || Fail "Error comparing jupiter_moons.py output." echo "Testing example: camera.py" ./camera.py 29 -81 2021-03-22T02:45:00Z > test/camera.txt || Fail "Error testing camera.py." diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index 285c0be8..ba2a9d0c 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -162,6 +162,9 @@ class Vector: """Returns the length of the vector in AU.""" return math.sqrt(self.x**2 + self.y**2 + self.z**2) + def __add__(self, other): + return Vector(self.x + other.x, self.y + other.y, self.z + other.z, self.t) + class StateVector: """A combination of a position vector, a velocity vector, and a time. @@ -197,7 +200,7 @@ class StateVector: self.t = t def __repr__(self): - return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t{}]'.format( + return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t={}]'.format( self.x, self.y, self.z, self.vx, self.vy, self.vz, str(self.t)) diff --git a/source/python/astronomy.py b/source/python/astronomy.py index 25bf4976..33931b07 100644 --- a/source/python/astronomy.py +++ b/source/python/astronomy.py @@ -162,6 +162,9 @@ class Vector: """Returns the length of the vector in AU.""" return math.sqrt(self.x**2 + self.y**2 + self.z**2) + def __add__(self, other): + return Vector(self.x + other.x, self.y + other.y, self.z + other.z, self.t) + class StateVector: """A combination of a position vector, a velocity vector, and a time. @@ -197,7 +200,7 @@ class StateVector: self.t = t def __repr__(self): - return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t{}]'.format( + return 'StateVector[pos=({}, {}, {}), vel=({}, {}, {}), t={}]'.format( self.x, self.y, self.z, self.vx, self.vy, self.vz, str(self.t))