@jjlmoya/utils-astronomy 1.28.0 → 1.33.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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +29 -29
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +144 -143
  7. package/src/tool/meteorShowerObservingPlanner/bibliography.astro +14 -0
  8. package/src/tool/meteorShowerObservingPlanner/bibliography.ts +12 -0
  9. package/src/tool/meteorShowerObservingPlanner/component.astro +165 -0
  10. package/src/tool/meteorShowerObservingPlanner/controller.ts +178 -0
  11. package/src/tool/meteorShowerObservingPlanner/dom-views.ts +187 -0
  12. package/src/tool/meteorShowerObservingPlanner/entry.ts +29 -0
  13. package/src/tool/meteorShowerObservingPlanner/evaluator.ts +20 -0
  14. package/src/tool/meteorShowerObservingPlanner/i18n/de.ts +223 -0
  15. package/src/tool/meteorShowerObservingPlanner/i18n/en.ts +223 -0
  16. package/src/tool/meteorShowerObservingPlanner/i18n/es.ts +223 -0
  17. package/src/tool/meteorShowerObservingPlanner/i18n/fr.ts +223 -0
  18. package/src/tool/meteorShowerObservingPlanner/i18n/id.ts +227 -0
  19. package/src/tool/meteorShowerObservingPlanner/i18n/it.ts +227 -0
  20. package/src/tool/meteorShowerObservingPlanner/i18n/ja.ts +223 -0
  21. package/src/tool/meteorShowerObservingPlanner/i18n/ko.ts +223 -0
  22. package/src/tool/meteorShowerObservingPlanner/i18n/nl.ts +227 -0
  23. package/src/tool/meteorShowerObservingPlanner/i18n/pl.ts +227 -0
  24. package/src/tool/meteorShowerObservingPlanner/i18n/pt.ts +227 -0
  25. package/src/tool/meteorShowerObservingPlanner/i18n/ru.ts +223 -0
  26. package/src/tool/meteorShowerObservingPlanner/i18n/sv.ts +227 -0
  27. package/src/tool/meteorShowerObservingPlanner/i18n/tr.ts +227 -0
  28. package/src/tool/meteorShowerObservingPlanner/i18n/zh.ts +223 -0
  29. package/src/tool/meteorShowerObservingPlanner/index.ts +11 -0
  30. package/src/tool/meteorShowerObservingPlanner/logic.test.ts +117 -0
  31. package/src/tool/meteorShowerObservingPlanner/logic.ts +263 -0
  32. package/src/tool/meteorShowerObservingPlanner/meteor-shower-observing-planner.css +404 -0
  33. package/src/tool/meteorShowerObservingPlanner/seo.astro +14 -0
  34. package/src/tool/meteorShowerObservingPlanner/storage.ts +41 -0
  35. package/src/tool/meteorShowerObservingPlanner/ui.ts +91 -0
  36. package/src/tool/starExposureCalculator/index.ts +3 -0
  37. package/src/tool/telescopeResolution/index.ts +3 -0
  38. package/src/tools.ts +4 -0
