@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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -0
  3. package/src/entries.ts +4 -0
  4. package/src/tests/diacritics_density.test.ts +8 -8
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/drone-battery-c-rating-calculator/i18n/es.ts +49 -49
  8. package/src/tool/fpv-drone-thrust-to-weight-ratio/bibliography.astro +6 -0
  9. package/src/tool/fpv-drone-thrust-to-weight-ratio/bibliography.ts +18 -0
  10. package/src/tool/fpv-drone-thrust-to-weight-ratio/component.astro +203 -0
  11. package/src/tool/fpv-drone-thrust-to-weight-ratio/controller.ts +240 -0
  12. package/src/tool/fpv-drone-thrust-to-weight-ratio/dom-views.ts +148 -0
  13. package/src/tool/fpv-drone-thrust-to-weight-ratio/entry.ts +27 -0
  14. package/src/tool/fpv-drone-thrust-to-weight-ratio/evaluator.ts +30 -0
  15. package/src/tool/fpv-drone-thrust-to-weight-ratio/fpv-drone-thrust-to-weight-ratio.css +404 -0
  16. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/de.ts +208 -0
  17. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/en.ts +208 -0
  18. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/es.ts +208 -0
  19. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/fr.ts +208 -0
  20. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/id.ts +208 -0
  21. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/it.ts +208 -0
  22. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ja.ts +208 -0
  23. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ko.ts +208 -0
  24. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/nl.ts +208 -0
  25. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/pl.ts +208 -0
  26. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/pt.ts +208 -0
  27. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/ru.ts +208 -0
  28. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/sv.ts +208 -0
  29. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/tr.ts +208 -0
  30. package/src/tool/fpv-drone-thrust-to-weight-ratio/i18n/zh.ts +208 -0
  31. package/src/tool/fpv-drone-thrust-to-weight-ratio/index.ts +10 -0
  32. package/src/tool/fpv-drone-thrust-to-weight-ratio/logic.test.ts +93 -0
  33. package/src/tool/fpv-drone-thrust-to-weight-ratio/logic.ts +147 -0
  34. package/src/tool/fpv-drone-thrust-to-weight-ratio/seo.astro +16 -0
  35. package/src/tool/fpv-drone-thrust-to-weight-ratio/storage.ts +19 -0
  36. package/src/tool/fpv-drone-thrust-to-weight-ratio/ui.ts +58 -0
  37. package/src/tools.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-drones",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -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.1,
11
+ minPerThousandLetters: 3.0,
12
12
  },
13
13
  es: {
14
14
  language: 'Spanish',
15
15
  expectedCharacters: 'á é í ó ú ü ñ',
16
16
  characters: /[áéíóúüñÁÉÍÓÚÜÑ]/g,
17
- minPerThousandLetters: 0.1,
17
+ minPerThousandLetters: 8.0,
18
18
  },
19
19
  fr: {
20
20
  language: 'French',
21
21
  expectedCharacters: 'à â æ ç é è ê ë î ï ô œ ù û ü ÿ',
22
22
  characters: /[àâæçéèêëîïôœùûüÿÀÂÆÇÉÈÊËÎÏÔŒÙÛÜŸ]/g,
23
- minPerThousandLetters: 0.1,
23
+ minPerThousandLetters: 8.0,
24
24
  },
25
25
  it: {
26
26
  language: 'Italian',
27
27
  expectedCharacters: 'à è é ì í î ò ó ù ú',
28
28
  characters: /[àèéìíîòóùúÀÈÉÌÍÎÒÓÙÚ]/g,
29
- minPerThousandLetters: 0.1,
29
+ minPerThousandLetters: 3.0,
30
30
  },
