Table of Contents



BOTEC is a simple astrophysical and orbital mechanics calculator, including a database of all named Solar System objects.


BOTEC is intended as a simple but useful calculator to assist with making astrophysical, orbital mechanics, and space navigation calculations. As the origin of the acronym applies, BOTEC is more of a "back-of-the-envelope calculator" rather than an industrial-strength calculator, although this may change in the future.

BOTEC is primarily intended for people familiar with physics and Python, and as such is unlikely to be useful to the average enduser. BOTEC really consists of two parts: The BOTEC software, which knows what to do with the data, and the Solar System data itself, which is represented in a large data file (a Python pickle, actually). This is deliberately modularized so that the Solar System data BOTEC uses can be updated independently of thet software, and also that alternative data files (e.g., hypothetical stellar systems for fictional purposes) can be supported.

All values are strictly in SI units.

Getting the software

The current version of botec is 0.3.4.

The latest version of the software is available in a tarball here:

The official URL for this Web site is


BOTEC requires Python 2.3 or greater.

In its present state, BOTEC will also not be of much use to endusers not familiar with Python, or people without some basic working knowledge of physics, astrophysics, orbital mechanics, and space navigation.


This code is released under the GPL.


It is very important to emphasize that BOTEC is intended as a simple calculator, and as such is not up to the task of industrial-strength calculations of the calibre that would be required for actually planning true space missions, although this may well change in the future.

BOTEC was originally intended as a data-generating tool for a computer roleplaying game system (which may or may not come to fruition), but was always intended to be able to exist as a standalone entity. However, this means that as a result of its primary goal, it incurs the same limitations that are present in that particular game design. In particular, these are:

  • All bodies are spherical, and rotate with constant angular velocity.

  • All orbits are circular, with another body or a gravitating point at the center (except for toplevel objects like the Sun); thus all bodies orbit with constant angular velocity.

  • All orbits are coplanar.

  • All orbits are prograde. This affects only a few major worlds (e.g., Triton) and numerous, tiny, outer Solar System distant satellites. The rotation of objects could possibly be retrograde, since that is only indicated by means of a negative period.

  • All bodies have negligible size compared to their orbits, and all suborbits have negligible size compared to their parent orbits.

  • Only objects with actual proper names are included in BOTEC's database. Even objects such as asteroids and satellites with provisional names are not included here. No comets, whether given proper names, or not, however, are included. This would not be hard to change.

  • For all orbital transfers, it is assumed that the durations of application of deltavee are much shorter than the duration of the flight time. That is, burns are treated as instantaneous (that is, orbital transfers are "impulsive"). This is a good approximation in most cases (even with chemical rockets), but not with more exotic drive systems like ion drives or solar/magnetic sails.

Although many of these limitations involving treating certain orbital parameters as trivial (i.e., coplanar, circular orbits implies setting the inclination and eccentricity both to zero), for the most part that actual physical data is contained within the file. So although BOTEC itself will not treat Mars' orbit as eccentric, mars.eccentricity() will in fact return Mars' actual orbital eccentricity.

What is missing

Note that this release contains almost no documentation whatsoever. As such, you are pretty much on your own. However, a fair number of docstrings are included in the BOTEC code so it should be possible for someone experienced in Python to wind their way through it. Look at the module documentation located in the doc subdirectory for information on the API.

