├── __init__.py ├── LICENSE ├── README.md └── sattracker.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Andres Vahter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pysattracker 2 | 3 | Python library for calculating azimuth, elevation, doppler shift etc for satellite overflights. 4 | 5 | # dependencies 6 | sudo pip install pyephem 7 | 8 | # install 9 | There is no special library installation available. Just clone it into your project directory. 10 | 11 | git clone https://github.com/cubehub/pysattracker.git 12 | 13 | Or it is better to use it as a submodule if your project is already using git for version control. 14 | 15 | git submodule add https://github.com/cubehub/pysattracker.git 16 | 17 | # example 18 | 19 | ```python 20 | import sys 21 | import time 22 | 23 | from pysattracker import sattracker 24 | 25 | ec1_tle = { 26 | "name": "ESTCUBE 1", 27 | "tle1": "1 39161U 13021C 24194.14473294 .00002706 00000+0 39461-3 0 9991", 28 | "tle2": "2 39161 97.8269 259.8501 0009982 143.1860 217.0037 14.76566041600819", 29 | } 30 | 31 | tallinn = ("59.4000", "24.8170", "0") 32 | 33 | tracker = sattracker.Tracker(satellite=ec1_tle, groundstation=tallinn) 34 | 35 | while 1: 36 | tracker.set_epoch(time.time()) 37 | print ("datetime:", tracker.groundstation.date.datetime()) 38 | print ("az : %0.1f" % tracker.azimuth()) 39 | print ("ele : %0.1f" % tracker.elevation()) 40 | print ("range : %0.0f km" % (tracker.range()/1000)) 41 | print ("range rate : %0.3f km/s" % (tracker.satellite.range_velocity/1000)) 42 | print ("doppler : %0.0f Hz" % (tracker.doppler(100e6))) 43 | 44 | time.sleep(0.5) 45 | ``` 46 | -------------------------------------------------------------------------------- /sattracker.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | https://github.com/cubehub/pysattracker/tree/master 4 | https://rhodesmill.org/pyephem/rise-set.html 5 | ''' 6 | import time 7 | import datetime 8 | 9 | from math import sin, cos, radians, degrees 10 | from pynmeagps import llh2ecef 11 | import ephem 12 | 13 | class Tracker(): 14 | 15 | def __init__(self, satellite, groundstation=("59.4000", "24.8170", "0")): 16 | self.groundstation = ephem.Observer() 17 | self.groundstation.pressure = 0 18 | #self.groundstation.horizon = '-0:34' # 1⁄60 of a degree of an angle 19 | self.groundstation.lat = groundstation[0] 20 | self.groundstation.lon = groundstation[1] 21 | self.groundstation.elevation = int(groundstation[2]) 22 | 23 | self.satellite = ephem.readtle(satellite["name"], satellite["tle1"], satellite["tle2"]) 24 | 25 | def set_epoch(self, epoch=time.time()): 26 | ''' sets epoch when parameters are observed ''' 27 | 28 | self.groundstation.date = datetime.datetime.fromtimestamp(epoch,tz=datetime.UTC) 29 | self.satellite.compute(self.groundstation) 30 | 31 | def azimuth(self): 32 | ''' returns satellite azimuth in degrees ''' 33 | return degrees(self.satellite.az) 34 | 35 | def elevation(self): 36 | ''' returns satellite elevation in degrees ''' 37 | return degrees(self.satellite.alt) 38 | 39 | def latitude(self): 40 | ''' returns satellite latitude in degrees ''' 41 | return degrees(self.satellite.sublat) 42 | 43 | def longitude(self): 44 | ''' returns satellite longitude in degrees ''' 45 | return degrees(self.satellite.sublong) 46 | 47 | def range(self): 48 | ''' returns satellite range in meters ''' 49 | return self.satellite.range 50 | 51 | def doppler(self, frequency_hz=437_505_000): 52 | ''' returns doppler shift in hertz ''' 53 | return -self.satellite.range_velocity / 299792458. * frequency_hz 54 | 55 | def ecef_coordinates(self): 56 | ''' returns satellite earth centered cartesian coordinates 57 | https://en.wikipedia.org/wiki/ECEF 58 | ''' 59 | x, y, z = self._aer2ecef(self.azimuth(), self.elevation(), self.range(), float(self.groundstation.lat), float(self.groundstation.lon), self.groundstation.elevation) 60 | return x, y, z 61 | 62 | def _aer2ecef(self, azimuthDeg, elevationDeg, slantRange, obs_lat, obs_long, obs_alt): 63 | 64 | #site ecef in meters 65 | sitex, sitey, sitez = llh2ecef(obs_lat,obs_long,obs_alt) 66 | 67 | #some needed calculations 68 | slat = sin(radians(obs_lat)) 69 | slon = sin(radians(obs_long)) 70 | clat = cos(radians(obs_lat)) 71 | clon = cos(radians(obs_long)) 72 | 73 | azRad = radians(azimuthDeg) 74 | elRad = radians(elevationDeg) 75 | 76 | # az,el,range to sez convertion 77 | south = -slantRange * cos(elRad) * cos(azRad) 78 | east = slantRange * cos(elRad) * sin(azRad) 79 | zenith = slantRange * sin(elRad) 80 | 81 | x = ( slat * clon * south) + (-slon * east) + (clat * clon * zenith) + sitex 82 | y = ( slat * slon * south) + ( clon * east) + (clat * slon * zenith) + sitey 83 | z = (-clat * south) + ( slat * zenith) + sitez 84 | 85 | return x, y, z 86 | 87 | 88 | 89 | if __name__ == "__main__": 90 | # taken from: http://celestrak.com/NORAD/elements/cubesat.txt 91 | ec1_tle = { 92 | "name": "ESTCUBE 1", 93 | "tle1": "1 39161U 13021C 24194.14473294 .00002706 00000+0 39461-3 0 9991", 94 | "tle2": "2 39161 97.8269 259.8501 0009982 143.1860 217.0037 14.76566041600819", 95 | } 96 | 97 | # http://www.gpscoordinates.eu/show-gps-coordinates.php 98 | tallinn = ("59.4000", "24.8170", "0") 99 | 100 | tracker = Tracker(satellite=ec1_tle, groundstation=tallinn) 101 | 102 | #diff = time.time() - tracker.satellite.epoch.datetime().timestamp() 103 | diff = 0 104 | 105 | while 1: 106 | tracker.set_epoch(time.time()- diff) 107 | print ("datetime:", tracker.groundstation.date.datetime()) 108 | print ("az : %0.1f" % tracker.azimuth()) 109 | print ("ele : %0.1f" % tracker.elevation()) 110 | print ("range : %0.0f km" % (tracker.range()/1000)) 111 | print ("range rate : %0.3f km/s" % (tracker.satellite.range_velocity/1000)) 112 | print ("doppler : %0.0f Hz" % (tracker.doppler(100e6))) 113 | 114 | time.sleep(0.5) 115 | --------------------------------------------------------------------------------