跳转到内容

模組:SuiStar

本页使用了标题或全文手工转换
维基百科,自由的百科全书

require('Module:Module wikitext')._addText('{{D|G3|G21|见[[U:12З4567/Wenlongtian调查]]}}')
-- Module:SuiStar 岁星纪年体系(独立模块,不依赖 SunXiu)
-- 双轨制:经说(古籍本义,太岁=岁星所居辰·同辰系)| 今说(现代反推,太岁=岁星对冲辰)
local p = {}

------------------------------------------------------------------
-- ===================== 静态数据 ================================
------------------------------------------------------------------
local XINGCI = {
    {name="析木",lon0=270,lon1=300,chen="寅",xiu="尾、箕",guo="燕"},
    {name="星纪",lon0=300,lon1=330,chen="丑",xiu="斗、牛",guo="吴越"},
    {name="玄枵",lon0=330,lon1=360,chen="子",xiu="女、虚、危",guo="齐"},
    {name="娵訾",lon0=0,  lon1=30, chen="亥",xiu="室、壁",guo="卫"},
    {name="降娄",lon0=30, lon1=60, chen="戌",xiu="奎、娄",guo="鲁"},
    {name="大梁",lon0=60, lon1=90, chen="酉",xiu="胃、昴、毕",guo="赵"},
    {name="实沈",lon0=90, lon1=120,chen="申",xiu="觜、参",guo="晋"},
    {name="鹑首",lon0=120,lon1=150,chen="未",xiu="井、鬼",guo="秦"},
    {name="鹑火",lon0=150,lon1=180,chen="午",xiu="柳、星、张",guo="周"},
    {name="鹑尾",lon0=180,lon1=210,chen="巳",xiu="翼、轸",guo="楚"},
    {name="寿星",lon0=210,lon1=240,chen="辰",xiu="角、亢",guo="郑"},
    {name="大火",lon0=240,lon1=270,chen="卯",xiu="氐、房、心",guo="宋"},
}
local CHEN_ORDER = {"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"}

-- 岁阴(十二辰别名):尔雅 / 史记异文
local SUIYIN = {
    erya = {["子"]="困敦",["丑"]="赤奋若",["寅"]="摄提格",["卯"]="单阏",
            ["辰"]="执徐",["巳"]="大荒落",["午"]="敦牂",["未"]="协洽",
            ["申"]="涒滩",["酉"]="作噩",["戌"]="阉茂",["亥"]="大渊献"},
    shiji = {["子"]="困敦",["丑"]="赤奋若",["寅"]="摄提格",["卯"]="单阏",
             ["辰"]="执徐",["巳"]="大芒落",["午"]="敦牂",["未"]="叶洽",
             ["申"]="涒滩",["酉"]="作鄂",["戌"]="淹茂",["亥"]="大渊献"},
}

-- 岁阳(十干别名):尔雅 / 史记异文
local SUIYANG = {
    erya = {["甲"]="阏逢",["乙"]="旃蒙",["丙"]="柔兆",["丁"]="强圉",
            ["戊"]="著雍",["己"]="屠维",["庚"]="上章",["辛"]="重光",
            ["壬"]="玄黓",["癸"]="昭阳"},
    shiji = {["甲"]="焉逢",["乙"]="端蒙",["丙"]="游兆",["丁"]="彊梧",
             ["戊"]="徒维",["己"]="祝犁",["庚"]="商横",["辛"]="横艾",
             ["壬"]="玄黓",["癸"]="尚章"},
}

-- 算法说明
local ALGO_NOTE = {
    jingshuo = "经说:太岁左行、岁星右行;太岁辰 = 岁星所居辰(同辰系,《史记·天官书》《淮南子·天文训》本义)",
    jinshuo = "今说:太岁 = 岁星对冲辰(黄经+180°,辰+6 位),现代天文反推之虚拟太阴",
}

-- 二十八宿名称(用于日期值宿计算)
local XIU_NAME = {"角","亢","氐","房","心","尾","箕","斗","牛","女","虚","危","室","壁",
                  "奎","娄","胃","昴","毕","觜","参","井","鬼","柳","星","张","翼","轸"}

------------------------------------------------------------------
-- ===================== 工具函数 ================================
------------------------------------------------------------------
local function mod360(x) return x - math.floor(x/360)*360 end
local function round(v,n) n=n or 2; local p=10^n; return math.floor(v*p+0.5)/p end

local function jdFromDate(y,m,d,h)
    h = h or 12
    if m <= 2 then y=y-1; m=m+12 end
    local a = math.floor(y/100)
    local b = 2 - a + math.floor(a/4)
    return math.floor(365.25*(y+4716)) + math.floor(30.6001*(m+1)) + d + b - 1524.5 + h/24
end