There are some major pieces of functionality missing from BOTEC, even for the original purpose it was intended. A few of them are enumerated here:

  • BOTEC needs the concept of the flow of time, so that it can arrange events and be better able to predict and understand opportunities given the instantaneous positions of the various objects in their orbits. This will probably be done via some sort of "timeline," where events can be registered on the timeline and then BOTEC can attach further events to the timeline. That isn't to say that BOTEC isn't able to understand anything about time whatsoever -- for instance, BOTEC can calculate the duration of a Hohmann transfer from Earth to Mars -- but it can only deal with durations and not events on a timeline.

  • At present only Hohmann and bielliptic transfers (the latter was really only implemented as a half-joke) are supported. This is the main place where BOTEC is lacking in functionality; the selection of different types of orbital transfer types is absolutely required, even for the planned game. Note that proper implementation of such transfers will require some acknowledgement of the first item, the timeline; after all, after all, an off-Hohmann transfer is going to take a very wide range of deltavees and durations depending on the relative location of where you want to go and how urgently you need to go there.

  • Proper handling of instability and stationkeeping are not yet handled.

  • Proper calculation (and selection of transfer modes) consisting of angular changes in position (say, plotting a course from the Sun-Earth L4 to Sun-Earth L5) are handled arbitrarily at this point.

  • Additionally, transfers to and from L1 or L2 are handled arbitrarily and somewhat peculiarly; first a transfer is plotted to the body and then a "send" or "return" transfer is plotted to the libration point. This is clearly preliminary.

  • The game will require special handling of transfers between objects at the same location, including objects at the same Lagrange points (e.g., transferring between two asteroids at the Sun-Jupiter L4 point). At present this data is available (see the zone method), but is not used in plotting courses.

  • An attempt is made to compensate for transfers off of the surface of worlds (taking into account atmospheric drag) which is extremely rudimentary (in fact the correction is made even for worlds with no significant atmosphere).

  • Other fancy transfers such as slingshots are not supported at all.

  • Information that is unavailable in the database is either estimated or substituted with a (hopefully) reasonable default. For a more advanced system, this behavior should probably have finer control.


The basic package consists of five things:
The BOTEC assembly module, which is used to build the .botec database files from data sets (the data sets themselves are not included).
The main BOTEC module. This contains all of the class definitions relevant to BOTEC.
The BOTEC executive. This is merely intended as a convenient wrapper around the botec module for interactive use; it imports the botec module and defines some helper functions for helping to show how do get things done.
This is the data file containing the Solar System data that the BOTEC system uses. When is run, it reports the version information for the default.botec file it's using
A documentation help file in HTML. Scroll to the end of the file and click on the botec module entry to get help on the classes and their methods.

Sample usage

Familiarity with Python and physics is assumed. Start BOTEC with:


or if that doesn't work:

        python -i ./

If you use an IDE, you can invoke BOTEC by starting up the interpreter and executing the Python command:


