from astroquery.simbad import Simbad from astropy.coordinates import SkyCoord from astropy import units as u from geopy.geocoders import Nominatim from skyfield.api import load, Topos, Star from smolagents import Tool # Make sure RA/DEC fields are included in SIMBAD query Simbad.add_votable_fields("ra", "dec") class StarInMySkyTool(Tool): name = "star_in_my_sky" description = ( "Checks if a star is currently visible in the sky at a given location." "If the star is visible, it returns the location - altitude and azimuth." "If the star is not visible, it just says the star is below the horizon." "This tool expects two arguments: the name of the star and the location in English." ) inputs = { "star_name": {"type": "string", "description": "The name of the star to check."}, "location_name": {"type": "string", "description": "The location to check. e.g. London, UK"}, } output_type = "string" def forward(self, star_name: str, location_name: str) -> str: try: resp = get_star_visibility(star_name, location_name) except Exception as e: return f"Error: {str(e)}" return resp def get_coordinates_from_location(location_name): """Use geopy to convert a place name into latitude/longitude.""" geolocator = Nominatim(user_agent="astrogeo") location = geolocator.geocode(location_name) if not location: raise ValueError(f"Could not geocode location: {location_name}") return location.latitude, location.longitude def get_star_visibility(star_name, location_name): """ 1. Convert a location name to lat/long. 2. Query SIMBAD for star's RA/Dec. 3. Compute altitude/azimuth via Skyfield. 4. Print whether the star is above or below the horizon. """ # 1. Get lat/long from location name latitude, longitude = get_coordinates_from_location(location_name) print(f"Location '{location_name}': lat={latitude:.4f}, lon={longitude:.4f}") # 2. Query SIMBAD for the star's RA/DEC result_table = Simbad.query_object(star_name) if result_table is None: return f"Could not find star '{star_name}' in SIMBAD." if "ra" not in result_table.colnames or "dec" not in result_table.colnames: return "The 'ra'/'dec' columns are missing in SIMBAD’s response!" ra_str = result_table["ra"][0] # e.g. '06 45 08.92' dec_str = result_table["dec"][0] # e.g. '-16 42 58.0' # Convert RA/DEC to numeric values using Astropy skycoord = SkyCoord(ra_str, dec_str, unit=(u.hourangle, u.deg)) # 3. Create a Skyfield Star object star = Star(ra_hours=skycoord.ra.hour, dec_degrees=skycoord.dec.degree) # 4. Load ephemeris, define observer, compute alt/az for now ts = load.timescale() t = ts.now() planets = load("de421.bsp") earth = planets["earth"] observer = Topos(latitude_degrees=latitude, longitude_degrees=longitude) # Calculate altitude, azimuth alt, az, distance = (earth + observer).at(t).observe(star).apparent().altaz() resp = "" # 5. Print results if alt.degrees > 0: resp = ( f"Star '{star_name}' is above the horizon at {location_name} (NOW).\n" f"Altitude: {alt.degrees:.2f}°, Azimuth: {az.degrees:.2f}°" ) else: resp = f"\nStar '{star_name}' is below the horizon at {location_name} (NOW)." return resp if __name__ == "__main__": # Example: "Sirius", "London, UK" get_star_visibility("Sirius", "London, UK")