@jjlmoya/utils-drones 1.36.0 → 1.37.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 (35) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +4 -0
  3. package/src/entries.ts +4 -0
  4. package/src/tests/locale_completeness.test.ts +2 -2
  5. package/src/tests/tool_validation.test.ts +2 -2
  6. package/src/tool/drone-mission-battery-reserve-planner/bibliography.astro +6 -0
  7. package/src/tool/drone-mission-battery-reserve-planner/bibliography.ts +7 -0
  8. package/src/tool/drone-mission-battery-reserve-planner/component.astro +229 -0
  9. package/src/tool/drone-mission-battery-reserve-planner/controller.ts +124 -0
  10. package/src/tool/drone-mission-battery-reserve-planner/dom-views.ts +114 -0
  11. package/src/tool/drone-mission-battery-reserve-planner/drone-mission-battery-reserve-planner.css +392 -0
  12. package/src/tool/drone-mission-battery-reserve-planner/entry.ts +27 -0
  13. package/src/tool/drone-mission-battery-reserve-planner/evaluator.ts +23 -0
  14. package/src/tool/drone-mission-battery-reserve-planner/i18n/de.ts +223 -0
  15. package/src/tool/drone-mission-battery-reserve-planner/i18n/en.ts +223 -0
  16. package/src/tool/drone-mission-battery-reserve-planner/i18n/es.ts +223 -0
  17. package/src/tool/drone-mission-battery-reserve-planner/i18n/fr.ts +223 -0
  18. package/src/tool/drone-mission-battery-reserve-planner/i18n/id.ts +223 -0
  19. package/src/tool/drone-mission-battery-reserve-planner/i18n/it.ts +223 -0
  20. package/src/tool/drone-mission-battery-reserve-planner/i18n/ja.ts +223 -0
  21. package/src/tool/drone-mission-battery-reserve-planner/i18n/ko.ts +223 -0
  22. package/src/tool/drone-mission-battery-reserve-planner/i18n/nl.ts +223 -0
  23. package/src/tool/drone-mission-battery-reserve-planner/i18n/pl.ts +223 -0
  24. package/src/tool/drone-mission-battery-reserve-planner/i18n/pt.ts +223 -0
  25. package/src/tool/drone-mission-battery-reserve-planner/i18n/ru.ts +223 -0
  26. package/src/tool/drone-mission-battery-reserve-planner/i18n/sv.ts +223 -0
  27. package/src/tool/drone-mission-battery-reserve-planner/i18n/tr.ts +223 -0
  28. package/src/tool/drone-mission-battery-reserve-planner/i18n/zh.ts +223 -0
  29. package/src/tool/drone-mission-battery-reserve-planner/index.ts +11 -0
  30. package/src/tool/drone-mission-battery-reserve-planner/logic.test.ts +94 -0
  31. package/src/tool/drone-mission-battery-reserve-planner/logic.ts +203 -0
  32. package/src/tool/drone-mission-battery-reserve-planner/seo.astro +15 -0
  33. package/src/tool/drone-mission-battery-reserve-planner/storage.ts +59 -0
  34. package/src/tool/drone-mission-battery-reserve-planner/ui.ts +78 -0
  35. package/src/tools.ts +4 -0