You are now presented with a debugging string indicating the database that BOTEC is using and a standard Python prompt. The botex module defines numerous helper functions (see info, plot), and quite a few precanned names referencing objects and locations (earth, sunEarthL4, lowMoonOrbit, stationaryJupiterOrbit, charonSurface, etc.). Use dir (or inspect itself) to see what's already defined:

        >>> dir() 
        ['AU', 'AltitudeLocation', 'BiellipticTransfer',
        'BiparabolicTransfer', 'C', 'COMPRESSED_EXTENSION',
        'CosinesTransfer', 'Course', 'DAY', 'DEBUG',
        'ExtractionTransfer', 'FOUR_PI', 'FOUR_PI_SQUARED',
        'FOUR_THIRDS_PI', 'G', 'GEE', 'HOUR', 'HohmannTransfer',
        'INFINITY', 'ImpulsiveTransfer', 'InsertionTransfer',
        'LEGAL_KEYS', 'LagrangeLocation', 'LandTransfer',
        'LateralTransfer', 'LaunchTransfer', 'LibrationLocation',
        'Location', 'MINUTE', 'MONTH', 'Maneuver', 'NUMERIC_KEYS',
        'ONE_THIRD', 'OberthManeuver', 'Opportunity', 'Orbit',
        'OrbitalLocation', 'PI', 'PI_OVER_THREE', 'PI_OVER_TWELVE',
        'PartialTransferManeuver', 'PhaseLocation', 'PhaseTransfer',
        'PlaneChangeTransfer', 'RADIANS_TO_DEGREES', 'ReprMixin',
        'ReturnTransfer', 'SI', 'SIGMA', 'STRING_KEYS', 'SYSTEM',
        'SendTransfer', 'SurfaceLocation', 'System', 'THREE_FIFTHS',
        'TURN', 'TWO_FIFTHS', 'TWO_G', 'TWO_PI', 'Transfer',
        'TransferManeuver', 'VectorTransfer', 'WEEK', 'World', 'YEAR',
        '__builtins__', '__doc__', '__file__', '__name__',
        '__package__', 'atmosphericJupiterOrbit', 'ceres', 'charon',
        'charonSurface', 'chiron', 'earth', 'earthMoonL1',
        'earthMoonL2', 'earthMoonL3', 'earthMoonL4', 'earthSurface',
        'europa', 'filename', 'gzip', 'highEarthOrbit',
        'highMoonOrbit', 'highTritonOrbit', 'iapetus', 'icarus',
        'info', 'init', 'io', 'jupiter', 'load', 'lowEarthOrbit',
        'lowIapetusOrbit', 'lowJupiterOrbit', 'lowMarsOrbit',
        'lowMoonOrbit', 'mars', 'marsSurface', 'math',
        'mediumIoOrbit', 'mercury', 'moon', 'moonSurface', 'neptune',
        'operator', 'os', 'phobos', 'pickle', 'plot', 'pluto', 're',
        'saturn', 'save', 'stationaryEarthOrbit',
        'stationaryJupiterOrbit', 'stationarySunOrbit', 'sun',
        'sunEarthL1', 'sunEarthL3', 'sunEarthL4', 'sunJupiterL4',
        'sunMercuryL2', 'sys', 'time', 'triton', 'types', 'uranus',
        'venus', 'vesta']

Most important, however, is the SYSTEM variable, which is a mapping-like object which allows you to reference any object contained within the database. To access, say, the asteroid Antigone, type:

        >>> SYSTEM['Antigone']
        <World @ 0x403b928c (Antigone)>

