def serialPortRead(passNumber): #passes = satOrb.get_next_passes(datetime.utcnow(),120, -30.8047389406, 72.9167, 180.85) #next get number of passes the user wants satOrb = Orbital(satName, tleFile) passes = satOrb.get_next_passes(current_time,int(passNumber), home.lat, home.lon, home.elevation) for eachPass in passes: rise = eachPass[0] fall = eachPass[1] apex = eachPass[2] # Lon, Lat obsRiseAngle, obsRiseElv = satOrb.get_observer_look(rise, home.lat, home.lon, home.elevation) obsFallAngle, obsFallElv = satOrb.get_observer_look(fall, home.lat, home.lon, home.elevation) obsApexAngle, obsApexElv = satOrb.get_observer_look(apex, home.lat, home.lon, home.elevation) print("observer apex", obsApexElv) print("Rise Time:", rise, "Azimuth:", round(obsRiseAngle, 2), '(', azimuthDirection(obsRiseAngle), ')', "Elevation:", abs(round(obsRiseElv, 2))) print("Apex Time:", apex, "Azimuth:", round(obsApexAngle, 2), '(', azimuthDirection(obsApexAngle), ')', "Elevation:", abs(round(obsApexElv, 2))) print("Fall Time:", fall, "Azimuth:", round(obsFallAngle, 2), '(', azimuthDirection(obsFallAngle), ')', "Elevation:", abs(round(obsFallElv, 2))) print() return
def get_angles(self): self.get_times() tle1, tle2 = self.get_tle_lines() orb = Orbital(self.spacecrafts_orbital[self.spacecraft_id], line1=tle1, line2=tle2) sat_azi, sat_elev = orb.get_observer_look(self.times[:, np.newaxis], self.lons, self.lats, 0) sat_zenith = 90 - sat_elev sun_zenith = astronomy.sun_zenith_angle(self.times[:, np.newaxis], self.lons, self.lats) alt, sun_azi = astronomy.get_alt_az(self.times[:, np.newaxis], self.lons, self.lats) del alt sun_azi = np.rad2deg(sun_azi) sun_azi = np.where(sun_azi < 0, sun_azi + 180, sun_azi - 180) rel_azi = abs(sat_azi - sun_azi) rel_azi = np.where(rel_azi > 180.0, 360.0 - rel_azi, rel_azi) return sat_azi, sat_zenith, sun_azi, sun_zenith, rel_azi
class Satellite: """ Retrieve orbital parameters of the selected LEO object """ def __init__(self, name): urllib.request.urlretrieve( 'http://celestrak.com/NORAD/elements/stations.txt', 'stations.txt') self.orb = Orbital(name, 'stations.txt') self.data = [] def azimuth(self): """ Return object azimuth """ self.__update_data() return self.data[0] def elevation(self): """ Return object elevation """ self.__update_data() return self.data[1] def __update_data(self): self.data = self.orb.get_observer_look(datetime.utcnow(), 25.279652, 54.687157, 1) def is_up(self): """ Return object elevation """ self.__update_data() return True if self.data[1] > 10 else False
def get_satellite_coordinates(lon, lat, alt, satellite_id): try: satellite = Satellite.objects.get(id=satellite_id) orb = Orbital(satellite.codename) now = datetime.utcnow() return orb.get_observer_look(now, lon, lat, alt) except Satellite.DoesNotExist: return None
def _get_sat_angles_with_tle(self): tle1, tle2 = self.get_tle_lines() orb = Orbital(self.spacecrafts_orbital[self.spacecraft_id], line1=tle1, line2=tle2) sat_azi, sat_elev = orb.get_observer_look(self.times[:, np.newaxis], self.lons, self.lats, 0) return sat_azi, sat_elev
def serial_az_el(name, stop_time=( datetime.utcnow())): # sends data over serial to arduino format (az, el) satellite = Orbital(name, tle_file=nextPass.path_to_tle) now = datetime.utcnow() while int( (stop_time - now).total_seconds()) >= 0: # while pass is occurring now = datetime.utcnow() print( satellite.get_observer_look(now, nextPass.lon, nextPass.lat, nextPass.alt)) # print (az, el) sleep(2) # wait 2 seconds
def ground_truth(lat, lon, time, sat, duration): orb = Orbital(sat, tle_file="active.txt") delta = timedelta(seconds=1) azs = [] els = [] for i in range(duration): az, el = pwm_for(*orb.get_observer_look(time, lon, lat, 0)) azs.append(az) els.append(el) time += delta return azs, els
def get_angles(self): """Get azimuth and zenith angles. Azimuth angle definition is the same as in pyorbital, but with different units (degrees not radians for sun azimuth angles) and different ranges. Returns: sat_azi: satellite azimuth angle degree clockwise from north in range ]-180, 180], sat_zentih: satellite zenith angles in degrees in range [0,90], sun_azi: sun azimuth angle degree clockwise from north in range ]-180, 180], sun_zentih: sun zenith angles in degrees in range [0,90], rel_azi: absolute azimuth angle difference in degrees between sun and sensor in range [0, 180] """ self.get_times() self.get_lonlat() tle1, tle2 = self.get_tle_lines() times = self.times orb = Orbital(self.spacecrafts_orbital[self.spacecraft_id], line1=tle1, line2=tle2) sat_azi, sat_elev = orb.get_observer_look(times[:, np.newaxis], self.lons, self.lats, 0) sat_zenith = 90 - sat_elev sun_zenith = astronomy.sun_zenith_angle(times[:, np.newaxis], self.lons, self.lats) alt, sun_azi = astronomy.get_alt_az(times[:, np.newaxis], self.lons, self.lats) del alt sun_azi = np.rad2deg(sun_azi) rel_azi = get_absolute_azimuth_angle_diff(sun_azi, sat_azi) # Scale angles range to half open interval ]-180, 180] sat_azi = centered_modulus(sat_azi, 360.0) sun_azi = centered_modulus(sun_azi, 360.0) # Mask corrupt scanlines for arr in (sat_azi, sat_zenith, sun_azi, sun_zenith, rel_azi): arr[self.mask] = np.nan return sat_azi, sat_zenith, sun_azi, sun_zenith, rel_azi
def get_parallaxed_coor(sat, tle, t, lat, lon, alt, h): """ """ # Setup orbital class with TLE file orbit = Orbital(sat, line1=tle.line1, line2=tle.line2) pos = orbit.get_position(t) # Calculate observer azimuth and elevation azi, ele = orbit.get_observer_look(t, lon, lat, alt) # Apply parallax correction for given heights x, y, z = parallax_corr(h, azi, ele) # WGS84 parameters f = 1 / 298.257223563 a = 6378137. # Convert coordinates and azimuth to radians radlat = np.deg2rad(lat) radlon = np.deg2rad(lon) radazi = np.deg2rad(azi) # Calculate shifted point coordinates nlat, nlon, nazi = vinc_pt(f, a, radlat, radlon, radazi, z) # Reconvert to degree nlat = np.rad2deg(nlat) nlon = np.rad2deg(nlon) nazi = np.rad2deg(nazi) info = "\n--------------------------------------------------------" + \ "\n ----- Parallax correction summary -----" +\ "\n--------------------------------------------------------" + \ "\n Satellite Name: " + sat + \ "\n Observation Time: " + \ datetime.datetime.strftime(t, "%Y-%m-%d %H:%M:%S") + \ "\n Satellite Position: " + str(pos[0]) + \ "\n Satellite Velocity: " + str(pos[1]) + \ "\n-----------------------------------" + \ "\n Latitude: " + str(lat) + \ "\n Longitude: " + str(lon) + \ "\n Altitude: " + \ str(alt) + "\n Height: " + str(h) + \ "\n-----------------------------------" +\ "\n Azimuth: " + str(azi) + \ "\n Elevation: " + str(ele) + \ "\n Parallax Distance: " + str(z) + \ "\n New Latitude: " + str(nlat) + \ "\n New Longitude: " + str(nlon) + \ "\n--------------------------------------------------------" logger.debug(info) return(z, nlat, nlon)
def get_view_zen_angles(sat_name, tle_filename, area_def_name, time_slot): """Calculate the satellite zenith angles for the given satellite (*sat_name*, *tle_filename*), *area_def_name* and *time slot*. Stores the result in the given *cache* parameter. """ try: from pyorbital.orbital import Orbital except ImportError: LOGGER.warning("Could not load pyorbital modules") return area_def = get_area_def(area_def_name) lons, lats = area_def.get_lonlats() orbital_obj = Orbital(sat_name, tle_filename) elevation = orbital_obj.get_observer_look(time_slot, lons, lats, 0)[1] view_zen_data = np.subtract(90, np.ma.masked_outside(elevation, 0, 90)) return view_zen_data
def getSentinel2Geometry(startDateUTC, lengthDays, lat, lon, alt=0.0, mission="Sentinel-2a", tleFile="../TLE/norad_resource_tle.txt"): """Calculate approximate geometry for Sentinel overpasses. Approximate because it assumes maximum satellite elevation is the time at which target is imaged. :param startDateUTC: a datetime object specifying when to start prediction. :type startDateUTC: object :param lengthDays: number of days over which to perform calculations. :type lengthDays: int :param lat: latitude of target. :type lat: float :param lon: longitude of target. :type lon: float :param alt: altitude of target (in km). :type alt: float :param mission: mission name as in TLE file. :type mission: str :param tleFile: TLE file. :type tleFile: str :return: a python list containing instances of the sensorGeometry class arranged in date order. :rtype: list """ orb = Orbital(mission, tleFile) passes = orb.get_next_passes(startDateUTC, 24 * lengthDays, lon, lat, alt) geomList = [] for p in passes: look = orb.get_observer_look(p[2], lon, lat, alt) vza = 90 - look[1] vaa = look[0] sza = ast.sun_zenith_angle(p[2], lon, lat) saa = np.rad2deg(ast.get_alt_az(p[2], lon, lat)[1]) if sza < 90 and vza < 10.3: thisGeom = sensorGeometry() thisGeom.date_utc = p[2] thisGeom.vza = vza thisGeom.vaa = vaa thisGeom.sza = sza thisGeom.saa = saa geomList.append(thisGeom) return geomList
def get_view_zen_angles(sat_name, tle_filename, area_def_name, time_slot): """Calculate the satellite zenith angles for the given satellite (*sat_name*, *tle_filename*), *area_def_name* and *time slot*. Stores the result in the given *cache* parameter. """ try: from pyorbital.orbital import Orbital except ImportError: LOGGER.warning("Could not load pyorbital modules") return area_def = get_area_def(area_def_name) lons, lats = area_def.get_lonlats() orbital_obj = Orbital(sat_name, tle_filename) elevation = orbital_obj.get_observer_look(time_slot, lons, lats, 0)[1] view_zen_data = np.subtract(90, np.ma.masked_outside(elevation, 0, 90)) return view_zen_data
def plot_iss(ax, obs_time, obs_loc, tle=None): """ Put International Space Station on the plot. The function uses TLE list if it was given or tries to retrieve it via PyOrbital package """ if tle is None: from pyorbital.orbital import Orbital print("request ISS' two-line elements via PyOrbital...") orb = Orbital("ISS (ZARYA)") iss = orb.get_observer_look(obs_time.value, obs_loc.lon.value, obs_loc.lat.value, obs_loc.height.value) iss_coord = SkyCoord(iss[0], iss[1], unit='deg', frame='altaz', obstime=obs_time, location=obs_loc) else: import ephem iss = ephem.readtle('ISS', tle[0], tle[1]) location = ephem.Observer() location.lat = str(obs_loc.lat.value) location.lon = str(obs_loc.lon.value) location.date = obs_time.value iss.compute(location) iss_coord = SkyCoord(iss.az, iss.alt, unit='rad', frame='altaz', obstime=obs_time, location=obs_loc) iss_coord = iss_coord.transform_to('icrs') ax.plot([iss_coord.ra.radian], [-iss_coord.dec.value + 45], label='ISS', linestyle='', color='green', marker='$⋈$', markersize=markers['iss']) print("ISS is plotted")
class PyOrbitalLayer(OrbitalLayer): """ pyorbital based orbital layer """ __implements__ = (OrbitalLayer,) def __init__(self, aoi, sat, instrument="AVHRR"): OrbitalLayer.__init__(self,aoi,sat,instrument) # instantiate orbital module config_file_path = "" try: config_file_path = os.environ['PYGRANULE_CONFIG_PATH'] except KeyError: print "pygranule config file path missing. Has the 'PYGRANULE_CONFIG_PATH' environment variable been set?" default_tle_file = config_file_path+"/default.tle" try: self.orbital = Orbital(sat,default_tle_file) except: print "Failed to open default tle file:", default_tle_file print "Downloading from internet:" try: self.orbital = Orbital(sat) except: raise OrbitalLayerError("Pyorbital Failed to fetch TLE from internet.") # create scan geometry - one scan line. if instrument == "AVHRR": scan_steps = np.arange(0,self.instrument_info['scan_steps'],self.instrument_info['scan_steps']/8-1) scan_steps[-1] = self.instrument_info['scan_steps']-1 self.scan_geom = avhrr(1,scan_steps) elif instrument == "VIIRS": self.scan_geom = viirs(1) def set_tle(self, line1, line2): # for now restart pyorbital with these new elements. del self.orbital self.orbital = Orbital(self.sat,line1=line1, line2=line2) def orbital_period(self): return 24*60/self.orbital.tle.mean_motion def scan_line_lonlats(self, t): """ Returns a single instrument scan line starting at datetime t """ s_times = self.scan_geom.times(t) pixels_pos = compute_pixels((self.orbital.tle.line1, self.orbital.tle.line2), self.scan_geom, s_times) pos_time = get_lonlatalt(pixels_pos, s_times) return np.array((pos_time[0],pos_time[1])) def next_transit(self, start=datetime.now(), resolution=100): """ Next transit time relative to center of aoi. Resolution accuracy defined by subdivision of orbital period. """ # accuracy in mintues from mean orbital period dt = self.orbital_period()/resolution # observer position alt = 0.0 lon, lat = self.aoi_center() # NOTE: For now I do not use the pyorbital # get_next_passes. Because it accepts integer (not float) hours # for the search period, and the accuracy of the transit time # search is not obvious to me at the moment. # Also I would like to allow negative transit time altitudes # - transits below horizon. # Therefore I re-implement the search myself, here to have more control: t_offsets = np.arange(0.0,self.orbital_period()*1.2,dt) e = np.array( [ self.orbital.get_observer_look(start + timedelta(minutes=t_offset), lon, lat, alt)[1] \ for t_offset in t_offsets ] ) # search for local maxima b = (np.roll(e,1)<e)&(np.roll(e,-1)<e) idx = np.where(b[1:]==True)[0][0]+1 #i.e. do not accept index 0 as maximum... ## return a quadratic maximum for good accuracy based on data. ## Quadratic fit to 3 points around maximum elevation ## seems to improve accuracy by 100-fold. x,y = t_offsets[idx-1:idx+2],e[idx-1:idx+2] fit = np.polyfit(x,y,2) t = -fit[1]/(2.0*fit[0]) #from matplotlib import pyplot as plt #plt.plot(x,y,'x') #fitx = np.arange(x[0]-1,x[2]+1,0.1) #fity = fit[2]+fit[1]*fitx+fit[0]*(fitx**2) #plt.plot(fitx,fity,'r-') #plt.show() return start + timedelta(minutes=t)
def SatStats(): #tle = r"C:\Users\Jake\Python\TLE Files\stations-tle.txt" # Download TLE file #downloadUrl = "http://www.celestrak.com/NORAD/elements/stations.txt" #urllib.request.urlretrieve(downloadUrl, tle) tle = GetTLEFile() stationList = GetStationList(tle) try: while(True): stationNumber = input("Enter Station Number: ") print() if stationNumber == 'n': for name in enumerate(stationList): print(name[0], ':', name[1].replace('\n', '')) print() else: break stationNumber = int(stationNumber) if stationNumber < len(stationList): # Get Platform Object station = stationList[stationNumber] stationObject = tlefile.read(station, tle) GoogleSearch(station) # Print Platform Info PrintTLEInfo(stationObject, station) lon, lat, alt, now = GetGPSPosition(station, tle, datetime.utcnow()) dmLon, dmLat = DegreeMinutes(lon, lat) print("Current GPS location:\n", "Latitude: ", lat, " Longitude: ", lon, " Altitude (km): ", alt, sep='') print("Current GPS location:\n", "Latitude: ", dmLat, " Longitude: ", dmLon, " Altitude (km): ", alt, sep='') satOrb = Orbital(station, tle) print() PrintFreqData(GetFreqData(station, "C:/Users/Jake/Python/Satellite Tracker/frequencies/satfreqlist.csv")) passes = satOrb.get_next_passes(datetime.utcnow(), 120, -90.5546910, 38.6475290, 180.85) print("Next passes in 5 days (Max Elevation Above 20 deg):") for eachPass in passes: rise = eachPass[0] fall = eachPass[1] apex = eachPass[2] # Lon, Lat obsRiseAngle, obsRiseElv = satOrb.get_observer_look(rise, -90.5546910, 38.6475290, 180.85) obsFallAngle, obsFallElv = satOrb.get_observer_look(fall, -90.5546910, 38.6475290, 180.85) obsApexAngle, obsApexElv = satOrb.get_observer_look(apex, -90.5546910, 38.6475290, 180.85) if obsApexElv >= 20.0: print("Rise Time:", rise, "Azimuth:", round(obsRiseAngle, 2), '(', AzimuthDirection(obsRiseAngle), ')', "Elevation:", abs(round(obsRiseElv, 2))) print("Apex Time:", apex, "Azimuth:", round(obsApexAngle, 2), '(', AzimuthDirection(obsApexAngle), ')', "Elevation:", abs(round(obsApexElv, 2))) print("Fall Time:", fall, "Azimuth:", round(obsFallAngle, 2), '(', AzimuthDirection(obsFallAngle), ')', "Elevation:", abs(round(obsFallElv, 2))) print() except: pass
class PyOrbitalLayer(OrbitalLayer): """ pyorbital based orbital layer """ __implements__ = (OrbitalLayer, ) def __init__(self, aoi, sat, instrument="AVHRR"): OrbitalLayer.__init__(self, aoi, sat, instrument) # instantiate orbital module config_file_path = "" try: config_file_path = os.environ['PYGRANULE_CONFIG_PATH'] except KeyError: print "pygranule config file path missing. Has the 'PYGRANULE_CONFIG_PATH' environment variable been set?" default_tle_file = config_file_path + "/default.tle" try: self.orbital = Orbital(sat, default_tle_file) except: print "Failed to open default tle file:", default_tle_file print "Downloading from internet:" try: self.orbital = Orbital(sat) except: raise OrbitalLayerError( "Pyorbital Failed to fetch TLE from internet.") # create scan geometry - one scan line. if instrument == "AVHRR": scan_steps = np.arange(0, self.instrument_info['scan_steps'], self.instrument_info['scan_steps'] / 8 - 1) scan_steps[-1] = self.instrument_info['scan_steps'] - 1 self.scan_geom = avhrr(1, scan_steps) elif instrument == "VIIRS": self.scan_geom = viirs(1) def set_tle(self, line1, line2): # for now restart pyorbital with these new elements. del self.orbital self.orbital = Orbital(self.sat, line1=line1, line2=line2) def orbital_period(self): return 24 * 60 / self.orbital.tle.mean_motion def scan_line_lonlats(self, t): """ Returns a single instrument scan line starting at datetime t """ s_times = self.scan_geom.times(t) pixels_pos = compute_pixels( (self.orbital.tle.line1, self.orbital.tle.line2), self.scan_geom, s_times) pos_time = get_lonlatalt(pixels_pos, s_times) return np.array((pos_time[0], pos_time[1])) def next_transit(self, start=datetime.now(), resolution=100): """ Next transit time relative to center of aoi. Resolution accuracy defined by subdivision of orbital period. """ # accuracy in mintues from mean orbital period dt = self.orbital_period() / resolution # observer position alt = 0.0 lon, lat = self.aoi_center() # NOTE: For now I do not use the pyorbital # get_next_passes. Because it accepts integer (not float) hours # for the search period, and the accuracy of the transit time # search is not obvious to me at the moment. # Also I would like to allow negative transit time altitudes # - transits below horizon. # Therefore I re-implement the search myself, here to have more control: t_offsets = np.arange(0.0, self.orbital_period() * 1.2, dt) e = np.array( [ self.orbital.get_observer_look(start + timedelta(minutes=t_offset), lon, lat, alt)[1] \ for t_offset in t_offsets ] ) # search for local maxima b = (np.roll(e, 1) < e) & (np.roll(e, -1) < e) idx = np.where( b[1:] == True)[0][0] + 1 #i.e. do not accept index 0 as maximum... ## return a quadratic maximum for good accuracy based on data. ## Quadratic fit to 3 points around maximum elevation ## seems to improve accuracy by 100-fold. x, y = t_offsets[idx - 1:idx + 2], e[idx - 1:idx + 2] fit = np.polyfit(x, y, 2) t = -fit[1] / (2.0 * fit[0]) #from matplotlib import pyplot as plt #plt.plot(x,y,'x') #fitx = np.arange(x[0]-1,x[2]+1,0.1) #fity = fit[2]+fit[1]*fitx+fit[0]*(fitx**2) #plt.plot(fitx,fity,'r-') #plt.show() return start + timedelta(minutes=t)
def trackHotspotSatellite(arglist=None): parser = ArgumentRecorder( description= 'Track satellite position, bearing, previous and next passdatetimes from hotspot data.', fromfile_prefix_chars='@') parser.add_argument('-v', '--verbosity', type=int, default=1, private=True) parser.add_argument('-u', '--user', type=str, required=True, help="PostgreSQL username") parser.add_argument('-d', '--database', type=str, required=True, help="PostgreSQL database") parser.add_argument('-l', '--limit', type=int, help='Limit number of rows to process') parser.add_argument('-t', '--tlefile', type=str, required=True, help='File containing TLE data') parser.add_argument('-w', '--where', type=str, required=True, help="'Where' clause to select hotspots") parser.add_argument('-H', '--hotspots', type=str, default='hotspots', help="Hotspot table name") parser.add_argument( '-s', '--suffix', type=str, required=True, help="Suffix to append to 'hotspots' to get output table name") parser.add_argument('-D', '--drop-table', action='store_true', help='Drop output table if it exists') parser.add_argument('--logfile', type=str, help="Logfile, default is 'hotspots'_'suffix'.log", private=True) parser.add_argument('--no-logfile', action='store_true', help='Do not output descriptive comments') args = parser.parse_args(arglist) if not args.no_logfile: if not args.logfile: args.logfile = args.hotspots + '_' + args.suffix + '.log' if os.path.exists(args.logfile): shutil.move(args.logfile, args.logfile.split('/')[-1].rsplit('.', 1)[0] + '.bak') logfile = open(args.logfile, 'w') parser.write_comments(args, logfile, incomments=ArgumentHelper.separator()) logfile.close() satcodes = {'N': 37849, 'Terra': 25994, 'Aqua': 27424} tledict = {} for norad_id in satcodes.values(): tledict[norad_id] = () tlefile = open(args.tlefile, 'r') line1 = tlefile.readline().rstrip() while len(line1) > 1: norad_id = int(line1[2:7]) year2d = int(line1[18:20]) daynum = float(line1[20:32]) tledate = datetime(2000 + year2d if year2d <= 56 else 1900 + year2d, 1, 1) + timedelta(days=daynum) line2 = tlefile.readline().rstrip() tledict[norad_id] += ((tledate, line1, line2), ) line1 = tlefile.readline().rstrip() psqlin = subprocess.Popen([ 'psql', args.database, args.user, '--quiet', '--command', r'\timing off', '--command', r'\copy (SELECT * FROM ' + args.hotspots + ' WHERE ' + args.where + ' ORDER BY acq_date + acq_time) TO STDOUT CSV HEADER' ], stdout=subprocess.PIPE, encoding='UTF-8') satcsv = csv.DictReader(psqlin.stdout) psqlout = subprocess.Popen([ 'psql', args.database, args.user, '--quiet', '--command', r'\timing off' ] + ([ '--command', 'DROP TABLE IF EXISTS ' + args.hotspots + '_' + args.suffix ] if args.drop_table else []) + [ '--command', 'CREATE TABLE ' + args.hotspots + '_' + args.suffix + ' AS TABLE ' + args.hotspots + ' WITH NO DATA', '--command', 'ALTER TABLE ' + args.hotspots + '_' + args.suffix + ' \ ADD COLUMN pass_azimuth NUMERIC(8,5), \ ADD COLUMN pass_elevation NUMERIC(8,5), \ ADD COLUMN pass_bearing NUMERIC(8,5), \ ADD COLUMN pass_datetime TIMESTAMP WITHOUT TIME ZONE, \ ADD COLUMN prev_azimuth NUMERIC(8,5), \ ADD COLUMN prev_elevation NUMERIC(8,5), \ ADD COLUMN prev_datetime TIMESTAMP WITHOUT TIME ZONE, \ ADD COLUMN next_azimuth NUMERIC(8,5), \ ADD COLUMN next_elevation NUMERIC(8,5), \ ADD COLUMN next_datetime TIMESTAMP WITHOUT TIME ZONE, \ DROP COLUMN IF EXISTS geometry, \ ADD COLUMN geometry geometry GENERATED ALWAYS AS (ST_Rotate(ST_MakeEnvelope((ST_X(ST_Transform(ST_SetSRID(ST_MakePoint((longitude)::DOUBLE PRECISION, (latitude)::DOUBLE PRECISION), 4326), 28350)) - ((scan * (500)::NUMERIC))::DOUBLE PRECISION), (ST_Y(ST_Transform(ST_SetSRID(ST_MakePoint((longitude)::DOUBLE PRECISION, (latitude)::DOUBLE PRECISION), 4326), 28350)) - ((track * (500)::NUMERIC))::DOUBLE PRECISION), (ST_X(ST_Transform(ST_SetSRID(ST_MakePoint((longitude)::DOUBLE PRECISION, (latitude)::DOUBLE PRECISION), 4326), 28350)) + ((scan * (500)::NUMERIC))::DOUBLE PRECISION), (ST_Y(ST_Transform(ST_SetSRID(ST_MakePoint((longitude)::DOUBLE PRECISION, (latitude)::DOUBLE PRECISION), 4326), 28350)) + ((track * (500)::NUMERIC))::DOUBLE PRECISION), 28350), ((((- pass_bearing) * 3.1415926) / (180)::NUMERIC))::DOUBLE PRECISION, ST_Transform(ST_SetSRID(ST_MakePoint((longitude)::DOUBLE PRECISION, (latitude)::DOUBLE PRECISION), 4326), 28350))) STORED', '--command', r'\copy ' + args.hotspots + '_' + args.suffix + ' FROM STDIN CSV HEADER', '--command', 'ALTER TABLE ' + args.hotspots + '_' + args.suffix + ' \ ADD COLUMN id SERIAL' ], stdin=subprocess.PIPE, encoding='UTF-8') satout = csv.DictWriter( psqlout.stdin, fieldnames=satcsv.fieldnames + [ 'pass_azimuth', 'pass_elevation', 'pass_bearing', 'pass_datetime', 'prev_azimuth', 'prev_elevation', 'prev_datetime', 'next_azimuth', 'next_elevation', 'next_datetime' ]) minelevation = 90 tleindexes = {} lastdatetime = datetime(MINYEAR, 1, 1) lastline1 = None lastline2 = None inrowcount = 0 for satline in satcsv: thisdatetime = dateparser.parse(satline['acq_date'] + ' ' + satline['acq_time']) satcode = satcodes[satline['satellite']] tletuples = tledict[satcode] tleidx = tleindexes.get(satcode, 0) assert (thisdatetime >= lastdatetime) lastdatetime = thisdatetime while tletuples[tleidx + 1][0] <= thisdatetime - timedelta(hours=12): tleidx += 1 tleindexes[satcode] = tleidx tletuple = tletuples[tleidx] line1 = tletuple[1] line2 = tletuple[2] if line1 != lastline1 or line2 != lastline2: orb = Orbital("", line1=line1, line2=line2) lastline1 = line1 lastline2 = line2 passdatetimes = [ next_pass[2] for next_pass in orb.get_next_passes(thisdatetime - timedelta(hours=24), 48, float(satline['longitude']), float(satline['latitude']), 0, horizon=0) ] nearpasses = [] leastoffset = 999999 leastoffsetidx = None for passidx in range(len(passdatetimes)): max_elevation_time = passdatetimes[passidx] (azimuth, elevation) = orb.get_observer_look(max_elevation_time, float(satline['longitude']), float(satline['latitude']), 0) thisoffset = (max_elevation_time - thisdatetime).total_seconds() if abs(thisoffset) < abs(leastoffset): leastoffsetidx = len(nearpasses) leastoffset = thisoffset nearpasses += [(max_elevation_time, azimuth, elevation)] if abs(leastoffset) > 600: print("WARNING: offset=", leastoffset, "prev offset=", (nearpasses[leastoffsetidx - 1][0] - thisdatetime).total_seconds() if leastoffsetidx > 0 else "", "next offset=", (nearpasses[leastoffsetidx + 1][0] - thisdatetime).total_seconds() if leastoffsetidx < len(nearpasses) - 1 else "", " satellite ", satline['satellite']) #print(" ", satline) if len(nearpasses): nearestpass = nearpasses[leastoffsetidx] satline['pass_datetime'] = nearestpass[0] satline['pass_azimuth'] = nearestpass[1] satline['pass_elevation'] = nearestpass[2] (lon1, lat1, alt1) = orb.get_lonlatalt(max_elevation_time - timedelta(seconds=30)) (lon2, lat2, alt2) = orb.get_lonlatalt(max_elevation_time + timedelta(seconds=30)) point1 = (lon1, lat1) point2 = (lon2, lat2) bearing = sphere.bearing(point1, point2) satline['pass_bearing'] = bearing if leastoffsetidx > 0: prevpass = nearpasses[leastoffsetidx - 1] satline['prev_datetime'] = prevpass[0] satline['prev_azimuth'] = prevpass[1] satline['prev_elevation'] = prevpass[2] if leastoffsetidx < len(nearpasses) - 1: nextpass = nearpasses[leastoffsetidx + 1] satline['next_datetime'] = nextpass[0] satline['next_azimuth'] = nextpass[1] satline['next_elevation'] = nextpass[2] satout.writerow(satline) inrowcount += 1 if args.limit and inrowcount == args.limit: break
def SatStats(): #tle = r"C:\Users\Jake\Python\TLE Files\stations-tle.txt" # Download TLE file #downloadUrl = "http://www.celestrak.com/NORAD/elements/stations.txt" #urllib.request.urlretrieve(downloadUrl, tle) tle = GetTLEFile() stationList = GetStationList(tle) try: while (True): stationNumber = input("Enter Station Number: ") print() if stationNumber == 'n': for name in enumerate(stationList): print(name[0], ':', name[1].replace('\n', '')) print() else: break stationNumber = int(stationNumber) if stationNumber < len(stationList): # Get Platform Object station = stationList[stationNumber] stationObject = tlefile.read(station, tle) GoogleSearch(station) # Print Platform Info PrintTLEInfo(stationObject, station) lon, lat, alt, now = GetGPSPosition(station, tle, datetime.utcnow()) dmLon, dmLat = DegreeMinutes(lon, lat) print("Current GPS location:\n", "Latitude: ", lat, " Longitude: ", lon, " Altitude (km): ", alt, sep='') print("Current GPS location:\n", "Latitude: ", dmLat, " Longitude: ", dmLon, " Altitude (km): ", alt, sep='') satOrb = Orbital(station, tle) print() PrintFreqData( GetFreqData( station, "C:/Users/Jake/Python/Satellite Tracker/frequencies/satfreqlist.csv" )) passes = satOrb.get_next_passes(datetime.utcnow(), 120, -90.5546910, 38.6475290, 180.85) print("Next passes in 5 days (Max Elevation Above 20 deg):") for eachPass in passes: rise = eachPass[0] fall = eachPass[1] apex = eachPass[2] # Lon, Lat obsRiseAngle, obsRiseElv = satOrb.get_observer_look( rise, -90.5546910, 38.6475290, 180.85) obsFallAngle, obsFallElv = satOrb.get_observer_look( fall, -90.5546910, 38.6475290, 180.85) obsApexAngle, obsApexElv = satOrb.get_observer_look( apex, -90.5546910, 38.6475290, 180.85) if obsApexElv >= 20.0: print("Rise Time:", rise, "Azimuth:", round(obsRiseAngle, 2), '(', AzimuthDirection(obsRiseAngle), ')', "Elevation:", abs(round(obsRiseElv, 2))) print("Apex Time:", apex, "Azimuth:", round(obsApexAngle, 2), '(', AzimuthDirection(obsApexAngle), ')', "Elevation:", abs(round(obsApexElv, 2))) print("Fall Time:", fall, "Azimuth:", round(obsFallAngle, 2), '(', AzimuthDirection(obsFallAngle), ')', "Elevation:", abs(round(obsFallElv, 2))) print() except: pass
class FileStreamer(FileSystemEventHandler): """Get the updates from files. TODO: separate holder from file handling. """ def __init__(self, holder, configfile, *args, **kwargs): FileSystemEventHandler.__init__(self, *args, **kwargs) self._file = None self._filename = "" self._where = 0 self._satellite = "" self._orbital = None cfg = ConfigParser() cfg.read(configfile) self._coords = cfg.get("local_reception", "coordinates").split(" ") self._coords = [float(self._coords[0]), float(self._coords[1]), float(self._coords[2])] logger.debug(self._coords) try: self._tle_files = cfg.get("local_reception", "tle_files") except NoOptionError: self._tle_files = None self._file_pattern = cfg.get("local_reception", "file_pattern") self.scanlines = holder def on_created(self, event): if event.src_path != self._filename: logger.debug("Closing: " + self._filename) if self._file: self._file.close() self._file = None self._filename = "" self._where = 0 self._satellite = "" logger.debug("Creating: " + event.src_path) def on_opened(self, event): fname = os.path.split(event.src_path)[1] if self._file is None and fnmatch(fname, self._file_pattern): logger.debug("Opening: " + event.src_path) self._filename = event.src_path self._file = open(event.src_path, "rb") self._where = 0 self._satellite = " ".join(event.src_path.split("_")[1:3])[:-5] if self._tle_files is not None: filelist = glob(self._tle_files) tle_file = max(filelist, key=lambda x: os.stat(x).st_mtime) else: tle_file = None self._orbital = Orbital(self._satellite, tle_file) def on_modified(self, event): self.on_opened(event) if event.src_path != self._filename: return self._file.seek(self._where) line = self._file.read(LINE_SIZE) while len(line) == LINE_SIZE: line_start = self._where self._where = self._file.tell() dtype = np.dtype([('frame_sync', '>u2', (6, )), ('id', [('id', '>u2'), ('spare', '>u2')]), ('timecode', '>u2', (4, )), ('telemetry', [("ramp_calibration", '>u2', (5, )), ("PRT", '>u2', (3, )), ("ch3_patch_temp", '>u2'), ("spare", '>u2'),]), ('back_scan', '>u2', (10, 3)), ('space_data', '>u2', (10, 5)), ('sync', '>u2'), ('TIP_data', '>u2', (520, )), ('spare', '>u2', (127, )), ('image_data', '>u2', (2048, 5)), ('aux_sync', '>u2', (100, ))]) array = np.fromstring(line, dtype=dtype) if np.all(abs(HRPT_SYNC_START - array["frame_sync"]) > 1): array = array.newbyteorder() # FIXME: this is bad!!!! Should not get the year from the filename year = int(os.path.split(event.src_path)[1][:4]) utctime = datetime(year, 1, 1) + timecode(array["timecode"][0]) # Check that we receive real-time data if not (np.all(array['aux_sync'] == HRPT_SYNC) and np.all(array['frame_sync'] == HRPT_SYNC_START)): logger.info("Garbage line: " + str(utctime)) line = self._file.read(LINE_SIZE) continue elevation = self._orbital.get_observer_look(utctime, *self._coords)[1] logger.info("Got line " + utctime.isoformat() + " " + self._satellite + " " + str(elevation)) # TODO: # - serve also already present files # - timeout and close the file self.scanlines.add_scanline(self._satellite, utctime, elevation, line_start, self._filename, line) line = self._file.read(LINE_SIZE) self._file.seek(self._where)
def get_site_passes( satno, site, duration, filename ): # Duration in hours from current time, # filename = file with TLEs in it now = datetime.utcnow() # set time to current time sat = Orbital(satellite='None', line1=getTLE(satno, filename)[0], line2=getTLE(satno, filename)[1]) x = Orbital.get_next_passes( sat, now, duration, site[1], site[0], site[2], tol=0.001, horizon=site[3]) # builds list of all passes that break 3 degrees passes = [] # begins empty list for passes for i in x: en = Orbital.get_observer_look(sat, i[0], site[1], site[0], site[2]) # Gets entry Az & El ex = Orbital.get_observer_look(sat, i[1], site[1], site[0], site[2]) # Gets exitAz & El hi = Orbital.get_observer_look(sat, i[2], site[1], site[0], site[2]) # Gets exitAz & El p1 = Orbital.get_observer_look(sat, i[0], site[1], site[0], site[2])[0] # az at 3 degree entry p2 = Orbital.get_observer_look(sat, i[0] + timedelta(seconds=1), site[1], site[0], site[2])[0] # p1 1 seconds later p3 = Orbital.get_observer_look(sat, i[1], site[1], site[0], site[2])[0] # az at 3 degree exit print(site[8], "Pass Start:", i[0], "Enter Az", en[0], "Pass Term:", i[1], "Exit Az:", ex[0], "MaxEl:", hi[1]) # prints passes (used for dev) if site[4] < en[0] < site[5] or site[6] < en[0] < site[ 7]: # if satellite enters FOV on face *checkpass* len = int( (i[1] - i[0]).total_seconds()) # length of pass in seconds passes.append( i[0] ) # appedns check passes to passes list print("Check Pass | Az:", en[0]) # prints pass info (used for dev) elif not site[4] < en[0] < site[5]: # if enters FOV not on face1 print("Fence Pass | Az:", ex[0]) # prints pass info (used for dev) if (p1 - p2) < 0: # if the azimuth is growing after 5 seconds rx = i[ 0] # Sets variables so it doesn't mess up other operations while p1 <= site[ 6]: # looks for when azimuth breaches sides of coverage p1 = Orbital.get_observer_look( sat, rx, site[1], site[0], site[2])[0] # gets new azimuth rx = rx + timedelta( seconds=10 ) # sets time for 10s later to retrieve azimuth at that time print("Fence Time:", rx, "Angle:", p1) # prints pass info (used for dev) len = int((i[1] - rx).total_seconds()) passes.append(rx) if (p1 - p2) > 0: # if the azimuth is shrinking after 5 seconds rx = i[ 0] # Sets variables so it doesn't mess up other operations while p1 >= site[ 6]: # looks for when azimuth breaches sides of coverage p1 = Orbital.get_observer_look( sat, rx, site[1], site[0], site[2])[0] # gets new azimuth rx = rx + timedelta(seconds=10) print("Fence Time:", rx, "Angle:", p1) # prints passe info (used for dev) len = int((i[1] - rx).total_seconds()) passes.append(rx) # appedns check passes to passes list elif not site[6] < en[0] < site[7]: # if enters FOV not on face2 print("Fence Pass | Az:", ex[0]) # prints pass info (used for dev) if (p1 - p2) < 0: # if the azimuth is growing after 5 seconds rx = i[ 0] # Sets variables so it doesn't mess up other operations while p1 <= site[ 6]: # looks for when azimuth breaches sides of coverage p1 = Orbital.get_observer_look( sat, rx, site[1], site[0], site[2])[0] # gets new azimuth rx = rx + timedelta( seconds=10 ) # sets time for 10s later to retrieve azimuth at that time print("Fence Time:", rx, "Angle:", p1) # prints pass info (used for dev) len = int((i[1] - rx).total_seconds()) passes.append(rx) # appedns check passes to passes list if (p1 - p2) > 0: # if the azimuth is shrinking after 5 seconds rx = i[ 0] # Sets variables so it doesn't mess up other operations while p1 >= site[ 6]: # looks for when azimuth breaches sides of coverage p1 = Orbital.get_observer_look( sat, rx, site[1], site[0], site[2])[0] # gets new azimuth rx = rx + timedelta( seconds=10 ) # sets time for 10s later to retrieve azimuth at that time print("Fence Time:", rx, "Angle:", p1) # prints pass info (used for dev) len = int((i[1] - rx).total_seconds()) passes.append(rx) # appedns check passes to passes list if len < 0: print("Not a Pass") elif len < 180: print("Pass Length: ", len, "sec (short)") else: print("Pass Length: ", len, "sec") return passes # Returns datetime objects for all passes
def parseISSTLEFile(): orb = Orbital("iss (zarya)", tle_filename) utc_time = datetime.utcnow() return orb.get_lonlatalt(utc_time) + orb.get_observer_look( utc_time, location[0], location[1], 0)
lat, lon, alt = 29.76, -95.36, 0 lat_rad = math.radians(lat) lon_rad = math.radians(lon) R_ECEF_to_NED = np.matrix([ [-math.sin(lat_rad)*math.cos(lon_rad), -math.sin(lat_rad)*math.sin(lon_rad), math.cos(lat_rad)], [-math.sin(lon_rad), math.cos(lon_rad), 0 ], [-math.cos(lat_rad)*math.cos(lon_rad), -math.cos(lat_rad)*math.sin(lon_rad), -math.sin(lat_rad)] ]) #iss_sat_num = 25544 orb = Orbital('ISS (ZARYA)') now = datetime.utcnow() look = orb.get_observer_look(now, lon, lat, alt) # NOTE: lon is first, lat is second! iss_llh = orb.get_lonlatalt(now) print("ISS (lat lon alt) = ({}, {}, {})".format(iss_llh[1], iss_llh[0], iss_llh[2])) print('ISS is {} deg east of north and {} degrees above the horizon'.format(look[0], look[1])) #print(astronomy.sun_zenith_angle(now, lon, lat)) iss_pos_m = lat_lon_to_ecef(iss_llh[1], # lat iss_llh[0], # lon earth_rad_m+(iss_llh[2]*1000)) # height (m) (need to cvt km to m) obs_pos_m = lat_lon_to_ecef(lat, lon) print('ECEF pos: {} m'.format(iss_pos_m)) print('obs ECEF pos: {} m'.format(obs_pos_m))
TLE_FILE_NAME = 'cubesat.txt' ALTITUDE_ASL = 0 LAT = 64.146 LONG = 21.942 passhour = 0 while passhour < 24: # set the date with a variable for the hour d = datetime(2020, 5, 9, passhour, 0, 0) orb = Orbital('CUBESAT', 'cubesat.txt') # generate pass information for the hour specified passes = orb.get_next_passes(d, 1, LONG, LAT, ALTITUDE_ASL, horizon=1) # if there is no pass for the current value of passhour, the length will be zero if len(passes) > 0: rise_az, rise_el = orb.get_observer_look(passes[0][0], LONG, LAT, ALTITUDE_ASL) transit_az, transit_el = orb.get_observer_look(passes[0][2], LONG, LAT, ALTITUDE_ASL) set_az, set_el = orb.get_observer_look(passes[0][1], LONG, LAT, ALTITUDE_ASL) # print out the pass info print("CUBESAT") print(passes[0][0], rise_az, rise_el) print(passes[0][2], transit_az, transit_el) print(passes[0][1], set_az, set_el) print() # use datetime.strptime() to split the pass time into its componenet parts # set the hour of the current pass passtring = datetime.strptime(str(passes[0][0]), '%Y-%m-%d %H:%M:%S.%f')
class InviewCalculator: """Class to compute inviews above a specified elevation angle from a latitude, longitude, elevation to a satellite (number) """ # Constructor def __init__(self, ground_station, satellite_tle): """Constructor: ground station as GroundStation, satellite TLE as TleManipulator""" self.__ground_station = ground_station self.__satellite_tle = satellite_tle self.__orb = Orbital(str(self.__satellite_tle.get_satellite_number()), \ line1=self.__satellite_tle.get_line1(), \ line2=self.__satellite_tle.get_line2()) # Member functions def __repr__(self): """Returns a string representing an instance of this class.""" out = 'Inview Calculator:\n' \ 'latitude=%f, longitude=%f, elevation=%f, ' \ 'minimum elevation angle=%s, satellite TLE=\n%s' % \ (self.__ground_station.get_latitude(), self.__ground_station.get_longitude(), self.__ground_station.get_minimum_elevation_angle(), \ self.__ground_station.get_minimum_elevation_angle(), self.__satellite_tle) return out def compute_inviews(self, in_start_time, in_end_time): """Method to compute inviews (in UTC) for the initialized location, elevation angle, and satellite over a specified time period. Returns a list of inview start/stop times, accurate to the nearest second and using the latest available TLE when the method is called.""" # NOTE: pyorbital EXPECTS naive date/times and interprets them # as UTC... so we need to satisfy it; however, we are making this # module DATETIME AWARE, so RETURN VALUES are DATETIME AWARE!! # Also, all naive inputs are assumed to be UTC and all aware inputs # are converted to UTC (and then made naive) if (in_start_time.tzinfo is not None): temp = in_start_time.astimezone(UTC) start_time = datetime(temp.year, temp.month, temp.day, \ temp.hour, temp.minute, temp.second) else: start_time = in_start_time if (in_end_time.tzinfo is not None): temp = in_end_time.astimezone(UTC) end_time = datetime(temp.year, temp.month, temp.day, \ temp.hour, temp.minute, temp.second) else: end_time = in_end_time # start_time, end_time are now naive inviews = [] time = start_time up = 0 el_increasing = 0 maxel = 0 try: (az, el) = self.__orb.get_observer_look(time, self.__ground_station.get_longitude(), \ self.__ground_station.get_latitude(), \ self.__ground_station.get_minimum_elevation_angle()) if (el > self.__ground_station.get_minimum_elevation_angle()): # start the first inview at the input start_time up = 1 el_increasing = 1 rising = time # Step through time, looking for inview starts and ends while (time < end_time): (az, el) = self.__orb.get_observer_look(time, self.__ground_station.get_longitude(), \ self.__ground_station.get_latitude(), \ self.__ground_station.get_minimum_elevation_angle()) if (el > self.__ground_station.get_minimum_elevation_angle() ) and (up == 0): rising = self.__find_exact_crossing(time, up) el_increasing = 1 up = 1 if (el < self.__ground_station.get_minimum_elevation_angle() ) and (up == 1): # make sure to append AWARE datetimes crossing = self.__find_exact_crossing(time, up) inviews.append((rising.replace(tzinfo=UTC), \ crossing.replace(tzinfo=UTC), \ maxel)) el_increasing = 0 up = 0 maxel = 0 if (el > self.__ground_station.get_minimum_elevation_angle()): if (el > maxel): maxel = el elif ( el_increasing ): # First point after el starts decreasing... find the exact max el el_increasing = 0 maxel = self.__find_exact_maxel(time, maxel) time += self.__oneminute if (up == 1): # end the last inview at the input end_time # make sure to append AWARE datetimes inviews.append((rising.replace(tzinfo=UTC), \ end_time.replace(tzinfo=UTC), \ maxel)) return inviews except NotImplementedError: # Does not seem to work? print("NotImplementedError computing inviews. Date/time = %s-%s-%sT%s:%s:%s, satellite number = %s" % \ (time.year, time.month, time.day, time.hour, time.minute, time.second, self.__satellite_tle.get_satellite_number())) sys.stdout.flush() raise except: # Does not seem to work? print("Unknown exception computing inviews. Date/time = %s-%s-%sT%s:%s:%s, satellite number = %s" % \ (time.year, time.month, time.day, time.hour, time.minute, time.second, self.__satellite_tle.get_satellite_number())) sys.stdout.flush() raise def print_inviews(self, inviews): """Method to print a table of inviews... assumes that inviews contains the data for such a table""" for iv in inviews: print("Rise: %s, Set: %s, Maximum Elevation: %f" % (iv[0], iv[1], iv[2])) def compute_azels(self, in_start_time, in_end_time, time_step_seconds): """Method to compute az/el angles at time_step intervals during the input time period, INDEPENDENT of whether the satellite is actually in view """ # NOTE: pyorbital EXPECTS naive date/times and interprets them # as UTC... so we need to satisfy it; however, we are making this # module DATETIME AWARE, so RETURN VALUES are DATETIME AWARE!! # Also, all naive inputs are assumed to be UTC and all aware inputs # are converted to UTC (and then made naive) if (in_start_time.tzinfo is not None): temp = in_start_time.astimezone(UTC) start_time = datetime(temp.year, temp.month, temp.day, \ temp.hour, temp.minute, temp.second) else: start_time = in_start_time if (in_end_time.tzinfo is not None): temp = in_end_time.astimezone(UTC) end_time = datetime(temp.year, temp.month, temp.day, \ temp.hour, temp.minute, temp.second) else: end_time = in_end_time # start_time, end_time are now naive try: delta = timedelta(seconds=time_step_seconds) except: delta = timedelta(seconds=60) azels = [] time = start_time # Naively compute the table... i.e. compute time, az, el for the # input duration at each time step... no matter whether the satellite # is really inview or not! while (time < end_time + delta): (az, el) = self.__orb.get_observer_look(time, self.__ground_station.get_longitude(), \ self.__ground_station.get_latitude(), \ self.__ground_station.get_minimum_elevation_angle()) range_km = self.__compute_range(time) outtime = time.replace(tzinfo=UTC) # time is unmodified! azels.append((outtime, az, el, range_km)) time += delta return azels def __compute_range(self, utc_time): """Method to compute range in kilometers from observer to satellite at *naive* UTC time utc_time""" # N.B. pyorbital works in kilometers (pos_x, pos_y, pos_z), (vel_x, vel_y, vel_z) = self.__orb.get_position(utc_time, normalize=False) (opos_x, opos_y, opos_z), (ovel_x, ovel_y, ovel_z) = astronomy.observer_position(utc_time, \ self.__ground_station.get_longitude(), self.__ground_station.get_latitude(), self.__ground_station.get_elevation_in_meters()/1000.0) dx = pos_x - opos_x dy = pos_y - opos_y dz = pos_z - opos_z return math.sqrt(dx * dx + dy * dy + dz * dz) # km # up is what it is **before** the crossing def __find_exact_crossing(self, time, up): """Private method to refine an in view/out of view crossing time from the nearest minute to the nearest second.""" exacttime = time # The crossing occurred in the minute before now... search backwards # for the exact second of crossing for j in range(0, 60): exacttime -= self.__onesecond (az, el) = self.__orb.get_observer_look(exacttime, \ self.__ground_station.get_longitude(), \ self.__ground_station.get_latitude(), \ self.__ground_station.get_minimum_elevation_angle()) if ((el > self.__ground_station.get_minimum_elevation_angle()) and (up == 1)) or \ ((el < self.__ground_station.get_minimum_elevation_angle()) and (up == 0)): break return exacttime def __find_exact_maxel(self, time, maxel): """Private method to refine the maximum elevation from occuring at the nearest minute to the nearest second.""" exacttime = time # The maximum elevation occurred in the **two** minutes before now... search backwards # for the exact second of maximum elevation for j in range(0, 120): exacttime -= self.__onesecond (az, el) = self.__orb.get_observer_look(exacttime, \ self.__ground_station.get_longitude(), \ self.__ground_station.get_latitude(), \ self.__ground_station.get_minimum_elevation_angle()) if (el > maxel): maxel = el return maxel # Class member constants __onesecond = timedelta(seconds=1) __oneminute = timedelta(minutes=1)
tle = tlefile.read(satellite, None, line1, line2) orb = Orbital(satellite, None, line1, line2) now = datetime.utcnow() # Get normalized position and velocity of the satellite: pos, vel = orb.get_position(now) # Get longitude, latitude and altitude of the satellite: position = orb.get_lonlatalt(now) data = {} timestamp = calendar.timegm(now.utctimetuple()) az, el = orb.get_observer_look(now, userLng, userLat, userAlt); data['user_view'] = {} data['user_view']['azimuth'] = az data['user_view']['elevation'] = el data['timestamp'] = timestamp data['satellite'] = satellite data['tle'] = {}; data['tle']['arg_perigee'] = orb.tle.arg_perigee data['tle']['bstar'] = orb.tle.bstar data['tle']['classification'] = orb.tle.classification data['tle']['element_number'] = orb.tle.element_number data['tle']['ephemeris_type'] = orb.tle.ephemeris_type data['tle']['epoch'] = (orb.tle.epoch - datetime(1970, 1, 1)).total_seconds()
class FileStreamer(FileSystemEventHandler): """Get the updates from files. TODO: separate holder from file handling. """ def __init__(self, holder, configfile, *args, **kwargs): FileSystemEventHandler.__init__(self, *args, **kwargs) self._file = None self._filename = "" self._where = 0 self._satellite = "" self._orbital = None cfg = ConfigParser() cfg.read(configfile) self._coords = cfg.get("local_reception", "coordinates").split(" ") self._coords = [float(self._coords[0]), float(self._coords[1]), float(self._coords[2])] self._station = cfg.get("local_reception", "station") logger.debug("Station " + self._station + " located at: " + str(self._coords)) try: self._tle_files = cfg.get("local_reception", "tle_files") except NoOptionError: self._tle_files = None self._file_pattern = cfg.get("local_reception", "file_pattern") self.scanlines = holder self._warn = True def update_satellite(self, satellite): """Update satellite and renew the orbital instance. """ if satellite != self._satellite: self._satellite = satellite if self._tle_files is not None: filelist = glob(self._tle_files) tle_file = max(filelist, key=lambda x: os.stat(x).st_mtime) else: tle_file = None self._orbital = Orbital(self._satellite.upper(), tle_file) def on_created(self, event): """Callback when file is created. """ if event.src_path != self._filename: if self._filename: logger.info("Closing: " + self._filename) if self._file: self._file.close() self._file = None self._filename = "" self._where = 0 self._satellite = "" self._warn = True logger.info("New file detected: " + event.src_path) def on_opened(self, event): """Callback when file is opened """ fname = os.path.split(event.src_path)[1] if self._file is None and fnmatch(fname, self._file_pattern): logger.info("File opened: " + event.src_path) self._filename = event.src_path self._file = open(event.src_path, "rb") self._where = 0 self._satellite = "" self._orbital = None self._warn = True def on_modified(self, event): self.on_opened(event) if event.src_path != self._filename: return self._file.seek(self._where) line = self._file.read(LINE_SIZE) while len(line) == LINE_SIZE: line_start = self._where self._where = self._file.tell() dtype = np.dtype([('frame_sync', '>u2', (6, )), ('id', [('id', '>u2'), ('spare', '>u2')]), ('timecode', '>u2', (4, )), ('telemetry', [("ramp_calibration", '>u2', (5, )), ("PRT", '>u2', (3, )), ("ch3_patch_temp", '>u2'), ("spare", '>u2'),]), ('back_scan', '>u2', (10, 3)), ('space_data', '>u2', (10, 5)), ('sync', '>u2'), ('TIP_data', '>u2', (520, )), ('spare', '>u2', (127, )), ('image_data', '>u2', (2048, 5)), ('aux_sync', '>u2', (100, ))]) array = np.fromstring(line, dtype=dtype) if np.all(abs(HRPT_SYNC_START - array["frame_sync"]) > 1): array = array.newbyteorder() # FIXME: this means server can only share 1 year of hrpt data. now = datetime.utcnow() year = now.year utctime = datetime(year, 1, 1) + timecode(array["timecode"][0]) if utctime > now: # Can't have data from the future... yet :) utctime = (datetime(year - 1, 1, 1) + timecode(array["timecode"][0])) # Check that we receive real-time data if not (np.all(array['aux_sync'] == HRPT_SYNC) and np.all(array['frame_sync'] == HRPT_SYNC_START)): logger.info("Garbage line: " + str(utctime)) line = self._file.read(LINE_SIZE) continue if (now - utctime).days > 7 and self._warn: logger.warning("Data is more than a week old: " + str(utctime)) self._warn = False satellite = SATELLITES[((array["id"]["id"] >> 3) & 15)[0]] self.update_satellite(satellite) elevation = self._orbital.get_observer_look(utctime, *self._coords)[1] logger.debug("Got line " + utctime.isoformat() + " " + self._satellite + " " + str(elevation)) # TODO: # - serve also already present files # - timeout and close the file self.scanlines.add_scanline(self._satellite, utctime, elevation, line_start, self._filename, line) line = self._file.read(LINE_SIZE) self._file.seek(self._where)
class FileStreamer(FileSystemEventHandler): """Get the updates from files. TODO: separate holder from file handling. """ def __init__(self, holder, configfile, *args, **kwargs): FileSystemEventHandler.__init__(self, *args, **kwargs) self._file = None self._filename = "" self._where = 0 self._satellite = "" self._orbital = None cfg = ConfigParser() cfg.read(configfile) self._coords = cfg.get("local_reception", "coordinates").split(" ") self._coords = [ float(self._coords[0]), float(self._coords[1]), float(self._coords[2]) ] self._station = cfg.get("local_reception", "station") logger.debug("Station " + self._station + " located at: " + str(self._coords)) try: self._tle_files = cfg.get("local_reception", "tle_files") except NoOptionError: self._tle_files = None self._file_pattern = cfg.get("local_reception", "file_pattern") self.scanlines = holder self._warn = True def update_satellite(self, satellite): """Update satellite and renew the orbital instance. """ if satellite != self._satellite: self._satellite = satellite if self._tle_files is not None: filelist = glob(self._tle_files) tle_file = max(filelist, key=lambda x: os.stat(x).st_mtime) else: tle_file = None self._orbital = Orbital(self._satellite.upper(), tle_file) def on_created(self, event): """Callback when file is created. """ if event.src_path != self._filename: if self._filename: logger.info("Closing: " + self._filename) if self._file: self._file.close() self._file = None self._filename = "" self._where = 0 self._satellite = "" self._warn = True logger.info("New file detected: " + event.src_path) def on_opened(self, event): """Callback when file is opened """ fname = os.path.split(event.src_path)[1] if self._file is None and fnmatch(fname, self._file_pattern): logger.info("File opened: " + event.src_path) self._filename = event.src_path self._file = open(event.src_path, "rb") self._where = 0 self._satellite = "" self._orbital = None self._warn = True def on_modified(self, event): self.on_opened(event) if event.src_path != self._filename: return self._file.seek(self._where) line = self._file.read(LINE_SIZE) while len(line) == LINE_SIZE: line_start = self._where self._where = self._file.tell() dtype = np.dtype([('frame_sync', '>u2', (6, )), ('id', [('id', '>u2'), ('spare', '>u2')]), ('timecode', '>u2', (4, )), ('telemetry', [ ("ramp_calibration", '>u2', (5, )), ("PRT", '>u2', (3, )), ("ch3_patch_temp", '>u2'), ("spare", '>u2'), ]), ('back_scan', '>u2', (10, 3)), ('space_data', '>u2', (10, 5)), ('sync', '>u2'), ('TIP_data', '>u2', (520, )), ('spare', '>u2', (127, )), ('image_data', '>u2', (2048, 5)), ('aux_sync', '>u2', (100, ))]) array = np.fromstring(line, dtype=dtype) if np.all(abs(HRPT_SYNC_START - array["frame_sync"]) > 1): array = array.newbyteorder() # FIXME: this means server can only share 1 year of hrpt data. now = datetime.utcnow() year = now.year utctime = datetime(year, 1, 1) + timecode(array["timecode"][0]) if utctime > now: # Can't have data from the future... yet :) utctime = (datetime(year - 1, 1, 1) + timecode(array["timecode"][0])) # Check that we receive real-time data if not (np.all(array['aux_sync'] == HRPT_SYNC) and np.all(array['frame_sync'] == HRPT_SYNC_START)): logger.info("Garbage line: " + str(utctime)) line = self._file.read(LINE_SIZE) continue if (now - utctime).days > 7 and self._warn: logger.warning("Data is more than a week old: " + str(utctime)) self._warn = False satellite = SATELLITES[((array["id"]["id"] >> 3) & 15)[0]] self.update_satellite(satellite) elevation = self._orbital.get_observer_look( utctime, *self._coords)[1] logger.debug("Got line " + utctime.isoformat() + " " + self._satellite + " " + str(elevation)) # TODO: # - serve also already present files # - timeout and close the file self.scanlines.add_scanline(self._satellite, utctime, elevation, line_start, self._filename, line) line = self._file.read(LINE_SIZE) self._file.seek(self._where)
eph.compute(ephObserver) data = {} data['next_pass'] = {} data['next_pass']['until'] = "%d" % round((rt - ephemNow) * 3600 * 24) data['next_pass']['rise_time'] = calendar.timegm(rt.datetime().utctimetuple()) data['next_pass']['transit_time'] = calendar.timegm(tt.datetime().utctimetuple()) data['next_pass']['set_time'] = calendar.timegm(st.datetime().utctimetuple()) data['next_pass']['rise_azimuth'] = "%5.1f" % math.degrees(razi) data['next_pass']['max_elevation'] = "%5.1f" % math.degrees(televation) data['next_pass']['set_azimuth'] = "%5.1f" % math.degrees(sazi) # azimuth = eph.az # elevation = eph.alt azimuth, elevation = orb.get_observer_look(now, userLng, userLat, userAlt); data['user_view'] = {} data['user_view']['azimuth'] = azimuth data['user_view']['elevation'] = elevation data['timestamp'] = timestamp data['satellite'] = satellite data['tle'] = {}; data['tle']['first_line'] = line1 data['tle']['second_line'] = line2 data['tle']['arg_perigee'] = orb.tle.arg_perigee data['tle']['bstar'] = orb.tle.bstar data['tle']['classification'] = orb.tle.classification data['tle']['element_number'] = orb.tle.element_number
tle = StringIO() tle.write(amateur_file) orb = Orbital( "AO-7", line1= "1 07530U 74089B 19134.19581420 -.00000034 00000-0 67853-4 0 9992", line2= "2 07530 101.7410 102.2252 0012571 56.5062 14.6811 12.53638015 36076") now = datetime.utcnow() # arrayat=Orbital.get_observer_look(lon=120.77,lat=15.15,alt=0.020) pprint(orb) # >>> # Get longitude, latitude and altitude of the satellite: >>> pprint(orb.get_lonlatalt(now)) passes = orb.get_next_passes(now, length=4, lon=120.77, lat=15.15, alt=0.02) # # Each Pass a tuple of 3, rise, set, max ele # start_pass = passes[0][0] end_pass = passes[0][1] min_elevation = 10 while (start_pass < end_pass): sky_location = orb.get_observer_look(start_pass, lon=120.77, lat=15.15, alt=0.02) if sky_location[1] >= min_elevation: print("Az {:6.2f} Ele {:6.2f} ".format(sky_location[0], sky_location[1])) start_pass = start_pass + timedelta(seconds=60) print("Pass ended")
def get_satellite_geometry(startDateUTC, lengthDays, lon, lat, alt=0.0, mission="Sentinel-2a", tleFile="/data/tle/norad_resource_tle.txt"): """Calculate approximate geometry for Sentinel overpasses. Approximate because it assumes maximum satellite elevation is the time at which target is imaged. :param startDateUTC: a datetime object specifying when to start prediction. :type startDateUTC: object :param lengthDays: number of days over which to perform calculations. :type lengthDays: int :param lat: latitude of target. :type lat: float :param lon: longitude of target. :type lon: float :param alt: altitude of target (in km). :type alt: float :param mission: mission name as in TLE file. :type mission: str :param tleFile: TLE file. :type tleFile: str :return: a python list containing instances of the sensorGeometry class arranged in date order. :rtype: list """ orb = Orbital(mission, tleFile) passes = orb.get_next_passes(startDateUTC, 24 * lengthDays, lon, lat, alt) geom_inst = SensorGeometry() geom_inst.date_utc = [] geom_inst.vza = [] geom_inst.vaa = [] geom_inst.sza = [] geom_inst.saa = [] for p in passes: look = orb.get_observer_look(p[2], lon, lat, alt) vza = 90 - look[1] vaa = look[0] sza = ast.sun_zenith_angle(p[2], lon, lat) saa = np.rad2deg(ast.get_alt_az(p[2], lon, lat)[1]) if mission == 'Sentinel-1b': if 75 < vaa < 105 and 20. < vza < 45.: # vaa usually [0, 180], testing observation times geom_inst.date_utc.append(p[2]) geom_inst.vza.append(vza) geom_inst.vaa.append(vaa) geom_inst.sza.append(sza) geom_inst.saa.append(saa) elif mission == 'Sentinel-1a': if 75 < vaa < 105 and 20. < vza < 45.: # vaa usually [0, 180], testing observation times geom_inst.date_utc.append(p[2]) geom_inst.vza.append(vza) geom_inst.vaa.append(vaa) geom_inst.sza.append(sza) geom_inst.saa.append(saa) elif mission == 'Sentinel-2a': if sza < 90 and vza < 10.3: geom_inst.date_utc.append(p[2]) geom_inst.vza.append(vza) geom_inst.vaa.append(vaa) geom_inst.sza.append(sza) geom_inst.saa.append(saa) return geom_inst
class Test_NightFogLowStratusAlgorithm(unittest.TestCase): def setUp(self): # Load test data inputs = np.dsplit(testdata_night, 14) self.ir108 = inputs[0] self.ir039 = inputs[1] self.vis008 = inputs[2] self.nir016 = inputs[3] self.vis006 = inputs[4] self.ir087 = inputs[5] self.ir120 = inputs[6] self.elev = inputs[7] self.cot = inputs[8] self.reff = inputs[9] self.lwp = inputs[10] self.lat = inputs[11] self.lon = inputs[12] self.cth = inputs[13] self.time = datetime(2013, 11, 12, 6, 00, 00) # METEOSAT-10 (MSG-3) TLE file from 01.08.2017 # http://celestrak.com/NORAD/elements/weather.txt line1 = "1 38552U 12035B 17212.14216600 -.00000019 00000-0 00000-0 0 9998" line2 = "2 38552 0.8450 357.8180 0002245 136.4998 225.6885 1.00275354 18379" # Import TLE file self.tle = tlefile.read('meteosat 10', line1=line1, line2=line2) self.orbital = Orbital('meteosat 10', line1=self.tle.line1, line2=self.tle.line2) # Compute satellite zenith angle azi, ele = self.orbital.get_observer_look(self.time, self.lon, self.lat, self.elev) self.sza = ele self.input = { 'ir108': self.ir108, 'ir039': self.ir039, 'lat': self.lat, 'lon': self.lon, 'time': self.time, 'plot': True, 'save': True, 'dir': '/tmp/FLS', 'resize': '5', 'sza': self.sza } inputs = np.dsplit(testdata_night2, 14) self.ir108 = inputs[0] self.ir039 = inputs[1] self.vis008 = inputs[2] self.nir016 = inputs[3] self.vis006 = inputs[4] self.ir087 = inputs[5] self.ir120 = inputs[6] self.elev = inputs[7] self.cot = inputs[8] self.reff = inputs[9] self.lwp = inputs[10] self.lat = inputs[11] self.lon = inputs[12] self.cth = inputs[13] self.time = datetime(2013, 12, 1, 4, 00, 00) self.input2 = { 'ir108': self.ir108, 'ir039': self.ir039, 'lat': self.lat, 'lon': self.lon, 'time': self.time, 'plot': True, 'save': True, 'dir': '/tmp/FLS', 'resize': '5', 'sza': self.sza } def tearDown(self): pass def test_nightfls_algorithm(self): flsalgo = NightFogLowStratusAlgorithm(**self.input) ret, mask = flsalgo.run() self.assertEqual(flsalgo.ir108.shape, (141, 298)) self.assertEqual(ret.shape, (141, 298)) self.assertEqual(flsalgo.shape, (141, 298)) self.assertEqual(np.ma.is_mask(flsalgo.mask), True) def test_nightfls_algorithm2(self): flsalgo = NightFogLowStratusAlgorithm(**self.input2) ret, mask = flsalgo.run() self.assertEqual(flsalgo.ir108.shape, (141, 298)) self.assertEqual(ret.shape, (141, 298)) self.assertEqual(flsalgo.shape, (141, 298)) self.assertEqual(np.ma.is_mask(flsalgo.mask), True) def test_nightfls_turningpoints_with_valley(self): y = np.array([1, 2, 4, 7, 5, 2, 0, 2, 3, 4, 6]) flsalgo = NightFogLowStratusAlgorithm(**self.input) tvalues, valleys = flsalgo.get_turningpoints(y) self.assertEqual(tvalues[5], True) self.assertEqual(tvalues[2], True) self.assertEqual(np.sum(tvalues), 2) self.assertEqual(np.alen(valleys), 1) self.assertEqual(valleys, 0) def test_nightfls_turningpoints_with_thres(self): y = np.array([1, 2, 4, 7, 5, 2, 0, 2, 3, 4, 6]) x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) flsalgo = NightFogLowStratusAlgorithm(**self.input) tvalues, thres = flsalgo.get_turningpoints(y, x) self.assertEqual(tvalues[5], True) self.assertEqual(tvalues[2], True) self.assertEqual(np.sum(tvalues), 2) self.assertEqual(np.alen(thres), 1) self.assertEqual(thres, 7) def test_nightfls_turningpoints_no_valley(self): y = np.array([1, 2, 4, 7, 5, 2, 1, 1, 1, 0]) flsalgo = NightFogLowStratusAlgorithm(**self.input) tvalues, valleys = flsalgo.get_turningpoints(y) self.assertEqual(tvalues[2], True) self.assertEqual(np.sum(tvalues), 1) self.assertEqual(np.alen(valleys), 0) def test_nightfls_slope(self): y = np.array([1, 2, 4, 7, 5, 2, 1, 1, 1, 0]) x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) flsalgo = NightFogLowStratusAlgorithm(**self.input) slope, thres = flsalgo.get_slope(y, x) self.assertEqual(slope[2], 3) self.assertEqual(slope[8], -1) self.assertEqual(np.alen(slope), 9) self.assertEqual(thres, 6)
current_time = datetime.utcfromtimestamp(time) def pwm_for(az, el): low = pwm_min high = pwm_max if (az > 180): az -= 180 el = 180 - el az_pwm = (az / 180) * (high - low) + low el_pwm = (el / 180) * (high - low) + low return az_pwm, el_pwm delta = timedelta(seconds=step) for i in range(duration): az, el = orb.get_observer_look(current_time, lon, lat, 0) paz, pel = (pwm_for(az, el)) res = (", ".join( [str(time + i), str(int(round(paz))), str(int(round(pel)))])) con.send(res.encode("utf8") + b"\n") current_time += delta con.send(b"\n") print(con.recv(1024).decode("utf8"))