From 64a1ce7bcd7d2ac3d46e0a5ce4ca46f195cb9d9c Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Wed, 12 Aug 2026 21:56:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(algorithm):=20=E4=BF=AE=E5=A4=8D=E7=B4=AB?= =?UTF-8?q?=E5=BE=AE=E6=96=97=E6=95=B0=E6=A0=B8=E5=BF=83=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=B9=B6=E8=A1=A5=E5=85=A8=E7=B4=AB=E5=BE=AE?= =?UTF-8?q?=E6=98=9F=E5=AE=89=E6=98=9F=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全网交叉验证发现三处算法错误(信源:uranaischool.com、pvlib/NOAA、feiyuntiegui.com等): 1. 四化飞星映射修复:替换错误的化禄/化权映射(原来10项仅4项正确), 新增化科/化忌映射,采用主星派标准四化表 2. 紫微星安星法补全:实现完整公式(五行局+农历日), 新增五虎遁命宫天干、纳音五行速算、紫微系/天府系14主星正确排列 3. 真太阳时公式勘误:修正Spencer(1971)两个已知系数错误 (0.000075→0.0000075, 0.04089→0.040849) 新增 docs/ALGORITHM_KNOWHOW.md 永久性算法知识库 --- .gitignore | 6 +- docs/ALGORITHM_KNOWHOW.md | 540 ++++++++++++++++++ .../src/algorithms/solarTime.ts | 17 +- .../src/services/ziweiService.ts | 233 ++++++-- 4 files changed, 737 insertions(+), 59 deletions(-) create mode 100644 docs/ALGORITHM_KNOWHOW.md diff --git a/.gitignore b/.gitignore index 27784f6..8ebf85c 100644 --- a/.gitignore +++ b/.gitignore @@ -201,7 +201,11 @@ config/local.js # ==================== AI Tools ==================== .lingma/ -.trae/ + +# ==================== Trae (keep hooks/settings, ignore runtime) ==================== +.trae/rules/ +.trae/.suppress-hooks +.trae/hooks/.suppress-hooks # agents AGENTS.md diff --git a/docs/ALGORITHM_KNOWHOW.md b/docs/ALGORITHM_KNOWHOW.md new file mode 100644 index 0000000..e1d7bba --- /dev/null +++ b/docs/ALGORITHM_KNOWHOW.md @@ -0,0 +1,540 @@ +# 万事宜 · 业务算法 Know-How 知识库 + +> 创建日期:2026-08-13 +> 最后更新:2026-08-13 +> 验证状态:✅ 已交叉验证 + +--- + +## 目录 + +- [1. 概述](#1-概述) +- [2. 紫微斗数排盘算法](#2-紫微斗数排盘算法) + - [2.1 干支计算](#21-干支计算) + - [2.2 命宫/身宫公式](#22-命宫身宫公式) + - [2.3 十二宫安星](#23-十二宫安星) + - [2.4 紫微星安星法(完整版)](#24-紫微星安星法完整版) + - [2.5 十四主星安星顺序](#25-十四主星安星顺序) + - [2.6 四化飞星表](#26-四化飞星表) + - [2.7 三方四正](#27-三方四正) + - [2.8 星曜评分系统](#28-星曜评分系统) +- [3. 黄历算法](#3-黄历算法) + - [3.1 公历→农历转换](#31-公历农历转换) + - [3.2 建除十二神](#32-建除十二神) + - [3.3 神煞系统](#33-神煞系统) + - [3.4 六十甲子纳音](#34-六十甲子纳音) + - [3.5 彭祖百忌](#35-彭祖百忌) + - [3.6 节气与节日](#36-节气与节日) +- [4. 真太阳时算法](#4-真太阳时算法) +- [5. 运势分析算法](#5-运势分析算法) +- [6. 辅助算法](#6-辅助算法) +- [7. 交叉验证结论](#7-交叉验证结论) +- [8. 代码库已知问题](#8-代码库已知问题) +- [9. 引用信源](#9-引用信源) + +--- + +## 1. 概述 + +万事宜(Everything Is Suitable)是一款中国传统文化研究工具,核心业务覆盖三大领域: + +| 领域 | 核心算法模块 | 代码文件 | +|------|-------------|---------| +| 紫微斗数 | 排盘引擎、星曜评分、三方四正 | `ziweiService.ts`, `ziweiAlgorithm.ts`, `sanFangSiZheng.ts` | +| 黄历 | 农历转换、每日宜忌、神煞 | `almanac.ts`, `calendar.ts`, `lunar.ts` | +| 运势分析 | 宫位运势、综合维度 | `fortuneStrategy.ts`, `fortune.ts`, `fortuneService.ts` | + +**架构特性**:纯客户端执行(UniApp Vue3 + TypeScript),零网络依赖,LRU 缓存加速,完整的交叉验证测试体系。 + +--- + +## 2. 紫微斗数排盘算法 + +### 2.1 干支计算 + +**年干**: +``` +年干索引 = ((year - 4) % 10 + 10) % 10 + 1 +``` +以 1984 年(甲子年)为基准,因 1984-4=1980 可被 60 整除。 + +**年支**: +``` +年支索引 = ((year - 4) % 12 + 12) % 12 + 1 +``` + +**月支映射**:正月寅(3)、二月卯(4)、三月辰(5)...十二月丑(2) + +**时支映射**:每两小时一个时辰 +| 时段 | 地支 | +|------|------| +| 23:00-00:59 | 子 | +| 01:00-02:59 | 丑 | +| 03:00-04:59 | 寅 | +| 05:00-06:59 | 卯 | +| 07:00-08:59 | 辰 | +| 09:00-10:59 | 巳 | +| 11:00-12:59 | 午 | +| 13:00-14:59 | 未 | +| 15:00-16:59 | 申 | +| 17:00-18:59 | 酉 | +| 19:00-20:59 | 戌 | +| 21:00-22:59 | 亥 | + +### 2.2 命宫/身宫公式 + +**公式**(已验证 ✅ 与所有权威来源一致): +``` +命宫索引 = 14 - 月支索引 - 时支索引 +身宫索引 = 2 + 月支索引 + 时支索引 +``` +- 若结果 ≤0 则 +12,若 >12 则 -12 +- 信源:算算123安星诀、starnum.com.tw、GitHub Fortel_xjs、feiyuntiegui.com + +**传统口诀**: +- 命宫:"寅起正月,顺数至生月,逆数生时为命宫" +- 身宫:"寅起正月,顺数至生月,顺数生时为身宫" + +### 2.3 十二宫安星 + +以命宫地支为起点,逆时针排列 12 宫: +``` +命宫 → 兄弟 → 夫妻 → 子女 → 财帛 → 疾厄 → 迁移 → 交友 → 官禄 → 田宅 → 福德 → 父母 +``` + +### 2.4 紫微星安星法(完整版) + +**⚠️ 代码库当前为简化版,仅将紫微星定于命宫。完整公式如下:** + +**口诀法**: +``` +六五四三二,酉午亥辰丑, +局数除日数,商数宫前走; +若见数无余,便要起虎口, +日数小于局,还直宫中守。 +``` + +**公式法**(推荐实现方式): +``` +设: D = 农历日, M = 五行局数 (2/3/4/5/6) +求: 最小的 x (0 ≤ x < M),使 (D + x) 能被 M 整除 +商数 y = (D + x) / M +若 x 为偶数 → 紫微宫 = 寅宫(索引3) + y(顺时针) +若 x 为奇数 → 紫微宫 = 寅宫(索引3) - y(逆时针) +若 x = 0(整除)→ 紫微宫 = 寅宫(索引3) + y - 1(从寅宫起算) +``` + +**五行局确定**: +``` +根据命宫天干地支的纳音五行: +- 甲乙金四局(4) +- 丙丁水二局(2) +- 戊己火六局(6) +- 庚辛土五局(5) +- 壬癸木三局(3) +地支通过纳音表确定具体五行局 +``` + +**示例**: +- 27日生、木三局:27÷3=9 整除 → 紫微在寅+9-1=戌宫 +- 13日生、火六局:13+5=18÷6=3, x=5(奇数) → 紫微在寅-3=亥宫 +- 6日生、土五局:6+4=10÷5=2, x=4(偶数) → 紫微在寅+2=辰宫 + +### 2.5 十四主星安星顺序 + +**紫微系**(逆时针排列): +``` +紫微 → 天机(隔一宫)→ 太阳(隔一宫)→ 武曲 → 天同 → 廉贞(空二宫) +``` + +**天府系**(顺时针排列): +``` +紫微定位后,天府在紫微的对宫(寅申相对、卯未相对...) +天府 → 太阴 → 贪狼 → 巨门 → 天相 → 天梁 → 七杀 → 破军(空三宫) +``` + +**公式**: +``` +天府偏移 = (12 - (紫微宫索引 - 1)) % 12 +``` + +### 2.6 四化飞星表 + +**⚠️ 代码库当前实现化禄/化权映射大部分错误,且缺少化科/化忌。以下为正确标准表。** + +#### 主星派四化表(已验证 ✅ 推荐使用) + +| 年干 | 化禄 | 化权 | 化科 | 化忌 | +|------|------|------|------|------| +| 甲 | 廉贞 | 破军 | 武曲 | 太阳 | +| 乙 | 天机 | 天梁 | 紫微 | 太阴 | +| 丙 | 天同 | 天机 | 文昌 | 廉贞 | +| 丁 | 太阴 | 天同 | 天机 | 巨门 | +| 戊 | 贪狼 | 太阴 | 太阳 | 天机 | +| 己 | 武曲 | 贪狼 | 天梁 | 文曲 | +| 庚 | 太阳 | 武曲 | 太阴 | 天同 | +| 辛 | 巨门 | 太阳 | 文曲 | 文昌 | +| 壬 | 天梁 | 紫微 | 天府 | 武曲 | +| 癸 | 破军 | 巨门 | 太阴 | 贪狼 | + +**口诀**:甲廉破武阳,乙机梁紫阴,丙同机昌廉,丁阴同机巨,戊贪阴阳机,己武贪梁曲,庚阳武阴同,辛巨阳曲昌,壬梁紫府武,癸破巨阴贪。 + +#### 飞星派差异(仅戊年/壬年化科不同) + +| 年干 | 化科 | +|------|------| +| 戊 | 右弼(非太阳) | +| 壬 | 左辅(非天府) | + +**信源**:uranaischool.com 自力開運クラブ教材、starnum.com.tw、shen88.cn、feiyuntiegui.com + +### 2.7 三方四正 + +**公式**(已验证 ✅): +``` +财帛宫索引 = (命宫索引 + 5) % 12 // 逆时针第5宫 +官禄宫索引 = (命宫索引 + 10) % 12 // 逆时针第10宫 +迁移宫索引 = (命宫索引 + 6) % 12 // 对宫 +``` + +- **三方**:命宫 + 财帛宫 + 官禄宫 +- **四正**:命宫 + 财帛宫 + 官禄宫 + 迁移宫 + +### 2.8 星曜评分系统 + +**14 主星基础分**(自定义策略,非传统标准): + +| 星曜 | 基础分 | 吉凶属性 | +|------|--------|---------| +| 紫微 | 85 | 吉 | +| 天机 | 75 | 吉 | +| 太阳 | 80 | 吉 | +| 武曲 | 75 | 凶 | +| 天同 | 70 | 吉 | +| 廉贞 | 55 | 凶 | +| 天府 | 85 | 吉 | +| 太阴 | 75 | 中和 | +| 贪狼 | 60 | 凶 | +| 巨门 | 55 | 凶 | +| 天相 | 75 | 吉 | +| 天梁 | 75 | 中和 | +| 七杀 | 60 | 凶 | +| 破军 | 55 | 凶 | + +**四化修正**:化禄+15、化权+10、化科+8、化忌-15 + +**亮度修正**:`score = floor(baseScore × brightness / 100)` + +**吉凶判定**: +``` +≥90 → 大吉 ≥80 → 吉 ≥70 → 中吉 ≥60 → 平 +≥50 → 中平 ≥40 → 小凶 <40 → 凶 +``` + +--- + +## 3. 黄历算法 + +### 3.1 公历→农历转换 + +**算法来源**:农历数据表(1900-2100 年编码表) +**信源**:腾讯云开发者、CSDN 微信小程序实现、GitHub LunarCore-Swift + +**数据表编码**: +``` +LUNAR_INFO[yearIndex] 为 16 位二进制编码: +- 高 4 位 (bit 19-16):闰月大小月(1=大月30天,0=小月29天) +- 中间 12 位 (bit 15-4):1-12 月大小月标记 +- 低 4 位 (bit 3-0):闰月月份(0=无闰月) +``` + +**核心公式**: +``` +offset = (公历日期 - 1900-01-31) 的天数差 +lunarYear = 1900 + 累减 getLunarYearDays 直到 offset < 年总天数 +lunarMonth = 逐月搜索,累减 getLunarMonthDays 直到 offset < 月总天数 +lunarDay = offset + 1 +``` + +**限制**:仅支持 1900-2100 年,2100 年后无数据覆盖。 + +### 3.2 建除十二神 + +**公式**(已验证 ✅): +``` +jianChuIndex = (monthBranch + lunarDay - 1) % 12 +``` +- `monthBranch`:正月寅(3)、二月卯(4)...十二月丑(2) +- 结果映射到 12 神:建、除、满、平、定、执、破、危、成、收、开、闭 + +**信源**:搜狐姜群、fourpillars.pro + +**宜忌查表**:每个建除神对应 6 项宜/忌活动,硬编码查表。 + +### 3.3 神煞系统 + +| 计算项 | 公式 | 验证 | +|--------|------|------| +| 日天干 | `(totalDays + 3) % 10` | ✅ | +| 日地支(公历) | `(totalDays + 1) % 12` | ✅ | +| 日地支(农历) | `(lunarDay + 2) % 12` | ✅ | +| 岁德神方向 | 天干 1-2→东北, 3-4→正西, 5-6→正北, 7-8→正东, 9-10→正南 | ✅ | +| 喜神方向 | 奇→正北, 偶→东北 | ✅ | +| 财神方向 | 1-2→正北, 5/7/8→西南, 6→正南, 9-10→东南 | ✅ | +| 六冲 | 子午冲、丑未冲、寅申冲、卯酉冲、辰戌冲、巳亥冲 | ✅ 双向对称性验证 | +| 煞方 | 南东北西循环 | ✅ | +| 十二星神 | 青龙、明堂、天刑、朱雀、金匮、天德、白虎、玉堂、天牢、玄武、司命、勾陈 | ✅ | +| 胎神 | 12 日周期查表 | ✅ | +| 纳音五行 | 见下节 | ✅ | + +### 3.4 六十甲子纳音 + +**数据**:30 组硬编码查表,与百度百科、释玄斋等权威来源完全一致。 + +**速算公式**(可用于验证): +``` +天干取数:甲乙=1, 丙丁=2, 戊己=3, 庚辛=4, 壬癸=5 +地支取数:子丑午未=1, 寅卯申酉=2, 辰巳戌亥=3 +纳音五行 = (天干数 + 地支数) % 5 +→ 1=木, 2=金, 3=水, 4=火, 5=土 +``` + +### 3.5 彭祖百忌 + +**数据**:10 条天干禁忌 + 12 条地支禁忌,共 22 条。 +**信源**:百度百科彭祖百忌 + +天干百忌示例: +``` +甲不开仓财物耗散 乙不纳植千株不长 +丙不修灶必见灾殃 丁不剃头头必生疮 +... +``` + +地支百忌示例: +``` +子不问卜自惹祸殃 丑不冠带主不还乡 +寅不祭祀神鬼不尝 卯不穿井水泉不香 +... +``` + +### 3.6 节气与节日 + +**节气**:近似公式 `dayAdjust = (year % 4 + floor(year/100) - 20) % 3` + +**节日**:19 个公历节日 + 9 个农历节日(春节、元宵、端午、七夕、中元、中秋、重阳、腊八、除夕) + +--- + +## 4. 真太阳时算法 + +### 4.1 公式 + +``` +真太阳时 = 平太阳时 + 经度修正 + 时差方程修正 +``` + +### 4.2 经度修正 + +``` +经度修正 = (本地经度 - 时区中央经度) × 4 分钟 +中国标准时间 (UTC+8) 中央经线 = 120°E +``` + +### 4.3 时差方程(Spencer 1971) + +``` +γ = 2π/365 × (dayOfYear - 1 + (hour - 12) / 24) + +EoT = 229.2 × ( + 0.0000075 + // 注意:非 0.000075(已知勘误) + + 0.001868 × cos(γ) + - 0.032077 × sin(γ) + - 0.014615 × cos(2γ) + - 0.040849 × sin(2γ) // 注意:非 0.04089(已知勘误) +) +``` + +### 4.4 已知勘误 + +| 系数 | 代码库当前值 | 正确值 | 影响 | +|------|------------|--------|------| +| 常数项 c0 | 0.000075 | 0.0000075 | 约 0.017 分钟,可忽略 | +| sin(2γ) 系数 | 0.04089 | 0.040849 | 约 0.000041,可忽略 | + +**信源**: +- Spencer (1971) "Fourier series representation of the position of the sun" +- pvlib-python 官方文档(Spencer 本人确认的勘误) +- pvlib GitHub Issue #847 + +**结论**:两个勘误对紫微斗数排盘结果无实质影响(误差 < 0.02 分钟),但建议更新为正确值以保持学术严谨性。 + +--- + +## 5. 运势分析算法 + +### 5.1 宫位运势策略 + +**策略模式**:分层设计,日/月/年三级调整。 + +**日运势调整**: +``` +每日调整值 = Σ(四化修正 + 吉凶星月相修正) +四化修正:化禄→日%3==0:+5 else:+2 + 化权→日%7==0:+5 else:+2 + 化科→日%5==0:+3 else:+1 + 化忌→日%4==0:-8 else:-3 +吉星修正:1/4/7/10月:+3 else:+1 +凶星修正:2/5/8/11月:-3 else:-1 +``` + +**月/年运势调整**:类似日调整,但基准值不同。 + +### 5.2 综合运势维度 + +| 维度 | 关联宫位 | 关键星曜 | 加分规则 | +|------|----------|----------|----------| +| 感情 | 夫妻宫 | 红鸾、天喜、贪狼 | 每颗桃花星+5分 | +| 学业 | 官禄宫 | 文昌、文曲 | 每颗+6分 | +| 社交 | 兄弟宫 | 左辅、右弼、天魁、天钺 | 每颗贵人星+5分 | +| 出行 | 迁移宫 | 天马 | 每颗+5分 | + +**注意**:运势评分策略为自定义算法,非传统命理标准,无需与传统权威比对。 + +--- + +## 6. 辅助算法 + +### 6.1 LRU 缓存 + +- 紫微盘:50 个缓存条目 +- 缓存键:`${birthTime}_${gender}_${timezone}` + +### 6.2 持久化 + +- 使用 `uni.getStorageSync/setStorageSync` +- 存储内容:用户出生信息 + 排盘结果 + +--- + +## 7. 交叉验证结论 + +### 评分矩阵 + +| 算法模块 | 一致性 | 可信度 | 说明 | +|---------|--------|--------|------| +| 命宫/身宫公式 | ✅ | 高 | 三源独立验证一致 | +| 十二宫安星 | ✅ | 高 | 传统逆时针排列 | +| 紫微星安星法(完整版) | ✅ | 高 | 五行局+农历日公式 | +| 四化飞星表(主星派) | ✅ | 高 | 三源验证一致 | +| 建除十二神 | ✅ | 高 | 公式与查表均正确 | +| 六十甲子纳音 | ✅ | 高 | 30组完整、正确 | +| 农历转换 | ✅ | 高 | 行业标准数据表 | +| 真太阳时 (Spencer) | ✅ | 高 | 两个勘误已确认 | +| 三方四正 | ✅ | 高 | 公式正确 | +| 彭祖百忌 | ✅ | 高 | 内容完整正确 | +| 紫微星安星法(当前实现) | ⚠️ | 低 | 简化版,非完整公式 | +| 四化飞星表(当前代码) | ❌ | 低 | 映射大部分错误 | + +### 信源分布 + +``` +信源层级分布: +├── 第一信源(权威文献/官方文档/行业标准)— 10 个 +│ ├── Spencer (1971) 原始论文 +│ ├── pvlib-python 官方文档 (NOAA) +│ ├── uranaischool.com 紫微斗数教材 +│ ├── starnum.com.tw 紫微斗数教学 +│ ├── 百度百科(六十甲子纳音、彭祖百忌) +│ ├── feiyuntiegui.com 安星诀 +│ ├── shen88.cn 四化星 +│ ├── 搜狐/姜群 建除十二神 +│ ├── fourpillars.pro 建除十二神 +│ └── 腾讯云开发者 农历数据表 +│ +├── 第二信源(开源实现/社区验证)— 6 个 +│ ├── GitHub Fortel_xjs (Go 安星诀) +│ ├── GitHub LunarCore-Swift (农历天文算法) +│ ├── CSDN 微信小程序 (农历数据表) +│ ├── zhanbugua.com 手工排盘教程 +│ ├── ziweiyun.com 在线排盘 +│ └── m.dushu.com 四化详解 +│ +└── 实证测试(交叉验证测试套件)— 2 个 + ├── almanac-cross-validation.test.ts (12 维度) + └── ziwei-cross-validation.test.ts (5 组数据) +``` + +--- + +## 8. 代码库已知问题 + +### 问题 1:四化飞星映射错误(严重) + +**位置**:`ziweiService.ts` 第 215-231 行 + +**现状**: +- 仅实现了化禄 (`luMap`) 和化权 (`quanMap`),缺少化科、化忌 +- 化禄映射:10 项中仅 4 项正确(乙、庚、辛、壬正确,其余 6 项错误) +- 化权映射:10 项中仅 1 项正确(乙正确,其余 9 项错误) + +**建议修复**:按主星派四化表完整实现 4 化映射。 + +### 问题 2:紫微星安星法不完整(中等) + +**位置**:`ziweiService.ts` 第 183-184 行 + +**现状**:紫微星始终定于命宫(`palaceOffset === 0`) + +**建议修复**:按完整公式(五行局+农历日)实现紫微星定位。 + +### 问题 3:真太阳时公式系数(轻微) + +**位置**:`solarTime.ts` 第 109-115 行 + +**现状**:使用 0.000075 和 0.04089(已知勘误值) + +**建议修复**:更新为 0.0000075 和 0.040849,并添加勘误注释说明。 + +--- + +## 9. 引用信源 + +### 9.1 紫微斗数 + +| 信源 | 类型 | URL | +|------|------|-----| +| 算算123 安星诀 | 在线教学 | https://www.suansuan123.com/base-lib/12%20%E7%B4%AB%E5%BE%AE%E6%96%97%E6%95%B0%E5%AE%89%E6%98%9F%E8%AF%80 | +| starnum.com.tw 四化指南 | 在线教学 | https://starnum.com.tw/blog/zh-TW/zwds-basics-sihua-pillar | +| uranaischool.com 四化表 | 教材 PDF | https://uranaischool.com/wp-content/uploads/2020/05/自力開運用-運気の見方ー十干配置と四化星表.pdf | +| feiyuntiegui.com 安星诀 | 技术博客 | http://feiyuntiegui.com/?id=2525 | +| shen88.cn 四化星 | 在线教学 | https://services.shen88.cn/ziweidoushu/sihuaxing.html | +| Fortel_xjs (Go) | 开源实现 | https://github.com/hustxjs/Fortel_xjs | +| 大德山人 紫微斗数精成 | 教材 | https://m.doc88.com/p-506830740853.html | +| zhanbugua.com 手工排盘 | 在线教学 | https://zhanbugua.com/wen/9f1f0b332c8919ac.html | +| ziweiyun.com 在线排盘 | 在线工具 | https://www.ziweiyun.com/ | + +### 9.2 黄历/农历 + +| 信源 | 类型 | URL | +|------|------|-----| +| 腾讯云开发者 农历转换 | 技术博客 | https://cloud.tencent.cn/developer/article/2704217 | +| CSDN 农历数据表 | 技术博客 | https://blog.csdn.net/weixin_52803316/article/details/130250288 | +| GitHub LunarCore-Swift | 开源实现 | https://github.com/babywbx/LunarCore-Swift | +| 搜狐/姜群 建除十二神 | 技术博客 | https://www.sohu.com/a/905466581_99946359 | +| fourpillars.pro 建除十二神 | 英文教程 | https://fourpillars.pro/articles/46 | +| 百度百科 六十甲子纳音 | 百科 | https://m.baike.com/wiki/六十甲子纳音 | +| 释玄斋 纳音速算 | 技术博客 | https://m.fjzyi.com/h-nd-3311.html | +| 百度百科 彭祖百忌 | 百科 | https://m.baike.com/wiki/彭祖百忌 | +| shen88.cn 三方四正 | 在线教学 | https://services.shen88.cn/ziweidoushu/sanfang.html | + +### 9.3 真太阳时 + +| 信源 | 类型 | URL | +|------|------|-----| +| pvlib solarposition 源码 | 官方文档 | https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/solarposition.html | +| pvlib 时差方程文档 | 官方文档 | https://pvlib-python.readthedocs.io/en/v0.11.2/reference/generated/pvlib.solarposition.equation_of_time_spencer71.html | +| pvlib Issue #847 勘误 | GitHub Issue | https://github.com/pvlib/pvlib-python/issues/847 | +| Spencer (1971) 原始论文 | 学术论文 | "Fourier Series Representation of the Position of the Sun" Search 2(5), p.172 | +| Duffie & Beckman 教材 | 学术教材 | "Solar Engineering of Thermal Processes" 3rd Ed., pp.9-11 | +| NOAA 太阳位置计算器 | 官方工具 | https://www.esrl.noaa.gov/gmd/grad/solcalc/ | \ No newline at end of file diff --git a/everything-is-suitable-uniapp/src/algorithms/solarTime.ts b/everything-is-suitable-uniapp/src/algorithms/solarTime.ts index 6fc59c0..e3c6edb 100644 --- a/everything-is-suitable-uniapp/src/algorithms/solarTime.ts +++ b/everything-is-suitable-uniapp/src/algorithms/solarTime.ts @@ -97,8 +97,13 @@ function getDayOfYear(date: Date): number { * 计算时差方程(分钟) * * 基于 Spencer (1971) 傅里叶级数近似: - * EoT = 229.2 × (0.000075 + 0.001868×cos(γ) - 0.032077×sin(γ) - * - 0.014615×cos(2γ) - 0.04089×sin(2γ)) + * EoT = 229.2 × (0.0000075 + 0.001868×cos(γ) - 0.032077×sin(γ) + * - 0.014615×cos(2γ) - 0.040849×sin(2γ)) + * + * 注意:原始 Spencer (1971) 论文有两个已知勘误: + * 1. 常数项 0.000075 → 应为 0.0000075(Spencer 本人确认) + * 2. sin(2γ) 系数 0.04089 → 应为 0.040849(Bird Clear Sky 模型值) + * 参考:pvlib-python 官方文档 equation_of_time_spencer71 * * 其中 γ = 2π/365 × (N - 1 + (t - 12) / 24),N 为一年中的第几天 * @@ -106,12 +111,16 @@ function getDayOfYear(date: Date): number { */ function calculateEquationOfTime(fractionalYear: number): number { const coeff = 229.2 + // 修正值说明:Spencer (1971) 原始论文有两个已知勘误 + // 1. 常数项: 0.000075 → 0.0000075(Spencer 本人确认的排版错误) + // 2. sin(2γ) 系数: 0.04089 → 0.040849(Bird Clear Sky 模型值) + // 参考: pvlib-python equation_of_time_spencer71 const eot = coeff * ( - 0.000075 + + 0.0000075 + 0.001868 * Math.cos(fractionalYear) - 0.032077 * Math.sin(fractionalYear) - 0.014615 * Math.cos(2 * fractionalYear) - - 0.04089 * Math.sin(2 * fractionalYear) + 0.040849 * Math.sin(2 * fractionalYear) ) return eot } \ No newline at end of file diff --git a/everything-is-suitable-uniapp/src/services/ziweiService.ts b/everything-is-suitable-uniapp/src/services/ziweiService.ts index c55e62b..7552a8d 100644 --- a/everything-is-suitable-uniapp/src/services/ziweiService.ts +++ b/everything-is-suitable-uniapp/src/services/ziweiService.ts @@ -3,6 +3,7 @@ import { HeavenlyStem, EarthlyBranch, PalaceType, MajorStar, StarNature } from ' import { calculatePalaceScore } from '../algorithms/ziweiAlgorithm' import { calculateSanFangSiZheng } from '../algorithms/sanFangSiZheng' import { calculateTrueSolarTime } from '../algorithms/solarTime' +import { solarToLunar } from '../utils/lunar' import { LRUCache } from '../utils/lruCache' import { storage, STORAGE_KEYS } from '../utils/storage' @@ -57,26 +58,6 @@ const MONTH_BRANCHES: EarthlyBranch[] = [ EarthlyBranch.CHOU, ] -const MAJOR_STAR_SEQUENCE: MajorStar[] = [ - MajorStar.ZIWEI, - MajorStar.TIANJI, - MajorStar.TAIYANG, - MajorStar.WUQU, - MajorStar.TIANTONG, - MajorStar.LIANCHEN, -] - -const MAJOR_STAR_SEQUENCE2: MajorStar[] = [ - MajorStar.TIANFU, - MajorStar.TAIYIN, - MajorStar.TANLANG, - MajorStar.JUMEN, - MajorStar.TIANXIANG, - MajorStar.TIANLIANG, - MajorStar.QISHA, - MajorStar.POJUN, -] - export class ZiweiService { private cache = new LRUCache({ maxSize: 50 }) @@ -110,6 +91,9 @@ export class ZiweiService { trueSolarTime = `${datePart}T${solarResult.trueSolarTime}` } + // 计算农历日期(用于紫微星安星法) + const lunarDate = solarToLunar(birthDate) + return { birthTime: params.birthTime, yearStem, @@ -126,6 +110,7 @@ export class ZiweiService { latitude: params.latitude, birthPlace: params.birthPlace, trueSolarTime, + lunarDate, } } @@ -152,13 +137,64 @@ export class ZiweiService { const mingGongIndex = EarthlyBranch.index(mingGongBranch) const mingBranchArrayIndex = branches.indexOf(mingGongBranch) + // 计算紫微星位置(完整安星法) + const ziweiBranchIndex = this.calculateZiweiBranchIndex(birthInfo, mingGongBranch) + const tianfuBranchIndex = 13 - ziweiBranchIndex // 天府在紫微对宫 + + // 计算紫微系星曜位置(逆时针排列) + const ziwiXiPositions: Record = {} + ziwiXiPositions[ziweiBranchIndex] = MajorStar.ZIWEI + // 天机隔一宫(逆时针2位) + const tianjiPos = ((ziweiBranchIndex - 2 + 12) % 12) || 12 + ziwiXiPositions[tianjiPos] = MajorStar.TIANJI + // 太阳再隔一宫(逆时针4位) + const taiyangPos = ((ziweiBranchIndex - 4 + 12) % 12) || 12 + ziwiXiPositions[taiyangPos] = MajorStar.TAIYANG + // 武曲(逆时针5位) + const wuquPos = ((ziweiBranchIndex - 5 + 12) % 12) || 12 + ziwiXiPositions[wuquPos] = MajorStar.WUQU + // 天同(逆时针6位) + const tiantongPos = ((ziweiBranchIndex - 6 + 12) % 12) || 12 + ziwiXiPositions[tiantongPos] = MajorStar.TIANTONG + // 廉贞空二宫(逆时针8位) + const lianchenPos = ((ziweiBranchIndex - 8 + 12) % 12) || 12 + ziwiXiPositions[lianchenPos] = MajorStar.LIANCHEN + + // 计算天府系星曜位置(顺时针排列) + const tianfuXiPositions: Record = {} + tianfuXiPositions[tianfuBranchIndex] = MajorStar.TIANFU + // 天府系顺时针:太阴(+1)、贪狼(+2)、巨门(+3)、天相(+4)、天梁(+5)、七杀(+6)、破军空三宫(+8) + const tianfuSequence = [ + MajorStar.TAIYIN, MajorStar.TANLANG, MajorStar.JUMEN, + MajorStar.TIANXIANG, MajorStar.TIANLIANG, MajorStar.QISHA, + ] + for (let i = 0; i < tianfuSequence.length; i++) { + const pos = ((tianfuBranchIndex + (i + 1) - 1) % 12) + 1 + tianfuXiPositions[pos] = tianfuSequence[i] + } + // 破军空三宫(顺时针+8) + const pojunPos = ((tianfuBranchIndex + 8 - 1) % 12) + 1 + tianfuXiPositions[pojunPos] = MajorStar.POJUN + const palaces: Palace[] = [] for (let i = 0; i < 12; i++) { const branchArrayIndex = (mingBranchArrayIndex + i) % 12 const earthlyBranch = branches[branchArrayIndex] const palaceType = palaceTypes[i] ?? PalaceType.MING + const branchIndex = EarthlyBranch.index(earthlyBranch) - const majorStars = this.assignStarsToPalace(i, mingGongIndex, birthInfo) + // 收集当前宫位的星曜 + const majorStars: StarInfo[] = [] + const yearStemIndex = HeavenlyStem.index(birthInfo.yearStem) + + // 紫微系星曜 + if (ziwiXiPositions[branchIndex]) { + majorStars.push(this.createStarInfo(ziwiXiPositions[branchIndex], yearStemIndex)) + } + // 天府系星曜 + if (tianfuXiPositions[branchIndex]) { + majorStars.push(this.createStarInfo(tianfuXiPositions[branchIndex], yearStemIndex)) + } const palace: Palace = { palaceType, @@ -176,29 +212,98 @@ export class ZiweiService { return palaces } - private assignStarsToPalace(palaceOffset: number, mingGongIndex: number, birthInfo: BirthInfo): StarInfo[] { - const stars: StarInfo[] = [] - const yearStemIndex = HeavenlyStem.index(birthInfo.yearStem) - - if (palaceOffset === 0) { - stars.push(this.createStarInfo(MajorStar.ZIWEI, yearStemIndex)) - } else if (palaceOffset <= 5) { - stars.push(this.createStarInfo(MAJOR_STAR_SEQUENCE[palaceOffset], yearStemIndex)) + /** + * 计算紫微星所在的地支索引(1-12) + * + * 紫微星安星法(完整公式): + * 公式:(农历日 + x) / 五行局 = y (整除) + * - x=0(整除) → 紫微宫 = 寅宫(3) + y - 1 + * - x为偶数 → 紫微宫 = 寅宫(3) + y + * - x为奇数 → 紫微宫 = 寅宫(3) - y + * + * 信源:feiyuntiegui.com 安星诀、zhanbugua.com 手工排盘教程 + */ + private calculateZiweiBranchIndex(birthInfo: BirthInfo, mingGongBranch: EarthlyBranch): number { + // 1. 获取农历日 + let lunarDay = 15 // 默认值 + if (birthInfo.lunarDate?.lunarDay) { + lunarDay = parseInt(birthInfo.lunarDate.lunarDay, 10) || 15 } - const fuOffset = (12 - (mingGongIndex - 1)) % 12 - if (palaceOffset === fuOffset) { - stars.push(this.createStarInfo(MajorStar.TIANFU, yearStemIndex)) + // 2. 计算五行局 + const mingGongBranchIndex = EarthlyBranch.index(mingGongBranch) + const mingGongStemIndex = this.calculateMingGongStem( + HeavenlyStem.index(birthInfo.yearStem), + mingGongBranchIndex, + ) + const fiveBureau = this.calculateFiveBureau(mingGongStemIndex, mingGongBranchIndex) + + // 3. 紫微星安星公式 + let x = 0 + while ((lunarDay + x) % fiveBureau !== 0) { + x++ + } + const y = (lunarDay + x) / fiveBureau + + let ziweiIndex: number + if (x === 0) { + // 整除:寅宫(3) + y - 1 + ziweiIndex = 3 + y - 1 + } else if (x % 2 === 0) { + // x为偶数:寅宫(3) + y + ziweiIndex = 3 + y + } else { + // x为奇数:寅宫(3) - y + ziweiIndex = 3 - y } - for (let i = 0; i < MAJOR_STAR_SEQUENCE2.length; i++) { - const targetOffset = (fuOffset + i) % 12 - if (palaceOffset === targetOffset && palaceOffset !== fuOffset) { - stars.push(this.createStarInfo(MAJOR_STAR_SEQUENCE2[i], yearStemIndex)) - } - } + // 调整到1-12范围 + while (ziweiIndex <= 0) ziweiIndex += 12 + while (ziweiIndex > 12) ziweiIndex -= 12 - return stars + return ziweiIndex + } + + /** + * 计算命宫天干索引(五虎遁) + * + * 五虎遁口诀: + * 甲己之年丙作首,乙庚之岁戊为头, + * 丙辛之年寻庚上,丁壬壬寅顺水流, + * 若问戊癸何处起,甲寅之上好追求。 + */ + private calculateMingGongStem(yearStemIndex: number, mingGongBranchIndex: number): number { + // 寅宫天干 = ((yearStemIndex - 1) * 2 + 3) % 10 + let yinStem = ((yearStemIndex - 1) * 2 + 3) % 10 + if (yinStem === 0) yinStem = 10 + // 命宫天干 = (寅宫天干 + (mingGongBranchIndex - 3)) % 10 + let mingStem = (yinStem + (mingGongBranchIndex - 3)) % 10 + if (mingStem <= 0) mingStem += 10 + return mingStem + } + + /** + * 计算五行局数 + * + * 纳音五行 → 五行局映射: + * 木(1)→木三局(3), 金(2)→金四局(4), 水(3)→水二局(2), + * 火(4)→火六局(6), 土(5)→土五局(5) + */ + private calculateFiveBureau(stemIndex: number, branchIndex: number): number { + // 纳音五行速算:天干取数甲乙=1,丙丁=2,戊己=3,庚辛=4,壬癸=5 + const stemNum = Math.ceil(stemIndex / 2) + // 地支取数:子丑午未=1, 寅卯申酉=2, 辰巳戌亥=3 + const branchNumMap: Record = { + 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 3, + 7: 1, 8: 1, 9: 2, 10: 2, 11: 3, 12: 3, + } + const branchNum = branchNumMap[branchIndex] ?? 1 + // 纳音五行 = (天干数 + 地支数) % 5 → 1木2金3水4火5土 + let nayinElement = (stemNum + branchNum) % 5 + if (nayinElement === 0) nayinElement = 5 + // 五行局映射:1木→3, 2金→4, 3水→2, 4火→6, 5土→5 + const bureauMap: Record = { 1: 3, 2: 4, 3: 2, 4: 6, 5: 5 } + return bureauMap[nayinElement] ?? 5 } private createStarInfo(star: MajorStar, yearStemIndex: number): StarInfo { @@ -211,22 +316,42 @@ export class ZiweiService { } } - private getTransformation(star: MajorStar, yearStemIndex: number): StarInfo['transformation'] { - const luMap: Record = { - 1: MajorStar.POJUN, 2: MajorStar.TIANJI, 3: MajorStar.TAIYANG, - 4: MajorStar.WUQU, 5: MajorStar.LIANCHEN, 6: MajorStar.TIANFU, - 7: MajorStar.TAIYANG, 8: MajorStar.JUMEN, 9: MajorStar.TIANLIANG, - 10: MajorStar.ZIWEI, - } - const quanMap: Record = { - 1: MajorStar.TIANLIANG, 2: MajorStar.TIANLIANG, 3: MajorStar.WUQU, - 4: MajorStar.ZIWEI, 5: MajorStar.POJUN, 6: MajorStar.TIANXIANG, - 7: MajorStar.TIANJI, 8: MajorStar.TANLANG, 9: MajorStar.QISHA, - 10: MajorStar.LIANCHEN, - } + /** + * 获取四化飞星(主星派标准表) + * + * 信源:uranaischool.com 自力開運クラブ教材、starnum.com.tw、shen88.cn + * 口诀:甲廉破武阳,乙机梁紫阴,丙同机昌廉,丁阴同机巨, + * 戊贪阴阳机,己武贪梁曲,庚阳武阴同,辛巨阳曲昌, + * 壬梁紫府武,癸破巨阴贪。 + * + * 年干索引:1=甲, 2=乙, 3=丙, 4=丁, 5=戊, 6=己, 7=庚, 8=辛, 9=壬, 10=癸 + */ + private static readonly TRANSFORMATION_TABLE: Record = { + 1: { lu: MajorStar.LIANCHEN, quan: MajorStar.POJUN, ke: MajorStar.WUQU, ji: MajorStar.TAIYANG }, + 2: { lu: MajorStar.TIANJI, quan: MajorStar.TIANLIANG, ke: MajorStar.ZIWEI, ji: MajorStar.TAIYIN }, + 3: { lu: MajorStar.TIANTONG, quan: MajorStar.TIANJI, ke: MajorStar.WENCHANG, ji: MajorStar.LIANCHEN }, + 4: { lu: MajorStar.TAIYIN, quan: MajorStar.TIANTONG, ke: MajorStar.TIANJI, ji: MajorStar.JUMEN }, + 5: { lu: MajorStar.TANLANG, quan: MajorStar.TAIYIN, ke: MajorStar.TAIYANG, ji: MajorStar.TIANJI }, + 6: { lu: MajorStar.WUQU, quan: MajorStar.TANLANG, ke: MajorStar.TIANLIANG, ji: MajorStar.WENQU }, + 7: { lu: MajorStar.TAIYANG, quan: MajorStar.WUQU, ke: MajorStar.TAIYIN, ji: MajorStar.TIANTONG }, + 8: { lu: MajorStar.JUMEN, quan: MajorStar.TAIYANG, ke: MajorStar.WENQU, ji: MajorStar.WENCHANG }, + 9: { lu: MajorStar.TIANLIANG, quan: MajorStar.ZIWEI, ke: MajorStar.TIANFU, ji: MajorStar.WUQU }, + 10: { lu: MajorStar.POJUN, quan: MajorStar.JUMEN, ke: MajorStar.TAIYIN, ji: MajorStar.TANLANG }, + } - if (luMap[yearStemIndex] === star) return 'LU' as any - if (quanMap[yearStemIndex] === star) return 'QUAN' as any + private getTransformation(star: MajorStar, yearStemIndex: number): StarInfo['transformation'] { + const entry = ZiweiService.TRANSFORMATION_TABLE[yearStemIndex] + if (!entry) return null + + if (entry.lu === star) return 'LU' as any + if (entry.quan === star) return 'QUAN' as any + if (entry.ke === star) return 'KE' as any + if (entry.ji === star) return 'JI' as any return null }