------------------------------------------------------------------
-- ===================== 木星位置(VSOP87 截断) =================
-- 日心平黄经 + 主要周期项,精度约 ±0.1°
------------------------------------------------------------------
local function jupiterLon(jd)
    local T = (jd - 2451545.0) / 36525.0
    local L0 = 34.3515
    local L1 = 3034.9057      -- 度/儒略世纪
    local L  = L0 + L1*T
    -- 主要周期项(幅度°, 相位°, 角速率°/世纪)
    local terms = {
        { 0.3314,  0.0,   529.6910},
        { 0.1385, 17.4,  1059.3820},
        { 0.0900, 46.0,  1589.0730},
        { 0.0488, 82.0,  2118.7640},
        { 0.0242,109.0,   2648.455},
        { 0.0138,126.0,   3178.146},
    }
    for _,t in ipairs(terms) do
        L = L + t[1]*math.cos(math.rad(t[2] + t[3]*T))
    end
    return mod360(L)
end

local function jupiterSpeed(lon, lonPrev)
    return mod360(lon - lonPrev)  -- 每日行度
end

------------------------------------------------------------------
-- ===================== 投影函数 ================================
------------------------------------------------------------------
local function lonToXingci(lon)
    for _,x in ipairs(XINGCI) do
        local lo,hi = x.lon0, x.lon1
        if lo > hi then hi = hi + 360 end       -- 跨 0°
        local l = lon
        if l < lo then l = l + 360 end
        if l >= lo and l < hi then
            local inDeg = l - lo
            return {name=x.name, chen=x.chen, xiu=x.xiu, guo=x.guo, inDeg=inDeg, lon0=lo%360, lon1=hi%360}
        end
    end
    return {name=XINGCI[1].name, chen=XINGCI[1].chen, xiu=XINGCI[1].xiu, guo=XINGCI[1].guo, inDeg=0}
end

local function chenIndex(c)
    for i,cc in ipairs(CHEN_ORDER) do if cc == c then return i end end
    return 1
end

-- 二十八宿(等距简化映射,宿度取整数)
local XIU_DEG  = {12,9,15,5,5,18,11,26,8,12,10,17,16,9,16,12,14,11,16,2,9,33,4,15,7,18,18,17}
local function lonToXiu(lon)
    local acc = 0
    for i=1,28 do
        if lon >= acc and lon < acc + XIU_DEG[i] then
            return XIU_NAME[i], lon - acc, acc
        end
        acc = acc + XIU_DEG[i]
    end
    return XIU_NAME[28], lon - acc, acc
end

------------------------------------------------------------------
-- ===================== 双轨算法 ================================
------------------------------------------------------------------
local function jingShuo(suiChen)
    -- 经说·同辰系:太岁 = 岁星所居辰
    return suiChen
end

local function jinShuo(suiChen)
    -- 今说·对冲:辰 +6 位
    local i = chenIndex(suiChen)
    return CHEN_ORDER[((i+5) % 12) + 1]
end

-- 岁名全称:岁阳 + 岁阴(基于太岁辰 + 干支轮)
local GAN = {"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"}
local ZHI = CHEN_ORDER
local function suiYearName(taisuiChen, year, track)
    track = track or "erya"
    local zhiIdx = chenIndex(taisuiChen) - 1
    local ganIdx = (year - 4) % 10   -- 公元4年为甲子
    if ganIdx <= 0 then ganIdx = ganIdx + 10 end
    local suiYang = SUIYANG[track][GAN[ganIdx]]
    local suiYin  = SUIYIN[track][taisuiChen]
    return (suiYang or "") .. (suiYin or "")
end