@@ -0,0 +1,227 @@
1
+ import type { MeteorShowerObservingPlannerLocaleContent } from '../ui';
2
+ import { METEOR_PLANNER_BIBLIOGRAPHY } from '../bibliography';
3
+
4
+ const slug = 'goktasi-yagmuru-gozlem-planlayici';
5
+
6
+ export const content: MeteorShowerObservingPlannerLocaleContent = {
7
+ slug,
8
+ title: 'Göktaşı Yağmuru Gözlem Zamanı Planlayıcı',
9
+ description: 'Zirve saatlik oran ışık kirliliği ve Ay etkisi hesaplamalarıyla ideal göktaşı gözlem seanslarını planlayın.',
10
+ ui: {
11
+ title: 'Göktaşı Yağmuru Planlayıcı',
12
+ subtitle: 'En iyi gözlem penceresini hesaplayın ve saatlik görünür oranı tahmin edin',
13
+ presetLabel: 'Ana Göktaşı Yağmurunu Seçin',
14
+ customZhrLabel: 'Özel Başucu Saatlik Oranı ZHR',
15
+ latitudeLabel: 'Gözlemci Enlemi',
16
+ bortleLabel: 'Bortle Karanlık Gökyüzü Sınıfı',
17
+ moonPhaseLabel: 'Ay Aydınlanma Oranı',
18
+ sessionHoursLabel: 'Planlanan Gözlem Saatleri',
19
+ toLabel: 'kadar',
20
+ classLabel: 'Sınıf',
21
+ radiantTelemetryLabel: 'RADYANT TELEMETRİSİ',
22
+ belowHorizonLabel: 'Ufkun altında',
23
+ altLabel: 'Yük',
24
+ moonLabel: 'Ay',
25
+ hrUnit: '/ saat',
26
+ presets: {
27
+ perseids: 'Perseid (12 Ağu)',
28
+ geminids: 'Geminid (14 Ara)',
29
+ quadrantids: 'Quadrantid (4 Oca)',
30
+ lyrids: 'Lyrid (22 Nis)',
31
+ orionids: 'Orionid (21 Eki)',
32
+ 'eta-aquariids': 'Eta Aquarid (6 May)',
33
+ leonids: 'Leonid (17 Kas)',
34
+ custom: 'Özel Yağmur',
35
+ },
36
+ constellations: {
37
+ Perseus: 'Perseus',
38
+ Gemini: 'Gemini',
39
+ Bootes: 'Bootes',
40
+ Lyra: 'Lyra',
41
+ Orion: 'Orion',
42
+ Aquarius: 'Aquarius',
43
+ Leo: 'Leo',
44
+ Zenith: 'Zenit',
45
+ },
46
+ badgeLabels: {
47
+ darkSky: 'Mükemmel Karanlık Gökyüzü',
48
+ lightPollution: 'Yüksek Işık Kirliliği',
49
+ favorableMoon: 'Uygun Ay Evresi',
50
+ moonWashout: 'Ay Işığı Parlama Riski',
51
+ primeWindow: 'Zirve Aktivite Penceresi',
52
+ },
53
+ bortleDescriptions: {
54
+ 2: 'Mükemmel karanlık gökyüzü',
55
+ 5: 'Kırsal şehir gökyüzü',
56
+ 8: 'Şehir içi ışık kirliliği',
57
+ },
58
+ moonPhaseNames: {
59
+ new: 'Minimum Ay etkisi',
60
+ quarter: 'Orta seviye Ay etkisi',
61
+ full: 'Ciddi Ay parlaması',
62
+ },
63
+ resultsTitle: 'Gözlem Seansı Analizi',
64
+ bestWindowLabel: 'En İyi Gözlem Zamanı',
65
+ maxRateLabel: 'Maksimum Görünür Oran',
66
+ skyQualityLabel: 'Gökyüzü Kalite İndeksi',
67
+ hourlyChartTitle: 'Saatlik Tahmin ve Radyant Yüksekliği',
68
+ checklistTitle: 'Gözlem Hazırlığı',
69
+ checklistItems: [
70
+ 'Sayıma başlamadan önce gözlerinizin karanlığa alışması için en az 20 dakika bekleyin',
71
+ 'Gece görüşünüzü korumak için kırmızı filtreli fener kullanın',
72
+ 'Ufkun 45 derece üstüne bakacak şekilde rahat bir kamp sandalyesine uzanın',
73
+ 'Gözlem sırasında parlak telefon ekranlarına bakmaktan kaçının',
74
+ ],
75
+ },
76
+ seo: [
77
+ {
78
+ type: 'title',
79
+ text: 'Başucu Saatlik Oranı ZHR ve Görünür Göktaşları',
80
+ level: 2,
81
+ },
82
+ {
83
+ type: 'paragraph',
84
+ html: 'Başucu Saatlik Oranı ZHR tam karanlık bir gökyüzünde radyant başucundayken tek bir gözlemcinin görebileceği teorik maksimum göktaşı sayısıdır. Gerçek şartlarda ışık kirliliği ve radyant yüksekliği nedeniyle görülen sayı daha düşüktür.',
85
+ },
86
+ {
87
+ type: 'paragraph',
88
+ html: 'Gözlem planlamasında geometrik faktörler önemlidir. Radyant ufka yaklaştıkça meteor izleri görüş alanı dışında kaldığından daha az göktaşı fark edilir.',
89
+ },
90
+ {
91
+ type: 'list',
92
+ items: [
93
+ 'Radyant yüksekliği görünür oranı yüksekliğin sinüsüne orantılı olarak düşürür',
94
+ 'Bortle skalasına göre ışık kirliliği zayıf meteor izlerini gizler',
95
+ 'Ay evresi gökyüzü arka planını aydınlatarak kontrastı azaltır',
96
+ 'Görüş alanı toplam kapsama alanını sınırlar',
97
+ ],
98
+ },
99
+ {
100
+ type: 'tip',
101
+ title: 'Karanlık Gökyüzü Tavsiyesi',
102
+ html: 'Bortle 2 veya 3 sınıfı bir noktada gözlem yapmak şehir içi bahçelere kıyasla görünen göktaşı sayısını katkat artırır.',
103
+ },
104
+ {
105
+ type: 'title',
106
+ text: 'Ay Evresi ve Şehir Işık Kirliliğinin Etkisi',
107
+ level: 2,
108
+ },
109
+ {
110
+ type: 'paragraph',
111
+ html: 'Ay ışığı en büyük doğal kirlilik kaynağıdır. Dolunay sönük izleri silerek görünür oranı yüzde 70 ten fazla düşürür hatta karanlık kırsal bölgelerde bile etkilidir.',
112
+ },
113
+ {
114
+ type: 'paragraph',
115
+ html: 'Ay batımından sonraki veya doğumundan önceki saatleri seçmek gökyüzünde en yüksek kontrastı sağlar. Astronomik gece yarısı civarı saatler en yüksek karanlığı sunar.',
116
+ },
117
+ {
118
+ type: 'title',
119
+ text: 'Göktaşı Gözlem Gecesine Hazırlık ve Tavsiyeler',
120
+ level: 2,
121
+ },
122
+ {
123
+ type: 'paragraph',
124
+ html: 'Göktaşı izlemek sabır ve sıcak giysiler gerektirir. Çıplak gözle gözlem yapmak teleskoplara kıyasla çok daha geniş bir görüş alanı sunar.',
125
+ },
126
+ {
127
+ type: 'list',
128
+ items: [
129
+ 'Yaz gecelerinde bile katkat sıcak giyinin',
130
+ 'Sandalye için uyku tulumu veya battaniye alın',
131
+ 'Vücut ısınızı korumak için sıcak içecekler bulundurun',
132
+ 'Parlak ateş toplarının saatini ve yönünü not edin',
133
+ ],
134
+ },
135
+ {
136
+ type: 'title',
137
+ text: 'Teknik Hesaplama Yöntemi ve Tahmin',
138
+ level: 2,
139
+ },
140
+ {
141
+ type: 'paragraph',
142
+ html: 'Bu araç zirve ZHR değerini radyant yüksekliği Bortle skalası ve Ay parlaklığı faktörleriyle birleştirerek gerçekçi oranlar sunar.',
143
+ },
144
+ {
145
+ type: 'paragraph',
146
+ html: 'Atmosferik sönümleme ve ışınım açısını hesaba katan hesaplayıcı amatör astronomlar için güvenilir sonuçlar üretir. Bu sayede hem yeni başlayanlar hem de deneyimli gözlemciler en verimli zaman dilimlerini kolayca belirleyebilirler. Önceden yapılan doğru bir gözlem planı gece boyunca en çok meteor izini yakalamanıza yardımcı olur. Gökyüzünü keyifle seyretmek için karanlık bir mekan seçin ve harika bir gece geçirin. Işık kirliliğinden uzak durmak gözlem kalitesini kat kat artıran en önemli faktördür.',
147
+ },
148
+ ],
149
+ faq: [
150
+ {
151
+ question: 'Başucu Saatlik Oranı ZHR nedir',
152
+ answer: 'ZHR zifiri karanlık gökyüzünde radyant başucundayken saatte görülebilecek teorik maksimum göktaşı sayısıdır.',
153
+ },
154
+ {
155
+ question: 'Neden ZHR değerinden daha az göktaşı görüyorum',
156
+ answer: 'ZHR mükemmel şartları varsayar. Işık kirliliği alçak radyant ve Ay ışığı gerçek sayıları azaltır.',
157
+ },
158
+ {
159
+ question: 'Göktaşı izlemek için teleskop gerekli mi',
160
+ answer: 'Hayır teleskoplar görüş alanını çok daraltır. En iyisi çıplak gözle gözlem yapmaktır.',
161
+ },
162
+ ],
163
+ howTo: {
164
+ name: 'Göktaşı Yağmuru Planlayıcı Nasıl Kullanılır',
165
+ description: 'En iyi gözlem zamanını bulmak için izlenecek adımlar.',
166
+ steps: [
167
+ {
168
+ name: 'Yağmuru seçin',
169
+ text: 'Perseid gibi bilinen bir yağmuru seçin veya ZHR değeri girin.',
170
+ },
171
+ {
172
+ name: 'Enlemi ayarlayın',
173
+ text: 'Kaydırıcıyı konumunuza göre ayarlayın.',
174
+ },
175
+ {
176
+ name: 'Karanlık ve Ay durumunu ayarlayın',
177
+ text: 'Bortle sınıfı ve Ay aydınlanmasını girin.',
178
+ },
179
+ {
180
+ name: 'Tahmini inceleyin',
181
+ text: 'En iyi zamanı bulmak için grafiği inceleyin.',
182
+ },
183
+ ],
184
+ },
185
+ bibliography: METEOR_PLANNER_BIBLIOGRAPHY,
186
+ schemas: [
187
+ {
188
+ '@context': 'https://schema.org',
189
+ '@type': 'SoftwareApplication',
190
+ name: 'Göktaşı Yağmuru Gözlem Zamanı Planlayıcı',
191
+ operatingSystem: 'Any',
192
+ applicationCategory: 'EducationalApplication',
193
+ offers: {
194
+ '@type': 'Offer',
195
+ price: '0',
196
+ priceCurrency: 'EUR',
197
+ },
198
+ },
199
+ {
200
+ '@context': 'https://schema.org',
201
+ '@type': 'FAQPage',
202
+ mainEntity: [
203
+ {
204
+ '@type': 'Question',
205
+ name: 'Başucu Saatlik Oranı ZHR nedir',
206
+ acceptedAnswer: {
207
+ '@type': 'Answer',
208
+ text: 'ZHR zifiri karanlık gökyüzünde radyant başucundayken saatte görülebilecek teorik maksimum göktaşı sayısıdır.',
209
+ },
210
+ },
211
+ ],
212
+ },
213
+ {
214
+ '@context': 'https://schema.org',
215
+ '@type': 'HowTo',
216
+ name: 'Göktaşı Yağmuru Planlayıcı Nasıl Kullanılır',
217
+ description: 'En iyi gözlem zamanını bulmak için izlenecek adımlar.',
218
+ step: [
219
+ {
220
+ '@type': 'HowToStep',
221
+ name: 'Yağmuru seçin',
222
+ text: 'Perseid gibi bilinen bir yağmuru seçin veya ZHR değeri girin.',
223
+ },
224
+ ],
225
+ },
226
+ ],
227
+ };
@@ -0,0 +1,223 @@
1
+ import type { MeteorShowerObservingPlannerLocaleContent } from '../ui';
2
+ import { METEOR_PLANNER_BIBLIOGRAPHY } from '../bibliography';
3
+
4
+ const slug = 'meteor-shower-observing-planner';
5
+
6
+ export const content: MeteorShowerObservingPlannerLocaleContent = {
7
+ slug,
8
+ title: '流星雨观测时间窗口规划器',
9
+ description: '通过计算天顶每小时出现率 ZHR、天空黑暗度及月光干扰,规划最佳流星观测时间窗口。',
10
+ ui: {
11
+ title: '流星雨观测规划器',
12
+ subtitle: '计算最佳观测时间窗口并预测每小时可见流星数量',
13
+ presetLabel: '选择主要流星雨',
14
+ customZhrLabel: '自定义天顶每小时出现率 ZHR',
15
+ latitudeLabel: '观测者纬度',
16
+ bortleLabel: '波特尔暗空等级',
17
+ moonPhaseLabel: '月球照射比例',
18
+ sessionHoursLabel: '计划观测时间段',
19
+ toLabel: '至',
20
+ classLabel: '等级',
21
+ radiantTelemetryLabel: '辐射点遥测',
22
+ belowHorizonLabel: '地平线下',
23
+ altLabel: '高度',
24
+ moonLabel: '月球',
25
+ hrUnit: '/ 小时',
26
+ presets: {
27
+ perseids: '英仙座流星雨 (8月12日)',
28
+ geminids: '双子座流星雨 (12月14日)',
29
+ quadrantids: '象限仪座流星雨 (1月4日)',
30
+ lyrids: '天琴座流星雨 (4月22日)',
31
+ orionids: '猎户座流星雨 (10月21日)',
32
+ 'eta-aquariids': '宝瓶座eta流星雨 (5月6日)',
33
+ leonids: '狮子座流星雨 (11月17日)',
34
+ custom: '自定义流星雨',
35
+ },
36
+ constellations: {
37
+ Perseus: '英仙座',
38
+ Gemini: '双子座',
39
+ Bootes: '牧夫座',
40
+ Lyra: '天琴座',
41
+ Orion: '猎户座',
42
+ Aquarius: '宝瓶座',
43
+ Leo: '狮子座',
44
+ Zenith: '天顶',
45
+ },
46
+ badgeLabels: {
47
+ darkSky: '极佳暗夜环境',
48
+ lightPollution: '强光害影响',
49
+ favorableMoon: '有利月相条件',
50
+ moonWashout: '月光冲淡风险',
51
+ primeWindow: '极大爆发窗口',
52
+ },
53
+ bortleDescriptions: {
54
+ 2: '极佳暗夜环境',
55
+ 5: '郊区暗夜环境',
56
+ 8: '城市光害环境',
57
+ },
58
+ moonPhaseNames: {
59
+ new: '月光影响微弱',
60
+ quarter: '中度月光干扰',
61
+ full: '严重月光冲淡',
62
+ },
63
+ resultsTitle: '观测时段分析结果',
64
+ bestWindowLabel: '最佳观测窗口',
65
+ maxRateLabel: '最大预测可见率',
66
+ skyQualityLabel: '夜空质量指数',
67
+ hourlyChartTitle: '每小时预测流量与辐射点高度',
68
+ checklistTitle: '观测前准备清单',
69
+ checklistItems: [
70
+ '在计数前至少留出 20 分钟让眼睛完全适应黑暗',
71
+ '使用带红光滤镜的手电筒以保护夜视能力',
72
+ '躺在舒适的折叠椅上面向地平线上方 45 度角观赏',
73
+ '观测期间避免观看亮屏手机',
74
+ ],
75
+ },
76
+ seo: [
77
+ {
78
+ type: 'title',
79
+ text: '理解天顶每小时出现率 ZHR 与实际可见流星',
80
+ level: 2,
81
+ },
82
+ {
83
+ type: 'paragraph',
84
+ html: '天顶每小时出现率 ZHR 代表在理想暗夜且辐射点位于天顶时,单人单小时内能看到的理论最大流星数。实际观测中由于光害和辐射点高度影响,能看到的流星数量会明显减少。',
85
+ },
86
+ {
87
+ type: 'paragraph',
88
+ html: '规划最佳观测时段需要考虑几何与环境因素。当辐射点高度靠近地平线时,流星划过的轨迹大多在大气层外侧,可观察到的数量随之下降。',
89
+ },
90
+ {
91
+ type: 'list',
92
+ items: [
93
+ '辐射点高度降低按高度正弦比例削减可见数',
94
+ '波特尔等级光害掩盖暗弱流星痕迹',
95
+ '月相与月光照亮夜空背景',
96
+ '观测者视野限制了可覆盖的天区范围',
97
+ ],
98
+ },
99
+ {
100
+ type: 'tip',
101
+ title: '暗夜观测建议',
102
+ html: '在波特尔 2 或 3 级的场地观测,相较于城市院落能成倍增加肉眼可见的暗弱流星数量。',
103
+ },
104
+ {
105
+ type: 'title',
106
+ text: '月相与光害的影响',
107
+ level: 2,
108
+ },
109
+ {
110
+ type: 'paragraph',
111
+ html: '月光是流星雨极大期的主要自然光害源。满月或大半月会冲淡暗弱流星,使实际可见流量在乡村暗夜下也会下降 70% 以上。',
112
+ },
113
+ {
114
+ type: 'paragraph',
115
+ html: '选择月落后或月出前的时段能确保夜空背景的最佳对比度。',
116
+ },
117
+ {
118
+ type: 'title',
119
+ text: '如何准备流星观测之夜',
120
+ level: 2,
121
+ },
122
+ {
123
+ type: 'paragraph',
124
+ html: '成功观测流星需要耐心与保暖防寒。流星随机出现在天空各处,肉眼观测比望远镜拥有宽广得多的视野。',
125
+ },
126
+ {
127
+ type: 'list',
128
+ items: [
129
+ '即使夏季夜晚也请准备多层保暖衣物',
130
+ '为折叠椅准备睡袋或保温毯',
131
+ '准备热饮维持体温',
132
+ '记录亮火流星的出现时间与方向',
133
+ ],
134
+ },
135
+ {
136
+ type: 'title',
137
+ text: '技术计算方法',
138
+ level: 2,
139
+ },
140
+ {
141
+ type: 'paragraph',
142
+ html: '本工具通过将极大 ZHR 与辐射点几何高度、波特尔暗空衰减及月光干扰进行综合计算,为天文爱好者提供贴近现实的预估。',
143
+ },
144
+ ],
145
+ faq: [
146
+ {
147
+ question: '什么是天顶每小时出现率 ZHR',
148
+ answer: 'ZHR 是指暗夜环境且辐射点位于天顶时理论上的每小时最大流星数。',
149
+ },
150
+ {
151
+ question: '为什么我看到的流星少于公布的 ZHR',
152
+ answer: '公布的 ZHR 假设完美理想条件。光害、低辐射点高度和月光都会减少实际数量。',
153
+ },
154
+ {
155
+ question: '观测流星雨需要望远镜吗',
156
+ answer: '不需要,望远镜视野太窄。肉眼全天域观测是最佳选择。',
157
+ },
158
+ ],
159
+ howTo: {
160
+ name: '如何使用流星雨观测规划器',
161
+ description: '寻找最佳流星观测时间窗口的步骤。',
162
+ steps: [
163
+ {
164
+ name: '选择流星雨',
165
+ text: '选择英仙座流星雨等预设流星雨或输入自定义 ZHR。',
166
+ },
167
+ {
168
+ name: '设置纬度',
169
+ text: '调整滑块至您所在的观测纬度。',
170
+ },
171
+ {
172
+ name: '设置暗空与月相',
173
+ text: '输入波特尔等级与月球照度。',
174
+ },
175
+ {
176
+ name: '查看预测',
177
+ text: '分析图表确定最佳观测窗口。',
178
+ },
179
+ ],
180
+ },
181
+ bibliography: METEOR_PLANNER_BIBLIOGRAPHY,
182
+ schemas: [
183
+ {
184
+ '@context': 'https://schema.org',
185
+ '@type': 'SoftwareApplication',
186
+ name: '流星雨观测时间窗口规划器',
187
+ operatingSystem: 'Any',
188
+ applicationCategory: 'EducationalApplication',
189
+ offers: {
190
+ '@type': 'Offer',
191
+ price: '0',
192
+ priceCurrency: 'CNY',
193
+ },
194
+ },
195
+ {
196
+ '@context': 'https://schema.org',
197
+ '@type': 'FAQPage',
198
+ mainEntity: [
199
+ {
200
+ '@type': 'Question',
201
+ name: '什么是天顶每小时出现率 ZHR',
202
+ acceptedAnswer: {
203
+ '@type': 'Answer',
204
+ text: 'ZHR 是指暗夜环境且辐射点位于天顶时理论上的每小时最大流星数。',
205
+ },
206
+ },
207
+ ],
208
+ },
209
+ {
210
+ '@context': 'https://schema.org',
211
+ '@type': 'HowTo',
212
+ name: '如何使用流星雨观测规划器',
213
+ description: '寻找最佳流星观测时间窗口的步骤。',
214
+ step: [
215
+ {
216
+ '@type': 'HowToStep',
217
+ name: '选择流星雨',
218
+ text: '选择英仙座流星雨等预设流星雨或输入自定义 ZHR。',
219
+ },
220
+ ],
221
+ },
222
+ ],
223
+ };
@@ -0,0 +1,11 @@
1
+ import { meteorShowerObservingPlanner } from './entry';
2
+ import type { ToolDefinition } from '../../types';
3
+
4
+ export * from './entry';
5
+
6
+ export const METEOR_SHOWER_OBSERVING_PLANNER_TOOL: ToolDefinition = {
7
+ entry: meteorShowerObservingPlanner,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ METEOR_SHOWERS,
4
+ getShowerById,
5
+ calculateRadiantElevation,
6
+ calculateMoonInterference,
7
+ calculateEffectiveZHR,
8
+ formatHourLabel,
9
+ calculateHourlyBreakdown,
10
+ evaluateObservingSession,
11
+ } from './logic';
12
+ import type { ObservingPlannerInput, MeteorShowerObservingPlannerUI } from './ui';
13
+
14
+ const MOCK_UI: MeteorShowerObservingPlannerUI = {
15
+ title: 'Meteor Shower Planner',
16
+ subtitle: 'Calculate candidate observing windows',
17
+ presetLabel: 'Meteor Shower',
18
+ customZhrLabel: 'Custom ZHR',
19
+ latitudeLabel: 'Observer Latitude',
20
+ bortleLabel: 'Bortle Dark Sky Scale',
21
+ moonPhaseLabel: 'Moon Phase',
22
+ sessionHoursLabel: 'Session Hours',
23
+ presets: { perseids: 'Perseids' },
24
+ bortleDescriptions: {
25
+ 2: 'Rural Dark Sky',
26
+ 5: 'Suburban Sky',
27
+ 8: 'Urban Sky',
28
+ },
29
+ moonPhaseNames: {
30
+ new: 'New Moon',
31
+ quarter: 'Quarter Moon',
32
+ full: 'Full Moon',
33
+ },
34
+ resultsTitle: 'Observing Assessment',
35
+ bestWindowLabel: 'Best Observing Window',
36
+ maxRateLabel: 'Peak Visible Rate',
37
+ skyQualityLabel: 'Sky Quality Index',
38
+ hourlyChartTitle: 'Hourly Rate Forecast',
39
+ checklistTitle: 'Preparation Checklist',
40
+ checklistItems: ['Allow 20 minutes for dark adaptation', 'Use red night-vision flashlight'],
41
+ };
42
+
43
+ describe('Meteor Shower Observing Planner Logic', () => {
44
+ it('should return valid presets list', () => {
45
+ expect(METEOR_SHOWERS.length).toBeGreaterThanOrEqual(7);
46
+ const perseids = getShowerById('perseids');
47
+ expect(perseids.name).toBe('Perseids');
48
+ expect(perseids.zhr).toBe(100);
49
+ });
50
+
51
+ it('should fallback to first shower for unknown ID', () => {
52
+ const fallback = getShowerById('non-existent');
53
+ expect(fallback.id).toBe('perseids');
54
+ });
55
+
56
+ it('should calculate radiant elevation within valid degrees range', () => {
57
+ const eleDay = calculateRadiantElevation(12, 40, 'perseids');
58
+ const eleNight = calculateRadiantElevation(3, 40, 'perseids');
59
+ expect(eleDay).toBeGreaterThanOrEqual(0);
60
+ expect(eleNight).toBeGreaterThan(eleDay);
61
+ });
62
+
63
+ it('should calculate moon interference percentage correctly', () => {
64
+ expect(calculateMoonInterference(0)).toBe(0);
65
+ expect(calculateMoonInterference(0.5)).toBe(100);
66
+ expect(calculateMoonInterference(1)).toBe(0);
67
+ });
68
+
69
+ it('should calculate effective ZHR zero when radiant is below horizon', () => {
70
+ const rate = calculateEffectiveZHR(100, 0, 1, 0);
71
+ expect(rate).toBe(0);
72
+ });
73
+
74
+ it('should reduce effective ZHR with higher Bortle class', () => {
75
+ const darkRate = calculateEffectiveZHR(100, 45, 1, 0);
76
+ const urbanRate = calculateEffectiveZHR(100, 45, 8, 0);
77
+ expect(darkRate).toBeGreaterThan(urbanRate);
78
+ });
79
+
80
+ it('should format hour labels correctly', () => {
81
+ expect(formatHourLabel(3)).toBe('03:00');
82
+ expect(formatHourLabel(23)).toBe('23:00');
83
+ expect(formatHourLabel(27)).toBe('03:00');
84
+ });
85
+
86
+ it('should calculate hourly breakdown across midnight', () => {
87
+ const input: ObservingPlannerInput = {
88
+ showerId: 'perseids',
89
+ customZhr: 100,
90
+ latitude: 40,
91
+ bortleClass: 3,
92
+ moonPhase: 0.1,
93
+ sessionStartHour: 22,
94
+ sessionEndHour: 4,
95
+ };
96
+ const breakdown = calculateHourlyBreakdown(input);
97
+ expect(breakdown.length).toBe(7);
98
+ expect(breakdown[0]?.formattedTime).toBe('22:00');
99
+ expect(breakdown[breakdown.length - 1]?.formattedTime).toBe('04:00');
100
+ });
101
+
102
+ it('should generate evaluation session with badges', () => {
103
+ const input: ObservingPlannerInput = {
104
+ showerId: 'geminids',
105
+ customZhr: 120,
106
+ latitude: 45,
107
+ bortleClass: 2,
108
+ moonPhase: 0,
109
+ sessionStartHour: 21,
110
+ sessionEndHour: 3,
111
+ };
112
+ const evalResult = evaluateObservingSession(input, MOCK_UI);
113
+ expect(evalResult.overallScore).toBeGreaterThan(0);
114
+ expect(evalResult.bestWindowStart).toBeDefined();
115
+ expect(evalResult.badges.length).toBeGreaterThan(0);
116
+ });
117
+ });