Use dir on an object to see what methods are available (there are a lot):

        >>> dir(earth)
        ['_Location__distance', '_Location__primary',
        '_World__secondaries', '__doc__', '__getitem__', '__init__',
        '__module__', '__repr__', '__str__', 'albedo',
        'alternateNames', 'altitude', 'altitudeAtPressure',
        'angularRadiusFromDistance', 'angularRadiusFromPrimary',
        'angularRadiusOfPrimary', 'angularRadiusOfSecondary',
        'apoapsis', 'apostationaryAltitude', 'apostationaryDistance',
        'areicAtmosphereMass', 'asteroids', 'atmosphereMass',
        'barycenterDistanceWithSecondary', 'biozone',
        'centripetalAccelerationAtSurface', 'circumference', 'class_',
        'closingSpeeds', 'crossSectionalArea', 'data', 'density',
        'diameter', 'distance', 'distanceAndPhaseAtCentralAngle',
        'distanceForIntensity', 'distanceFrom', 'distanceToSecondary',
        'eccentricity', 'effectiveDeltaveeDifference', 'ellipticity',
        'emissivity', 'ensure', 'equatorialRadius',
        'escapeSpeedExcess', 'escapeSpeedFromDistance',
        'escapeSpeedFromPrimary', 'escapeSpeedFromSurface',
        'findCommonPrimary', 'get', 'gravitationalBindingEnergy',
        'gravityAtDistance', 'gravityAtSurface', 'had', 'has',
        'hasRetrogradeRevolution', 'hasRetrogradeRotation',
        'hasSurface', 'hierarchy', 'hillRadius', 'idealTemperature',
        'inclination', 'indirectIntensityAtDistance', 'instability',
        'intensityAtDistance', 'intensityFromPrimary',
        'intensityFromRoot', 'interceptedPower', 'irradianceAt',
        'isAncestorOf', 'isDescendentOf', 'isInclined', 'isLuminous',
        'isNonSpherical', 'isOblate', 'isOblique', 'isRoot',
        'isSynchronous', 'luminosity', 'mass', 'massEnergy',
        'massRatio', 'massicAngularMomentum', 'massicKineticEnergy',
        'massicPotentialEnergy', 'massicTotalEnergy',
        'maximumTidalStrengthFromSibling', 'name',
        'netAccelerationAtSurface', 'normalizedMomentOfInertia',
        'number', 'obliquity', 'opportunitiesPeriodWith',
        'opportunityAngleWith', 'opticalDepth',
        'orbitalAngularMomentum', 'orbitalAngularSpeedAroundPrimary',
        'orbitalAngularSpeedAtDistance', 'orbitalArea',
        'orbitalKineticEnergy', 'orbitalMomentOfInertia',
        'orbitalPeriodAroundPrimary', 'orbitalPeriodAtDistance',
        'orbitalPeriodAtSurface', 'orbitalPotentialEnergy',
        'orbitalSpeedAtDistance', 'orbitalSpeedAtSurface',
        'orbitalTotalEnergy', 'periapsis', 'period', 'phaseAngle',
        'planets', 'polarRadius', 'portionOfOrbitInShadow',
        'pressure', 'pressureAt', 'pressureAtAltitude', 'primaries',
        'primary', 'radius', 'rawData', 'reflectedPower',
        'relativeOrbitalAngularSpeedWith', 'rings',
        'rocheLimitForDensity', 'rotationSpeed',
        'rotationalAngularMomentum', 'rotationalAngularSpeed',
        'rotationalKineticEnergy', 'rotationalMomentOfInertia',
        'roundTripTimeWith', 'roundTripWaitTimeWith', 'satellites',
        'scaleHeight', 'secondaries', 'select',
        'solidAngleFromDistance', 'solidAngleFromPrimary',
        'solidAngleOfPrimary', 'solidAngleOfSecondary',
        'sphereOfInfluence', 'stationKeeping', 'surfaceArea',
        'surfaceBrightness', 'temperature', 'tidalStrengthAtDistance',
        'tidalStrengthFromPrimary', 'tidalStrengthFromSecondary',
        'tidalStrengthRangeFromSibling', 'tisserandParameter',
        'totalAngularMomentum', 'type', 'volume', 'zone']

