Module:HostStars: Difference between revisions

From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs)
Created page with "--------------------------------------------------------- -- Module: HostStars_Midnight -- Purpose: Provide a "starsForTonight" function that: -- 1) Uses Micras' 183-day orbit, 24-hour day, 22.5° tilt, -- 2) Approximates day length (~9..15 hours) with a sine wave, -- 3) Assigns random star rise/set times within the night, -- 4) Checks if each star is visible at EXACTLY midnight (00:00). -- -- Usage: -- {{#invoke:HostStars_Midnight|starsForTonight}} -----------..."
 
NewZimiaGov (talk | contribs)
No edit summary
Line 1: Line 1:
---------------------------------------------------------
---------------------------------------------------------
-- Module: HostStars_Midnight
-- Module: HostStars_AccurateMap
-- Purpose: Provide a "starsForTonight" function that:
-- Tries a more "chart-based" star logic for top image,
--  1) Uses Micras' 183-day orbit, 24-hour day, 22.5° tilt,
-- focusing on whether each star is visible at midnight
--  2) Approximates day length (~9..15 hours) with a sine wave,
-- from Vaeringheim (~44°N) on a given day-of-year.
--  3) Assigns random star rise/set times within the night,
--   4) Checks if each star is visible at EXACTLY midnight (00:00).
--
--
-- Usage:
-- * Some stars become circumpolar if star-lat >= 46°.
--   {{#invoke:HostStars_Midnight|starsForTonight}}
-- * Others have a sine-based season: visible ~ half the year.
---------------------------------------------------------
---------------------------------------------------------


Line 14: Line 12:


---------------------------------------------------------
---------------------------------------------------------
-- 1. dayOfYear (Micras-based)
-- 1. dayOfYear (Micras-based):
--    183-day orbit, start date=6 Aug 1999 => day=0
--    183-day orbit, epoch = 6 Aug 1999 => day=0
---------------------------------------------------------
---------------------------------------------------------
local function getDayOfYearForPSSC()
local function getDayOfYearForPSSC()
     local startDate  = os.time({year = 1999, month = 8, day = 6})
     local startDate  = os.time({year=1999, month=8, day=6})
     local secPerDay  = 86400
     local secPerDay  = 86400
     local daysPerYear = 183
     local daysPerYear = 183
Line 25: Line 23:
     local totalDays  = math.floor((currentTime - startDate) / secPerDay)
     local totalDays  = math.floor((currentTime - startDate) / secPerDay)


    -- fractional year
     local fracYear = totalDays / daysPerYear
     local yearFrac    = totalDays / daysPerYear
     local psscYear= math.floor(fracYear)
     local psscYear   = math.floor(yearFrac)
     local dayOfYear= math.floor((fracYear-psscYear)*daysPerYear) + 1
     local dayOfYear   = math.floor((yearFrac - psscYear) * daysPerYear) + 1
     return dayOfYear
     return dayOfYear
end
end


---------------------------------------------------------
---------------------------------------------------------
-- 2. Sine-based day length at latitude=44°, tilt=22.5°
-- 2. Our star data (N. hemisphere top image).
--    We'll approximate day length from ~9h to ~15h:
--    We'll keep a "latitude" (really 'declination'-like).
--      dayLength = base + amplitude * sin(2π*(dayOfYear - offset)/183)
--    where base=12, amplitude=3 => range [9..15].
--    offset=81 => "longest day" near day 81, purely by choice.
---------------------------------------------------------
---------------------------------------------------------
local function getApproxDayLength(dayOfYear)
local starDataNorth = {
     local base     = 12   -- 12h is average day length
    { name="Agave",    starClass="M",      lat=29.5 },
     local amplitude = 3    -- ±3 => from 9 to 15
    { name="Amaäz",    starClass="K",      lat=31  },
     local offset    = 81   -- shift so max day ~ mid-year
    { name="Amáenu",    starClass="G",      lat=45  },
 
     { name="Amap",     starClass="A",      lat=55   },
     local sineTerm = math.sin( (2 * math.pi / 183) * (dayOfYear - offset) )
     { name="Amazä",    starClass="F",      lat=39  },
     local length    = base + amplitude * sineTerm
    { name="Atämios",  starClass="F",      lat=56  },
     return length  -- hours
    { name="Aprobelle", starClass="A",      lat=59  },
end
    { name="Azos",      starClass="Galaxy",  lat=60  },
    { name="Bebeakaus", starClass="K",      lat=30.5 },
    { name="Bulhanu",  starClass="A",      lat=40  },
    { name="Crösacío",  starClass="K",      lat=17  },
    { name="Danaß",    starClass="A",      lat=67  },
    { name="Dilëtaz",  starClass="G",      lat=36  },
    { name="Dranamos",  starClass="A",      lat=58  },
    { name="Gaht",      starClass="G",      lat=29  },
    { name="Häpi",      starClass="F",      lat=78  },
    { name="Hazaméos",  starClass="A",      lat=72  },
     { name="Liléigos",  starClass="F",      lat=18   },
    { name="Nyama",    starClass="F",      lat=64  },
     { name="Ocananus", starClass="F",      lat=60.5 },
    { name="Orebele",  starClass="F",      lat=22  },
    { name="Osiríos",  starClass="G",      lat=53  },
    { name="Pythe",    starClass="A",      lat=45  },
    { name="Sanashalo", starClass="G",      lat=27  },
    { name="Tä",        starClass="F",      lat=39.5 },
     { name="Vï",        starClass="A",      lat=46  },
     { name="Wedíos",    starClass="A",      lat=54  },
}


---------------------------------------------------------
---------------------------------------------------------
-- 3. Star Data (from "Top Image," Northern Hemisphere)
-- 3. Our location: Vaeringheim ~44°N
---------------------------------------------------------
---------------------------------------------------------
local starDataNorth = {
local observerLat = 44
    { name="Agave",    starClass="M",      latitude=29.5 },
    { name="Amaäz",    starClass="K",      latitude=31  },
    { name="Amáenu",    starClass="G",      latitude=45  },
    { name="Amap",      starClass="A",      latitude=55  },
    { name="Amazä",    starClass="F",      latitude=39  },
    { name="Atämios",  starClass="F",      latitude=56  },
    { name="Aprobelle", starClass="A",      latitude=59  },
    { name="Azos",      starClass="Galaxy",  latitude=60  },
    { name="Bebeakaus", starClass="K",      latitude=30.5 },
    { name="Bulhanu",  starClass="A",      latitude=40  },
    { name="Crösacío",  starClass="K",      latitude=17  },
    { name="Danaß",    starClass="A",      latitude=67  },
    { name="Dilëtaz",  starClass="G",      latitude=36  },
    { name="Dranamos",  starClass="A",      latitude=58  },
    { name="Gaht",      starClass="G",      latitude=29  },
    { name="Häpi",      starClass="F",      latitude=78  },
    { name="Hazaméos",  starClass="A",      latitude=72  },
    { name="Liléigos",  starClass="F",      latitude=18  },
    { name="Nyama",    starClass="F",      latitude=64  },
    { name="Ocananus",  starClass="F",      latitude=60.5 },
    { name="Orebele",  starClass="F",      latitude=22  },
    { name="Osiríos",  starClass="G",      latitude=53  },
    { name="Pythe",    starClass="A",      latitude=45  },
    { name="Sanashalo", starClass="G",      latitude=27  },
    { name="Tä",        starClass="F",      latitude=39.5 },
    { name="Vï",        starClass="A",      latitude=46  },
    { name="Wedíos",    starClass="A",      latitude=54  },
}


---------------------------------------------------------
---------------------------------------------------------
-- 4. Convert decimal hours to "HH:MM"
-- 4. Check "circumpolar?"  
--    For star-lat >= (90 - observerLat)=46 => circumpolar
---------------------------------------------------------
---------------------------------------------------------
local function hourToHM(decimalHour)
local function isCircumpolar(starLat)
     local hh = math.floor(decimalHour)
     local limit = 90 - observerLat
    local mm = math.floor((decimalHour - hh) * 60 + 0.5)
     return (starLat >= limit)
    hh = hh % 24
     return string.format("%02d:%02d", hh, mm)
end
end


---------------------------------------------------------
---------------------------------------------------------
-- 5. MAIN: starsForTonight
-- 5. For non-circumpolar stars, we do a "season window" logic:
--    1) dayOfYear => approximate dayLen => nightLen=24-dayLen
--    We'll define star "peak visibility" day = some function of lat.
--    2) define sunrise, sunset
--    Then we define it visible for ~ half the year around that peak.
--    3) star rise in [sunset..(sunset+some fraction)], set later
--    4) check if star is visible at midnight (00:00 local).
---------------------------------------------------------
---------------------------------------------------------
function p.starsForTonight(frame)
local function isStarUpAtMidnight(starLat, dayOfYear)
     local dayOfYear = getDayOfYearForPSSC()
     -- let "peak" day = 10 * starLat mod 183 (some made-up formula)
     local dailySeed = (os.date("*t").year * 1000) + dayOfYear
    -- or let's do simpler: starLat*2 => wrap mod 183
    math.randomseed(dailySeed)
     local peakDay = (starLat*2) % 183


     local dayLen  = getApproxDayLength(dayOfYear)  -- ~9..15
     -- we define "visible range" ~ +/- 45 days from peak => 90 days total
     local nightLen= 24 - dayLen
    -- that means about half the year. Tweak as desired
     local halfWindow = 45


     -- We'll assume "sunrise = 12 - dayLen/2"
     -- difference in dayOfYear
     -- so day centered around ~noon. Then "sunset = sunrise + dayLen"
    local diff = dayOfYear - peakDay
     local sunrise = 12 - (dayLen/2)
     -- wrap around 183 => we want minimal cyc mod 183
     local sunset  = sunrise + dayLen
     diff = (diff + 183) % 183  -- ensures in [0..182]
     if diff>91.5 then
        diff = 183 - diff  -- symmetrical
    end


     -- We'll define star rise times after sunset, up until well into night
     return (diff <= halfWindow)
    local nightStart = sunset