------------------------------------------------------------------
-- ===================== 主计算 ================================
------------------------------------------------------------------
local function compute(args)
    -- [改] 获取当前北京时间(UTC+8)作为默认值
    local utc_now = os.date("!*t")              -- 当前UTC时间
    local bj_now = os.date("*t", os.time(utc_now) + 8 * 3600)  -- 转为北京时间
    local y = tonumber(args.year)  or bj_now.year
    local m = tonumber(args.month) or bj_now.month
    local d = tonumber(args.day)   or bj_now.day
    local bj_h = tonumber(args.hour) or bj_now.hour  -- 北京时间小时(显示用)
    local track = args.track or "erya"

    -- [改] 将北京时间转换为世界时(UT)用于天文计算
    local ut_h = bj_h - 8
    local ut_day = d
    if ut_h < 0 then ut_h = ut_h + 24; ut_day = d - 1 end
    if ut_h >= 24 then ut_h = ut_h - 24; ut_day = d + 1 end

    local jd = jdFromDate(y, m, ut_day, ut_h)
    local T = (jd - 2451545.0) / 36525.0

    -- 平黄经
    local L0 = 34.3515 + 3034.9057 * T
    local period_sum = 0
    local terms = {
        { 0.3314,  0.0,   529.6910},
        { 0.1385, 17.4,  1059.3820},
        { 0.0900, 46.0,  1589.0730},
        { 0.0488, 82.0,  2118.7640},
        { 0.0242,109.0,   2648.455},
        { 0.0138,126.0,   3178.146},
    }
    for _,t in ipairs(terms) do
        period_sum = period_sum + t[1]*math.cos(math.rad(t[2] + t[3]*T))
    end
    local lon = mod360(L0 + period_sum)
    local lonPrev = jupiterLon(jd - 1)
    local speed = jupiterSpeed(lon, lonPrev)

    local xc = lonToXingci(lon)
    local xiuName, xiuIn = lonToXiu(lon)

    -- 经说
    local jc = jingShuo(xc.chen)
    -- 今说
    local nc = jinShuo(xc.chen)

    -- 日期值宿(二十八宿循环,基准:2025-01-01 12:00 UT 为角宿)
    local baseJD = jdFromDate(2025,1,1,12)
    local offset = math.floor((jd - baseJD) % 28 + 56) % 28 + 1  -- 确保正数
    local zhi_xiu = XIU_NAME[offset]

    -- ========== 新增:计算岁星赤纬与去极度 ==========
    local eps = math.rad(23.4392911)  -- 黄赤交角(约值)
    local lambda = math.rad(lon)
    local beta = 0                     -- 岁星黄纬近似为0
    local sin_dec = math.sin(beta)*math.cos(eps) + math.cos(beta)*math.sin(eps)*math.sin(lambda)
    local dec_deg = math.deg(math.asin(sin_dec))
    local quji = 90 - dec_deg         -- 去极度(度)
    -- ==============================================

    local out = {
        jd = jd,
        T = T,
        jupiter_L0 = round(L0,4),
        jupiter_period_sum = round(period_sum,4),
        jupiter_lon = round(lon,4),
        jupiter_speed = round(speed,5),
        sui_xingci = xc.name,
        sui_chen = xc.chen,
        sui_xiu = xiuName,
        sui_xiu_in_deg = round(xiuIn,2),
        -- 新增字段
        sui_xiu_quji = round(quji,2),
        sui_xiu_sysname = "《淮南子·天文训》距度",
        -- 原有字段
        jingshuo_taisui_chen = jc,
        jingshuo_suiyin = SUIYIN[track][jc],
        jingshuo_suiyang = SUIYANG[track][GAN[(((y-4)%10)<=0 and ((y-4)%10+10) or (y-4)%10)]],
        jingshuo_nianhao = suiYearName(jc,y,track),
        jingshuo_source = ALGO_NOTE.jingshuo,
        jinshuo_taisui_chen = nc,
        jinshuo_suiyin = SUIYIN[track][nc],
        jinshuo_nianhao = suiYearName(nc,y,track),
        jinshuo_note = ALGO_NOTE.jinshuo,
        xingfen = xc.guo,
        jian_chu = "岁建 " .. xc.chen .. " · 岁破 " .. jinShuo(xc.chen),
        zhi_xiu = zhi_xiu,
        -- [改] 存储北京时间(显示用),不再存UT小时
        year = y, month = m, day = d, hour = bj_h,
    }
    return out
end