Here is a sample transcript of a BOTEC session to give you the idea of what can be done:

        >>> jupiter.satellites()
        [<World @ 0x402f590c (Io)>, <World @ 0x402f5a8c (Europa)>,
        <World @ 0x402f5c0c (Ganymede)>, <World @ 0x402f5d8c
        (Callisto)>, <World @ 0x402f5f0c (Metis)>, <World @ 0x402f80ac
        (Adrastea)>, <World @ 0x402f822c (Amalthea)>, <World @
        0x402f83cc (Thebe)>, <World @ 0x402f854c (Themisto)>, <World @
        0x402f868c (Leda)>, <World @ 0x402f880c (Himalia)>, <World @
        0x402f89cc (Lysithea)>, <World @ 0x402f8b4c (Elara)>, <World @
        0x402f8d0c (Harpalyke)>, <World @ 0x402f8e2c (Praxidike)>,
        <World @ 0x402f8f4c (Iocaste)>, <World @ 0x402fc0ac (Ananke)>,
        <World @ 0x402fc22c (Chaldene)>, <World @ 0x402fc36c
        (Isonoe)>, <World @ 0x402fc4ac (Erinome)>, <World @ 0x402fc5ec
        (Taygete)>, <World @ 0x402fc72c (Carme)>, <World @ 0x402fc8ac
        (Kalyke)>, <World @ 0x402fc9ec (Pasiphae)>, <World @
        0x402fcb6c (Megaclite)>, <World @ 0x402fcc8c (Sinope)>, <World
        @ 0x402fce0c (Callirrhoe)>, <World @ 0x402fcf2c (Autonoe)>,
        <World @ 0x402ff04c (Thyone)>, <World @ 0x402ff14c
        (Hermippe)>, <World @ 0x402ff24c (Eurydome)>, <World @
        0x402ff34c (Sponde)>, <World @ 0x402ff44c (Pasithee)>, <World
        @ 0x402ff54c (Euanthe)>, <World @ 0x402ff64c (Kale)>, <World @
        0x402ff74c (Orthosie)>, <World @ 0x402ff84c (Euporie)>, <World
        @ 0x402ff94c (Aitne)>]

        >>> info(stationaryEarthOrbit)
        Object: stationary orbit around `Earth'
         Primary: Earth
         Distance: 4.22e+07 m
         Phase angle: 0.00e+00 rad

        >>> info(io)
        Object: Io
         Name: Io
         Alternate names: JI
         Number: 0
         Type: satellite
         Class: unspecified
         Zone: Jupiter
         Primary: Jupiter
         Distance: 4.22e+08 m
         Eccentricity: 4.00e-03
         Inclination: 6.98e-04 rad
         Obliquity: 0.00e+00 rad
         Mass: 8.93e+22 kg
         Radius: 1.82e+06 m
         Ellipticity: 0.00e+00
         Period: 1.53e+05 s = 1.77 d
         Temperature: 0.00e+00 K
         Pressure: 0.00e+00 Pa
         Scale height: 0.00e+00 m
         Optical depth: 0.00e+00
         Albedo: 6.20e-01
         Luminosity: 0.00e+00 W

        >>> lowEarthOrbit.orbitalPeriodAroundPrimary()
        5181.5057206353176 # s

        >>> earth.rocheLimitForDensity(1e3) # 1e3 kg/m^3 for water
        14181619.914894238 # m

        >>> pluto.intensityFromPrimary()/earth.intensityFromPrimary()
        0.00064958695692894674 # Sun is that much dimmer from Pluto than
                               # from Earth

        >>> triton.angularRadiusOfPrimary()/earth.angularRadiusOfSecondary(moon)
        15.335201130577547 # from Triton, Neptune looks 15 times bigger than
                           # the Moon does from Earth

        >>> pluto.atmosphereMass()
        92207637091168.484 # kg, computed from surface gravity and pressure

        >>> io.tidalStrengthFromPrimary()/earth.tidalStrengthFromSecondary(moon)
        19581.842788149675 # Jupiter's tides on Io are this much stronger than
                           # the Moon's from Earth

        >>> info(SYSTEM['Yamatotakeru'])
        Object: Yamatotakeru
         Name: Yamatotakeru
         Alternate names: 
         Number: 5282
         Type: asteroid
         Class: unspecified
         Zone: main belt IIb
         Primary: Sun
         Distance: 4.09e+11 m = 2.73 au
         Eccentricity: 1.21e-01
         Inclination: 2.13e-01 rad = 12.2 deg
         Obliquity: 0.00e+00 rad
         Mass: 0.00e+00 kg
         Radius: 0.00e+00 m
         Ellipticity: 0.00e+00
         Period: 0.00e+00 s
         Temperature: 0.00e+00 K
         Pressure: 0.00e+00 Pa
         Scale height: 0.00e+00 m
         Optical depth: 0.00e+00
         Albedo: 0.00e+00
         Luminosity: 0.00e+00 W

        >>> Course(earth, SYSTEM['Yamatotakeru']).deltavee()
        11080.157152067533 # m/s

        >>> plot(lowEarthOrbit, lowMarsOrbit)
        Course: from `low orbit around `Earth'' to `low orbit around `Mars''
         Source: low orbit around `Earth'
         Destination: low orbit around `Mars'
         Opportunity: opportunity from `Earth' to `Mars'
          Angle: 7.74e-01 rad = 44.3 deg
          Period: 6.74e+07 s = 2.14 y
          Transfer: out of `low orbit around `Earth''
          Transfer: Hohmann from `Earth' to `Mars'
          Transfer: into `low orbit around `Mars''
          Maneuver: Oberth from `low orbit around `Earth'' to `Earth' with 2.94e+03 m/s = 2.94 km/s excess
           Burns: 3.63e+03 m/s = 3.63 km/s
            Transfer: out of `low orbit around `Earth''
          Maneuver: Oberth from `Mars' to `low orbit around `Mars'' with 2.65e+03 m/s = 2.65 km/s excess
           Burns: 2.11e+03 m/s = 2.11 km/s
            Transfer: into `low orbit around `Mars''
         Duration: 2.24e+07 s = 8.63 mo
         Deltavee: 5.75e+03 m/s = 5.75 km/s

        >>> plot(stationaryEarthOrbit, highTritonOrbit)
        Course: from `stationary orbit around `Earth'' to `high orbit around `Triton''
         Source: stationary orbit around `Earth'
         Destination: high orbit around `Triton'
         Opportunity: opportunity from `Earth' to `Neptune'
          Angle: 1.97e+00 rad = 113 deg
          Period: 3.18e+07 s = 1.01 y
          Transfer: out of `stationary orbit around `Earth''
          Transfer: Hohmann from `Earth' to `Neptune'
          Transfer: into `Triton'
          Transfer: into `high orbit around `Triton''
          Maneuver: Oberth from `stationary orbit around `Earth'' to `Earth' with 1.17e+04 m/s = 11.7 km/s excess
           Burns: 9.36e+03 m/s = 9.36 km/s
            Transfer: out of `stationary orbit around `Earth''
          Maneuver: Oberth from `Neptune' to `high orbit around `Triton'' with 4.05e+03 m/s = 4.05 km/s excess
           Burns: 2.71e+03 m/s = 2.71 km/s
            Transfer: into `Triton'
            Transfer: into `high orbit around `Triton''
         Duration: 9.65e+08 s = 30.6 y
         Deltavee: 1.21e+04 m/s = 12.1 km/s