end
    local nightEnd  = math.min(sunset + nightLen, 24)  -- clamp to midnight if needed


    local queryTime  = 0.0  -- EXACT midnight local time
---------------------------------------------------------
-- 6. Build the table
---------------------------------------------------------
function p.starsForMidnight(frame)
    local dayOfYear = getDayOfYearForPSSC()


     local out = {}
     local out = {}
     table.insert(out, "=== Host Stars Visible at Midnight (Vaeringheim ~44°N) ===\n")
     table.insert(out, "=== Stars Visible at Midnight in Vaeringheim (44°N) ===\n")
     table.insert(out, string.format(
     table.insert(out, string.format("''Day %d of Micras Year (183 days)''\n\n", dayOfYear))
        "''DayOfYear=%d, Approx. DayLen=%.1fh, Sunrise=%.2f, Sunset=%.2f''\n\n",
        dayOfYear, dayLen, sunrise, sunset
    ))
 
     table.insert(out, '{| class="wikitable" style="width:100%; text-align:left;"\n')
     table.insert(out, '{| class="wikitable" style="width:100%; text-align:left;"\n')
     table.insert(out, "! Star Name || Star Class || Rise Time || Set Time || Visible @ 00:00?\n")
     table.insert(out, "! Star Name !! Star Class !! Circumpolar? !! Visible at 00:00? !! Note\n")


     for _, star in ipairs(starDataNorth) do
     for _, star in ipairs(starDataNorth) do
        -------------------------------------------------------
         local circ = isCircumpolar(star.lat)
        -- random star rise in the evening or late night
         local isUp
        -------------------------------------------------------
        local note=""
         local rise = nightStart + math.random()*(nightLen*0.5)
         if rise>24 then
            rise = rise - 24  -- crosses midnight
        end


         -- star set: some random fraction after rise
         if circ then
         local starSet = rise + math.random()*(nightLen*0.5 + 1.0)
            isUp = true
        if starSet>24 then
            note="Circumpolar => always above horizon"
             starSet = starSet -24
         else
            local val = isStarUpAtMidnight(star.lat, dayOfYear)
            isUp = val
             note="Season-based ~ half-year window"
         end
         end


        -- Format times
         local circStr = circ and "Yes" or "No"
        local riseStr = hourToHM(rise)
         local upStr  = isUp and "Yes" or "No"
         local setStr  = hourToHM(starSet)
 
        -- is star up at midnight?
        local isVisible
        if rise < starSet then
            -- typical (doesn't cross midnight)
            if (rise <= queryTime) and (queryTime < starSet) then
                isVisible = "Yes"
            else
                isVisible = "No"
            end
         else
            -- star crosses midnight
            if (queryTime >= rise) or (queryTime < starSet) then
                isVisible = "Yes"
            else
                isVisible = "No"
            end
        end


         table.insert(out, "|-\n")
         table.insert(out, "|-\n")
         table.insert(out, string.format("| %s || %s || %s || %s || %s\n",
         table.insert(out, string.format("| %s || %s || %s || %s || %s\n",
             star.name, star.starClass, riseStr, setStr, isVisible))
             star.name, star.starClass, circStr, upStr, note
        ))
     end
     end



Revision as of 06:55, 27 December 2024

Documentation for this module may be created at Module:HostStars/doc

---------------------------------------------------------
-- Module: HostStars_AccurateMap
-- Tries a more "chart-based" star logic for top image,
-- focusing on whether each star is visible at midnight
-- from Vaeringheim (~44°N) on a given day-of-year.
--
-- * Some stars become circumpolar if star-lat >= 46°.
-- * Others have a sine-based season: visible ~ half the year.
---------------------------------------------------------

local p = {}

---------------------------------------------------------
-- 1. dayOfYear (Micras-based):
--    183-day orbit, epoch = 6 Aug 1999 => day=0
---------------------------------------------------------
local function getDayOfYearForPSSC()
    local startDate   = os.time({year=1999, month=8, day=6})
    local secPerDay   = 86400
    local daysPerYear = 183

    local currentTime = os.time()
    local totalDays   = math.floor((currentTime - startDate) / secPerDay)

    local fracYear = totalDays / daysPerYear
    local psscYear= math.floor(fracYear)
    local dayOfYear= math.floor((fracYear-psscYear)*daysPerYear) + 1
    return dayOfYear
end

---------------------------------------------------------
-- 2. Our star data (N. hemisphere top image).
--    We'll keep a "latitude" (really 'declination'-like).
---------------------------------------------------------
local starDataNorth = {
    { name="Agave",     starClass="M",       lat=29.5 },
    { name="Amaäz",     starClass="K",       lat=31   },
    { name="Amáenu",    starClass="G",       lat=45   },
    { name="Amap",      starClass="A",       lat=55   },
    { name="Amazä",     starClass="F",       lat=39   },
    { name="Atämios",   starClass="F",       lat=56   },
    { name="Aprobelle", starClass="A",       lat=59   },
    { name="Azos",      starClass="Galaxy",  lat=60   },
    { name="Bebeakaus", starClass="K",       lat=30.5 },
    { name="Bulhanu",   starClass="A",       lat=40   },
    { name="Crösacío",  starClass="K",       lat=17   },
    { name="Danaß",     starClass="A",       lat=67   },
    { name="Dilëtaz",   starClass="G",       lat=36   },
    { name="Dranamos",  starClass="A",       lat=58   },
    { name="Gaht",      starClass="G",       lat=29   },
    { name="Häpi",      starClass="F",       lat=78   },
    { name="Hazaméos",  starClass="A",       lat=72   },
    { name="Liléigos",  starClass="F",       lat=18   },
    { name="Nyama",     starClass="F",       lat=64   },
    { name="Ocananus",  starClass="F",       lat=60.5 },
    { name="Orebele",   starClass="F",       lat=22   },
    { name="Osiríos",   starClass="G",       lat=53   },
    { name="Pythe",     starClass="A",       lat=45   },
    { name="Sanashalo", starClass="G",       lat=27   },
    { name="Tä",        starClass="F",       lat=39.5 },
    { name="Vï",        starClass="A",       lat=46   },
    { name="Wedíos",    starClass="A",       lat=54   },
}

---------------------------------------------------------
-- 3. Our location: Vaeringheim ~44°N
---------------------------------------------------------
local observerLat = 44

---------------------------------------------------------
-- 4. Check "circumpolar?" 
--    For star-lat >= (90 - observerLat)=46 => circumpolar
---------------------------------------------------------
local function isCircumpolar(starLat)
    local limit = 90 - observerLat
    return (starLat >= limit)
end

---------------------------------------------------------
-- 5. For non-circumpolar stars, we do a "season window" logic:
--    We'll define star "peak visibility" day = some function of lat.
--    Then we define it visible for ~ half the year around that peak.
---------------------------------------------------------
local function isStarUpAtMidnight(starLat, dayOfYear)
    -- let "peak" day = 10 * starLat mod 183 (some made-up formula)
    -- or let's do simpler: starLat*2 => wrap mod 183
    local peakDay = (starLat*2) % 183

    -- we define "visible range" ~ +/- 45 days from peak => 90 days total
    -- that means about half the year. Tweak as desired
    local halfWindow = 45

    -- difference in dayOfYear
    local diff = dayOfYear - peakDay
    -- wrap around 183 => we want minimal cyc mod 183
    diff = (diff + 183) % 183  -- ensures in [0..182]
    if diff>91.5 then 
        diff = 183 - diff   -- symmetrical
    end

    return (diff <= halfWindow)
end

---------------------------------------------------------
-- 6. Build the table
---------------------------------------------------------
function p.starsForMidnight(frame)
    local dayOfYear = getDayOfYearForPSSC()

    local out = {}
    table.insert(out, "=== Stars Visible at Midnight in Vaeringheim (44°N) ===\n")
    table.insert(out, string.format("''Day %d of Micras Year (183 days)''\n\n", dayOfYear))
    table.insert(out, '{| class="wikitable" style="width:100%; text-align:left;"\n')
    table.insert(out, "! Star Name !! Star Class !! Circumpolar? !! Visible at 00:00? !! Note\n")

    for _, star in ipairs(starDataNorth) do
        local circ = isCircumpolar(star.lat)
        local isUp
        local note=""

        if circ then
            isUp = true
            note="Circumpolar => always above horizon"
        else
            local val = isStarUpAtMidnight(star.lat, dayOfYear)
            isUp = val
            note="Season-based ~ half-year window"
        end

        local circStr = circ and "Yes" or "No"
        local upStr   = isUp and "Yes" or "No"

        table.insert(out, "|-\n")
        table.insert(out, string.format("| %s || %s || %s || %s || %s\n",
            star.name, star.starClass, circStr, upStr, note
        ))
    end

    table.insert(out, "|}\n")
    return table.concat(out)
end

return p