@@ -0,0 +1,223 @@
1
+ import type { DroneMissionBatteryReservePlannerLocaleContent } from '../entry';
2
+ import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ const slug = 'dron-gorev-pil-rezerv-planlayici';
6
+ const title = 'Dron Görev Pil Rezerv Planlayıcı';
7
+ const description = 'İHA görevleri için güvenli eve dönüş pil rezerv marjlarını, karşı rüzgar güç kayıplarını ve güvenli uçuş yarıçapını hesaplayın.';
8
+
9
+ const faqItems = [
10
+ {
11
+ question: 'Karşı rüzgara karşı uçmak neden daha fazla enerji tüketir?',
12
+ answer: 'Karşı rüzgarda uçmak, hava direncini yenmek ve yer hızını korumak için daha dik bir eğim açısı gerektirir ve motor akım çekimini doğrusal olmayan şekilde artırır.',
13
+ },
14
+ {
15
+ question: 'Hedef bölgedeki askıda kalma süresi geri dönüş noktasını nasıl etkiler?',
16
+ answer: 'Hedefte harcanan askıda kalma süresi, kalan güvenli gidiş-dönüş menzili hesaplanmadan önce doğrudan kullanılabilir pil enerjisinden düşülür.',
17
+ },
18
+ {
19
+ question: 'Yüksek yük altında LiPo pillerde voltaj düşüşüne ne sebep olur?',
20
+ answer: 'Yüksek akım çekimi, lityum hücrelerinin iç direnç kayıplarını artırarak efektif kullanılabilir Watt-saat kapasitesini düşürür.',
21
+ },
22
+ ];
23
+
24
+ const howToSteps = [
25
+ {
26
+ name: 'Pil ve İtki Özelliklerini Girin',
27
+ text: 'Pil kapasitesini mAh, nominal voltajı ve ortalama seyir akımını girin.',
28
+ },
29
+ {
30
+ name: 'Mesafe ve Askıda Kalma Süresini Ayarlayın',
31
+ text: 'Gidiş mesafesini ve hedef bölgedeki tahmini askıda kalma süresini belirtin.',
32
+ },
33
+ {
34
+ name: 'Rüzgar Hızını ve Yönünü Yapılandırın',
35
+ text: 'Rüzgar hızını ve gidiş yönüne göre açısını seçin.',
36
+ },
37
+ {
38
+ name: 'Güvenli Yarıçapı ve Telemetriyi İnceleyin',
39
+ text: 'Hesaplanan geri dönüş noktasını, bölüm bazında güç tüketimini ve inişteki pil seviyesini inceleyin.',
40
+ },
41
+ ];
42
+
43
+ const schemas: DroneMissionBatteryReservePlannerLocaleContent['schemas'] = [
44
+ {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'FAQPage',
47
+ mainEntity: faqItems.map((item) => ({
48
+ '@type': 'Question',
49
+ name: item.question,
50
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
51
+ })),
52
+ } as WithContext<FAQPage>,
53
+ {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'HowTo',
56
+ name: title,
57
+ description: description,
58
+ step: howToSteps.map((step, i) => ({
59
+ '@type': 'HowToStep',
60
+ position: i + 1,
61
+ name: step.name,
62
+ text: step.text,
63
+ })),
64
+ } as WithContext<HowTo>,
65
+ {
66
+ '@context': 'https://schema.org',
67
+ '@type': 'SoftwareApplication',
68
+ name: title,
69
+ description: description,
70
+ applicationCategory: 'UtilityApplication',
71
+ operatingSystem: 'Web',
72
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'TRY' },
73
+ } as WithContext<SoftwareApplication>,
74
+ ];
75
+
76
+ export const content: DroneMissionBatteryReservePlannerLocaleContent = {
77
+ slug,
78
+ title,
79
+ description,
80
+ ui: {
81
+ title: 'Dron Görev Pil Rezerv Planlayıcı',
82
+ subtitle: 'Güvenli eve dönüş rezervlerini ve uçuş yarıçapını hesaplayın',
83
+ description: 'Rüzgar düzeltmeleri ve geri dönüş noktası eşikleri ile hassas İHA pil planlaması.',
84
+ sections: {
85
+ batteryPropulsion: '1. Pil ve İtki Sistemi',
86
+ flightAtmosphere: '2. Uçuş Profili ve Atmosfer',
87
+ },
88
+ inputs: {
89
+ unitSystemLabel: 'Birim Sistemi',
90
+ metricLabel: 'Metrik',
91
+ imperialLabel: 'İngiliz',
92
+ presetLabel: 'Hızlı Görev Şablonları',
93
+ batteryCapacityLabel: 'Pil Kapasitesi',
94
+ batteryVoltageLabel: 'Nominal Voltaj',
95
+ averageCurrentLabel: 'Ortalama Seyir Akımı',
96
+ cruiseSpeedLabel: 'Hava Seyir Hızı',
97
+ oneWayDistanceLabel: 'Tek Yön Hedef Mesafesi',
98
+ targetHoverTimeLabel: 'Hedef Bölge Askı Süresi',
99
+ windSpeedLabel: 'Rüzgar Hızı',
100
+ windDirectionLabel: 'Gidişe Göre Rüzgar Yönü',
101
+ windHeadwindLabel: 'Gidişte Karşı Rüzgar',
102
+ windTailwindLabel: 'Gidişte Arka Rüzgar',
103
+ windCrosswindLabel: 'Yan Rüzgar',
104
+ reservePolicyLabel: 'Güvenlik Rezerv Tamponu',
105
+ },
106
+ presets: {
107
+ mappingSurvey: 'Fotogrametri ve Haritalama',
108
+ fpvRecon: 'FPV Uzun Menzil Keşif',
109
+ cinematicInspection: 'Sinematik Yapı Denetimi',
110
+ microRecon: 'Mikro Dron Görevi',
111
+ surveyMeta: 'haritalama',
112
+ scoutMeta: 'keşif',
113
+ hoverMeta: 'askıda kalma',
114
+ },
115
+ results: {
116
+ totalCapacityEnergy: 'Toplam Enerji Kapasitesi',
117
+ usableEnergy: 'Kullanılabilir Görev Enerjisi',
118
+ reserveEnergyBuffer: 'Yedek Enerji Tamponu',
119
+ totalAutonomyTime: 'Toplam Uçuş Otonomisi',
120
+ maxSafeMissionRadius: 'Geri Dönüş Noktası Yarıçapı',
121
+ outboundLegTime: 'Gidiş Uçuş Süresi',
122
+ targetHoverTime: 'Hedef Askı Süresi',
123
+ returnLegTime: 'Dönüş Uçuş Süresi',
124
+ totalMissionTime: 'Toplam Transit Süresi',
125
+ remainingEnergyLanding: 'İnişte Tahmini Pil Seviyesi',
126
+ feasibilityStatus: 'Görev Uygulanabilirlik Değerlendirmesi',
127
+ voltageSagSubLabel: 'Voltaj düşüşü',
128
+ maxRadiusSubLabel: 'Askı süresi dahil maks güvenli yarıçap',
129
+ powerSubLabel: 'Güç',
130
+ },
131
+ statusBadges: {
132
+ optimalTitle: 'OPTİMAL ENERJİ REZERV MARJI',
133
+ optimalSubtitle: 'İniş için yeterli rezerv marjına sahip güvenli uçuş profili',
134
+ tightTitle: 'DAR ENERJİ REZERV MARJI',
135
+ tightSubtitle: 'Düşük iniş rezervi, pil voltajını yakından takip edin',
136
+ criticalTitle: 'KRİTİK ENERJİ UYARISI',
137
+ criticalSubtitle: 'Rezerv sınırı aşıldı, hemen eve dönüşü başlatın',
138
+ exceededTitle: 'GÖREV GÜVENLİ KAPASİTEYİ AŞIYOR',
139
+ exceededSubtitle: 'Görevi güvenle tamamlamak için yetersiz pil enerjisi',
140
+ },
141
+ chart: {
142
+ batteryProfileTitle: 'DOĞRUSAL OLMAYAN ENERJİ DAĞILIM PROFİLİ',
143
+ modelTitle: 'AERODİNAMİK GÜÇ VE RÜZGAR MODELİ',
144
+ windLabel: 'Rüzgar',
145
+ homeNode: 'EV',
146
+ targetNode: 'HEDEF',
147
+ landNode: 'İNİŞ',
148
+ launchPadLabel: 'Kalkış noktası',
149
+ surveyHoverLabel: 'Hedefte askı',
150
+ safeRadiusLabel: 'Güvenli yarıçap',
151
+ outboundSegment: 'Gidiş',
152
+ targetSegment: 'Askıda kalma',
153
+ returnSegment: 'Dönüş',
154
+ reserveSegment: 'Rezerv',
155
+ },
156
+ },
157
+ seo: [
158
+ {
159
+ type: 'title',
160
+ text: 'Karşı Rüzgarda Aerodinamik Güç Kayıpları',
161
+ level: 2,
162
+ },
163
+ {
164
+ type: 'paragraph',
165
+ html: 'İHA uçuş güvenliği doğrusal olmayan fizik kurallarına dayanır. Karşı rüzgarda uçmak, sürtünmeyi yenmek ve yer hızını korumak için daha fazla açı gerektirir.',
166
+ },
167
+ {
168
+ type: 'paragraph',
169
+ html: 'Planlayıcımız rüzgar koşullarına göre her bölüm için güç tüketimini dinamik olarak hesaplar.',
170
+ },
171
+ {
172
+ type: 'title',
173
+ text: 'Hedef Bölgesindeki Askı Süresinin Hesaplanması',
174
+ level: 2,
175
+ },
176
+ {
177
+ type: 'paragraph',
178
+ html: 'Haritalama ve denetim görevleri hedef üzerinde askıda kalmayı gerektirir. Bu tüketim dönüş yarıçapı hesaplanmadan önce düşülür.',
179
+ },
180
+ {
181
+ type: 'list',
182
+ items: [
183
+ 'Uçuş sınırlarını hesaplamadan önce askı süresini girin.',
184
+ 'Karşı rüzgarda güç artışlarını dikkate alın.',
185
+ 'Yük altında LiPo hücre voltaj düşüşünü izleyin.',
186
+ 'Rezerv sınırına ulaşıldığında hemen dönüşü başlatın.',
187
+ ],
188
+ },
189
+ {
190
+ type: 'tip',
191
+ title: 'LiPo Pillerde Voltaj Düşüşü Uyarısı',
192
+ html: 'Yüksek akım çekimi iç direnç nedeniyle voltaj düşüşüne yol açarak kullanılabilir enerjiyi azaltır.',
193
+ },
194
+ {
195
+ type: 'title',
196
+ text: 'İHA Pil Rezervi Hesaplama Formülleri',
197
+ level: 2,
198
+ },
199
+ {
200
+ type: 'table',
201
+ headers: ['Parametre', 'Formül / Model', 'Birim'],
202
+ rows: [
203
+ ['Brüt Enerji', 'Kapasite (mAh) x Voltaj (V) / 1000', 'Watt-saat (Wh)'],
204
+ ['Voltaj Düşüş Kaybı', 'Brüt Enerji x Sag Faktörü', 'Watt-saat (Wh)'],
205
+ ['Rüzgarlı Güç', 'Baz Güç x (1 + 0.65 x RüzgarOranı)^1.3', 'Watt (W)'],
206
+ ['Maks Güvenli Yarıçap', '(Kullanılabilir Enerji - Askı Enerjisi) / Km Başına Tüketim', 'Kilometre (km)'],
207
+ ],
208
+ },
209
+ {
210
+ type: 'title',
211
+ text: 'İHA Uçuş Planlamasında En İyi Uygulamalar',
212
+ level: 2,
213
+ },
214
+ {
215
+ type: 'paragraph',
216
+ html: 'En yüksek operasyonel güvenlik için uçuş öncesi hesaplamaları her zaman telemetri verileriyle doğrulayın.',
217
+ },
218
+ ],
219
+ faq: faqItems,
220
+ bibliography,
221
+ howTo: howToSteps,
222
+ schemas,
223
+ };
@@ -0,0 +1,223 @@
1
+ import type { DroneMissionBatteryReservePlannerLocaleContent } from '../entry';
2
+ import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ const slug = 'drone-mission-battery-reserve-planner';
6
+ const title = '无人机任务电池储备规划工具';
7
+ const description = '计算无人机返航安全电池储备余量、逆风阻力功率损耗及最大安全不可逆返航半径。';
8
+
9
+ const faqItems = [
10
+ {
11
+ question: '为什么逆风飞行比顺风飞行消耗更多电量?',
12
+ answer: '逆风飞行需要更大的倾角来克服空气阻力并维持地速,从而导致电机电流消耗非线性急剧增加。',
13
+ },
14
+ {
15
+ question: '目标区域悬停时间如何影响返航临界点?',
16
+ answer: '在目标区域的作业悬停时间会直接消耗可用电池电量,从而缩短可用于往返飞行的安全半径。',
17
+ },
18
+ {
19
+ question: '负载下LiPo电池电压压降的原因是什么?',
20
+ answer: '大电流放电会增加锂电池内部电阻损耗,从而降低实际可用的瓦时容量。',
21
+ },
22
+ ];
23
+
24
+ const howToSteps = [
25
+ {
26
+ name: '输入电池与动力参数',
27
+ text: '输入电池容量(mAh)、标称电压(V)和平均巡航电流(A)。',
28
+ },
29
+ {
30
+ name: '设置单程距离与目标悬停时间',
31
+ text: '指定单程飞行距离以及在目标区域预计的作业悬停时间。',
32
+ },
33
+ {
34
+ name: '配置风速与风向',
35
+ text: '选择环境风速以及相对于去程的风向,应用空气动力学功率修正。',
36
+ },
37
+ {
38
+ name: '查看安全半径与遥测数据',
39
+ text: '检查计算出的不可逆返航点、分段功率消耗以及降落时的预计剩余电量。',
40
+ },
41
+ ];
42
+
43
+ const schemas: DroneMissionBatteryReservePlannerLocaleContent['schemas'] = [
44
+ {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'FAQPage',
47
+ mainEntity: faqItems.map((item) => ({
48
+ '@type': 'Question',
49
+ name: item.question,
50
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
51
+ })),
52
+ } as WithContext<FAQPage>,
53
+ {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'HowTo',
56
+ name: title,
57
+ description: description,
58
+ step: howToSteps.map((step, i) => ({
59
+ '@type': 'HowToStep',
60
+ position: i + 1,
61
+ name: step.name,
62
+ text: step.text,
63
+ })),
64
+ } as WithContext<HowTo>,
65
+ {
66
+ '@context': 'https://schema.org',
67
+ '@type': 'SoftwareApplication',
68
+ name: title,
69
+ description: description,
70
+ applicationCategory: 'UtilityApplication',
71
+ operatingSystem: 'Web',
72
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'CNY' },
73
+ } as WithContext<SoftwareApplication>,
74
+ ];
75
+
76
+ export const content: DroneMissionBatteryReservePlannerLocaleContent = {
77
+ slug,
78
+ title,
79
+ description,
80
+ ui: {
81
+ title: '无人机任务电池储备规划工具',
82
+ subtitle: '计算返航安全余量、风力影响及飞行半径',
83
+ description: '具有逆风修正与返航临界点计算的高精度无人机电池储备规划器。',
84
+ sections: {
85
+ batteryPropulsion: '1. 电池与动力系统',
86
+ flightAtmosphere: '2. 飞行剖面与气象环境',
87
+ },
88
+ inputs: {
89
+ unitSystemLabel: '单位制',
90
+ metricLabel: '公制',
91
+ imperialLabel: '英制',
92
+ presetLabel: '快速任务预设',
93
+ batteryCapacityLabel: '电池容量',
94
+ batteryVoltageLabel: '标称电压',
95
+ averageCurrentLabel: '平均巡航电流',
96
+ cruiseSpeedLabel: '空速巡航速度',
97
+ oneWayDistanceLabel: '单程目标距离',
98
+ targetHoverTimeLabel: '目标区域作业时长',
99
+ windSpeedLabel: '环境风速',
100
+ windDirectionLabel: '相对于去程的风向',
101
+ windHeadwindLabel: '去程逆风',
102
+ windTailwindLabel: '去程顺风',
103
+ windCrosswindLabel: '侧风',
104
+ reservePolicyLabel: '安全储备缓冲比例',
105
+ },
106
+ presets: {
107
+ mappingSurvey: '测绘与航拍',
108
+ fpvRecon: 'FPV远航侦察',
109
+ cinematicInspection: '建筑结构巡检',
110
+ microRecon: '微型无人机侦察',
111
+ surveyMeta: '测绘',
112
+ scoutMeta: '侦察',
113
+ hoverMeta: '悬停',
114
+ },
115
+ results: {
116
+ totalCapacityEnergy: '总容量电量',
117
+ usableEnergy: '任务可用电量',
118
+ reserveEnergyBuffer: '安全储备缓冲电量',
119
+ totalAutonomyTime: '总续航飞行时间',
120
+ maxSafeMissionRadius: '不可逆返航点半径',
121
+ outboundLegTime: '去程飞行时长',
122
+ targetHoverTime: '目标悬停时长',
123
+ returnLegTime: '返航飞行时长',
124
+ totalMissionTime: '总飞行总时长',
125
+ remainingEnergyLanding: '降落时预计剩余电量',
126
+ feasibilityStatus: '任务可行性评估',
127
+ voltageSagSubLabel: '电压压降损失',
128
+ maxRadiusSubLabel: '包含作业悬停的最大安全半径',
129
+ powerSubLabel: '功率',
130
+ },
131
+ statusBadges: {
132
+ optimalTitle: '最佳电池储备余量',
133
+ optimalSubtitle: '具备充足降落储备电量的安全飞行剖面',
134
+ tightTitle: '储备余量偏紧警告',
135
+ tightSubtitle: '降落储备偏低,请密切关注电池电压变化',
136
+ criticalTitle: '紧急电量警报',
137
+ criticalSubtitle: '已触及储备底线,请立即开启自动返航',
138
+ exceededTitle: '超出安全容量任务',
139
+ exceededSubtitle: '电池电量不足以安全完成任务并降落',
140
+ },
141
+ chart: {
142
+ batteryProfileTitle: '非线性电池电量分配剖面',
143
+ modelTitle: '空气动力学功率与风场模型',
144
+ windLabel: '风速',
145
+ homeNode: '起飞点',
146
+ targetNode: '目标点',
147
+ landNode: '降落点',
148
+ launchPadLabel: '起飞平台',
149
+ surveyHoverLabel: '目标作业悬停',
150
+ safeRadiusLabel: '安全半径',
151
+ outboundSegment: '去程飞行',
152
+ targetSegment: '作业悬停',
153
+ returnSegment: '返航飞行',
154
+ reserveSegment: '安全储备',
155
+ },
156
+ },
157
+ seo: [
158
+ {
159
+ type: 'title',
160
+ text: '逆风飞行中的空气动力学功率损失',
161
+ level: 2,
162
+ },
163
+ {
164
+ type: 'paragraph',
165
+ html: '无人机飞行安全基于非线性空气动力学规律。在逆风条件下飞行需要更大的倾角来维持地速,这会导致电机负载和功耗非线性增加。',
166
+ },
167
+ {
168
+ type: 'paragraph',
169
+ html: '规划引擎根据风向和风速条件动态计算每个飞行航段的功耗。',
170
+ },
171
+ {
172
+ type: 'title',
173
+ text: '目标区域悬停时间的计算',
174
+ level: 2,
175
+ },
176
+ {
177
+ type: 'paragraph',
178
+ html: '在测绘和巡检任务中,无人机需在目标区域悬停作业,这会在返航前消耗一部分固定电量。在求解最大返航半径前需先扣除该作业电量。',
179
+ },
180
+ {
181
+ type: 'list',
182
+ items: [
183
+ '在计算飞行边界前先设置目标作业悬停时间。',
184
+ '充分评估去程逆风航段的峰值功率。',
185
+ '实时监控大电流放电下的LiPo电芯压降。',
186
+ '一旦触及预设的安全储备线请立即返航。',
187
+ ],
188
+ },
189
+ {
190
+ type: 'tip',
191
+ title: 'LiPo电池压降警示',
192
+ html: '大电流放电会导致锂电池内部电阻压降,从而减少实际可释放的瓦时容量。',
193
+ },
194
+ {
195
+ type: 'title',
196
+ text: '无人机电池储备计算公式',
197
+ level: 2,
198
+ },
199
+ {
200
+ type: 'table',
201
+ headers: ['参数', '计算模型', '单位'],
202
+ rows: [
203
+ ['总能量容量', '容量 (mAh) x 电压 (V) / 1000', '瓦时 (Wh)'],
204
+ ['压降损失', '总能量 x 压降因子', '瓦时 (Wh)'],
205
+ ['逆风功率', '基础功率 x (1 + 0.65 x 风速比)^1.3', '瓦特 (W)'],
206
+ ['最大安全半径', '(可用能量 - 悬停能量) / 每公里耗电量', '公里 (km)'],
207
+ ],
208
+ },
209
+ {
210
+ type: 'title',
211
+ text: '无人机飞行规划最佳实践',
212
+ level: 2,
213
+ },
214
+ {
215
+ type: 'paragraph',
216
+ html: '在商业无人机作业中,始终将实际遥测日志与飞行前计算数据对比,以确保最高安全等级。',
217
+ },
218
+ ],
219
+ faq: faqItems,
220
+ bibliography,
221
+ howTo: howToSteps,
222
+ schemas,
223
+ };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { droneMissionBatteryReservePlanner } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const DRONE_MISSION_BATTERY_RESERVE_PLANNER_TOOL: ToolDefinition = {
7
+ entry: droneMissionBatteryReservePlanner,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,94 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ clampValue,
4
+ computeEffectiveSpeeds,
5
+ calculateWindPowerPenalty,
6
+ calculateEnergyMetrics,
7
+ determineMissionStatus,
8
+ calculateMissionReserve,
9
+ convertDistance,
10
+ convertSpeed,
11
+ type MissionInputs,
12
+ } from './logic';
13
+
14
+ describe('Drone Mission Battery Reserve Planner Logic Suite', () => {
15
+ it('clamps values correctly within bounds', () => {
16
+ expect(clampValue(50, 0, 100)).toBe(50);
17
+ expect(clampValue(-10, 0, 100)).toBe(0);
18
+ expect(clampValue(150, 0, 100)).toBe(100);
19
+ });
20
+
21
+ it('computes effective speeds for headwind, tailwind and crosswind', () => {
22
+ const head = computeEffectiveSpeeds(50, 15, 'headwind');
23
+ expect(head.outbound).toBe(35);
24
+ expect(head.returnLeg).toBe(62.75);
25
+
26
+ const tail = computeEffectiveSpeeds(50, 15, 'tailwind');
27
+ expect(tail.outbound).toBe(62.75);
28
+ expect(tail.returnLeg).toBe(35);
29
+
30
+ const cross = computeEffectiveSpeeds(50, 15, 'crosswind');
31
+ expect(cross.outbound).toBeGreaterThan(0);
32
+ expect(cross.returnLeg).toBeGreaterThan(0);
33
+ });
34
+
35
+ it('calculates wind power penalty increasing outbound power during headwind', () => {
36
+ const penalty = calculateWindPowerPenalty(400, 50, 15, 'headwind');
37
+ expect(penalty.outboundPower).toBeGreaterThan(400);
38
+ expect(penalty.returnPower).toBeLessThan(400);
39
+ });
40
+
41
+ it('calculates energy metrics and voltage sag loss correctly', () => {
42
+ const inputs: MissionInputs = {
43
+ unitSystem: 'metric',
44
+ batteryCapacityMah: 10000,
45
+ voltageNominal: 22.2,
46
+ averageCurrentAmps: 20,
47
+ cruiseSpeedKmh: 45,
48
+ oneWayDistanceKm: 10,
49
+ targetHoverTimeMin: 5,
50
+ windSpeedKmh: 10,
51
+ windDirection: 'headwind',
52
+ reservePolicyPercent: 20,
53
+ };
54
+
55
+ const energy = calculateEnergyMetrics(inputs);
56
+ expect(energy.basePowerDrawWatts).toBe(444);
57
+ expect(energy.voltageSagWhLoss).toBeGreaterThan(0);
58
+ expect(energy.totalEnergyWh).toBeLessThan(222);
59
+ });
60
+
61
+ it('calculates mission reserve including target hover time and wind power difference', () => {
62
+ const inputs: MissionInputs = {
63
+ unitSystem: 'metric',
64
+ batteryCapacityMah: 10000,
65
+ voltageNominal: 22.2,
66
+ averageCurrentAmps: 20,
67
+ cruiseSpeedKmh: 45,
68
+ oneWayDistanceKm: 5,
69
+ targetHoverTimeMin: 5,
70
+ windSpeedKmh: 12,
71
+ windDirection: 'headwind',
72
+ reservePolicyPercent: 20,
73
+ };
74
+
75
+ const result = calculateMissionReserve(inputs);
76
+ expect(result.targetHoverTimeMinutes).toBe(5);
77
+ expect(result.targetHoverEnergyWh).toBeGreaterThan(0);
78
+ expect(result.outboundPowerWatts).toBeGreaterThan(result.basePowerDrawWatts);
79
+ });
80
+
81
+ it('determines status correctly', () => {
82
+ expect(determineMissionStatus(50, 40, 200)).toBe('optimal');
83
+ expect(determineMissionStatus(30, 40, 200)).toBe('tight');
84
+ expect(determineMissionStatus(15, 40, 200)).toBe('critical');
85
+ expect(determineMissionStatus(-5, 40, 200)).toBe('exceeded');
86
+ });
87
+
88
+ it('converts distance and speed between metric and imperial', () => {
89
+ expect(convertDistance(10, 'metric')).toBe(10);
90
+ expect(convertDistance(10, 'imperial')).toBeCloseTo(6.21371, 3);
91
+ expect(convertSpeed(100, 'metric')).toBe(100);
92
+ expect(convertSpeed(100, 'imperial')).toBeCloseTo(62.1371, 3);
93
+ });
94
+ });