31
31
  pl: {
32
32
  language: 'Polish',
33
33
  expectedCharacters: 'ą ć ę ł ń ó ś ź ż',
34
34
  characters: /[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/g,
35
- minPerThousandLetters: 0.1,
35
+ minPerThousandLetters: 10.0,
36
36
  },
37
37
  pt: {
38
38
  language: 'Portuguese',
39
39
  expectedCharacters: 'á â ã à ç é ê í ó ô õ ú ü',
40
40
  characters: /[áâãàçéêíóôõúüÁÂÃÀÇÉÊÍÓÔÕÚÜ]/g,
41
- minPerThousandLetters: 0.1,
41
+ minPerThousandLetters: 8.0,
42
42
  },
43
43
  sv: {
44
44
  language: 'Swedish',
45
45
  expectedCharacters: 'å ä ö',
46
46
  characters: /[åäöÅÄÖ]/g,
47
- minPerThousandLetters: 0.1,
47
+ minPerThousandLetters: 5.0,
48
48
  },
49
49
  tr: {
50
50
  language: 'Turkish',
51
51
  expectedCharacters: 'ç ğ ı İ ö ş ü',
52
52
  characters: /[çğıöşüÇĞİÖŞÜ]/g,
53
- minPerThousandLetters: 0.1,
53
+ minPerThousandLetters: 10.0,
54
54
  },
55
55
  } as const;
56
56
 
@@ -17,8 +17,8 @@ describe('Locale Completeness Validation', () => {
17
17
  });
18
18
  });
19
19
 
20
- it('all 6 tools registered', () => {
21
- expect(ALL_TOOLS.length).toBe(6);
20
+ it('all 7 tools registered', () => {
21
+ expect(ALL_TOOLS.length).toBe(7);
22
22
  });
23
23
  });
24
24
 
@@ -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 6 tools in ALL_TOOLS', () => {
8
- expect(ALL_TOOLS.length).toBe(6);
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 Bateria 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 baterias LiPo de drones basandote en la resistencia interna y consumo de motores.';
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 Bateria LiPo para Drones',
12
- subtitle: 'Analiza la descarga continua real, exigencia de burst y sag de voltaje para multirotores',
13
- lipoSpecsHeader: 'Especificaciones de la Bateria',
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: 'Numero de Celdas (Serie)',
17
- chemistryLabel: 'Quimica de la Bateria',
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: 'Numero de Motores',
20
+ motorCountLabel: 'Número de Motores',
21
21
  peakMotorCurrentLabel: 'Corriente Pico por Motor (Amperios)',
22
- auxCurrentLabel: 'Consumo Auxiliar (VTX, FC, Camara) (Amperios)',
23
- presetSelectLabel: 'Ajustes Rapidos',
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: 'Analisis de Rendimiento y Potencia',
31
- claimedMaxCurrentLabel: 'Corriente Maxima Anunciada',
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: 'Caida de Tencion Estimada',
35
+ voltageSagLabel: 'Caída de Tensión Estimada',
36
36
  sagNominalVoltageLabel: 'Voltaje Nominal Bajo Carga',
37
- flightTimeFullThrottleLabel: 'Autonomia a Acelerador Maximo',
38
- flightTimeHoverLabel: 'Autonomia Estimada en Estacionario',
39
- safetyStatusLabel: 'Diagnostico de Seguridad',
40
- statusOptimalTitle: 'Bateria Segura y Optima',
41
- statusOptimalDesc: 'La bateria suministra la corriente pico holgadamente sin sobrecalentamiento ni sag excesivo. Se garantiza una larga vida util de las celdas.',
42
- statusWarningTitle: 'Estres Termico y de Voltaje Moderado',
43
- statusWarningDesc: 'El consumo pico esta cerca del limite real de la bateria. Espera cierta caida de voltaje en aceleraciones bruscas.',
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 bateria. Alto riesgo de sobrecalentamiento, caida severa de voltaje y degradacion prematura.',
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: 'Estres de Bateria',
48
+ batteryHealthLabel: 'Estrés de Batería',
49
49
  burstRatingRequiredLabel: 'Tasa C de Burst Requerida',
