Module:TarsicanSaros
From MicrasWiki
Jump to navigationJump to search
Documentation for this module may be created at Module:TarsicanSaros/doc
-- Module:TarsicanSaros
-- Generates a Tarsican Saros Cycle table, factoring perigee/apogee for eclipse subtypes.
local p = {}
----------------------------------------------------------------
-- 1. Define all embedded holidays in a single Lua table "events"
----------------------------------------------------------------
-- Format: events["Month,Day"] = { name = "Holiday Name", link = "URL or nil", city = "CityLink" }
local events = {}
-- Atosiel (Days 1..61)
events["Atosiel,1"] = { name = "Cricket Rise Festival", link = "https://micras.org/mwiki/Alperkin#Winter-to-Spring:_The_Cricket_Rise_Festival", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Alperkin|Alperkin]]" }
events["Atosiel,6"] = { name = "Bayram al-Nur (Festival of Light)", link = "https://micras.org/mwiki/Atos#Bayram_al-Nur:_The_Festival_of_Light", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Atosiel,18"] = { name = "Chag Or Hadash (Festival of New Light)", link = "https://micras.org/mwiki/Eos#Chag_Or_Hadash_(Festival_of_New_Light)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Luminaria|Luminaria]]" }
events["Atosiel,30"] = { name = "Symposion Eirinis (Symposium of Harmony)", link = "https://micras.org/mwiki/Micras#Symposion_Eirinis_(Symposium_of_Harmony)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Serena|Serena]]" }
events["Atosiel,34"] = { name = "Crösakarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Luminaria|Luminaria]]" }
events["Atosiel,36"] = { name = "Liléigostros", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Somniumpolis|Somniumpolis]]" }
events["Atosiel,43"] = { name = "Alev Günü (Day of Flame)", link = "https://micras.org/mwiki/Pyros#Alev_G%C3%BCn%C3%BC_(Day_of_Flame)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Pyralis|Pyralis]]" }
events["Atosiel,44"] = { name = "Orebellion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Thermosalem|Thermosalem]]" }
events["Atosiel,48"] = { name = "Day of Abandonment", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Atosiel,54"] = { name = "Sanashalros", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Keybir-Aviv|Keybir-Aviv]]" }
events["Atosiel,55"] = { name = "Tikkun Tzel (Repair of Shadows)", link = "https://micras.org/mwiki/Indigo#Tikkun_Tzel_(Repair_of_Shadows)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Symphonara|Symphonara]]" }
events["Atosiel,58"] = { name = "Gahtarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Atosiel,59"] = { name = "Agavronos", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Aurelia|Aurelia]]" }
events["Atosiel,61"] = { name = "Bebekarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Akróstadium|Akróstadium]]" }
-- Thalassiel (Days 62..122)
events["Thalassiel,1"] = { name = "Cicada Song Festival", link = "https://micras.org/mwiki/Alperkin#Spring-to-Summer:_The_Cicada_Song_Festival", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Alperkin|Alperkin]]" }
events["Thalassiel,2"] = { name = "Amaäzakar", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Delphica|Delphica]]" }
events["Thalassiel,6"] = { name = "Panegyris Chrysou (Golden Gathering)", link = "https://micras.org/mwiki/Chrysos#Panegyris_Chrysou_(Golden_Gathering)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Aurelia|Aurelia]]" }
events["Thalassiel,10"] = { name = "Dilëtazorium", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Sufriya|Sufriya]]" }
events["Thalassiel,11"] = { name = "Agrypnía Kyrenaïou", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Erythros|Erythros]]" }
events["Thalassiel,16"] = { name = "Amazärum", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Acheron|Acheron]]" }
events["Thalassiel,17"] = { name = "Tävrosion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Thermosalem|Thermosalem]]" }
events["Thalassiel,18"] = { name = "Bulhanum", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Luminaria|Luminaria]]" }
events["Thalassiel,19"] = { name = "Mehtap Dalgası (Moonlit Tide)", link = "https://micras.org/mwiki/Thalassa_(planet)#Mehtap_Dalgas%C4%B1_(Moonlit_Tide)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Thalassiel,30"] = { name = "Amáenarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Thalassiel,31"] = { name = "Oneiro Foteino (Dream of Illumination)", link = "https://micras.org/mwiki/Nephele#Oneiro_Foteino_(Dream_of_Illumination)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Somniumpolis|Somniumpolis]]" }
events["Thalassiel,39"] = { name = "Anniversary of victory in the New South Jangsong Campaign", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Nexa|Nexa]]" }
events["Thalassiel,44"] = { name = "Erev Galgal (Eve of Cycles)", link = "https://micras.org/mwiki/Glinos#Erev_Galgal_(Eve_of_Cycles)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Nexa|Nexa]]" }
events["Thalassiel,45"] = { name = "Bassaridia Festival", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Somniumpolis|Somniumpolis]]" }
events["Thalassiel,46"] = { name = "Osirionos", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Diamandis|Diamandis]]" }
events["Thalassiel,48"] = { name = "Anniversary of victory in the Southern Lake Morovia Campaign", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Somniumpolis|Somniumpolis]]" }
events["Thalassiel,50"] = { name = "Amaprion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Serena|Serena]]" }
events["Thalassiel,52"] = { name = "Atämiorium", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Catonis_Atrium|Catonis Atrium]]" }
events["Thalassiel,54"] = { name = "Leilat al-Kamar (Night of the Moon)", link = "https://micras.org/mwiki/Noctis#Leilat_al-Kamar_(Night_of_the_Moon)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Lunalis_Sancta|Lunalis Sancta]]" }
events["Thalassiel,56"] = { name = "Dranamorium", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Lykopolis|Lykopolis]]" }
events["Thalassiel,58"] = { name = "Aprobellium", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Thermosalem|Thermosalem]]" }
events["Thalassiel,59"] = { name = "Azorion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Pyralis|Pyralis]]" }
events["Thalassiel,60"] = { name = "Ocanarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Tel-Amin|Tel-Amin]]" }
events["Thalassiel,61"] = { name = "Taşrakah (Reverence of the Stone)", link = "https://micras.org/mwiki/Tarsica#Ta%C5%9Frakah_(Reverence_of_the_Stone)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Luminaria|Luminaria]]" }
-- Opsitheiel (Days 123..183)
events["Opsitheiel,1"] = { name = "Cricket Fall Festival", link = "https://micras.org/mwiki/Alperkin#Summer-to-Fall:_The_Cricket_Fall_Festival", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Alperkin|Alperkin]]" }
events["Opsitheiel,6"] = { name = "Chag Tvuah (Festival of Harvest)", link = "https://micras.org/mwiki/Opsithe#Chag_Tvuah_(Festival_of_Harvest)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Sylvapolis|Sylvapolis]]" }
events["Opsitheiel,7"] = { name = "Nyarion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Delphica|Delphica]]" }
events["Opsitheiel,13"] = { name = "Danassion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Diamandis|Diamandis]]" }
events["Opsitheiel,18"] = { name = "Anagenesis Eirmos (Procession of Rebirth)", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Acheron|Acheron]]" }
events["Opsitheiel,23"] = { name = "Hazamorium", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Sufriya|Sufriya]]" }
events["Opsitheiel,28"] = { name = "Panagia Therizis (Holy Day of the Reaper)", link = "https://micras.org/mwiki/Opsithe#Panagia_Therizis_(Holy_Day_of_the_Reaper)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Sylvapolis|Sylvapolis]]" }
events["Opsitheiel,30"] = { name = "The Silence", link = "https://micras.org/mwiki/Alperkin#Fall-to-Winter:_The_Silence", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Alperkin|Alperkin]]" }
events["Opsitheiel,35"] = { name = "Häparion", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Vaeringheim|Vaeringheim]]" }
events["Opsitheiel,37"] = { name = "Anniversary of victory in the Morovian Frontier Campaign", link = nil, city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Acheron|Acheron]]" }
events["Opsitheiel,43"] = { name = "Karnavali Thysias (Carnival of Celebration)", link = "https://micras.org/mwiki/Silenus#Karnavali_Thysias_(Carnival_of_Celebration)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Erythros|Erythros]]" }
events["Opsitheiel,53"] = { name = "Sefar Yashar (Straight Path Celebration)", link = "https://micras.org/mwiki/Cato_(moon)#Sefar_Yashar_(Straight_Path_Celebration)", city = "[[List_of_cities_in_Bassaridia_Vaeringheim#Catonis_Atrium|Catonis Atrium]]" }
----------------------------------------------------------------
-- 2. Orbital parameters and constants
----------------------------------------------------------------
local semiMajorAxis = 384399 -- km
local eccentricity = 0.0549 -- approximate, Earth-Moon analog
local moonRadius = 1737 -- km
-- Assume the star is Sun-like
local sunRadius = 696000 -- km
local sunDistance = 87600000 -- km (Micras–star distance)
local synodicPeriod = 24.8 -- days between successive new/full alignments
local micrasYearLength = 183 -- days per PSSC year
----------------------------------------------------------------
-- 3. Compute moon–planet distance on a given day of its orbit
-- using a simple elliptic approximation
----------------------------------------------------------------
local function getTarsicaDistance(day)
-- Compute mean anomaly M (radians)
local phase = ((day % synodicPeriod) / synodicPeriod)
local M = phase * 2 * math.pi -- mean anomaly
-- Approximate eccentric anomaly E via first-order iteration:
local E = M + eccentricity * math.sin(M)
-- True anomaly approximation → r = a * (1 - e * cos(E))
local r = semiMajorAxis * (1 - eccentricity * math.cos(E))
return r -- km
end
----------------------------------------------------------------
-- 4. Determine solar eclipse subtype from instantaneous distance
----------------------------------------------------------------
local function getSolarSubtype(day)
local r = getTarsicaDistance(day)
-- Compute angular diameters (radians)
local moonAng = 2 * math.atan(moonRadius / r)
local sunAng = 2 * math.atan(sunRadius / sunDistance)
-- Compare: if moon covers sun, total; otherwise annular
if moonAng >= sunAng then
return "Total Solar"
else
return "Annular Solar"
end
end
----------------------------------------------------------------
-- 5. Determine lunar eclipse subtype (simplified)
-- If Tarsica is near perigee, we assume total; otherwise penumbral
----------------------------------------------------------------
local function getLunarSubtype(day)
local r = getTarsicaDistance(day)
-- If within 5% of perigee, categorize as total; else penumbral
local perigee = semiMajorAxis * (1 - eccentricity)
if r <= perigee * 1.05 then
return "Total Lunar"
else
return "Penumbral Lunar"
end
end
----------------------------------------------------------------
-- 6. Zodiac definitions
----------------------------------------------------------------
local zodiacSigns = {
{ name = "Atosien", start = 1, stop = 12 },
{ name = "Eosena", start = 13, stop = 24 },
{ name = "Micrasha", start = 25, stop = 36 },
{ name = "Pyreska", start = 37, stop = 48 },
{ name = "Indomin", start = 49, stop = 61 },
{ name = "Chrysen", start = 62, stop = 73 },
{ name = "Thalassian", start = 74, stop = 85 },
{ name = "Nephelia", start = 86, stop = 97 },
{ name = "Glinaeus", start = 98, stop = 109 },
{ name = "Noctien", start = 110, stop = 122 },
{ name = "Opsithia", start = 123, stop = 133 },
{ name = "Stygian", start = 134, stop = 145 },
{ name = "Faunian", start = 146, stop = 157 },
{ name = "Silenian", start = 158, stop = 169 },
{ name = "Catosien", start = 170, stop = 183 }
}
----------------------------------------------------------------
-- 7. Helper: Determine month
----------------------------------------------------------------
local function getMonth(day)
if day <= 61 then
return "Atosiel"
elseif day <= 122 then
return "Thalassiel"
else
return "Opsitheiel"
end
end
----------------------------------------------------------------
-- 8. Helper: Determine zodiac sign
----------------------------------------------------------------
local function getZodiac(day)
for _, z in ipairs(zodiacSigns) do
if day >= z.start and day <= z.stop then
return z.name
end
end
return "Unknown"
end
----------------------------------------------------------------
-- 9. Helper: Format holiday entry
----------------------------------------------------------------
local function formatEvent(event)
if event.link and event.name then
-- Show as: [URL name] (then city in italics underneath)
return string.format("[%s %s]<br><small>''%s''</small>", event.link, event.name, event.city or "")
elseif event.name then
-- No URL, just bold name
return string.format("'''%s'''", event.name)
else
return "—"
end
end
----------------------------------------------------------------
-- 10. Main function: Build the Tarsican Saros table
----------------------------------------------------------------
function p.sarosTable(frame)
local output = {}
table.insert(output, "== Tarsican Eclipse Schedule ==\n")
table.insert(output, '{| class="wikitable sortable" style="width:100%; text-align:center;"')
table.insert(output, '|-\n! # !! Day !! Month !! Zodiac !! Eclipse Type !! Nearby Ritual or Holiday')
local eclipseDay = 18 -- Starting offset
local isSolar = true
for i = 1, 10 do
if eclipseDay > micrasYearLength then
break
end
local eclipseType, subtype
if isSolar then
eclipseType = "Solar"
subtype = getSolarSubtype(eclipseDay)
else
eclipseType = "Lunar"
subtype = getLunarSubtype(eclipseDay)
end
local month = getMonth(eclipseDay)
local zodiac = getZodiac(eclipseDay)
-- Find adjacent holiday (±1 day)
local holidayOutput = "—"
for offset = -1, 1 do
local checkDay = eclipseDay + offset
if checkDay >= 1 and checkDay <= micrasYearLength then
local checkMonth = getMonth(checkDay)
local dayInMonth = checkDay
if checkMonth == "Thalassiel" then
dayInMonth = checkDay - 61
elseif checkMonth == "Opsitheiel" then
dayInMonth = checkDay - 122
end
local key = checkMonth .. "," .. dayInMonth
if events[key] then
holidayOutput = formatEvent(events[key])
break
end
end
end
table.insert(output, string.format(
"|-\n| %d || %d || %s || %s || %s || %s",
i, eclipseDay, month, zodiac, subtype, holidayOutput
))
-- Advance to next eclipse
local drift = ((-1)^i) * 2.3
eclipseDay = math.floor(eclipseDay + synodicPeriod + drift)
isSolar = not isSolar
end
table.insert(output, "|}")
return table.concat(output, "\n")
end
return p