ziwei-cli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/SKILL.md +142 -0
- package/bin/install.cjs +249 -0
- package/bin/ziwei.js +151 -0
- package/lib/commands/bazi.js +35 -0
- package/lib/commands/palace.js +156 -0
- package/lib/commands/synastry.js +124 -0
- package/lib/engine/astrolabe.js +216 -0
- package/lib/engine/bazi.js +207 -0
- package/lib/engine/config.js +167 -0
- package/lib/engine/geo.js +252 -0
- package/lib/engine/patterns.js +371 -0
- package/lib/engine/solar.js +211 -0
- package/lib/engine/synastry.js +437 -0
- package/package.json +49 -0
- package/system-prompt.md +539 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 紫微斗数格局检测模块
|
|
3
|
+
* 检测命盘中的各种格局
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PALACE_NAMES, MAJOR_14 } from './config.js';
|
|
7
|
+
|
|
8
|
+
const SHA_SET = new Set(["擎羊", "陀罗", "火星", "铃星", "地空", "地劫"]);
|
|
9
|
+
|
|
10
|
+
// 格局定义
|
|
11
|
+
const PATTERN_COPY = {
|
|
12
|
+
junchen_qinghui_A: { group: "富贵格", title: "君臣庆会A", blurb: "紫破同宫,左右辅弼分拱,主位与辅佐齐备,利权位与统筹。" },
|
|
13
|
+
junchen_qinghui_B: { group: "富贵格", title: "君臣庆会B", blurb: "紫微天相坐命,命迁分见昌曲,科名与口碑相应,利名望与制度。" },
|
|
14
|
+
junchen_qinghui_C: { group: "富贵格", title: "君臣庆会C", blurb: "天府坐命,左右会机梁与同阴,主从分明,资源稳健。" },
|
|
15
|
+
zifu_tonggong: { group: "富贵格", title: "紫府同宫", blurb: "紫微天府同宫,统筹与守成并见,利大平台与稳态经营。" },
|
|
16
|
+
jinyu_fujia: { group: "富贵格", title: "金舆扶驾", blurb: "天府守命,左右会日月,内外辅佐兼备,名器加身。" },
|
|
17
|
+
zifu_jiaming: { group: "富贵格", title: "紫府夹命", blurb: "命坐机月,左右夹紫府,谋定而后动,得贵佐助。" },
|
|
18
|
+
jixiang_liming: { group: "富贵格", title: "极向离明", blurb: "紫微在午会清局,权名昭著,忌三方煞忌冲破。" },
|
|
19
|
+
|
|
20
|
+
big5_shapolang: { group: "五大格局", title: "杀破狼格", blurb: "动中求变、攻坚突破,宜开拓与竞技,忌情绪化对撞。" },
|
|
21
|
+
big5_fuxiang: { group: "五大格局", title: "府相格", blurb: "稳重守成、执行见长,宜大组织内管控配合,稳中求进。" },
|
|
22
|
+
big5_jiyue_tongliang: { group: "五大格局", title: "机月同梁格", blurb: "温和细致、专业匠心,按部就班,宜稳定岗位或自由才艺线。" },
|
|
23
|
+
big5_ziwulian_fuxiang: { group: "五大格局", title: "紫武廉府相", blurb: "统筹驾驭与稳重并存,能主导亦能配合,宜高阶管理/政务统筹。" },
|
|
24
|
+
big5_ziwulian_shapolang: { group: "五大格局", title: "紫武廉杀破狼", blurb: "能文能武、敢战能守,宜阶段化目标与护城河建设稳态推进。" },
|
|
25
|
+
|
|
26
|
+
huotan: { group: "爆发格", title: "火贪", blurb: "贪狼会火,果敢激进,宜冲锋开拓,忌躁进损耗。" },
|
|
27
|
+
lingtan: { group: "爆发格", title: "铃贪", blurb: "贪狼会铃,灵敏多变,宜创意试错,注意节奏与稳定。" },
|
|
28
|
+
huo_or_ling_tan_weak: { group: "爆发格", title: "火/铃贪(弱)", blurb: "贪狼与火/铃三方会,动能较弱,适合阶段性试点。" },
|
|
29
|
+
|
|
30
|
+
shapolang: { group: "事业格", title: "杀破狼", blurb: "变动开拓加速,适合攻坚重构,需制度化控险。" },
|
|
31
|
+
yangliang_changlu: { group: "事业格", title: "阳梁昌禄", blurb: "名位俱隆,有制度与文书背书,利晋升与公开场域。" },
|
|
32
|
+
qisha_chaodou: { group: "事业格", title: "七杀朝斗", blurb: "七杀坐命(子午寅申),锋锐果断,宜攻坚突破与执法军警线。" },
|
|
33
|
+
yingxing_rumiao: { group: "事业格", title: "英星入庙", blurb: "破军坐命(子/午),变革重来,宜重构升级,忌反复与冲毁。" },
|
|
34
|
+
|
|
35
|
+
sanqi_jiahui: { group: "财禄格", title: "三奇加会", blurb: "禄权科齐聚,资源/权责/名望共振,利关键节点。" },
|
|
36
|
+
luma_jiaochi: { group: "财禄格", title: "禄马交驰", blurb: "俸禄与机动并举,利财务流转与外勤奔走。" },
|
|
37
|
+
luhe_yuanyang: { group: "财禄格", title: "禄合鸳鸯", blurb: "禄存同宫化禄,财源并合,利俸禄加成与回报兑现。" },
|
|
38
|
+
shuanglu_chaoyuan: { group: "四化格局", title: "双禄朝垣", blurb: "三方四正会照禄存与化禄,财运亨通,尤利财官两宫。" },
|
|
39
|
+
|
|
40
|
+
zuoyou_jiaming: { group: "辅助格", title: "左右夹命", blurb: "左右夹命,内外助力相随,宜发挥主位统筹。" },
|
|
41
|
+
kuiyue_jiaming: { group: "辅助格", title: "魁钺夹命", blurb: "魁钺夹命,贵气与解题并见,利关键节点获提携。" },
|
|
42
|
+
|
|
43
|
+
liangma_piaodang: { group: "负面格", title: "梁马飘荡", blurb: "奔波不定、聚少散多,适合短期流动项目,避免长期绑定。" },
|
|
44
|
+
lu_feng_chongpo: { group: "四化格局", title: "禄逢冲破", blurb: "化禄遭空劫夹破或对宫化忌冲照,先得后失,虚发易破。" },
|
|
45
|
+
|
|
46
|
+
ziwei_duzuo: { group: "基本盘", title: "紫微独坐", blurb: "帝星独座(子/午),自尊独立,午位更显庙势与统筹感。" },
|
|
47
|
+
baiguan_chaogong: { group: "基本盘", title: "百官朝拱", blurb: "紫微坐命,三方成对吉星齐集(≥3对),贵气与援助俱足。" },
|
|
48
|
+
zaiye_goujun: { group: "基本盘", title: "在野孤君", blurb: "紫微坐命,三方少吉亦无煞,清高独行,需自建体系。" },
|
|
49
|
+
wudao_zhijun: { group: "基本盘", title: "无道之君", blurb: "紫微坐命,三方见煞/忌冲破,易骄矜误判,宜制衡控险。" },
|
|
50
|
+
zipo_tonggong: { group: "基本盘", title: "紫破组合", blurb: "紫微+破军同宫(丑/未),先破后成,大器晚成,变革强。" },
|
|
51
|
+
zitan_tonggong: { group: "基本盘", title: "紫贪组合", blurb: "紫微+贪狼同宫(卯/酉),欲望与权柄并行,需驾欲定边界。" },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 工具函数
|
|
55
|
+
function _left(i) { return (i - 1 + 12) % 12; }
|
|
56
|
+
function _right(i) { return (i + 1) % 12; }
|
|
57
|
+
function _opp(i) { return (i + 6) % 12; }
|
|
58
|
+
|
|
59
|
+
function buildMaps(chart, scope) {
|
|
60
|
+
const palStars = {};
|
|
61
|
+
const palMutagen = {};
|
|
62
|
+
const nameToIdx = {};
|
|
63
|
+
const idxToName = {};
|
|
64
|
+
const idxToBranch = {};
|
|
65
|
+
|
|
66
|
+
if (!scope) {
|
|
67
|
+
for (let i = 0; i < 12; i++) {
|
|
68
|
+
const palaceName = PALACE_NAMES[i];
|
|
69
|
+
const palace = chart.palace(palaceName);
|
|
70
|
+
|
|
71
|
+
nameToIdx[palace.name] = palace.index;
|
|
72
|
+
idxToName[palace.index] = palace.name;
|
|
73
|
+
idxToBranch[palace.index] = palace.earthlyBranch;
|
|
74
|
+
|
|
75
|
+
const stars = new Set();
|
|
76
|
+
const muts = new Set();
|
|
77
|
+
|
|
78
|
+
for (const s of palace.majorStars || []) {
|
|
79
|
+
stars.add(s.name);
|
|
80
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
81
|
+
}
|
|
82
|
+
for (const s of palace.minorStars || []) {
|
|
83
|
+
stars.add(s.name);
|
|
84
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
85
|
+
}
|
|
86
|
+
for (const s of palace.adjectiveStars || []) {
|
|
87
|
+
stars.add(s.name);
|
|
88
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
palStars[palace.index] = stars;
|
|
92
|
+
palMutagen[palace.index] = muts;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
for (let i = 0; i < 12; i++) {
|
|
96
|
+
const palaceName = PALACE_NAMES[i];
|
|
97
|
+
const palace = chart.palace(palaceName, scope);
|
|
98
|
+
|
|
99
|
+
nameToIdx[palaceName] = palace.index;
|
|
100
|
+
idxToName[palace.index] = palaceName;
|
|
101
|
+
idxToBranch[palace.index] = palace.earthlyBranch;
|
|
102
|
+
|
|
103
|
+
const stars = new Set();
|
|
104
|
+
const muts = new Set();
|
|
105
|
+
|
|
106
|
+
for (const s of palace.majorStars || []) {
|
|
107
|
+
stars.add(s.name);
|
|
108
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
109
|
+
}
|
|
110
|
+
for (const s of palace.minorStars || []) {
|
|
111
|
+
stars.add(s.name);
|
|
112
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
113
|
+
}
|
|
114
|
+
for (const s of palace.adjectiveStars || []) {
|
|
115
|
+
stars.add(s.name);
|
|
116
|
+
if (s.mutagen) muts.add(s.mutagen);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
palStars[palace.index] = stars;
|
|
120
|
+
palMutagen[palace.index] = muts;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { palStars, palMutagen, nameToIdx, idxToName, idxToBranch };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function starsIn(palStars, idx, stars) {
|
|
128
|
+
const palaceStars = palStars[idx] || new Set();
|
|
129
|
+
for (const star of stars) {
|
|
130
|
+
if (!palaceStars.has(star)) return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function anyStarIn(palStars, idx, stars) {
|
|
136
|
+
const palaceStars = palStars[idx] || new Set();
|
|
137
|
+
for (const star of stars) {
|
|
138
|
+
if (palaceStars.has(star)) return true;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function noShaJi(palStars, palMutagen, idxs) {
|
|
144
|
+
for (const i of idxs) {
|
|
145
|
+
const stars = palStars[i] || new Set();
|
|
146
|
+
const muts = palMutagen[i] || new Set();
|
|
147
|
+
for (const sha of SHA_SET) {
|
|
148
|
+
if (stars.has(sha)) return false;
|
|
149
|
+
}
|
|
150
|
+
if (muts.has("忌")) return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function hit(pid, reason) {
|
|
156
|
+
const copy = PATTERN_COPY[pid];
|
|
157
|
+
return {
|
|
158
|
+
id: pid,
|
|
159
|
+
reason: reason,
|
|
160
|
+
title: copy?.title || pid,
|
|
161
|
+
blurb: copy?.blurb || ""
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 检测命盘格局
|
|
167
|
+
* @param {Object} chart - iztro 星盘对象
|
|
168
|
+
* @param {string} scope - 运限范围(可选)
|
|
169
|
+
* @returns {Array} 检测到的格局列表
|
|
170
|
+
*/
|
|
171
|
+
export function detectPatterns(chart, scope = null) {
|
|
172
|
+
const { palStars, palMutagen, nameToIdx, idxToName, idxToBranch } = buildMaps(chart, scope);
|
|
173
|
+
|
|
174
|
+
const ret = [];
|
|
175
|
+
|
|
176
|
+
const iMing = nameToIdx["命宫"];
|
|
177
|
+
const iCai = nameToIdx["财帛"];
|
|
178
|
+
const iGuan = nameToIdx["官禄"];
|
|
179
|
+
const iQian = nameToIdx["迁移"];
|
|
180
|
+
const iLeft = _left(iMing);
|
|
181
|
+
const iRight = _right(iMing);
|
|
182
|
+
const tri = [iMing, iCai, iGuan, iQian];
|
|
183
|
+
const tri3 = [iMing, iCai, iGuan];
|
|
184
|
+
|
|
185
|
+
// 紫微独坐
|
|
186
|
+
const mingMajor = new Set([...(palStars[iMing] || new Set())].filter(s => MAJOR_14.has(s)));
|
|
187
|
+
if (mingMajor.size === 1 && mingMajor.has("紫微") && ["子", "午"].includes(idxToBranch[iMing])) {
|
|
188
|
+
ret.push(hit("ziwei_duzuo", `紫微独坐于${idxToBranch[iMing]}支`));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 百官朝拱
|
|
192
|
+
if ((palStars[iMing] || new Set()).has("紫微")) {
|
|
193
|
+
const triUnion = new Set();
|
|
194
|
+
for (const j of tri) for (const s of (palStars[j] || new Set())) triUnion.add(s);
|
|
195
|
+
const pairSets = [
|
|
196
|
+
new Set(["天魁", "天钺"]), new Set(["文昌", "文曲"]), new Set(["左辅", "右弼"]),
|
|
197
|
+
new Set(["三台", "八座"]), new Set(["恩光", "天贵"]), new Set(["台辅", "封诰"]), new Set(["天官", "天福"])
|
|
198
|
+
];
|
|
199
|
+
let pairCount = 0;
|
|
200
|
+
for (const ps of pairSets) {
|
|
201
|
+
let ok = true;
|
|
202
|
+
for (const st of ps) if (!triUnion.has(st)) { ok = false; break; }
|
|
203
|
+
if (ok) pairCount += 1;
|
|
204
|
+
}
|
|
205
|
+
if (pairCount >= 3) {
|
|
206
|
+
ret.push(hit("baiguan_chaogong", `紫微坐命,三方四正成对吉星≥3对(实际${pairCount}对)`));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const goodSet = new Set(["天魁", "天钺", "文昌", "文曲", "左辅", "右弼", "三台", "八座", "恩光", "天贵", "台辅", "封诰", "天官", "天福"]);
|
|
210
|
+
let hasGood = false;
|
|
211
|
+
for (const j of tri) if ([...(palStars[j] || new Set())].some(s => goodSet.has(s))) { hasGood = true; break; }
|
|
212
|
+
const hasShaOrJi = !noShaJi(palStars, palMutagen, tri);
|
|
213
|
+
|
|
214
|
+
if (!hasGood && !hasShaOrJi) {
|
|
215
|
+
ret.push(hit("zaiye_goujun", "紫微坐命,三方四正少吉亦无煞忌"));
|
|
216
|
+
}
|
|
217
|
+
if (hasShaOrJi) {
|
|
218
|
+
ret.push(hit("wudao_zhijun", "紫微坐命,三方四正见煞/忌冲破"));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 紫破组合
|
|
223
|
+
for (let i = 0; i < 12; i++) {
|
|
224
|
+
if (starsIn(palStars, i, new Set(["紫微", "破军"])) && ["丑", "未"].includes(idxToBranch[i])) {
|
|
225
|
+
ret.push(hit("zipo_tonggong", `${idxToName[i]}同宫紫微+破军`));
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 紫贪组合
|
|
231
|
+
for (let i = 0; i < 12; i++) {
|
|
232
|
+
if (starsIn(palStars, i, new Set(["紫微", "贪狼"])) && ["卯", "酉"].includes(idxToBranch[i])) {
|
|
233
|
+
ret.push(hit("zitan_tonggong", `${idxToName[i]}同宫紫微+贪狼`));
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 紫府同宫
|
|
239
|
+
for (let i = 0; i < 12; i++) {
|
|
240
|
+
if (starsIn(palStars, i, new Set(["紫微", "天府"]))) {
|
|
241
|
+
ret.push(hit("zifu_tonggong", `${idxToName[i]}同宫见紫微+天府`));
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 极向离明
|
|
247
|
+
if ((palStars[iMing] || new Set()).has("紫微") && idxToBranch[iMing] === "午") {
|
|
248
|
+
if (noShaJi(palStars, palMutagen, tri)) {
|
|
249
|
+
ret.push(hit("jixiang_liming", "命宫紫微在午,三方无煞忌"));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 火贪/铃贪
|
|
254
|
+
if ((palStars[iMing] || new Set()).has("贪狼")) {
|
|
255
|
+
if ((palStars[iMing] || new Set()).has("火星")) {
|
|
256
|
+
ret.push(hit("huotan", "命宫同宫:贪狼+火星"));
|
|
257
|
+
} else if ((palStars[iMing] || new Set()).has("铃星")) {
|
|
258
|
+
ret.push(hit("lingtan", "命宫同宫:贪狼+铃星"));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const triUnion = new Set();
|
|
263
|
+
for (const j of tri) {
|
|
264
|
+
for (const star of palStars[j] || []) {
|
|
265
|
+
triUnion.add(star);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (triUnion.has("贪狼") && (triUnion.has("火星") || triUnion.has("铃星"))) {
|
|
270
|
+
ret.push(hit("huo_or_ling_tan_weak", "命三方四正会照:贪+火/铃"));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 杀破狼
|
|
274
|
+
if (triUnion.has("七杀") && triUnion.has("破军") && triUnion.has("贪狼")) {
|
|
275
|
+
ret.push(hit("shapolang", "命三方四正集齐:七杀/破军/贪狼"));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 五大格局
|
|
279
|
+
const tri3Union = new Set();
|
|
280
|
+
for (const j of tri3) for (const s of (palStars[j] || new Set())) tri3Union.add(s);
|
|
281
|
+
const tri3Major = new Set([...tri3Union].filter(s => MAJOR_14.has(s)));
|
|
282
|
+
|
|
283
|
+
if (["七杀", "破军", "贪狼"].every(s => tri3Major.has(s))) {
|
|
284
|
+
ret.push(hit("big5_shapolang", "命/财/官三宫主星包含:七杀、破军、贪狼"));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const fuxiangSet = new Set(["天府", "天相"]);
|
|
288
|
+
if (tri3Major.size > 0 && [...tri3Major].every(s => fuxiangSet.has(s))) {
|
|
289
|
+
ret.push(hit("big5_fuxiang", "命/财/官三宫主星仅由天府/天相构成"));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const jiyueSet = new Set(["天机", "太阴", "天同", "天梁"]);
|
|
293
|
+
if (tri3Major.size >= 3 && [...tri3Major].every(s => jiyueSet.has(s))) {
|
|
294
|
+
ret.push(hit("big5_jiyue_tongliang", "命/财/官三宫主星由天机/太阴/天同/天梁构成"));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 阳梁昌禄
|
|
298
|
+
const need = new Set(["太阳", "天梁", "文昌", "禄存"]);
|
|
299
|
+
let hasAll = true;
|
|
300
|
+
for (const star of need) {
|
|
301
|
+
if (!triUnion.has(star)) { hasAll = false; break; }
|
|
302
|
+
}
|
|
303
|
+
if (hasAll) {
|
|
304
|
+
ret.push(hit("yangliang_changlu", "命三方四正集齐:日/梁/昌/禄"));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 七杀朝斗
|
|
308
|
+
if ((palStars[iMing] || new Set()).has("七杀") && ["子", "午", "寅", "申"].includes(idxToBranch[iMing])) {
|
|
309
|
+
ret.push(hit("qisha_chaodou", `七杀坐命,命支=${idxToBranch[iMing]}`));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 英星入庙
|
|
313
|
+
if ((palStars[iMing] || new Set()).has("破军") && ["子", "午"].includes(idxToBranch[iMing])) {
|
|
314
|
+
ret.push(hit("yingxing_rumiao", `破军坐命,命支=${idxToBranch[iMing]}`));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 三奇加会
|
|
318
|
+
const hasLu = tri.some(j => palMutagen[j] && palMutagen[j].has("禄"));
|
|
319
|
+
const hasQuan = tri.some(j => palMutagen[j] && palMutagen[j].has("权"));
|
|
320
|
+
const hasKe = tri.some(j => palMutagen[j] && palMutagen[j].has("科"));
|
|
321
|
+
if (hasLu && hasQuan && hasKe) {
|
|
322
|
+
ret.push(hit("sanqi_jiahui", "命三方四正见化禄/化权/化科"));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 禄马交驰
|
|
326
|
+
for (let i = 0; i < 12; i++) {
|
|
327
|
+
if (starsIn(palStars, i, new Set(["禄存", "天马"]))) {
|
|
328
|
+
ret.push(hit("luma_jiaochi", `${idxToName[i]}同宫禄存+天马`));
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 禄合鸳鸯
|
|
334
|
+
for (let i = 0; i < 12; i++) {
|
|
335
|
+
if ((palStars[i] || new Set()).has("禄存") && (palMutagen[i] || new Set()).has("禄")) {
|
|
336
|
+
ret.push(hit("luhe_yuanyang", `${idxToName[i]}同宫禄存+化禄`));
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 双禄朝垣
|
|
342
|
+
const triHasLucun = tri.some(j => (palStars[j] || new Set()).has("禄存"));
|
|
343
|
+
const triHasHualu = tri.some(j => (palMutagen[j] || new Set()).has("禄"));
|
|
344
|
+
if (triHasLucun && triHasHualu) {
|
|
345
|
+
if (noShaJi(palStars, palMutagen, tri)) {
|
|
346
|
+
ret.push(hit("shuanglu_chaoyuan", "命三方四正会照:禄存与化禄,三方无煞忌"));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 左右夹命
|
|
351
|
+
if (((palStars[iLeft] || new Set()).has("左辅") && (palStars[iRight] || new Set()).has("右弼")) ||
|
|
352
|
+
((palStars[iRight] || new Set()).has("左辅") && (palStars[iLeft] || new Set()).has("右弼"))) {
|
|
353
|
+
ret.push(hit("zuoyou_jiaming", "左右邻宫分见左辅与右弼"));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 魁钺夹命
|
|
357
|
+
if (((palStars[iLeft] || new Set()).has("天魁") && (palStars[iRight] || new Set()).has("天钺")) ||
|
|
358
|
+
((palStars[iRight] || new Set()).has("天魁") && (palStars[iLeft] || new Set()).has("天钺"))) {
|
|
359
|
+
ret.push(hit("kuiyue_jiaming", "左右邻宫分见天魁与天钺"));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// 梁马飘荡
|
|
363
|
+
for (let i = 0; i < 12; i++) {
|
|
364
|
+
if (starsIn(palStars, i, new Set(["天梁", "天马"]))) {
|
|
365
|
+
ret.push(hit("liangma_piaodang", `${idxToName[i]}同宫天梁+天马`));
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return ret;
|
|
371
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 真太阳时计算器
|
|
3
|
+
* 基于天文算法实现高精度真太阳时计算
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const J2000 = 2451545.0;
|
|
7
|
+
const DEGREES_TO_RADIANS = Math.PI / 180.0;
|
|
8
|
+
const RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 计算儒略日 (Julian Day)
|
|
12
|
+
*/
|
|
13
|
+
function calculateJulianDay(year, month, day, hour = 0, minute = 0, second = 0) {
|
|
14
|
+
const fractionalDay = (hour + minute / 60.0 + second / 3600.0) / 24.0;
|
|
15
|
+
|
|
16
|
+
if (month <= 2) {
|
|
17
|
+
year -= 1;
|
|
18
|
+
month += 12;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const a = Math.floor(year / 100);
|
|
22
|
+
const b = Math.floor(a / 4);
|
|
23
|
+
const c = 2 - a + b;
|
|
24
|
+
const e = Math.floor(365.25 * (year + 4716));
|
|
25
|
+
const f = Math.floor(30.6001 * (month + 1));
|
|
26
|
+
|
|
27
|
+
return c + day + e + f - 1524.5 + fractionalDay;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 角度归一化到0-360度
|
|
32
|
+
*/
|
|
33
|
+
function normalizeAngle(angle) {
|
|
34
|
+
let normalized = angle % 360;
|
|
35
|
+
if (normalized < 0) normalized += 360;
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 计算太阳平黄经
|
|
41
|
+
*/
|
|
42
|
+
function calculateMeanLongitude(T) {
|
|
43
|
+
let L0 = 280.46646 + 36000.76983 * T + 0.0003032 * T * T;
|
|
44
|
+
return normalizeAngle(L0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 计算太阳平近点角
|
|
49
|
+
*/
|
|
50
|
+
function calculateMeanAnomaly(T) {
|
|
51
|
+
let M = 357.52911 + 35999.05029 * T - 0.0001537 * T * T;
|
|
52
|
+
return normalizeAngle(M);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 计算太阳中心方程
|
|
57
|
+
*/
|
|
58
|
+
function calculateEquationOfCenter(M, T) {
|
|
59
|
+
const Mr = M * DEGREES_TO_RADIANS;
|
|
60
|
+
|
|
61
|
+
const C = (1.914602 - 0.004817 * T - 0.000014 * T * T) * Math.sin(Mr) +
|
|
62
|
+
(0.019993 - 0.000101 * T) * Math.sin(2 * Mr) +
|
|
63
|
+
0.000289 * Math.sin(3 * Mr);
|
|
64
|
+
|
|
65
|
+
return C;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 计算太阳真黄经
|
|
70
|
+
*/
|
|
71
|
+
function calculateTrueLongitude(L0, C) {
|
|
72
|
+
return normalizeAngle(L0 + C);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 计算黄赤交角
|
|
77
|
+
*/
|
|
78
|
+
function calculateObliquity(T) {
|
|
79
|
+
const eps0 = 23.0 + 26.0/60.0 + 21.448/3600.0 -
|
|
80
|
+
46.8150/3600.0 * T -
|
|
81
|
+
0.00059/3600.0 * T * T +
|
|
82
|
+
0.001813/3600.0 * T * T * T;
|
|
83
|
+
return eps0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 计算太阳赤经
|
|
88
|
+
*/
|
|
89
|
+
function calculateRightAscension(lambda, eps) {
|
|
90
|
+
const lambdaRad = lambda * DEGREES_TO_RADIANS;
|
|
91
|
+
const epsRad = eps * DEGREES_TO_RADIANS;
|
|
92
|
+
|
|
93
|
+
const alpha = Math.atan2(
|
|
94
|
+
Math.cos(epsRad) * Math.sin(lambdaRad),
|
|
95
|
+
Math.cos(lambdaRad)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return normalizeAngle(alpha * RADIANS_TO_DEGREES);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 计算时间方程
|
|
103
|
+
*/
|
|
104
|
+
function calculateEquationOfTime(L0, alpha) {
|
|
105
|
+
let E = L0 - 0.0057183 - alpha;
|
|
106
|
+
|
|
107
|
+
if (E > 180) E -= 360;
|
|
108
|
+
if (E < -180) E += 360;
|
|
109
|
+
|
|
110
|
+
return E * 4.0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 分钟转换为时分秒格式
|
|
115
|
+
*/
|
|
116
|
+
function minutesToHourMinute(totalMinutes) {
|
|
117
|
+
let minutes = totalMinutes;
|
|
118
|
+
while (minutes < 0) minutes += 24 * 60;
|
|
119
|
+
while (minutes >= 24 * 60) minutes -= 24 * 60;
|
|
120
|
+
|
|
121
|
+
const hour = Math.floor(minutes / 60);
|
|
122
|
+
const minute = Math.floor(minutes % 60);
|
|
123
|
+
const second = (minutes % 1) * 60;
|
|
124
|
+
|
|
125
|
+
return { hour, minute, second };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 计算真太阳时
|
|
130
|
+
* @param {Object} params - 参数对象
|
|
131
|
+
* @param {Object} params.dateTime - 日期时间 {year, month, day, hour, minute, second}
|
|
132
|
+
* @param {number} params.longitude - 经度 (东经为正)
|
|
133
|
+
* @param {number} params.latitude - 纬度 (北纬为正)
|
|
134
|
+
* @returns {Object} 计算结果
|
|
135
|
+
*/
|
|
136
|
+
export function getSolarTime({ dateTime, longitude, latitude }) {
|
|
137
|
+
try {
|
|
138
|
+
const { year, month, day, hour, minute, second = 0 } = dateTime;
|
|
139
|
+
|
|
140
|
+
// 计算儒略日
|
|
141
|
+
const JD = calculateJulianDay(year, month, day, hour, minute, second);
|
|
142
|
+
|
|
143
|
+
// 计算儒略世纪数
|
|
144
|
+
const T = (JD - J2000) / 36525.0;
|
|
145
|
+
|
|
146
|
+
// 计算各天文要素
|
|
147
|
+
const L0 = calculateMeanLongitude(T);
|
|
148
|
+
const M = calculateMeanAnomaly(T);
|
|
149
|
+
const C = calculateEquationOfCenter(M, T);
|
|
150
|
+
const lambda = calculateTrueLongitude(L0, C);
|
|
151
|
+
const eps = calculateObliquity(T);
|
|
152
|
+
const alpha = calculateRightAscension(lambda, eps);
|
|
153
|
+
const E = calculateEquationOfTime(L0, alpha);
|
|
154
|
+
|
|
155
|
+
// 计算地方时修正 (相对于标准时区的经度修正)
|
|
156
|
+
const standardLongitude = 120.0; // 中国标准时间基准经度
|
|
157
|
+
const longitudeCorrection = (longitude - standardLongitude) * 4.0;
|
|
158
|
+
|
|
159
|
+
// 计算真太阳时
|
|
160
|
+
const totalCorrection = E + longitudeCorrection;
|
|
161
|
+
const meanSolarMinutes = hour * 60 + minute + second / 60.0;
|
|
162
|
+
const trueSolarMinutes = meanSolarMinutes + totalCorrection;
|
|
163
|
+
|
|
164
|
+
const trueSolarTime = minutesToHourMinute(trueSolarMinutes);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
success: true,
|
|
168
|
+
data: {
|
|
169
|
+
trueSolarTime: {
|
|
170
|
+
hour: trueSolarTime.hour,
|
|
171
|
+
minute: trueSolarTime.minute,
|
|
172
|
+
second: Math.round(trueSolarTime.second)
|
|
173
|
+
},
|
|
174
|
+
corrections: {
|
|
175
|
+
equationOfTime: E,
|
|
176
|
+
longitudeCorrection,
|
|
177
|
+
totalCorrection
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
error: error.message,
|
|
185
|
+
data: null
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 计算时辰索引
|
|
192
|
+
* @param {number} hour - 小时
|
|
193
|
+
* @param {number} minute - 分钟
|
|
194
|
+
* @returns {number} 时辰索引 (0-11对应子-亥时)
|
|
195
|
+
*/
|
|
196
|
+
export function getTimeIndex(hour, minute) {
|
|
197
|
+
const totalMinutes = hour * 60 + minute;
|
|
198
|
+
|
|
199
|
+
if (totalMinutes >= 23 * 60 || totalMinutes < 1 * 60) return 0; // 子时
|
|
200
|
+
else if (totalMinutes >= 1 * 60 && totalMinutes < 3 * 60) return 1; // 丑时
|
|
201
|
+
else if (totalMinutes >= 3 * 60 && totalMinutes < 5 * 60) return 2; // 寅时
|
|
202
|
+
else if (totalMinutes >= 5 * 60 && totalMinutes < 7 * 60) return 3; // 卯时
|
|
203
|
+
else if (totalMinutes >= 7 * 60 && totalMinutes < 9 * 60) return 4; // 辰时
|
|
204
|
+
else if (totalMinutes >= 9 * 60 && totalMinutes < 11 * 60) return 5; // 巳时
|
|
205
|
+
else if (totalMinutes >= 11 * 60 && totalMinutes < 13 * 60) return 6; // 午时
|
|
206
|
+
else if (totalMinutes >= 13 * 60 && totalMinutes < 15 * 60) return 7; // 未时
|
|
207
|
+
else if (totalMinutes >= 15 * 60 && totalMinutes < 17 * 60) return 8; // 申时
|
|
208
|
+
else if (totalMinutes >= 17 * 60 && totalMinutes < 19 * 60) return 9; // 酉时
|
|
209
|
+
else if (totalMinutes >= 19 * 60 && totalMinutes < 21 * 60) return 10; // 戌时
|
|
210
|
+
else return 11; // 亥时
|
|
211
|
+
}
|