Release history

  • 0.3.4, 2006 Aug 26. Added comets.

  • 0.3.3, 2006 Aug 20. Fixes to Oberth maneuvers starting from surfaces.

  • 0.3.2, 2006 Jun 15. Minor tweaks, updated database.

  • 0.3.1, 2005 Jul 30. Bugfixes in total energy computations, better handling of Oberth maneuvers.

  • 0.3, 2005 Jan 15. Separate transfers from maneuvers; support Oberth maneuvers.

  • 0.2.1, 2005 Jan 8. Various collected modifications.

  • 0.2, 2004 Sep 18. Initial public release.

  • 0.1x2, 2004 Jan 22. A "clever" way the data pickle was written was causing a confusing import error on Windows, this has been corrected.

  • 0.1x1, 2004 Jan 10. Initial prerelease.

Wish list

  • More extensive document is a must.

  • Making the ability of the calculator more industrial strength is also a high priority. At present the simplifications it makes are really not very useful for actual space mission planning.

  • Once the more realistic modeling has been included, handling more exotic spacecraft types and orbital tricks such as gravitational slingshot, aerobraking, solar sails, and the like would make sense.

  • Accompanying more realistic orbit handling and course plotting would probably be visualization, in the form of OpenGL graphics.

  • A component including data and information about stars and star systems (obviously starting only with the Solar System) would be useful.

  • A stellar evolution calculator, including information about luminosities, masses, lifetimes, and such would also be useful.


This module was written by Erik Max Francis. If you use this software, have suggestions for future releases, or bug reports, I'd love to hear about it.


Version 0.3.4 $Date: 2006/08/26 $ $Author: max $

Modules and Packages   


Data file parser for BOTEC.


BOTEC is a simple astrophysical and orbital mechanics calculator,


Back-of-the-Envelope Calculator executive.

Table of Contents

This document was automatically generated on Sat Aug 26 18:38:02 2006 by HappyDoc version 2.1