------------------------------------------------------------------
-- ===================== 渲染:全面信息框 ========================
------------------------------------------------------------------
local function infoBox(args)
    local d = compute(args)
    local out = {}
    local function row(label, val, bg)
        bg = bg or "#fafcff"
        return '<tr style="background:' .. bg .. ';"><td style="width:96px;padding:6px 8px;font-weight:bold;background:#f1f4f6;border-right:1px solid #eaecf0;border-bottom:1px solid #eaecf0;">' .. label .. '</td><td style="padding:6px 8px;border-bottom:1px solid #eaecf0;">' .. val .. '</td></tr>'
    end

    -- [改] 直接用 d.hour(北京时间),标题标注 UTC+8
    local timeStr = string.format("%04d-%02d-%02d %02d:00 UTC+8", d.year, d.month, d.day, d.hour)
    table.insert(out, '<div style="width:300px;float:right;clear:right;margin:0 0 1em 1em;'..
        'border:1px solid #a2a9b1;border-radius:10px;overflow:hidden;font-family:sans-serif;'..
        'box-shadow:0 2px 6px rgba(0,0,0,.12);background:#fff;font-size:13px;line-height:1.5">')

    -- 标题
    table.insert(out, string.format(
        '<div style="background:linear-gradient(90deg,#5b6abf,#8a94d6);color:#fff;padding:10px 12px;'..
        'font-weight:bold;text-align:center;font-size:15px">岁星推算 · %s</div>', timeStr))

    -- 核心结论(修改为与太阳模块一致的格式)
    table.insert(out, string.format(
        '<div style="padding:12px;text-align:center;background:#f0f2ff;border-bottom:1px solid #eaecf0">'..
        '<div style="font-size:18px;font-weight:bold;color:#3d4a9e">岁星在【%s】之次(辰『%s』)</div>'..
        '<div style="margin-top:4px">入宿距度 <b>%.2f°</b> · 去极度 <b>%.2f°</b></div>'..
        '<div style="margin-top:4px;font-size:12px;color:#666">体系:%s · 今日值宿%s宿</div></div>',
        d.sui_xingci, d.sui_chen, d.sui_xiu_in_deg, d.sui_xiu_quji, d.sui_xiu_sysname, d.zhi_xiu))

    -- 表格开始
    table.insert(out, '<table style="width:100%;border-collapse:collapse;">')

    -- 1. 计算参数
    table.insert(out, row("计算参数",
        string.format("JD %.4f · T=%.6f<br/>平黄经 %.4f° · 周期项 %.4f°",
        d.jd, d.T, d.jupiter_L0, d.jupiter_period_sum)))

    -- 2. 岁星位置
    table.insert(out, row("岁星位置",
        string.format("真黄经 %.4f° · 日行 %.5f°<br/>在『%s』之次(辰%s)<br/>入【%s】宿 %.2f°",
        d.jupiter_lon, d.jupiter_speed, d.sui_xingci, d.sui_chen, d.sui_xiu, d.sui_xiu_in_deg), "#f6f8fa"))

    -- 3. 经说·太岁
    table.insert(out, row("经说·太岁",
        string.format("辰『%s』 · %s<br/>岁阴 %s · 岁阳 %s<br/><small>%s</small>",
        d.jingshuo_taisui_chen, d.jingshuo_nianhao, d.jingshuo_suiyin, d.jingshuo_suiyang, d.jingshuo_source)))

    -- 4. 今说·太岁
    table.insert(out, row("今说·太岁",
        string.format("辰『%s』 · %s<br/>岁阴 %s<br/><small>%s</small>",
        d.jinshuo_taisui_chen, d.jinshuo_nianhao, d.jinshuo_suiyin, d.jinshuo_note), "#f6f8fa"))

    -- 5. 分野
    table.insert(out, row("分野", d.xingfen))

    -- 6. 岁建/岁破
    table.insert(out, row("岁建/岁破", d.jian_chu, "#f6f8fa"))

    table.insert(out, '</table>')
    table.insert(out, '</div>')
    return table.concat(out)
end

------------------------------------------------------------------
-- ===================== 公开接口 ================================
------------------------------------------------------------------
function p.compute(args) return compute(args) end

function p.get(frame)
    local args = frame.args
    local d = compute(args)
    local key = args.param or args[1]
    if key and d[key] ~= nil then
        if type(d[key]) == "number" then return round(d[key],5) end
        return tostring(d[key])
    end
    return "未知参数:"..tostring(key)
end

function p.getJD(frame) return tostring(jdFromDate(tonumber(frame.args.year) or os.date("%Y"), tonumber(frame.args.month) or os.date("%m"), tonumber(frame.args.day) or os.date("%d"), tonumber(frame.args.hour) or 12)) end

function p.suiTable(frame)
    local d = compute(frame.args)
    local r = {}
    table.insert(r,'{| class="wikitable" style="text-align:center;"')
    table.insert(r,'|-')
    table.insert(r,'! 经说(古籍本义) !! 今说(现代反推)')
    table.insert(r,'|-')
    table.insert(r,'| 太岁在『'..d.jingshuo_taisui_chen..'』<br>'..d.jingshuo_nianhao..' || 太岁在『'..d.jinshuo_taisui_chen..'』<br>'..d.jinshuo_nianhao)
    table.insert(r,'|-')
    table.insert(r,'| colspan="2" | '..d.jingshuo_source..'<br>'..d.jinshuo_note)
    table.insert(r,'|}')
    return table.concat(r,"\n")
end

function p.short(frame)
    local d = compute(frame.args)
    local mode = frame.args.mode or "both"
    if mode == "jing" then
        return "经说:太岁在"..d.jingshuo_taisui_chen.."("..d.jingshuo_nianhao.."),分野"..d.xingfen
    elseif mode == "jin" then
        return "今说:太岁在"..d.jinshuo_taisui_chen.."("..d.jinshuo_nianhao.."),分野"..d.xingfen
    else
        return "岁星在"..d.sui_xingci.."("..d.sui_chen..")· 入"..d.sui_xiu.."宿"..d.sui_xiu_in_deg.."°;经说太岁在"..d.jingshuo_taisui_chen.."("..d.jingshuo_nianhao.."),今说太岁在"..d.jinshuo_taisui_chen.."("..d.jinshuo_nianhao..");分野"..d.xingfen
    end
end

function p.infoBox(frame) return infoBox(frame.args) end

return p