@jjlmoya/utils-astronomy 1.26.0 → 1.28.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/package.json +1 -1
- package/src/category/index.ts +3 -2
- package/src/data.ts +2 -1
- package/src/entries.ts +4 -1
- package/src/tests/locale_completeness.test.ts +2 -3
- package/src/tests/seo_translation_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +7 -6
- package/src/tool/telescopeExitPupilPlanner/bibliography.astro +14 -0
- package/src/tool/telescopeExitPupilPlanner/bibliography.ts +12 -0
- package/src/tool/telescopeExitPupilPlanner/component.astro +93 -0
- package/src/tool/telescopeExitPupilPlanner/controller.ts +102 -0
- package/src/tool/telescopeExitPupilPlanner/dom-views.ts +64 -0
- package/src/tool/telescopeExitPupilPlanner/entry.ts +31 -0
- package/src/tool/telescopeExitPupilPlanner/evaluator.ts +17 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/de.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/en.ts +177 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/es.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/fr.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/id.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/it.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/ja.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/ko.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/nl.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/pl.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/pt.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/ru.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/sv.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/tr.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/translated.ts +140 -0
- package/src/tool/telescopeExitPupilPlanner/i18n/zh.ts +2 -0
- package/src/tool/telescopeExitPupilPlanner/index.ts +10 -0
- package/src/tool/telescopeExitPupilPlanner/logic.test.ts +39 -0
- package/src/tool/telescopeExitPupilPlanner/logic.ts +57 -0
- package/src/tool/telescopeExitPupilPlanner/seo.astro +15 -0
- package/src/tool/telescopeExitPupilPlanner/storage.ts +24 -0
- package/src/tool/telescopeExitPupilPlanner/telescope-exit-pupil-magnification-planner.css +454 -0
- package/src/tool/telescopeExitPupilPlanner/ui.ts +40 -0
- package/src/tools.ts +3 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { FAQItem, HowToStep, SEOSection } from '../../../types';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { TelescopeExitPupilPlannerLocaleContent } from '../entry';
|
|
4
|
+
import { content as deBase } from '../../smartEyepieceCalculator/i18n/de';
|
|
5
|
+
import { content as esBase } from '../../smartEyepieceCalculator/i18n/es';
|
|
6
|
+
import { content as frBase } from '../../smartEyepieceCalculator/i18n/fr';
|
|
7
|
+
import { content as idBase } from '../../smartEyepieceCalculator/i18n/id';
|
|
8
|
+
import { content as itBase } from '../../smartEyepieceCalculator/i18n/it';
|
|
9
|
+
import { content as jaBase } from '../../smartEyepieceCalculator/i18n/ja';
|
|
10
|
+
import { content as koBase } from '../../smartEyepieceCalculator/i18n/ko';
|
|
11
|
+
import { content as nlBase } from '../../smartEyepieceCalculator/i18n/nl';
|
|
12
|
+
import { content as plBase } from '../../smartEyepieceCalculator/i18n/pl';
|
|
13
|
+
import { content as ptBase } from '../../smartEyepieceCalculator/i18n/pt';
|
|
14
|
+
import { content as ruBase } from '../../smartEyepieceCalculator/i18n/ru';
|
|
15
|
+
import { content as svBase } from '../../smartEyepieceCalculator/i18n/sv';
|
|
16
|
+
import { content as trBase } from '../../smartEyepieceCalculator/i18n/tr';
|
|
17
|
+
import { content as zhBase } from '../../smartEyepieceCalculator/i18n/zh';
|
|
18
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
19
|
+
|
|
20
|
+
type BaseContent = {
|
|
21
|
+
ui: Record<string, string>;
|
|
22
|
+
seo: SEOSection[];
|
|
23
|
+
faq: FAQItem[];
|
|
24
|
+
howTo: HowToStep[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type LocalePack = {
|
|
28
|
+
slug: string;
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
barlowLabel: string;
|
|
32
|
+
apparentFieldLabel: string;
|
|
33
|
+
focalRatioLabel: string;
|
|
34
|
+
effectiveEyepieceLabel: string;
|
|
35
|
+
opticalPathLabel: string;
|
|
36
|
+
opticalPathDescription: string;
|
|
37
|
+
resetLabel: string;
|
|
38
|
+
fieldEstimateLabel: string;
|
|
39
|
+
fieldEstimateText: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const bases: Record<string, BaseContent> = {
|
|
43
|
+
de: deBase,
|
|
44
|
+
es: esBase,
|
|
45
|
+
fr: frBase,
|
|
46
|
+
id: idBase,
|
|
47
|
+
it: itBase,
|
|
48
|
+
ja: jaBase,
|
|
49
|
+
ko: koBase,
|
|
50
|
+
nl: nlBase,
|
|
51
|
+
pl: plBase,
|
|
52
|
+
pt: ptBase,
|
|
53
|
+
ru: ruBase,
|
|
54
|
+
sv: svBase,
|
|
55
|
+
tr: trBase,
|
|
56
|
+
zh: zhBase,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const packs: Record<string, LocalePack> = {
|
|
60
|
+
de: { slug: 'teleskop-austrittspupille-vergroesserungsplaner', title: 'Planer für Austrittspupille und Vergrößerung am Teleskop', description: 'Plane Kombinationen aus Teleskop und Okular mit Vergrößerung, Austrittspupille, Öffnungsverhältnis und geschätztem tatsächlichem Gesichtsfeld.', barlowLabel: 'Barlowfaktor', apparentFieldLabel: 'Scheinbares Gesichtsfeld des Okulars', focalRatioLabel: 'Öffnungsverhältnis', effectiveEyepieceLabel: 'Effektive Okularbrennweite', opticalPathLabel: 'Der optische Weg', opticalPathDescription: 'Ein breiterer Lichtkegel ist heller und leichter zu überblicken. Ein schmalerer Kegel erlaubt mehr Detail, verzeiht aber weniger.', resetLabel: 'Werte zurücksetzen', fieldEstimateLabel: 'Gesichtsfeldschätzung', fieldEstimateText: 'Das tatsächliche Gesichtsfeld ist eine Schätzung aus scheinbarem Gesichtsfeld und Vergrößerung.' },
|
|
61
|
+
es: { slug: 'planificador-pupila-salida-aumentos-telescopio', title: 'Planificador de pupila de salida y aumentos del telescopio', description: 'Planifica combinaciones de telescopio y ocular calculando aumentos, pupila de salida, relación focal y un campo de visión real estimado.', barlowLabel: 'Factor de Barlow', apparentFieldLabel: 'Campo aparente del ocular', focalRatioLabel: 'Relación focal', effectiveEyepieceLabel: 'Ocular efectivo', opticalPathLabel: 'El recorrido óptico', opticalPathDescription: 'Un haz más ancho es luminoso y fácil de colocar en el ojo. Un haz más estrecho permite más detalle, pero perdona menos.', resetLabel: 'Restablecer valores', fieldEstimateLabel: 'Estimación del campo', fieldEstimateText: 'El campo real es una estimación basada en el campo aparente y los aumentos.' },
|
|
62
|
+
fr: { slug: 'planificateur-pupille-sortie-grossissement-telescope', title: 'Planificateur de pupille de sortie et de grossissement du télescope', description: 'Planifiez une combinaison télescope oculaire en calculant le grossissement, la pupille de sortie, le rapport focal et le champ réel estimé.', barlowLabel: 'Facteur de Barlow', apparentFieldLabel: 'Champ apparent de l\'oculaire', focalRatioLabel: 'Rapport focal', effectiveEyepieceLabel: 'Focale effective de l\'oculaire', opticalPathLabel: 'Le trajet optique', opticalPathDescription: 'Un faisceau large est plus lumineux et plus facile à placer devant l\'œil. Un faisceau étroit révèle davantage de détails, mais pardonne moins.', resetLabel: 'Réinitialiser les valeurs', fieldEstimateLabel: 'Estimation du champ', fieldEstimateText: 'Le champ réel est une estimation fondée sur le champ apparent et le grossissement.' },
|
|
63
|
+
id: { slug: 'perencana-pupil-keluar-pembesaran-teleskop', title: 'Perencana pupil keluar dan pembesaran teleskop', description: 'Rencanakan kombinasi teleskop dan eyepiece dengan menghitung pembesaran, pupil keluar, rasio fokus, dan perkiraan bidang pandang sebenarnya.', barlowLabel: 'Faktor Barlow', apparentFieldLabel: 'Bidang tampak eyepiece', focalRatioLabel: 'Rasio fokus', effectiveEyepieceLabel: 'Fokus eyepiece efektif', opticalPathLabel: 'Jalur optik', opticalPathDescription: 'Berkas yang lebih lebar lebih terang dan mudah ditempatkan ke mata. Berkas yang lebih sempit mendukung detail lebih tinggi tetapi lebih menuntut.', resetLabel: 'Atur ulang nilai', fieldEstimateLabel: 'Perkiraan bidang', fieldEstimateText: 'Bidang sebenarnya adalah perkiraan dari bidang tampak dan pembesaran.' },
|
|
64
|
+
it: { slug: 'pianificatore-pupilla-uscita-ingrandimento-telescopio', title: 'Pianificatore della pupilla duscita e dellingrandimento del telescopio', description: 'Pianifica una combinazione telescopio oculare calcolando ingrandimento, pupilla duscita, rapporto focale e campo reale stimato.', barlowLabel: 'Fattore di Barlow', apparentFieldLabel: 'Campo apparente delloculare', focalRatioLabel: 'Rapporto focale', effectiveEyepieceLabel: 'Focale effettiva delloculare', opticalPathLabel: 'Il percorso ottico', opticalPathDescription: 'Un fascio più ampio è luminoso e facile da posizionare allocchio. Un fascio più stretto mostra più dettagli ma perdona meno.', resetLabel: 'Reimposta valori', fieldEstimateLabel: 'Stima del campo', fieldEstimateText: 'Il campo reale è una stima basata sul campo apparente e sullingrandimento.' },
|
|
65
|
+
ja: { slug: 'telescope-exit-pupil-magnification-planner', title: '望遠鏡の射出瞳と倍率プランナー', description: '望遠鏡と接眼レンズの組み合わせについて、倍率、射出瞳、焦点比、推定実視野を計算します。', barlowLabel: 'バローレンズ倍率', apparentFieldLabel: '接眼レンズの見かけ視野', focalRatioLabel: '焦点比', effectiveEyepieceLabel: '実効接眼レンズ焦点距離', opticalPathLabel: '光路', opticalPathDescription: '幅の広い光束は明るく、目を合わせやすくなります。細い光束は細部を見やすくしますが、条件への要求が高くなります。', resetLabel: '数値をリセット', fieldEstimateLabel: '視野の推定', fieldEstimateText: '実視野は見かけ視野と倍率から求める推定値です。' },
|
|
66
|
+
ko: { slug: 'telescope-exit-pupil-magnification-planner', title: '망원경 사출동공과 배율 플래너', description: '망원경과 접안렌즈 조합의 배율, 사출동공, 초점비와 추정 실제 시야를 계산합니다.', barlowLabel: '바로우 배율', apparentFieldLabel: '접안렌즈 겉보기 시야', focalRatioLabel: '초점비', effectiveEyepieceLabel: '유효 접안렌즈 초점거리', opticalPathLabel: '광학 경로', opticalPathDescription: '넓은 광선 다발은 더 밝고 눈을 맞추기 쉽습니다. 좁은 광선 다발은 세부 묘사에 유리하지만 관측 조건의 영향을 더 많이 받습니다.', resetLabel: '값 초기화', fieldEstimateLabel: '시야 추정', fieldEstimateText: '실제 시야는 겉보기 시야와 배율로 계산한 추정값입니다.' },
|
|
67
|
+
nl: { slug: 'telescoop-uittreepupil-vergrotingsplanner', title: 'Planner voor uittreepupil en vergroting van de telescoop', description: 'Plan een combinatie van telescoop en oculair met vergroting, uittreepupil, brandpuntsverhouding en een geschat werkelijk beeldveld.', barlowLabel: 'Barlowfactor', apparentFieldLabel: 'Schijnbaar beeldveld van het oculair', focalRatioLabel: 'Brandpuntsverhouding', effectiveEyepieceLabel: 'Effectieve oculairbrandpuntsafstand', opticalPathLabel: 'De optische weg', opticalPathDescription: 'Een bredere lichtbundel is helderder en gemakkelijker voor het oog te plaatsen. Een smallere bundel toont meer detail maar is veeleisender.', resetLabel: 'Waarden herstellen', fieldEstimateLabel: 'Schatting van het beeldveld', fieldEstimateText: 'Het werkelijke beeldveld is een schatting op basis van schijnbaar beeldveld en vergroting.' },
|
|
68
|
+
pl: { slug: 'planer-zrenicy-wyjscia-powiekszenia-teleskopu', title: 'Planer źrenicy wyjściowej i powiększenia teleskopu', description: 'Zaplanuj zestaw teleskopu i okularu, obliczając powiększenie, źrenicę wyjściową, światłosiłę oraz szacowane rzeczywiste pole widzenia.', barlowLabel: 'Współczynnik Barlowa', apparentFieldLabel: 'Pozorne pole widzenia okularu', focalRatioLabel: 'Światłosiła', effectiveEyepieceLabel: 'Efektywna ogniskowa okularu', opticalPathLabel: 'Droga optyczna', opticalPathDescription: 'Szersza wiązka jest jaśniejsza i łatwiejsza do ustawienia dla oka. Węższa wiązka pokazuje więcej szczegółów, ale wymaga lepszych warunków.', resetLabel: 'Przywróć wartości', fieldEstimateLabel: 'Szacowane pole', fieldEstimateText: 'Rzeczywiste pole widzenia jest szacunkiem opartym na polu pozornym i powiększeniu.' },
|
|
69
|
+
pt: { slug: 'planeador-pupila-saida-ampliacao-telescopio', title: 'Planeador da pupila de saída e ampliação do telescópio', description: 'Planeie uma combinação de telescópio e ocular calculando a ampliação, a pupila de saída, a relação focal e o campo real estimado.', barlowLabel: 'Fator de Barlow', apparentFieldLabel: 'Campo aparente da ocular', focalRatioLabel: 'Relação focal', effectiveEyepieceLabel: 'Distância focal efetiva da ocular', opticalPathLabel: 'O percurso ótico', opticalPathDescription: 'Um feixe mais largo é mais luminoso e fácil de colocar no olho. Um feixe mais estreito favorece o detalhe, mas exige melhores condições.', resetLabel: 'Repor valores', fieldEstimateLabel: 'Estimativa do campo', fieldEstimateText: 'O campo real é uma estimativa baseada no campo aparente e na ampliação.' },
|
|
70
|
+
ru: { slug: 'planirovshchik-vykhodnogo-zrachka-uvelicheniya-teleskopa', title: 'Планировщик выходного зрачка и увеличения телескопа', description: 'Планируйте сочетание телескопа и окуляра, рассчитывая увеличение, выходной зрачок, относительное отверстие и примерное истинное поле зрения.', barlowLabel: 'Кратность линзы Барлоу', apparentFieldLabel: 'Поле зрения окуляра', focalRatioLabel: 'Относительное отверстие', effectiveEyepieceLabel: 'Эффективное фокусное расстояние окуляра', opticalPathLabel: 'Оптический путь', opticalPathDescription: 'Широкий световой пучок ярче и удобнее для глаза. Узкий пучок показывает больше деталей, но сильнее зависит от условий.', resetLabel: 'Сбросить значения', fieldEstimateLabel: 'Оценка поля', fieldEstimateText: 'Истинное поле является оценкой на основе видимого поля и увеличения.' },
|
|
71
|
+
sv: { slug: 'teleskop-planer-utgangspupill-forstoring', title: 'Planerare för utgångspupill och förstoring i teleskop', description: 'Planera en kombination av teleskop och okular genom att beräkna förstoring, utgångspupill, brännviddsförhållande och uppskattat verkligt synfält.', barlowLabel: 'Barlowfaktor', apparentFieldLabel: 'Okularets skenbara synfält', focalRatioLabel: 'Brännviddsförhållande', effectiveEyepieceLabel: 'Effektiv okulärbrännvidd', opticalPathLabel: 'Den optiska vägen', opticalPathDescription: 'En bredare ljusstråle är ljusare och lättare att placera för ögat. En smalare stråle ger mer detalj men ställer högre krav.', resetLabel: 'Återställ värden', fieldEstimateLabel: 'Uppskattat synfält', fieldEstimateText: 'Det verkliga synfältet är en uppskattning från skenbart synfält och förstoring.' },
|
|
72
|
+
tr: { slug: 'teleskop-cikis-gozu-buyutme-plani', title: 'Teleskop çıkış gözbebeği ve büyütme planlayıcısı', description: 'Büyütme, çıkış gözbebeği, odak oranı ve tahmini gerçek görüş alanını hesaplayarak teleskop ve göz merceği kombinasyonu planlayın.', barlowLabel: 'Barlow katsayısı', apparentFieldLabel: 'Göz merceği görünen alanı', focalRatioLabel: 'Odak oranı', effectiveEyepieceLabel: 'Etkili göz merceği odak uzaklığı', opticalPathLabel: 'Optik yol', opticalPathDescription: 'Daha geniş ışık demeti daha parlak ve göze yerleştirmesi daha kolaydır. Daha dar demet daha fazla ayrıntı sağlar ancak koşullara daha duyarlıdır.', resetLabel: 'Değerleri sıfırla', fieldEstimateLabel: 'Alan tahmini', fieldEstimateText: 'Gerçek görüş alanı, görünen alan ve büyütmeden elde edilen bir tahmindir.' },
|
|
73
|
+
zh: { slug: 'telescope-exit-pupil-magnification-planner', title: '望远镜出瞳与放大倍数规划器', description: '通过计算放大倍数、出瞳、焦比和估算真实视场,规划望远镜与目镜的组合。', barlowLabel: '巴罗镜倍数', apparentFieldLabel: '目镜视场角', focalRatioLabel: '焦比', effectiveEyepieceLabel: '目镜有效焦距', opticalPathLabel: '光路', opticalPathDescription: '较宽的光束更明亮,也更容易让眼睛对准。较窄的光束有利于观察细节,但对观测条件要求更高。', resetLabel: '重置数值', fieldEstimateLabel: '视场估算', fieldEstimateText: '真实视场是根据视场角和放大倍数得到的估算值。' },
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function buildSchemas(title: string, description: string, faq: FAQItem[], howTo: HowToStep[], language: string): any[] {
|
|
77
|
+
const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
|
|
78
|
+
const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
|
|
79
|
+
const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'UtilitiesApplication', operatingSystem: 'Web', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: language };
|
|
80
|
+
return [faqSchema, howToSchema, appSchema];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createTranslatedContent(locale: string): TelescopeExitPupilPlannerLocaleContent {
|
|
84
|
+
const base = bases[locale];
|
|
85
|
+
const pack = packs[locale];
|
|
86
|
+
const b = base.ui;
|
|
87
|
+
const ui = {
|
|
88
|
+
apertureLabel: b.apertureLabel,
|
|
89
|
+
scopeFocalLengthLabel: b.focalLabel,
|
|
90
|
+
eyepieceFocalLengthLabel: b.eyepieceFocal,
|
|
91
|
+
barlowLabel: pack.barlowLabel,
|
|
92
|
+
apparentFieldLabel: pack.apparentFieldLabel,
|
|
93
|
+
millimetreUnit: 'mm',
|
|
94
|
+
presetHint: `${b.apertureHelp} ${b.focalHelp} ${b.afovHelp}`,
|
|
95
|
+
barlowOptions: [{ value: '1', label: '1x' }, { value: '1.5', label: '1.5x' }, { value: '2', label: '2x' }, { value: '2.5', label: '2.5x' }, { value: '3', label: '3x' }],
|
|
96
|
+
apparentFieldOptions: [{ value: '40', label: '40°' }, { value: '50', label: '50°' }, { value: '60', label: '60°' }, { value: '68', label: '68°' }, { value: '82', label: '82°' }],
|
|
97
|
+
magnificationLabel: b.magLabel,
|
|
98
|
+
exitPupilLabel: b.pupilLabel,
|
|
99
|
+
trueFieldLabel: b.tfovLabel,
|
|
100
|
+
focalRatioLabel: pack.focalRatioLabel,
|
|
101
|
+
effectiveEyepieceLabel: pack.effectiveEyepieceLabel,
|
|
102
|
+
magnificationUnit: 'x',
|
|
103
|
+
exitPupilUnit: 'mm',
|
|
104
|
+
fieldUnit: '°',
|
|
105
|
+
focalRatioUnit: 'f ratio',
|
|
106
|
+
opticalPathLabel: pack.opticalPathLabel,
|
|
107
|
+
opticalPathDescription: pack.opticalPathDescription,
|
|
108
|
+
pupilTooSmall: b.statusWarningHighMag,
|
|
109
|
+
pupilDetail: b.statusPerfectPlanetary,
|
|
110
|
+
pupilWorking: b.statusPerfectGeneral,
|
|
111
|
+
pupilWide: b.statusPerfectDeepSky,
|
|
112
|
+
pupilTooSmallDetail: b.statusWarningHighMag,
|
|
113
|
+
pupilDetailDetail: b.statusPerfectPlanetary,
|
|
114
|
+
pupilWorkingDetail: b.statusPerfectGeneral,
|
|
115
|
+
pupilWideDetail: b.statusPerfectDeepSky,
|
|
116
|
+
assumptionLabel: pack.fieldEstimateLabel,
|
|
117
|
+
assumptionText: pack.fieldEstimateText,
|
|
118
|
+
magnificationHint: `${b.focalHelp} / ${b.eyepieceFocal}`,
|
|
119
|
+
trueFieldHint: pack.fieldEstimateText,
|
|
120
|
+
resetLabel: pack.resetLabel,
|
|
121
|
+
};
|
|
122
|
+
const faq = [...base.faq, { question: pack.barlowLabel, answer: `${pack.barlowLabel}: ${pack.fieldEstimateText} ${b.focalHelp}` }];
|
|
123
|
+
const howTo = [...base.howTo, { name: pack.opticalPathLabel, text: `${pack.opticalPathDescription} ${b.apertureHelp}` }];
|
|
124
|
+
const seo: SEOSection[] = [
|
|
125
|
+
{ type: 'title', text: pack.title, level: 2 },
|
|
126
|
+
{ type: 'paragraph', html: `${pack.description} ${b.apertureHelp} ${b.focalHelp} ${b.afovHelp}` },
|
|
127
|
+
...base.seo,
|
|
128
|
+
];
|
|
129
|
+
return {
|
|
130
|
+
slug: pack.slug,
|
|
131
|
+
title: pack.title,
|
|
132
|
+
description: pack.description,
|
|
133
|
+
ui,
|
|
134
|
+
seo,
|
|
135
|
+
faq,
|
|
136
|
+
bibliography,
|
|
137
|
+
howTo,
|
|
138
|
+
schemas: buildSchemas(pack.title, pack.description, faq, howTo, locale),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { telescopeExitPupilPlanner } from './entry';
|
|
2
|
+
import type { ToolDefinition } from '../../types';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
export const TELESCOPE_EXIT_PUPIL_PLANNER_TOOL: ToolDefinition = {
|
|
6
|
+
entry: telescopeExitPupilPlanner,
|
|
7
|
+
Component: () => import('./component.astro'),
|
|
8
|
+
SEOComponent: () => import('./seo.astro'),
|
|
9
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
10
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { calculateOptics, DEFAULT_INPUTS } from './logic';
|
|
3
|
+
|
|
4
|
+
describe('calculateOptics', () => {
|
|
5
|
+
it('calculates the default optical path', () => {
|
|
6
|
+
const result = calculateOptics(DEFAULT_INPUTS);
|
|
7
|
+
|
|
8
|
+
expect(result.magnification).toBe(30);
|
|
9
|
+
expect(result.exitPupilMm).toBe(5);
|
|
10
|
+
expect(result.trueFieldDeg).toBeCloseTo(1.6667, 4);
|
|
11
|
+
expect(result.focalRatio).toBe(5);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('includes the Barlow factor in magnification and exit pupil', () => {
|
|
15
|
+
const result = calculateOptics({ ...DEFAULT_INPUTS, barlowFactor: 2 });
|
|
16
|
+
|
|
17
|
+
expect(result.effectiveEyepieceMm).toBe(12.5);
|
|
18
|
+
expect(result.magnification).toBe(60);
|
|
19
|
+
expect(result.exitPupilMm).toBe(2.5);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('uses the apparent field assumption for true field', () => {
|
|
23
|
+
const result = calculateOptics({ ...DEFAULT_INPUTS, apparentFieldDeg: 82 });
|
|
24
|
+
|
|
25
|
+
expect(result.trueFieldDeg).toBeCloseTo(2.7333, 4);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('falls back safely for invalid values', () => {
|
|
29
|
+
const result = calculateOptics({
|
|
30
|
+
apertureMm: 0,
|
|
31
|
+
scopeFocalLengthMm: Number.NaN,
|
|
32
|
+
eyepieceFocalLengthMm: -1,
|
|
33
|
+
barlowFactor: 0,
|
|
34
|
+
apparentFieldDeg: Number.POSITIVE_INFINITY,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual(calculateOptics(DEFAULT_INPUTS));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface OpticsInputs {
|
|
2
|
+
apertureMm: number;
|
|
3
|
+
scopeFocalLengthMm: number;
|
|
4
|
+
eyepieceFocalLengthMm: number;
|
|
5
|
+
barlowFactor: number;
|
|
6
|
+
apparentFieldDeg: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface OpticsResult {
|
|
10
|
+
apertureMm: number;
|
|
11
|
+
scopeFocalLengthMm: number;
|
|
12
|
+
eyepieceFocalLengthMm: number;
|
|
13
|
+
barlowFactor: number;
|
|
14
|
+
apparentFieldDeg: number;
|
|
15
|
+
effectiveEyepieceMm: number;
|
|
16
|
+
focalRatio: number;
|
|
17
|
+
magnification: number;
|
|
18
|
+
exitPupilMm: number;
|
|
19
|
+
trueFieldDeg: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_INPUTS: OpticsInputs = {
|
|
23
|
+
apertureMm: 150,
|
|
24
|
+
scopeFocalLengthMm: 750,
|
|
25
|
+
eyepieceFocalLengthMm: 25,
|
|
26
|
+
barlowFactor: 1,
|
|
27
|
+
apparentFieldDeg: 50,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function positive(value: number, fallback: number): number {
|
|
31
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function calculateOptics(inputs: OpticsInputs): OpticsResult {
|
|
35
|
+
const apertureMm = positive(inputs.apertureMm, DEFAULT_INPUTS.apertureMm);
|
|
36
|
+
const scopeFocalLengthMm = positive(inputs.scopeFocalLengthMm, DEFAULT_INPUTS.scopeFocalLengthMm);
|
|
37
|
+
const eyepieceFocalLengthMm = positive(inputs.eyepieceFocalLengthMm, DEFAULT_INPUTS.eyepieceFocalLengthMm);
|
|
38
|
+
const barlowFactor = positive(inputs.barlowFactor, DEFAULT_INPUTS.barlowFactor);
|
|
39
|
+
const apparentFieldDeg = positive(inputs.apparentFieldDeg, DEFAULT_INPUTS.apparentFieldDeg);
|
|
40
|
+
const effectiveEyepieceMm = eyepieceFocalLengthMm / barlowFactor;
|
|
41
|
+
const magnification = scopeFocalLengthMm / effectiveEyepieceMm;
|
|
42
|
+
const exitPupilMm = apertureMm / magnification;
|
|
43
|
+
const trueFieldDeg = apparentFieldDeg / magnification;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
apertureMm,
|
|
47
|
+
scopeFocalLengthMm,
|
|
48
|
+
eyepieceFocalLengthMm,
|
|
49
|
+
barlowFactor,
|
|
50
|
+
apparentFieldDeg,
|
|
51
|
+
effectiveEyepieceMm,
|
|
52
|
+
focalRatio: scopeFocalLengthMm / apertureMm,
|
|
53
|
+
magnification,
|
|
54
|
+
exitPupilMm,
|
|
55
|
+
trueFieldDeg,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { telescopeExitPupilPlanner } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await telescopeExitPupilPlanner.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<SEORenderer content={{ locale, sections: content.seo }} />
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { OpticsInputs } from './logic';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = 'jjlmoya:telescope-exit-pupil-planner';
|
|
4
|
+
|
|
5
|
+
export function readStoredInputs(): Partial<OpticsInputs> | null {
|
|
6
|
+
try {
|
|
7
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
8
|
+
return raw ? (JSON.parse(raw) as Partial<OpticsInputs>) : null;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function writeStoredInputs(inputs: OpticsInputs): void {
|
|
15
|
+
try {
|
|
16
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(inputs));
|
|
17
|
+
} catch {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function clearStoredInputs(): void {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|