Created
April 18, 2025 01:57
-
-
Save jimdiroffii/d48ced249ab7dbcf7e4a06ed0459d370 to your computer and use it in GitHub Desktop.
A Stellarium script for finding the best date and time for viewing Mercury. The script tries to find when Mercury will be at least 5 degrees above the horizon, at least 0.0 magnitude, and within an hour of sunset or sunrise. The script is centered on Miami, Florida, and set to check for specific dates. Most variables need adjustment to work.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////////////////////////////////// | |
// Stellarium Script: Mercury Twilight Search | |
// Date Range: Feb 10, 2025 – Apr 30, 2025 | |
// Time Step: 5 minutes | |
// Location: Miami (lat: 25.7617, lon: -80.1918), ignoring DST complexities. | |
////////////////////////////////////////////////////////////////////////////// | |
// ------------------------ USER CONFIGURATION ------------------------ | |
// Observing location | |
var observerLatitude = 25.7617; | |
var observerLongitude = -80.1918; | |
var observerAltitude = 2; // meters | |
var timezoneOffset = -5; // ignoring DST, just use EST offset | |
// Date range (UTC) – note that JS Date months are zero-based | |
var startDateUTC = new Date(Date.UTC(2025, 1, 10, 0, 0, 0)); // 2025-02-10 00:00:00 UTC | |
var endDateUTC = new Date(Date.UTC(2025, 3, 30, 23, 59, 59)); // 2025-04-30 23:59:59 UTC | |
// Mercury visibility constraints | |
var minAltitudeDeg = 5.0; // Must be above 5° | |
var maxMagnitude = 0.0; // Must be magnitude 0.0 or brighter | |
// Time step in minutes (5 minutes) | |
var timeStepMin = 5; | |
// Sunrise/sunset times (LOCAL) on start/end dates | |
// (Ignoring DST shift, we do a linear interpolation.) | |
var sunriseFeb10 = 6.9833; // ~6:59 local | |
var sunsetFeb10 = 18.1833; // ~18:11 local | |
var sunriseApr30 = 6.75; // ~6:45 local | |
var sunsetApr30 = 19.8667; // ~19:52 local | |
// We define a 1-hour margin before/after sunrise or sunset | |
var marginHours = 1; | |
// ------------------------ END CONFIGURATION -------------------------- | |
// Set observer location in Stellarium | |
core.setObserverLocation( | |
observerLatitude, | |
observerLongitude, | |
observerAltitude, | |
timezoneOffset, | |
"Miami" | |
); | |
// Calculate total number of days in our search period | |
var totalDays = (endDateUTC - startDateUTC) / (24 * 3600 * 1000); // days as float | |
/** | |
* Linear interpolation helper: | |
* Given x in [0,1], returns startVal + x*(endVal - startVal). | |
*/ | |
function lerp(startVal, endVal, x) { | |
return startVal + x * (endVal - startVal); | |
} | |
/** | |
* Returns an approximate local sunrise time (decimal hours) | |
* for a given fraction of the way between Feb 10 and Apr 30. | |
*/ | |
function approximateSunrise(dayIndex) { | |
var fraction = dayIndex / totalDays; // from 0.0 (Feb10) to 1.0 (Apr30) | |
return lerp(sunriseFeb10, sunriseApr30, fraction); | |
} | |
/** | |
* Returns an approximate local sunset time (decimal hours) | |
* for a given fraction of the way between Feb 10 and Apr 30. | |
*/ | |
function approximateSunset(dayIndex) { | |
var fraction = dayIndex / totalDays; | |
return lerp(sunsetFeb10, sunsetApr30, fraction); | |
} | |
/** | |
* Convert a local decimal hour (e.g., 6.9833) to a JS Date *in UTC*, | |
* for a particular UTC date. | |
* | |
* NOTE: Because we are ignoring DST, we assume a constant offset = timezoneOffset. | |
*/ | |
function localHourToUTCDate(baseDateUTC, localDecHour) { | |
// localDecHour is something like 6.9833 (i.e. 6:59 local). | |
// Our offset is negative, e.g. -5 for EST. So local time = UTC + offset. | |
// => UTC hour = localHour + (-offset). | |
// Because offset is -5, local = UTC - 5 => UTC = local + 5. | |
var utcHour = localDecHour + (timezoneOffset * -1); | |
// Break down decimal hour | |
var hour = Math.floor(utcHour); | |
var minute = Math.floor((utcHour - hour) * 60); | |
var second = 0; | |
// Create a new date from baseDateUTC (copy the day/month/year), | |
// then set the hours/min/seconds to the calculated UTC time. | |
var newDate = new Date(baseDateUTC.getTime()); // clone | |
newDate.setUTCHours(hour, minute, second, 0); | |
return newDate; | |
} | |
/** | |
* Convert JS Date to Stellarium date string: "YYYY:MM:DDThh:mm:ss" | |
*/ | |
function dateToStellariumDateString(jsDate) { | |
var year = jsDate.getUTCFullYear(); | |
var month = jsDate.getUTCMonth() + 1; // 0-based => 1-based | |
var day = jsDate.getUTCDate(); | |
var hour = jsDate.getUTCHours(); | |
var min = jsDate.getUTCMinutes(); | |
var sec = jsDate.getUTCSeconds(); | |
function pad(num) { return (num < 10) ? "0"+num : ""+num; } | |
return year + ":" + pad(month) + ":" + pad(day) + "T" + | |
pad(hour) + ":" + pad(min) + ":" + pad(sec); | |
} | |
/** | |
* Check Mercury's altitude/magnitude at the given JS Date (UTC). | |
* Print if it meets our thresholds. | |
*/ | |
function checkMercuryVisibility(jsDate) { | |
// Set Stellarium time | |
var stelDateStr = dateToStellariumDateString(jsDate); | |
core.setDate(stelDateStr, true); | |
var mercury = SolarSystem.getPlanet("Mercury"); | |
if (!mercury) { | |
print("Error: Could not retrieve Mercury object!"); | |
return; | |
} | |
// Altitude in radians => convert to degrees | |
var altDeg = mercury.getAlt() * (180 / Math.PI); | |
var mag = mercury.getVMagnitude(); | |
// Check thresholds | |
if (altDeg >= minAltitudeDeg && mag <= maxMagnitude) { | |
print("UTC=" + jsDate.toISOString() + | |
" | Alt=" + altDeg.toFixed(2) + | |
"° | Mag=" + mag.toFixed(2)); | |
} | |
} | |
// -------------------- MAIN LOOP -------------------- | |
print("Starting Mercury Twilight Search..."); | |
// Current date pointer (UTC) | |
var currentDate = new Date(startDateUTC.getTime()); | |
// We'll iterate day by day | |
while (currentDate <= endDateUTC) { | |
// dayIndex = how far we are between start & end | |
var dayIndex = (currentDate - startDateUTC) / (24 * 3600 * 1000); | |
// Approx. local sunrise/sunset for this day | |
var srLocal = approximateSunrise(dayIndex); | |
var ssLocal = approximateSunset(dayIndex); | |
// Define the search intervals in local time: | |
// from (sunrise - margin) to (sunrise + margin), and similarly for sunset | |
var srStart = srLocal - marginHours; // e.g., 1 hr before sunrise | |
var srEnd = srLocal + marginHours; // 1 hr after | |
var ssStart = ssLocal - marginHours; // 1 hr before sunset | |
var ssEnd = ssLocal + marginHours; // 1 hr after | |
// We'll do a 5-minute step (in decimal hours, that is 5/60 = 0.0833) | |
var timeStepHrs = timeStepMin / 60.0; | |
// ---- Morning loop ---- | |
var tLocal = srStart; | |
while (tLocal <= srEnd) { | |
var checkDateUTC = localHourToUTCDate(currentDate, tLocal); | |
checkMercuryVisibility(checkDateUTC); | |
tLocal += timeStepHrs; | |
} | |
// ---- Evening loop ---- | |
tLocal = ssStart; | |
while (tLocal <= ssEnd) { | |
var checkDateUTC = localHourToUTCDate(currentDate, tLocal); | |
checkMercuryVisibility(checkDateUTC); | |
tLocal += timeStepHrs; | |
} | |
// Move to the next (UTC) day | |
currentDate.setUTCDate(currentDate.getUTCDate() + 1); | |
} | |
print("Search Completed."); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment