@jjlmoya/utils-drones 1.23.0 → 1.25.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 +2 -0
- package/src/entries.ts +4 -0
- package/src/tests/diacritics_density.test.ts +8 -8
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/drone-battery-c-rating-calculator/i18n/es.ts +49 -49
- package/src/tool/fpv-drone-thrust-to-weight-ratio/bibliography.astro +6 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/bibliography.ts +18 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/component.astro +203 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/controller.ts +240 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/dom-views.ts +148 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/entry.ts +27 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/evaluator.ts +30 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/fpv-drone-thrust-to-weight-ratio.css +404 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/de.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/en.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/es.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/fr.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/id.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/it.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ja.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ko.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/nl.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/pl.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/pt.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ru.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/sv.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/tr.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/zh.ts +208 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/index.ts +10 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/logic.test.ts +93 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/logic.ts +147 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/seo.astro +16 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/storage.ts +19 -0
- package/src/tool/fpv-drone-thrust-to-weight-ratio/ui.ts +58 -0
- package/src/tools.ts +3 -0
package/package.json
CHANGED
package/src/category/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { gpsCoordinatesConverter } from '../tool/gps-coordinates-converter/entry
|
|
|
5
5
|
import { dronePowerAnalyzer } from '../tool/drone-power-analyzer/entry';
|
|
6
6
|
import { gsdFlightPlanner } from '../tool/gsd-flight-planner/entry';
|
|
7
7
|
import { droneBatteryCRatingCalculator } from '../tool/drone-battery-c-rating-calculator/entry';
|
|
8
|
+
import { fpvDroneThrustToWeightRatio } from '../tool/fpv-drone-thrust-to-weight-ratio/entry';
|
|
8
9
|
|
|
9
10
|
export const dronesCategory: DronesCategoryEntry = {
|
|
10
11
|
icon: 'mdi:drone',
|
|
@@ -15,6 +16,7 @@ export const dronesCategory: DronesCategoryEntry = {
|
|
|
15
16
|
dronePowerAnalyzer,
|
|
16
17
|
gsdFlightPlanner,
|
|
17
18
|
droneBatteryCRatingCalculator,
|
|
19
|
+
fpvDroneThrustToWeightRatio,
|
|
18
20
|
],
|
|
19
21
|
i18n: {
|
|
20
22
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/entries.ts
CHANGED
|
@@ -10,6 +10,8 @@ export { gsdFlightPlanner } from './tool/gsd-flight-planner/entry';
|
|
|
10
10
|
export type { GsdFlightPlannerUI, GsdFlightPlannerLocaleContent } from './tool/gsd-flight-planner/entry';
|
|
11
11
|
export { droneBatteryCRatingCalculator } from './tool/drone-battery-c-rating-calculator/entry';
|
|
12
12
|
export type { DroneBatteryCRatingCalculatorUI, DroneBatteryCRatingCalculatorLocaleContent } from './tool/drone-battery-c-rating-calculator/entry';
|
|
13
|
+
export { fpvDroneThrustToWeightRatio } from './tool/fpv-drone-thrust-to-weight-ratio/entry';
|
|
14
|
+
export type { FpvDroneThrustToWeightRatioUI, FpvDroneThrustToWeightRatioLocaleContent } from './tool/fpv-drone-thrust-to-weight-ratio/entry';
|
|
13
15
|
export { dronesCategory } from './category';
|
|
14
16
|
|
|
15
17
|
import { antennaLengthCalculator } from './tool/antenna-length-calculator/entry';
|
|
@@ -18,6 +20,7 @@ import { gpsCoordinatesConverter } from './tool/gps-coordinates-converter/entry'
|
|
|
18
20
|
import { dronePowerAnalyzer } from './tool/drone-power-analyzer/entry';
|
|
19
21
|
import { gsdFlightPlanner } from './tool/gsd-flight-planner/entry';
|
|
20
22
|
import { droneBatteryCRatingCalculator } from './tool/drone-battery-c-rating-calculator/entry';
|
|
23
|
+
import { fpvDroneThrustToWeightRatio } from './tool/fpv-drone-thrust-to-weight-ratio/entry';
|
|
21
24
|
|
|
22
25
|
export const ALL_ENTRIES = [
|
|
23
26
|
antennaLengthCalculator,
|
|
@@ -26,4 +29,5 @@ export const ALL_ENTRIES = [
|
|
|
26
29
|
dronePowerAnalyzer,
|
|
27
30
|
gsdFlightPlanner,
|
|
28
31
|
droneBatteryCRatingCalculator,
|
|
32
|
+
fpvDroneThrustToWeightRatio,
|
|
29
33
|
];
|
|
@@ -8,49 +8,49 @@ const DIACRITIC_RULES = {
|
|
|
8
8
|
language: 'German',
|
|
9
9
|
expectedCharacters: 'ä ö ü ß',
|
|
10
10
|
characters: /[äöüÄÖÜß]/g,
|
|
11
|
-
minPerThousandLetters: 0
|
|
11
|
+
minPerThousandLetters: 3.0,
|
|
12
12
|
},
|
|
13
13
|
es: {
|
|
14
14
|
language: 'Spanish',
|
|
15
15
|
expectedCharacters: 'á é í ó ú ü ñ',
|
|
16
16
|
characters: /[áéíóúüñÁÉÍÓÚÜÑ]/g,
|
|
17
|
-
minPerThousandLetters: 0
|
|
17
|
+
minPerThousandLetters: 8.0,
|
|
18
18
|
},
|
|
19
19
|
fr: {
|
|
20
20
|
language: 'French',
|
|
21
21
|
expectedCharacters: 'à â æ ç é è ê ë î ï ô œ ù û ü ÿ',
|
|
22
22
|
characters: /[àâæçéèêëîïôœùûüÿÀÂÆÇÉÈÊËÎÏÔŒÙÛÜŸ]/g,
|
|
23
|
-
minPerThousandLetters: 0
|
|
23
|
+
minPerThousandLetters: 8.0,
|
|
24
24
|
},
|
|
25
25
|
it: {
|
|
26
26
|
language: 'Italian',
|
|
27
27
|
expectedCharacters: 'à è é ì í î ò ó ù ú',
|
|
28
28
|
characters: /[àèéìíîòóùúÀÈÉÌÍÎÒÓÙÚ]/g,
|
|
29
|
-
minPerThousandLetters: 0
|
|
29
|
+
minPerThousandLetters: 3.0,
|
|
30
30
|
},
|
|
31
31
|
pl: {
|
|
32
32
|
language: 'Polish',
|
|
33
33
|
expectedCharacters: 'ą ć ę ł ń ó ś ź ż',
|
|
34
34
|
characters: /[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/g,
|
|
35
|
-
minPerThousandLetters: 0
|
|
35
|
+
minPerThousandLetters: 10.0,
|
|
36
36
|
},
|
|
37
37
|
pt: {
|
|
38
38
|
language: 'Portuguese',
|
|
39
39
|
expectedCharacters: 'á â ã à ç é ê í ó ô õ ú ü',
|
|
40
40
|
characters: /[áâãàçéêíóôõúüÁÂÃÀÇÉÊÍÓÔÕÚÜ]/g,
|
|
41
|
-
minPerThousandLetters: 0
|
|
41
|
+
minPerThousandLetters: 8.0,
|
|
42
42
|
},
|
|
43
43
|
sv: {
|
|
44
44
|
language: 'Swedish',
|
|
45
45
|
expectedCharacters: 'å ä ö',
|
|
46
46
|
characters: /[åäöÅÄÖ]/g,
|
|
47
|
-
minPerThousandLetters: 0
|
|
47
|
+
minPerThousandLetters: 5.0,
|
|
48
48
|
},
|
|
49
49
|
tr: {
|
|
50
50
|
language: 'Turkish',
|
|
51
51
|
expectedCharacters: 'ç ğ ı İ ö ş ü',
|
|
52
52
|
characters: /[çğıöşüÇĞİÖŞÜ]/g,
|
|
53
|
-
minPerThousandLetters: 0
|
|
53
|
+
minPerThousandLetters: 10.0,
|
|
54
54
|
},
|
|
55
55
|
} as const;
|
|
56
56
|
|
|
@@ -4,8 +4,8 @@ import { dronesCategory } from '../data';
|
|
|
4
4
|
|
|
5
5
|
describe('Tool Validation Suite', () => {
|
|
6
6
|
describe('Library Registration', () => {
|
|
7
|
-
it('should have
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
7
|
+
it('should have 7 tools in ALL_TOOLS', () => {
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(7);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('dronesCategory should be defined', () => {
|
|
@@ -4,108 +4,108 @@ import type { SEOSection } from '../../../types';
|
|
|
4
4
|
import { BIBLIOGRAPHY_ITEMS } from '../bibliography';
|
|
5
5
|
|
|
6
6
|
const slug = 'calculadora-tasa-c-bateria-lipo-dron';
|
|
7
|
-
const title = 'Calculadora de Tasa C de
|
|
8
|
-
const description = 'Calcula la corriente de descarga continua real, tasa C realista, sag de voltaje y seguridad de vuelo para
|
|
7
|
+
const title = 'Calculadora de Tasa C de Batería LiPo y Descarga Continua para Drones';
|
|
8
|
+
const description = 'Calcula la corriente de descarga continua real, tasa C realista, sag de voltaje y seguridad de vuelo para baterías LiPo de drones basándote en la resistencia interna y consumo de motores.';
|
|
9
9
|
|
|
10
10
|
const ui = {
|
|
11
|
-
title: 'Calculadora de Tasa C de
|
|
12
|
-
subtitle: 'Analiza la descarga continua real, exigencia de burst y sag de voltaje para
|
|
13
|
-
lipoSpecsHeader: 'Especificaciones de la
|
|
11
|
+
title: 'Calculadora de Tasa C de Batería LiPo para Drones',
|
|
12
|
+
subtitle: 'Analiza la descarga continua real, exigencia de burst y sag de voltaje para multirrotores',
|
|
13
|
+
lipoSpecsHeader: 'Especificaciones de la Batería',
|
|
14
14
|
capacityLabel: 'Capacidad (mAh)',
|
|
15
15
|
claimedCRatingLabel: 'Tasa C Anunciada',
|
|
16
|
-
cellCountLabel: '
|
|
17
|
-
chemistryLabel: '
|
|
16
|
+
cellCountLabel: 'Número de Celdas (Serie)',
|
|
17
|
+
chemistryLabel: 'Química de la Batería',
|
|
18
18
|
internalResistanceLabel: 'Resistencia Interna por Celda (mΩ)',
|
|
19
19
|
quadSpecsHeader: 'Consumo del Dron',
|
|
20
|
-
motorCountLabel: '
|
|
20
|
+
motorCountLabel: 'Número de Motores',
|
|
21
21
|
peakMotorCurrentLabel: 'Corriente Pico por Motor (Amperios)',
|
|
22
|
-
auxCurrentLabel: 'Consumo Auxiliar (VTX, FC,
|
|
23
|
-
presetSelectLabel: 'Ajustes
|
|
22
|
+
auxCurrentLabel: 'Consumo Auxiliar (VTX, FC, Cámara) (Amperios)',
|
|
23
|
+
presetSelectLabel: 'Ajustes Rápidos',
|
|
24
24
|
customPreset: 'Personalizado',
|
|
25
25
|
whoopPreset: '1S TinyWhoop',
|
|
26
26
|
freestyle5Preset: '6S Freestyle 5 Pulgadas',
|
|
27
27
|
cinewhoopPreset: '4S CineWhoop 3 Pulgadas',
|
|
28
28
|
longRange7Preset: '6S Long Range 7 Pulgadas',
|
|
29
29
|
racing5Preset: '6S Carreras 5 Pulgadas',
|
|
30
|
-
resultsHeader: '
|
|
31
|
-
claimedMaxCurrentLabel: 'Corriente
|
|
30
|
+
resultsHeader: 'Análisis de Rendimiento y Potencia',
|
|
31
|
+
claimedMaxCurrentLabel: 'Corriente Máxima Anunciada',
|
|
32
32
|
realisticCRatingLabel: 'Tasa C Continua Realista',
|
|
33
33
|
realisticMaxCurrentLabel: 'Corriente Continua Realista',
|
|
34
34
|
totalPeakDrawLabel: 'Consumo Pico Total',
|
|
35
|
-
voltageSagLabel: '
|
|
35
|
+
voltageSagLabel: 'Caída de Tensión Estimada',
|
|
36
36
|
sagNominalVoltageLabel: 'Voltaje Nominal Bajo Carga',
|
|
37
|
-
flightTimeFullThrottleLabel: '
|
|
38
|
-
flightTimeHoverLabel: '
|
|
39
|
-
safetyStatusLabel: '
|
|
40
|
-
statusOptimalTitle: '
|
|
41
|
-
statusOptimalDesc: 'La
|
|
42
|
-
statusWarningTitle: '
|
|
43
|
-
statusWarningDesc: 'El consumo pico
|
|
37
|
+
flightTimeFullThrottleLabel: 'Autonomía a Acelerador Máximo',
|
|
38
|
+
flightTimeHoverLabel: 'Autonomía Estimada en Estacionario',
|
|
39
|
+
safetyStatusLabel: 'Diagnóstico de Seguridad',
|
|
40
|
+
statusOptimalTitle: 'Batería Segura y Óptima',
|
|
41
|
+
statusOptimalDesc: 'La batería suministra la corriente pico holgadamente sin sobrecalentamiento ni sag excesivo. Se garantiza una larga vida útil de las celdas.',
|
|
42
|
+
statusWarningTitle: 'Estrés Térmico y de Voltaje Moderado',
|
|
43
|
+
statusWarningDesc: 'El consumo pico está cerca del límite real de la batería. Espera cierta caída de voltaje en aceleraciones bruscas.',
|
|
44
44
|
statusDangerTitle: 'Alto Riesgo de Sobrecarga y Sag de Voltaje',
|
|
45
|
-
statusDangerDesc: 'El consumo pico supera la capacidad real de la
|
|
45
|
+
statusDangerDesc: 'El consumo pico supera la capacidad real de la batería. Alto riesgo de sobrecalentamiento, caída severa de voltaje y degradación prematura.',
|
|
46
46
|
lipoVisualizerTitle: 'Visualizador de Estado LiPo en Tiempo Real',
|
|
47
47
|
cellVoltageLabel: 'Voltaje por Celda',
|
|
48
|
-
batteryHealthLabel: '
|
|
48
|
+
batteryHealthLabel: 'Estrés de Batería',
|
|
49
49
|
burstRatingRequiredLabel: 'Tasa C de Burst Requerida',
|
|
50
|
-
currentRatioLabel: '
|
|
50
|
+
currentRatioLabel: 'Relación de Carga de Potencia',
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const faqItems = [
|
|
54
54
|
{
|
|
55
|
-
question: '¿
|
|
56
|
-
answer: 'La tasa C representa la velocidad
|
|
55
|
+
question: '¿Qué es la tasa C en una batería LiPo de dron?',
|
|
56
|
+
answer: 'La tasa C representa la velocidad máxima de descarga continua en relación con la capacidad de la batería. Por ejemplo, una batería de 1500mAh con 100C puede entregar teóricamente 150 Amperios.',
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
question: '¿Por
|
|
59
|
+
question: '¿Por qué la tasa C anunciada suele ser superior a la real?',
|
|
60
60
|
answer: 'Los fabricantes suelen publicitar tasas de ráfaga o valores de marketing. La descarga continua real depende directamente de la resistencia interna de cada celda.',
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
|
-
question: '¿
|
|
64
|
-
answer: 'Una alta resistencia interna
|
|
63
|
+
question: '¿Cómo afecta la resistencia interna al sag de voltaje y al calor?',
|
|
64
|
+
answer: 'Una alta resistencia interna actúa como un resistor indeseado en la celda. Al exigir alta corriente, esta resistencia provoca una caída de voltaje abrupta y disipa calor excesivo.',
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
question: '¿
|
|
67
|
+
question: '¿Cómo puedo evitar el sag de voltaje durante vuelos de freestyle?',
|
|
68
68
|
answer: 'Utiliza celdas de baja resistencia interna, selecciona un margen de seguridad de al menos el 15 por ciento sobre el consumo pico y no vueles por debajo de 3.5V por celda en reposo.',
|
|
69
69
|
},
|
|
70
70
|
];
|
|
71
71
|
|
|
72
72
|
const howToSteps = [
|
|
73
73
|
{
|
|
74
|
-
name: 'Seleccionar Ajuste
|
|
75
|
-
text: 'Ingresa la capacidad en mAh, la tasa C anunciada, el
|
|
74
|
+
name: 'Seleccionar Ajuste Rápido o Introducir Datos de Batería',
|
|
75
|
+
text: 'Ingresa la capacidad en mAh, la tasa C anunciada, el número de celdas en serie y la resistencia interna promedio por celda.',
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
name: 'Configurar el Consumo de Motores y
|
|
79
|
-
text: 'Especifica la cantidad de motores, la corriente pico por motor a
|
|
78
|
+
name: 'Configurar el Consumo de Motores y Electrónica',
|
|
79
|
+
text: 'Especifica la cantidad de motores, la corriente pico por motor a máximo acelerador y el consumo de componentes auxiliares.',
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
|
-
name: 'Revisar el
|
|
83
|
-
text: 'Compara la corriente continua realista calculada con el consumo pico del dron para asegurar un vuelo estable sin riesgo de
|
|
82
|
+
name: 'Revisar el Diagnóstico de Seguridad y Amperaje Real',
|
|
83
|
+
text: 'Compara la corriente continua realista calculada con el consumo pico del dron para asegurar un vuelo estable sin riesgo de degradación.',
|
|
84
84
|
},
|
|
85
85
|
];
|
|
86
86
|
|
|
87
87
|
const seoSections: SEOSection[] = [
|
|
88
88
|
{
|
|
89
89
|
type: 'title',
|
|
90
|
-
text: 'Comprendiendo la Tasa C y la Entrega Real de Potencia en
|
|
90
|
+
text: 'Comprendiendo la Tasa C y la Entrega Real de Potencia en Baterías LiPo',
|
|
91
91
|
level: 2,
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
type: 'paragraph',
|
|
95
|
-
html: 'Elegir la
|
|
95
|
+
html: 'Elegir la batería LiPo adecuada para un dron FPV requiere relacionar la capacidad de la batería, la tasa C y el consumo ampérico de los motores. Aunque los fabricantes suelen anunciar tasas de 100C o más, la capacidad real de descarga continua está limitada por la resistencia interna de las celdas y la disipación térmica. Esta calculadora evalúa el amperaje continuo realista ofreciendo un margen de seguridad real.',
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
type: 'title',
|
|
99
|
-
text: 'Tabla Comparativa de
|
|
99
|
+
text: 'Tabla Comparativa de Químicas de Baterías RC',
|
|
100
100
|
level: 2,
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
type: 'table',
|
|
104
|
-
headers: ['
|
|
104
|
+
headers: ['Química', 'Voltaje Nominal', 'Voltaje Máximo', 'Densidad Energética', 'Descarga Pico', 'Uso Recomendado'],
|
|
105
105
|
rows: [
|
|
106
|
-
['LiPo (
|
|
107
|
-
['LiHV (Alta
|
|
108
|
-
['Li-Ion (18650/21700)', '3.6V', '4.20V', '
|
|
106
|
+
['LiPo (Estándar)', '3.7V', '4.20V', 'Alta', '100C - 150C', 'Drones FPV de Carreras y Freestyle 5"'],
|
|
107
|
+
['LiHV (Alta Tensión)', '3.8V', '4.35V', 'Muy Alta', '80C - 120C', 'TinyWhoops y Micro Multirrotores'],
|
|
108
|
+
['Li-Ion (18650/21700)', '3.6V', '4.20V', 'Máxima', '15C - 35C', 'Drones Long Range de 7"'],
|
|
109
109
|
['LiFePO4', '3.3V', '3.65V', 'Moderada', '30C - 50C', 'Estaciones de Carga en Campo'],
|
|
110
110
|
],
|
|
111
111
|
},
|
|
@@ -116,29 +116,29 @@ const seoSections: SEOSection[] = [
|
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
type: 'paragraph',
|
|
119
|
-
html: 'El sag de voltaje es la
|
|
119
|
+
html: 'El sag de voltaje es la caída repentina de tensión que experimenta la batería bajo aceleraciones fuertes. Al pasar corriente a través de la resistencia interna, parte de la energía se convierte en calor en lugar de empuje. Una batería envejecida o de alta resistencia sufrirá avisos prematuros de bajo voltaje en el OSD telemétrico.',
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
type: 'list',
|
|
123
123
|
items: [
|
|
124
|
-
'Baja Resistencia Interna (1-4 mΩ por celda): Excelente respuesta, sag
|
|
125
|
-
'Resistencia Interna Moderada (5-10 mΩ por celda): Rendimiento
|
|
126
|
-
'Alta Resistencia Interna (>12 mΩ por celda):
|
|
124
|
+
'Baja Resistencia Interna (1-4 mΩ por celda): Excelente respuesta, sag mínimo y temperatura fresca.',
|
|
125
|
+
'Resistencia Interna Moderada (5-10 mΩ por celda): Rendimiento estándar para freestyle con leve sag.',
|
|
126
|
+
'Alta Resistencia Interna (>12 mΩ por celda): Pérdidas notorias de potencia, sag severo y calentamiento rápido.',
|
|
127
127
|
],
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
type: 'title',
|
|
131
|
-
text: '
|
|
131
|
+
text: 'Optimización de Baterías para Freestyle Carreras y Drones Long Range',
|
|
132
132
|
level: 2,
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
type: 'paragraph',
|
|
136
|
-
html: 'Cada estilo de vuelo exige requisitos
|
|
136
|
+
html: 'Cada estilo de vuelo exige requisitos energéticos distintos. Los drones de freestyle de 5 pulgadas generan picos intensos de corriente superiores a 120 Amperios, mientras que los drones de 7 pulgadas para largo alcance buscan eficiencia constante. Ajustar la batería exacta para cada setup previene cortes de alimentación en vuelo.',
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
type: 'tip',
|
|
140
140
|
title: 'Consejo de Mantenimiento LiPo',
|
|
141
|
-
html: 'Guarda siempre tus
|
|
141
|
+
html: 'Guarda siempre tus baterías LiPo a 3.80V o 3.85V por celda cuando no las uses. Almacenarlas completamente cargadas durante más de 48 horas incrementa permanentemente la resistencia interna y degrada su capacidad.',
|
|
142
142
|
},
|
|
143
143
|
];
|
|
144
144
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Betaflight Firmware Documentation - Multirotor Flight Dynamics and Thrust Tuning',
|
|
6
|
+
url: 'https://betaflight.com/docs/wiki/tuning/4-3-Tuning-Notes',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'IEEE Xplore - Dynamic Thrust and Power Modeling of Brushless Multirotor Propellers',
|
|
10
|
+
url: 'https://ieeexplore.ieee.org/document/7036341/',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'NASA Technical Reports Server - Aerodynamic Performance of Small Multirotor Propellers in Hover',
|
|
14
|
+
url: 'https://ntrs.nasa.gov/api/citations/20250009190/downloads/NASA-TM-20250009190.pdf?attachment=true',
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const BIBLIOGRAPHY_ITEMS = bibliography;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { FpvDroneThrustToWeightRatioUI } from './ui';
|
|
3
|
+
import { calculateTwrMetrics, type DroneTwrInputs } from './logic';
|
|
4
|
+
import { renderTwrSVG } from './dom-views';
|
|
5
|
+
import './fpv-drone-thrust-to-weight-ratio.css';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
ui: FpvDroneThrustToWeightRatioUI;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { ui } = Astro.props;
|
|
12
|
+
|
|
13
|
+
const defaultInputs: DroneTwrInputs = {
|
|
14
|
+
auwGrams: 680,
|
|
15
|
+
motorCount: 4,
|
|
16
|
+
thrustPerMotorGrams: 1950,
|
|
17
|
+
propellerSizeInches: 5.1,
|
|
18
|
+
propellerPitchInches: 4.3,
|
|
19
|
+
bladeCount: 3,
|
|
20
|
+
throttleStickPercent: 70,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const initialResults = calculateTwrMetrics(defaultInputs);
|
|
24
|
+
const initialSvg = renderTwrSVG(defaultInputs, initialResults, ui);
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div class="sc-fpv-cockpit" id="fpv-drone-thrust-to-weight-ratio-root">
|
|
28
|
+
<div class="sc-cockpit-sidebar">
|
|
29
|
+
<div>
|
|
30
|
+
<div class="sc-cockpit-title">{ui.presetsHeader}</div>
|
|
31
|
+
<div class="sc-preset-group">
|
|
32
|
+
<button type="button" class="sc-preset-chip active" data-preset="freestyle5">{ui.freestyle5Preset}</button>
|
|
33
|
+
<button type="button" class="sc-preset-chip" data-preset="freestyle35">{ui.freestyle35Preset}</button>
|
|
34
|
+
<button type="button" class="sc-preset-chip" data-preset="whoop1s">{ui.whoop1sPreset}</button>
|
|
35
|
+
<button type="button" class="sc-preset-chip" data-preset="longrange7">{ui.longrange7Preset}</button>
|
|
36
|
+
<button type="button" class="sc-preset-chip" data-preset="cinelifter8">{ui.cinelifter8Preset}</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<div class="sc-cockpit-title">{ui.specsHeader}</div>
|
|
42
|
+
<div class="sc-field-group">
|
|
43
|
+
<div class="sc-field-header">
|
|
44
|
+
<label for="input-auw">{ui.auwGramsLabel}</label>
|
|
45
|
+
</div>
|
|
46
|
+
<input type="number" id="input-auw" class="sc-input" value="680" min="10" max="15000" step="5" />
|
|
47
|
+
<input type="range" id="range-auw" class="sc-slider" value="680" min="20" max="2500" step="5" aria-label={ui.auwGramsLabel} />
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="sc-field-group" style="margin-top: 0.8rem;">
|
|
51
|
+
<label>{ui.motorCountLabel}</label>
|
|
52
|
+
<div class="sc-custom-select" data-custom-select="motor-count">
|
|
53
|
+
<div class="sc-select-trigger">
|
|
54
|
+
<span>4 Motors (Quadcopter)</span>
|
|
55
|
+
<svg width="12" height="12" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="sc-select-dropdown">
|
|
58
|
+
<div class="sc-select-option active" data-value="4">4 Motors (Quadcopter)</div>
|
|
59
|
+
<div class="sc-select-option" data-value="6">6 Motors (Hexacopter)</div>
|
|
60
|
+
<div class="sc-select-option" data-value="8">8 Motors (Octocopter / X8)</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="sc-field-group" style="margin-top: 0.8rem;">
|
|
66
|
+
<div class="sc-field-header">
|
|
67
|
+
<label for="input-thrust-motor">{ui.thrustPerMotorLabel}</label>
|
|
68
|
+
</div>
|
|
69
|
+
<input type="number" id="input-thrust-motor" class="sc-input" value="1950" min="10" max="10000" step="10" />
|
|
70
|
+
<input type="range" id="range-thrust-motor" class="sc-slider" value="1950" min="50" max="4000" step="25" aria-label={ui.thrustPerMotorLabel} />
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="sc-field-group" style="margin-top: 0.8rem;">
|
|
74
|
+
<label>{ui.bladeCountLabel}</label>
|
|
75
|
+
<div class="sc-custom-select" data-custom-select="blade-count">
|
|
76
|
+
<div class="sc-select-trigger">
|
|
77
|
+
<span>{ui.blade3Option}</span>
|
|
78
|
+
<svg width="12" height="12" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="sc-select-dropdown">
|
|
81
|
+
<div class="sc-select-option" data-value="2">{ui.blade2Option}</div>
|
|
82
|
+
<div class="sc-select-option active" data-value="3">{ui.blade3Option}</div>
|
|
83
|
+
<div class="sc-select-option" data-value="4">{ui.blade4Option}</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="sc-field-group" style="margin-top: 0.8rem;">
|
|
89
|
+
<div class="sc-field-header">
|
|
90
|
+
<label for="input-prop-size">{ui.propellerSizeLabel}</label>
|
|
91
|
+
</div>
|
|
92
|
+
<input type="number" id="input-prop-size" class="sc-input" value="5.1" min="1" max="15" step="0.1" />
|
|
93
|
+
<input type="range" id="range-prop-size" class="sc-slider" value="5.1" min="1.5" max="10" step="0.1" aria-label={ui.propellerSizeLabel} />
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="sc-field-group" style="margin-top: 0.8rem;">
|
|
97
|
+
<div class="sc-field-header">
|
|
98
|
+
<label for="input-prop-pitch">{ui.propellerPitchLabel}</label>
|
|
99
|
+
</div>
|
|
100
|
+
<input type="number" id="input-prop-pitch" class="sc-input" value="4.3" min="1" max="10" step="0.1" />
|
|
101
|
+
<input type="range" id="range-prop-pitch" class="sc-slider" value="4.3" min="1.5" max="6.5" step="0.1" aria-label={ui.propellerPitchLabel} />
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div class="sc-throttle-box">
|
|
106
|
+
<div class="sc-field-header">
|
|
107
|
+
<label for="range-throttle-stick" class="sc-throttle-label">{ui.throttleStickLabel}</label>
|
|
108
|
+
<span class="sc-throttle-badge" data-metric="stickPercentBadge">70%</span>
|
|
109
|
+
</div>
|
|
110
|
+
<input type="range" id="range-throttle-stick" class="sc-slider sc-throttle-slider" value="70" min="0" max="100" step="1" aria-label={ui.throttleStickLabel} />
|
|
111
|
+
<div class="sc-snap-group">
|
|
112
|
+
<button type="button" class="sc-snap-btn" data-snap="idle">{ui.snapIdleLabel}</button>
|
|
113
|
+
<button type="button" class="sc-snap-btn" data-snap="hover">{ui.snapHoverLabel}</button>
|
|
114
|
+
<button type="button" class="sc-snap-btn" data-snap="cruise">{ui.snapCruiseLabel}</button>
|
|
115
|
+
<button type="button" class="sc-snap-btn" data-snap="punch">{ui.snapPunchLabel}</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="sc-cockpit-telemetry">
|
|
121
|
+
<div class="sc-telemetry-topbar">
|
|
122
|
+
<div class="sc-cockpit-title" style="margin-bottom: 0;">{ui.telemetryHeader}</div>
|
|
123
|
+
<div class="sc-safety-badge status-accent" data-metric="agilityBadge">{ui.tierAcroProTitle}</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="sc-hud-wrapper" data-element="svgContainer" set:html={initialSvg}></div>
|
|
127
|
+
|
|
128
|
+
<div class="sc-hud-metrics-grid">
|
|
129
|
+
<div class="sc-hud-card">
|
|
130
|
+
<div class="sc-hud-label">{ui.twrRatioLabel}</div>
|
|
131
|
+
<div class="sc-hud-val" data-metric="twrRatio">{initialResults.twrRatio.toFixed(2)} : 1</div>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="sc-hud-card">
|
|
134
|
+
<div class="sc-hud-label">{ui.hoverThrottleLabel}</div>
|
|
135
|
+
<div class="sc-hud-val" data-metric="hoverThrottle">{initialResults.hoverThrottlePercent.toFixed(1)} %</div>
|
|
136
|
+
</div>
|
|
137
|
+
<div class="sc-hud-card highlight-cyan">
|
|
138
|
+
<div class="sc-hud-label">{ui.currentThrustLabel}</div>
|
|
139
|
+
<div class="sc-hud-val" data-metric="currentThrust">{initialResults.currentThrustGrams.toLocaleString()} g</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="sc-hud-card highlight-purple">
|
|
142
|
+
<div class="sc-hud-label">{ui.instantGForceLabel}</div>
|
|
143
|
+
<div class="sc-hud-val" data-metric="instantGForce">{initialResults.instantGForce.toFixed(2)} G</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="sc-hud-card">
|
|
146
|
+
<div class="sc-hud-label">{ui.zeroToHundredLabel}</div>
|
|
147
|
+
<div class="sc-hud-val" data-metric="zeroToHundred">{initialResults.zeroToHundredTimeSec.toFixed(2)} s</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="sc-hud-card">
|
|
150
|
+
<div class="sc-hud-label">{ui.recommendedCamAngleLabel}</div>
|
|
151
|
+
<div class="sc-hud-val" data-metric="recommendedCamAngle">{initialResults.recommendedCamAngleDeg}°</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="sc-hud-card">
|
|
154
|
+
<div class="sc-hud-label">{ui.windResistanceLabel}</div>
|
|
155
|
+
<div class="sc-hud-val" data-metric="windResistance">{initialResults.windResistanceKmh} km/h</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="sc-hud-card">
|
|
158
|
+
<div class="sc-hud-label">{ui.totalMaxThrustLabel}</div>
|
|
159
|
+
<div class="sc-hud-val" data-metric="totalMaxThrust">{initialResults.totalMaxThrustGrams.toLocaleString()} g</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div class="sc-tuning-grid">
|
|
164
|
+
<div class="sc-tuning-card">
|
|
165
|
+
<div class="sc-hud-label">{ui.tpaRecommendationLabel}</div>
|
|
166
|
+
<div class="sc-tuning-val" data-metric="tpaSetting">{initialResults.tuneAdvice.tpaSetting}</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="sc-tuning-card">
|
|
169
|
+
<div class="sc-hud-label">{ui.dynamicIdleLabel}</div>
|
|
170
|
+
<div class="sc-tuning-val" data-metric="dynamicIdleSetting">{initialResults.tuneAdvice.dynamicIdleSetting}</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="sc-tuning-card">
|
|
173
|
+
<div class="sc-hud-label">{ui.propwashRiskLabel}</div>
|
|
174
|
+
<div class="sc-tuning-val" data-metric="propwashRisk">{initialResults.tuneAdvice.propwashRisk}</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div class="sc-flight-status-card">
|
|
179
|
+
<h4 data-metric="statusTitle">{ui.tierAcroProTitle}</h4>
|
|
180
|
+
<p data-metric="statusDesc">{ui.tierAcroProDesc}</p>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<script is:inline type="application/json" id="fpv-twr-ui-data" set:html={JSON.stringify(ui)}></script>
|
|
186
|
+
|
|
187
|
+
<script>
|
|
188
|
+
import { FpvDroneThrustToWeightRatioController } from './controller';
|
|
189
|
+
|
|
190
|
+
function initController() {
|
|
191
|
+
const root = document.getElementById('fpv-drone-thrust-to-weight-ratio-root');
|
|
192
|
+
if (!root) return;
|
|
193
|
+
const uiEl = document.getElementById('fpv-twr-ui-data');
|
|
194
|
+
const uiData = uiEl ? JSON.parse(uiEl.textContent || '{}') : {};
|
|
195
|
+
new FpvDroneThrustToWeightRatioController(root, uiData);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (document.readyState === 'loading') {
|
|
199
|
+
document.addEventListener('DOMContentLoaded', initController);
|
|
200
|
+
} else {
|
|
201
|
+
initController();
|
|
202
|
+
}
|
|
203
|
+
</script>
|