50
- currentRatioLabel: 'Relacion de Carga de Potencia',
50
+ currentRatioLabel: 'Relación de Carga de Potencia',
51
51
  };
52
52
 
53
53
  const faqItems = [
54
54
  {
55
- question: '¿Que es la tasa C en una bateria LiPo de dron?',
56
- answer: 'La tasa C representa la velocidad maxima de descarga continua en relacion con la capacidad de la bateria. Por ejemplo, una bateria de 1500mAh con 100C puede entregar teoricamente 150 Amperios.',
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 que la tasa C anunciada suele ser superior a la real?',
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: '¿Como afecta la resistencia interna al sag de voltaje y al calor?',
64
- answer: 'Una alta resistencia interna actua como un resistor indeseado en la celda. Al exigir alta corriente, esta resistencia provoca una caida de voltaje abrupta y disipa calor excesivo.',
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: '¿Como puedo evitar el sag de voltaje durante vuelos de freestyle?',
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 Rapido o Introducir Datos de Bateria',
75
- text: 'Ingresa la capacidad en mAh, la tasa C anunciada, el numero de celdas en serie y la resistencia interna promedio por celda.',
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 Electronica',
79
- text: 'Especifica la cantidad de motores, la corriente pico por motor a maximo acelerador y el consumo de componentes auxiliares.',
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 Diagnostico 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 degradacion.',
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 Baterias LiPo',
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 bateria LiPo adecuada para un dron FPV requiere relacionar la capacidad de la bateria, la tasa C y el consumo amperico de los motores. Aunque los fabricantes suelen anunciar tasas de 100C o mas, la capacidad real de descarga continua esta limitada por la resistencia interna de las celdas y la disipacion termica. Esta calculadora evalua el amperaje continuo realista ofreciendo margen de seguridad real.',
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 Quimicas de Baterias RC',
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: ['Quimica', 'Voltaje Nominal', 'Voltaje Maximo', 'Densidad Energetica', 'Descarga Pico', 'Uso Recomendado'],
104
+ headers: ['Química', 'Voltaje Nominal', 'Voltaje Máximo', 'Densidad Energética', 'Descarga Pico', 'Uso Recomendado'],
105
105
  rows: [
106
- ['LiPo (Estandar)', '3.7V', '4.20V', 'Alta', '100C - 150C', 'Drones FPV de Carreras y Freestyle 5"'],
107
- ['LiHV (Alta Tension)', '3.8V', '4.35V', 'Muy Alta', '80C - 120C', 'TinyWhoops y Micro Multirotores'],
108
- ['Li-Ion (18650/21700)', '3.6V', '4.20V', 'Maxima', '15C - 35C', 'Drones Long Range de 7"'],
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 caida repentina de tension que experimenta la bateria bajo aceleraciones fuertes. Al pasar corriente a traves de la resistencia interna, parte de la energia se convierte en calor en lugar de empuje. Una bateria envejecida o de alta resistencia sufrira avisos prematuros de bajo voltaje en el OSD telemetrico.',
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 minimo y temperatura fresca.',
125
- 'Resistencia Interna Moderada (5-10 mΩ por celda): Rendimiento estandar para freestyle con leve sag.',
126
- 'Alta Resistencia Interna (>12 mΩ por celda): Perdedas notorias de potencia, sag severo y calentamiento rapido.',
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: 'Optimizacion de Baterias para Freestyle Carreras y Drones Long Range',
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 energeticos 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 bateria exacta para cada setup previene cortes de alimentacion en vuelo.',
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 baterias LiPo a 3.80V o 3.85V por celda cuando no las uses. Almacenarlas completamente cargadas durante mas de 48 horas incrementa permanentemente la resistencia interna y degrada su capacidad.',
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,6 @@
1
+ ---
2
+ import { Bibliography as BibliographyComponent } from "@jjlmoya/utils-shared";
3
+ import { bibliography } from "./bibliography";
4
+ ---
5
+
6
+ <BibliographyComponent links={bibliography} />
@@ -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>