@jjlmoya/utils-drones 1.35.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.
- package/package.json +1 -1
- package/src/category/index.ts +4 -0
- package/src/entries.ts +8 -0
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/drone-mission-battery-reserve-planner/bibliography.astro +6 -0
- package/src/tool/drone-mission-battery-reserve-planner/bibliography.ts +7 -0
- package/src/tool/drone-mission-battery-reserve-planner/component.astro +229 -0
- package/src/tool/drone-mission-battery-reserve-planner/controller.ts +124 -0
- package/src/tool/drone-mission-battery-reserve-planner/dom-views.ts +114 -0
- package/src/tool/drone-mission-battery-reserve-planner/drone-mission-battery-reserve-planner.css +392 -0
- package/src/tool/drone-mission-battery-reserve-planner/entry.ts +27 -0
- package/src/tool/drone-mission-battery-reserve-planner/evaluator.ts +23 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/de.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/en.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/es.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/fr.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/id.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/it.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/ja.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/ko.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/nl.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/pl.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/pt.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/ru.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/sv.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/tr.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/i18n/zh.ts +223 -0
- package/src/tool/drone-mission-battery-reserve-planner/index.ts +11 -0
- package/src/tool/drone-mission-battery-reserve-planner/logic.test.ts +94 -0
- package/src/tool/drone-mission-battery-reserve-planner/logic.ts +203 -0
- package/src/tool/drone-mission-battery-reserve-planner/seo.astro +15 -0
- package/src/tool/drone-mission-battery-reserve-planner/storage.ts +59 -0
- package/src/tool/drone-mission-battery-reserve-planner/ui.ts +78 -0
- package/src/tool/fpv-drone-speed-calculator/bibliography.astro +6 -0
- package/src/tool/fpv-drone-speed-calculator/bibliography.ts +14 -0
- package/src/tool/fpv-drone-speed-calculator/component.astro +115 -0
- package/src/tool/fpv-drone-speed-calculator/controller.ts +105 -0
- package/src/tool/fpv-drone-speed-calculator/dom-views.ts +80 -0
- package/src/tool/fpv-drone-speed-calculator/entry.ts +27 -0
- package/src/tool/fpv-drone-speed-calculator/fpv-drone-speed-calculator.css +558 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/de.ts +20 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/en.ts +47 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/es.ts +40 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/fr.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/id.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/it.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/ja.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/ko.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/nl.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/pl.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/pt.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/ru.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/sv.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/tr.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/i18n/zh.ts +13 -0
- package/src/tool/fpv-drone-speed-calculator/index.ts +11 -0
- package/src/tool/fpv-drone-speed-calculator/logic.test.ts +53 -0
- package/src/tool/fpv-drone-speed-calculator/logic.ts +126 -0
- package/src/tool/fpv-drone-speed-calculator/seo.astro +16 -0
- package/src/tool/fpv-drone-speed-calculator/ui.ts +47 -0
- package/src/tools.ts +7 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export interface FpvDroneSpeedInputs {
|
|
2
|
+
motorKv: number;
|
|
3
|
+
batteryVoltage: number;
|
|
4
|
+
propellerPitchInches: number;
|
|
5
|
+
efficiencyPercent: number;
|
|
6
|
+
aircraftMassGrams: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type SpeedDiagnosis = 'overspeed' | 'high-slip' | 'heavy-load' | 'planning';
|
|
10
|
+
|
|
11
|
+
export interface SpeedSensitivityPoint {
|
|
12
|
+
label: 'lower' | 'current' | 'higher';
|
|
13
|
+
pitchInches: number;
|
|
14
|
+
speedKmh: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface FpvDroneSpeedResults {
|
|
18
|
+
noLoadRpm: number;
|
|
19
|
+
loadedRpm: number;
|
|
20
|
+
pitchSpeedKmh: number;
|
|
21
|
+
estimatedSpeedKmh: number;
|
|
22
|
+
slipPercent: number;
|
|
23
|
+
loadPenaltyPercent: number;
|
|
24
|
+
rpmPerVolt: number;
|
|
25
|
+
diagnosis: SpeedDiagnosis;
|
|
26
|
+
sensitivity: SpeedSensitivityPoint[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clamp(value: number, min: number, max: number): number {
|
|
30
|
+
return Math.min(max, Math.max(min, value));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validInputs(inputs: FpvDroneSpeedInputs): boolean {
|
|
34
|
+
return inputs.motorKv > 0
|
|
35
|
+
&& inputs.batteryVoltage > 0
|
|
36
|
+
&& inputs.propellerPitchInches > 0
|
|
37
|
+
&& inputs.efficiencyPercent > 0
|
|
38
|
+
&& inputs.aircraftMassGrams > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function calculateNoLoadRpm(motorKv: number, batteryVoltage: number): number {
|
|
42
|
+
if (motorKv <= 0 || batteryVoltage <= 0) return 0;
|
|
43
|
+
return motorKv * batteryVoltage;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function calculateLoadedRpm(inputs: FpvDroneSpeedInputs): number {
|
|
47
|
+
if (!validInputs(inputs)) return 0;
|
|
48
|
+
const noLoadRpm = calculateNoLoadRpm(inputs.motorKv, inputs.batteryVoltage);
|
|
49
|
+
const efficiencyFactor = 0.82 + clamp(inputs.efficiencyPercent, 50, 98) / 1000;
|
|
50
|
+
const loadFactor = clamp(0.98 - inputs.aircraftMassGrams / 12000, 0.78, 0.98);
|
|
51
|
+
return Math.round(noLoadRpm * efficiencyFactor * loadFactor);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function calculatePitchSpeedKmh(propellerPitchInches: number, rpm: number): number {
|
|
55
|
+
if (propellerPitchInches <= 0 || rpm <= 0) return 0;
|
|
56
|
+
return propellerPitchInches * rpm * 0.001524;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function calculateEstimatedSpeedKmh(pitchSpeedKmh: number, efficiencyPercent: number): number {
|
|
60
|
+
if (pitchSpeedKmh <= 0 || efficiencyPercent <= 0) return 0;
|
|
61
|
+
return pitchSpeedKmh * clamp(efficiencyPercent, 50, 98) / 100;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function calculateSlipPercent(pitchSpeedKmh: number, estimatedSpeedKmh: number): number {
|
|
65
|
+
if (pitchSpeedKmh <= 0) return 0;
|
|
66
|
+
return clamp((1 - estimatedSpeedKmh / pitchSpeedKmh) * 100, 0, 60);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function calculateLoadPenaltyPercent(aircraftMassGrams: number): number {
|
|
70
|
+
if (aircraftMassGrams <= 0) return 0;
|
|
71
|
+
return (1 - clamp(0.98 - aircraftMassGrams / 12000, 0.78, 0.98)) * 100;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function diagnoseSpeed(inputs: FpvDroneSpeedInputs, slipPercent: number): SpeedDiagnosis {
|
|
75
|
+
const noLoadRpm = calculateNoLoadRpm(inputs.motorKv, inputs.batteryVoltage);
|
|
76
|
+
if (noLoadRpm > 140000) return 'overspeed';
|
|
77
|
+
if (slipPercent >= 28) return 'high-slip';
|
|
78
|
+
if (inputs.aircraftMassGrams >= 2500) return 'heavy-load';
|
|
79
|
+
return 'planning';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function makeSensitivity(inputs: FpvDroneSpeedInputs, loadedRpm: number): SpeedSensitivityPoint[] {
|
|
83
|
+
const lowerPitch = Math.max(0.5, inputs.propellerPitchInches - 0.5);
|
|
84
|
+
const higherPitch = inputs.propellerPitchInches + 0.5;
|
|
85
|
+
return [
|
|
86
|
+
{ label: 'lower', pitchInches: lowerPitch, speedKmh: calculateEstimatedSpeedKmh(calculatePitchSpeedKmh(lowerPitch, loadedRpm), inputs.efficiencyPercent) },
|
|
87
|
+
{ label: 'current', pitchInches: inputs.propellerPitchInches, speedKmh: calculateEstimatedSpeedKmh(calculatePitchSpeedKmh(inputs.propellerPitchInches, loadedRpm), inputs.efficiencyPercent) },
|
|
88
|
+
{ label: 'higher', pitchInches: higherPitch, speedKmh: calculateEstimatedSpeedKmh(calculatePitchSpeedKmh(higherPitch, loadedRpm), inputs.efficiencyPercent) },
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function emptyResults(): FpvDroneSpeedResults {
|
|
93
|
+
return {
|
|
94
|
+
noLoadRpm: 0,
|
|
95
|
+
loadedRpm: 0,
|
|
96
|
+
pitchSpeedKmh: 0,
|
|
97
|
+
estimatedSpeedKmh: 0,
|
|
98
|
+
slipPercent: 0,
|
|
99
|
+
loadPenaltyPercent: 0,
|
|
100
|
+
rpmPerVolt: 0,
|
|
101
|
+
diagnosis: 'planning',
|
|
102
|
+
sensitivity: [],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function calculateFpvDroneSpeed(inputs: FpvDroneSpeedInputs): FpvDroneSpeedResults {
|
|
107
|
+
if (!validInputs(inputs)) return emptyResults();
|
|
108
|
+
|
|
109
|
+
const noLoadRpm = calculateNoLoadRpm(inputs.motorKv, inputs.batteryVoltage);
|
|
110
|
+
const loadedRpm = calculateLoadedRpm(inputs);
|
|
111
|
+
const pitchSpeedKmh = calculatePitchSpeedKmh(inputs.propellerPitchInches, loadedRpm);
|
|
112
|
+
const estimatedSpeedKmh = calculateEstimatedSpeedKmh(pitchSpeedKmh, inputs.efficiencyPercent);
|
|
113
|
+
const slipPercent = calculateSlipPercent(pitchSpeedKmh, estimatedSpeedKmh);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
noLoadRpm,
|
|
117
|
+
loadedRpm,
|
|
118
|
+
pitchSpeedKmh,
|
|
119
|
+
estimatedSpeedKmh,
|
|
120
|
+
slipPercent,
|
|
121
|
+
loadPenaltyPercent: calculateLoadPenaltyPercent(inputs.aircraftMassGrams),
|
|
122
|
+
rpmPerVolt: inputs.motorKv,
|
|
123
|
+
diagnosis: diagnoseSpeed(inputs, slipPercent),
|
|
124
|
+
sensitivity: makeSensitivity(inputs, loadedRpm),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { fpvDroneSpeedCalculator } from './entry';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const loader = fpvDroneSpeedCalculator.i18n[locale] || fpvDroneSpeedCalculator.i18n.en;
|
|
12
|
+
const content = await loader?.();
|
|
13
|
+
if (!content) return null;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
{content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface FpvDroneSpeedUI extends Record<string, string> {
|
|
2
|
+
presetsLabel: string;
|
|
3
|
+
presetRacing: string;
|
|
4
|
+
presetFreestyle: string;
|
|
5
|
+
presetCruiser: string;
|
|
6
|
+
unitsLabel: string;
|
|
7
|
+
metricUnit: string;
|
|
8
|
+
imperialUnit: string;
|
|
9
|
+
inputsLabel: string;
|
|
10
|
+
motorKvLabel: string;
|
|
11
|
+
batteryVoltageLabel: string;
|
|
12
|
+
propellerPitchLabel: string;
|
|
13
|
+
efficiencyLabel: string;
|
|
14
|
+
aircraftMassLabel: string;
|
|
15
|
+
calculateFromLabel: string;
|
|
16
|
+
resultsLabel: string;
|
|
17
|
+
estimatedSpeedLabel: string;
|
|
18
|
+
pitchSpeedLabel: string;
|
|
19
|
+
loadedRpmLabel: string;
|
|
20
|
+
noLoadRpmLabel: string;
|
|
21
|
+
slipLabel: string;
|
|
22
|
+
loadEffectLabel: string;
|
|
23
|
+
speedLaneLabel: string;
|
|
24
|
+
sensitivityLabel: string;
|
|
25
|
+
lowerPitchLabel: string;
|
|
26
|
+
selectedPitchLabel: string;
|
|
27
|
+
higherPitchLabel: string;
|
|
28
|
+
diagnosisLabel: string;
|
|
29
|
+
diagnosisPlanning: string;
|
|
30
|
+
diagnosisHighSlip: string;
|
|
31
|
+
diagnosisHeavyLoad: string;
|
|
32
|
+
diagnosisOverspeed: string;
|
|
33
|
+
diagnosisPlanningAdvice: string;
|
|
34
|
+
diagnosisHighSlipAdvice: string;
|
|
35
|
+
diagnosisHeavyLoadAdvice: string;
|
|
36
|
+
diagnosisOverspeedAdvice: string;
|
|
37
|
+
assumptionsLabel: string;
|
|
38
|
+
assumptionsText: string;
|
|
39
|
+
safetyText: string;
|
|
40
|
+
speedAxisStart: string;
|
|
41
|
+
speedAxisEnd: string;
|
|
42
|
+
noLoadCaption: string;
|
|
43
|
+
loadedCaption: string;
|
|
44
|
+
slipCaption: string;
|
|
45
|
+
massUnit: string;
|
|
46
|
+
speedUnit: string;
|
|
47
|
+
}
|
package/src/tools.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { GSD_FLIGHT_PLANNER_TOOL } from './tool/gsd-flight-planner/index';
|
|
|
7
7
|
import { DRONE_BATTERY_C_RATING_CALCULATOR_TOOL } from './tool/drone-battery-c-rating-calculator/index';
|
|
8
8
|
import { FPV_DRONE_THRUST_TO_WEIGHT_RATIO_TOOL } from './tool/fpv-drone-thrust-to-weight-ratio/index';
|
|
9
9
|
import { DRONE_MOTOR_PROPELLER_CALCULATOR_TOOL } from './tool/drone-motor-propeller-calculator/index';
|
|
10
|
+
import { FPV_DRONE_SPEED_CALCULATOR_TOOL } from './tool/fpv-drone-speed-calculator/index';
|
|
11
|
+
import { DRONE_MISSION_BATTERY_RESERVE_PLANNER_TOOL } from './tool/drone-mission-battery-reserve-planner/index';
|
|
10
12
|
import type { ToolDefinition } from './types';
|
|
11
13
|
|
|
12
14
|
export const ALL_TOOLS: ToolDefinition[] = [
|
|
@@ -18,6 +20,8 @@ export const ALL_TOOLS: ToolDefinition[] = [
|
|
|
18
20
|
DRONE_BATTERY_C_RATING_CALCULATOR_TOOL,
|
|
19
21
|
FPV_DRONE_THRUST_TO_WEIGHT_RATIO_TOOL,
|
|
20
22
|
DRONE_MOTOR_PROPELLER_CALCULATOR_TOOL,
|
|
23
|
+
FPV_DRONE_SPEED_CALCULATOR_TOOL,
|
|
24
|
+
DRONE_MISSION_BATTERY_RESERVE_PLANNER_TOOL,
|
|
21
25
|
];
|
|
22
26
|
|
|
23
27
|
export {
|
|
@@ -29,4 +33,7 @@ export {
|
|
|
29
33
|
DRONE_BATTERY_C_RATING_CALCULATOR_TOOL,
|
|
30
34
|
FPV_DRONE_THRUST_TO_WEIGHT_RATIO_TOOL,
|
|
31
35
|
DRONE_MOTOR_PROPELLER_CALCULATOR_TOOL,
|
|
36
|
+
FPV_DRONE_SPEED_CALCULATOR_TOOL,
|
|
37
|
+
DRONE_MISSION_BATTERY_RESERVE_PLANNER_TOOL,
|
|
32
38
|
};
|
|
39
|
+
|