{"id":26127283,"url":"https://github.com/nsat/pypredict","last_synced_at":"2025-06-15T16:38:27.217Z","repository":{"id":22653940,"uuid":"25997060","full_name":"nsat/pypredict","owner":"nsat","description":"Spire port of predict open-source tracking library","archived":false,"fork":false,"pushed_at":"2024-10-23T15:34:43.000Z","size":1788,"stargazers_count":69,"open_issues_count":15,"forks_count":17,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-05-26T09:43:22.526Z","etag":null,"topics":["predict","python","satellite","space","tracking"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nsat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2014-10-31T01:30:40.000Z","updated_at":"2025-04-10T14:53:21.000Z","dependencies_parsed_at":"2024-06-20T21:59:19.628Z","dependency_job_id":"df9d6ec7-0d69-4479-bfe3-8f83805a13e4","html_url":"https://github.com/nsat/pypredict","commit_stats":{"total_commits":86,"total_committers":11,"mean_commits":7.818181818181818,"dds":0.5813953488372092,"last_synced_commit":"1afee8032eee525b07f70633d511adb3c720ada7"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/nsat/pypredict","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nsat%2Fpypredict","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nsat%2Fpypredict/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nsat%2Fpypredict/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nsat%2Fpypredict/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nsat","download_url":"https://codeload.github.com/nsat/pypredict/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nsat%2Fpypredict/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260009784,"owners_count":22945536,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["predict","python","satellite","space","tracking"],"created_at":"2025-03-10T18:07:51.479Z","updated_at":"2025-06-15T16:38:27.183Z","avatar_url":"https://github.com/nsat.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![ci](https://github.com/nsat/pypredict/actions/workflows/python-app.yml/badge.svg)](https://github.com/nsat/pypredict/actions/workflows/python-app.yml)\n\nPyPredict\n=======\n\n\u003e\u003cb\u003eNOTE\u003c/b\u003e: To preserve compatibility with `predict`, pypredict uses __north__ latitude and __west__ longitude for terrestrial coordinates.\n\nDo you want accurate and time-tested satellite tracking and pass prediction in a convenient python wrapper?\nYou're in the right place.\n\nPyPredict is a C Python extension directly adapted from the ubiquitous [predict](http://www.qsl.net/kd2bd/predict.html) satellite tracking command line application.\nOriginally written for the commodore 64, predict has a proven pedigree; We just aim to provide a convenient API.\nPyPredict is a port of the predict codebase and should yield identical results.\n\nIf you think you've found an error in `pypredict`, please include output from `predict` on same inputs to the bug report.  \nIf you think you've found a bug in predict, please report and we'll coordinate with upstream.\n\n### Installation\n\n```bash\nsudo apt-get install python-dev\nsudo python setup.py install\n```\n\n## Usage\n\n#### Observe a satellite (relative to a position on earth)\n\n```python\nimport predict\ntle = \"\"\"0 LEMUR 1\n1 40044U 14033AL  15013.74135905  .00002013  00000-0  31503-3 0  6119\n2 40044 097.9584 269.2923 0059425 258.2447 101.2095 14.72707190 30443\"\"\"\nqth = (37.771034, 122.413815, 7)  # lat (N), long (W), alt (meters)\npredict.observe(tle, qth) # optional time argument defaults to time.time()\n# =\u003e {'altitude': 676.8782276657903,\n#     'azimuth': 96.04762045174824,\n#     'beta_angle': -27.92735429908726,\n#     'decayed': 0,\n#     'doppler': 1259.6041017128405,\n#     'eci_obs_x': -2438.227652191655,\n#     'eci_obs_y': -4420.154476060397,\n#     'eci_obs_z': 3885.390601342013,\n#     'eci_sun_x': 148633398.020844,\n#     'eci_sun_y': -7451536.44122029,\n#     'eci_sun_z': -3229999.50056359,\n#     'eci_vx': 0.20076213530665032,\n#     'eci_vy': -1.3282146055077213,\n#     'eci_vz': 7.377067234096598,\n#     'eci_x': 6045.827328897242,\n#     'eci_y': -3540.5885778261277,\n#     'eci_z': -825.4065096776636,\n#     'eclipse_depth': -87.61858291647795,\n#     'elevation': -43.711904591801726,\n#     'epoch': 1521290038.347793,\n#     'footprint': 5633.548906707907,\n#     'geostationary': 0,\n#     'has_aos': 1,\n#     'latitude': -6.759563817939698,\n#     'longitude': 326.1137007912563,\n#     'name': '0 LEMUR 1',\n#     'norad_id': 40044,\n#     'orbit': 20532,\n#     'orbital_model': 'SGP4',\n#     'orbital_phase': 145.3256815318047,\n#     'orbital_velocity': 26994.138671706416,\n#     'slant_range': 9743.943478523843,\n#     'sunlit': 1,\n#     'visibility': 'D'\n#    }\n```\n\n#### Show upcoming transits of satellite over ground station\n\n```python\n# start and stop transit times as UNIX timestamp\ntransit_start = 1680775200\ntransit_stop = 1681034400\n\np = predict.transits(tle, qth, transit_start, transit_stop)\n\nprint(\"Start of Transit\\tTransit Duration (s)\\tPeak Elevation\")\nfor transit in p:\n    print(f\"{transit.start}\\t{transit.duration()}\\t{transit.peak()['elevation']}\")\n```\n\n\n#### Modeling an entire constellation\n\nGenerating transits for a lot of satellites over a lot of ground stations can be slow.\nLuckily, generating transits for each satellite-groundstation pair can be parallelized for a big speed-up.\n\n```python\nimport itertools\nfrom multiprocessing.pool import Pool\nimport time\n\nimport predict\nimport requests\n\n# Define a function that returns arguments for all the transits() calls you want to make\ndef _transits_call_arguments():\n    now = time.time()\n    tle = requests.get('http://tle.spire.com/25544').text.rstrip()\n    for latitude in range(-90, 91, 15):\n        for longitude in range(-180, 181, 15):\n            qth = (latitude, longitude, 0)\n            yield {'tle': tle, 'qth': qth, 'ending_before': now+60*60*24*7}\n\n# Define a function that calls the transit function on a set of arguments and does per-transit processing\ndef _transits_call_fx(kwargs):\n    try:\n        transits = list(predict.transits(**kwargs))\n        return [t.above(10) for t in transits]\n    except predict.PredictException:\n        pass\n\n# Map the transit() caller across all the arguments you want, then flatten results into a single list\npool = Pool(processes=10)\narray_of_results = pool.map(_transits_call_fx, _transits_call_arguments())\nflattened_results = list(itertools.chain.from_iterable(filter(None, array_of_results)))\ntransits = flattened_results\n```\n\nNOTE: If precise accuracy isn't necessary (for modeling purposes, for example) setting the tolerance argument\n      to the `above` call to a larger value, say 1 degree, can provide a significant performance boost.\n\n#### Call predict analogs directly\n\n```python\npredict.quick_find(tle.split('\\n'), time.time(), (37.7727, 122.407, 25))\npredict.quick_predict(tle.split('\\n'), time.time(), (37.7727, 122.407, 25))\n```\n\n## API\n\u003cpre\u003e\n\u003cb\u003eobserve\u003c/b\u003e(\u003ci\u003etle, qth[, at=None]\u003c/i\u003e)  \n    Return an observation of a satellite relative to a groundstation.\n    \u003ci\u003eqth\u003c/i\u003e groundstation coordinates as (lat(N),long(W),alt(m))\n    If \u003ci\u003eat\u003c/i\u003e is not defined, defaults to current time (time.time())\n    Returns an \"observation\" or dictionary containing:  \n        \u003ci\u003ealtitude\u003c/i\u003e _ altitude of satellite in kilometers\n        \u003ci\u003eazimuth\u003c/i\u003e - azimuth of satellite in degrees from perspective of groundstation.\n        \u003ci\u003ebeta_angle\u003c/i\u003e\n        \u003ci\u003edecayed\u003c/i\u003e - 1 if satellite has decayed out of orbit, 0 otherwise.\n        \u003ci\u003edoppler\u003c/i\u003e - doppler shift between groundstation and satellite.\n        \u003ci\u003eeci_obs_x\u003c/i\u003e\n        \u003ci\u003eeci_obs_y\u003c/i\u003e\n        \u003ci\u003eeci_obs_z\u003c/i\u003e\n        \u003ci\u003eeci_sun_x\u003c/i\u003e\n        \u003ci\u003eeci_sun_y\u003c/i\u003e\n        \u003ci\u003eeci_sun_z\u003c/i\u003e\n        \u003ci\u003eeci_vx\u003c/i\u003e\n        \u003ci\u003eeci_vy\u003c/i\u003e\n        \u003ci\u003eeci_vz\u003c/i\u003e\n        \u003ci\u003eeci_x\u003c/i\u003e\n        \u003ci\u003eeci_y\u003c/i\u003e\n        \u003ci\u003eeci_z\u003c/i\u003e\n        \u003ci\u003eeclipse_depth\u003c/i\u003e\n        \u003ci\u003eelevation\u003c/i\u003e - elevation of satellite in degrees from perspective of groundstation.\n        \u003ci\u003eepoch\u003c/i\u003e - time of observation in seconds (unix epoch)\n        \u003ci\u003efootprint\u003c/i\u003e\n        \u003ci\u003egeostationary\u003c/i\u003e - 1 if satellite is determined to be geostationary, 0 otherwise.\n        \u003ci\u003ehas_aos\u003c/i\u003e - 1 if the satellite will eventually be visible from the groundstation\n        \u003ci\u003elatitude\u003c/i\u003e - north latitude of point on earth directly under satellite.\n        \u003ci\u003elongitude\u003c/i\u003e - west longitude of point on earth directly under satellite.\n        \u003ci\u003ename\u003c/i\u003e - name of satellite from first line of TLE.\n        \u003ci\u003enorad_id\u003c/i\u003e - NORAD id of satellite.\n        \u003ci\u003eorbit\u003c/i\u003e\n        \u003ci\u003eorbital_phase\u003c/i\u003e\n        \u003ci\u003eorbital_model\u003c/i\u003e\n        \u003ci\u003eorbital_velocity\u003c/i\u003e\n        \u003ci\u003eslant_range\u003c/i\u003e - distance to satellite from groundstation in meters.\n        \u003ci\u003esunlit\u003c/i\u003e - 1 if satellite is in sunlight, 0 otherwise.\n        \u003ci\u003evisibility\u003c/i\u003e\n\u003cb\u003etransits\u003c/b\u003e(\u003ci\u003etle, qth[, ending_after=None][, ending_before=None]\u003c/i\u003e)  \n    Returns iterator of \u003cb\u003eTransit\u003c/b\u003e objects representing passes of tle over qth.  \n    If \u003ci\u003eending_after\u003c/i\u003e is not defined, defaults to current time  \n    If \u003ci\u003eending_before\u003c/i\u003e is not defined, the iterator will yield until calculation failure.\n\u003c/pre\u003e\n\u003e\u003cb\u003eNOTE\u003c/b\u003e: We yield passes based on their end time.  This means we'll yield currently active passes in the two-argument invocation form, but their start times will be in the past.\n\n\u003cpre\u003e\n\u003cb\u003eTransit\u003c/b\u003e(\u003ci\u003etle, qth, start, end\u003c/i\u003e)  \n    Utility class representing a pass of a satellite over a groundstation.\n    Instantiation parameters are parsed and made available as fields.\n    \u003cb\u003eduration\u003c/b\u003e()  \n        Returns length of transit in seconds\n    \u003cb\u003epeak\u003c/b\u003e(\u003ci\u003eepsilon=0.1\u003c/i\u003e)  \n        Returns epoch time where transit reaches maximum elevation (within ~\u003ci\u003eepsilon\u003c/i\u003e)\n    \u003cb\u003eat\u003c/b\u003e(\u003ci\u003etimestamp\u003c/i\u003e)  \n        Returns observation during transit via \u003cb\u003equick_find\u003c/b\u003e(\u003ci\u003etle, timestamp, qth\u003c/i\u003e)\n    \u003cb\u003eabove\u003c/b\u003eb(\u003ci\u003eelevation\u003c/i\u003e, \u003ci\u003etolerance\u003c/i\u003e)\n        Returns portion of transit above elevation. If the entire transit is below the target elevation, both\n        endpoints will be set to the peak and the duration will be zero. If a portion of the transit is above\n        the elevation target, the endpoints will be between elevation and elevation + tolerance (unless\n        endpoint is already above elevation, in which case it will be unchanged)\n\u003cb\u003equick_find\u003c/b\u003e(\u003ci\u003etle[, time[, (lat, long, alt)]]\u003c/i\u003e)  \n    \u003ci\u003etime\u003c/i\u003e defaults to current time   \n    \u003ci\u003e(lat, long, alt)\u003c/i\u003e defaults to values in ~/.predict/predict.qth  \n    Returns observation dictionary equivalent to observe(tle, time, (lat, long, alt))\n\u003cb\u003equick_predict\u003c/b\u003e(\u003ci\u003etle[, time[, (lat, long, alt)]]\u003c/i\u003e)  \n        Returns an array of observations for the next pass as calculated by predict.\n        Each observation is identical to that returned by \u003cb\u003equick_find\u003c/b\u003e.\n\u003c/pre\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnsat%2Fpypredict","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnsat%2Fpypredict","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnsat%2Fpypredict/lists"}