Documentación

This commit is contained in:
Pedro Romero 2025-03-11 15:01:29 +01:00
parent 8bb059f4c4
commit 72c8d6fba4
9 changed files with 325 additions and 437 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@ venv/
__pycache__/
*.build/
*.dist/
*.log
*.log
Docs/

View File

@ -1,14 +0,0 @@
\relax
\@writefile{toc}{\contentsline {section}{\numberline {1}Introducción}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {2}Bobina e Inductancias}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Autoinductancia de una espira}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Inductancia mutua entre dos espiras coaxiales}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Inductancia total de la bobina}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Resistencia en el bobinado de cobre}{1}{}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {3}Rozamiento Aerodinámico: Coeficiente $b$}{2}{}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {4}Dinámica del Proyectil}{2}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Caso sin rozamiento}{2}{}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Caso con rozamiento lineal}{2}{}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {5}Energía Mecánica}{2}{}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {6}Conclusión}{2}{}\protected@file@percent }
\gdef \@abspage@last{2}

View File

@ -1,58 +0,0 @@
# Fdb version 4
["pdflatex"] 1741606732.43984 "c:/Users/pedro/Desktop/Projects/source/Docs/Models.tex" "Models.pdf" "Models" 1741606733.02877 0
"C:/Users/pedro/AppData/Local/MiKTeX/fonts/map/pdftex/pdftex.map" 1739986129 80909 eab91d9745dd2edfd62a31d53cd5fe15 ""
"C:/Users/pedro/AppData/Local/MiKTeX/miktex/data/le/pdftex/pdflatex.fmt" 1739984920 12004849 764d2605c2e5b05bd7dacdf1301c2e98 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1233951848 1004 54797486969f23fa377b128694d548df ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1233951848 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/symbols/msam10.tfm" 1233951854 916 f87d7c45f9c908e672703b83b72241a3 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/symbols/msam7.tfm" 1233951854 928 2dc8d444221b7a635bb58038579b861a ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/symbols/msbm10.tfm" 1233951854 908 2921f8a10601f252058503cc6570e581 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/amsfonts/symbols/msbm7.tfm" 1233951854 940 228d6584342e91276bf566bcf9716b83 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmbx12.tfm" 1136765053 1324 c910af8c371558dc20f2d7822f66fe64 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmex10.tfm" 1136765053 992 662f679a0b3d2d53c1b94050fdaa3f50 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmmi12.tfm" 1136765053 1524 4414a8315f39513458b80dfc63bff03a ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmmi6.tfm" 1136765053 1512 f21f83efb36853c0b70002322c1ab3ad ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmmi8.tfm" 1136765053 1520 eccf95517727cb11801f4f1aee3a21b4 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmr12.tfm" 1136765053 1288 655e228510b4c2a1abe905c368440826 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmr17.tfm" 1136765053 1292 296a67155bdbfc32aa9c636f21e91433 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmr6.tfm" 1136765053 1300 b62933e007d01cfd073f79b963c01526 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmr8.tfm" 1136765053 1292 21c1c5bfeaebccffdb478fd231a0997d ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmsy10.tfm" 1136765053 1124 6c73e740cf17375f03eec0ee63599741 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmsy6.tfm" 1136765053 1116 933a60c408fc0a863a92debe84b2d294 ""
"D:/Appdata/MiKTeX/fonts/tfm/public/cm/cmsy8.tfm" 1136765053 1120 8b7d695260f3cff42e636090a8002094 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmbx12.pfb" 1247596666 32080 340ef9bf63678554ee606688e7b5339d ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmex10.pfb" 1247596667 30251 6afa5cb1d0204815a708a080681d4674 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmmi12.pfb" 1247596667 36741 0ee9e374ec3e30da87cdfb0ea3575226 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmmi8.pfb" 1247596666 35469 dcf3a5f2fc1862f5952e3ee5eb1d98c4 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmr12.pfb" 1247596667 32722 d7379af29a190c3f453aba36302ff5a9 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmr17.pfb" 1247596666 32362 bc3f3eec7ab7d65fe700963d4017d32c ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmr6.pfb" 1247596667 32734 69e00a6b65cedb993666e42eedb3d48f ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmr8.pfb" 1247596667 32726 39f0f9e62e84beb801509898a605dbd5 ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1247596667 32569 5e5ddc8df908dea60932f3c484a54c0d ""
"D:/Appdata/MiKTeX/fonts/type1/public/amsfonts/cm/cmsy8.pfb" 1247596667 32626 5abc8bb2f28aa647d4c70f8ea38cc0d3 ""
"D:/Appdata/MiKTeX/tex/generic/iftex/iftex.sty" 1734114575 7984 7dbb9280f03c0a315425f1b4f35d43ee ""
"D:/Appdata/MiKTeX/tex/generic/iftex/ifvtex.sty" 1734114575 1057 525c2192b5febbd8c1f662c9468335bb ""
"D:/Appdata/MiKTeX/tex/latex/amsfonts/amsfonts.sty" 1358197772 5949 3f3fd50a8cc94c3d4cbf4fc66cd3df1c ""
"D:/Appdata/MiKTeX/tex/latex/amsfonts/amssymb.sty" 1358197772 13829 94730e64147574077f8ecfea9bb69af4 ""
"D:/Appdata/MiKTeX/tex/latex/amsfonts/umsa.fd" 1358197772 961 6518c6525a34feb5e8250ffa91731cff ""
"D:/Appdata/MiKTeX/tex/latex/amsfonts/umsb.fd" 1358197772 961 d02606146ba5601b5645f987c92e6193 ""
"D:/Appdata/MiKTeX/tex/latex/amsmath/amsbsy.sty" 1731152860 2222 2166a1f7827be30ddc30434e5efcee1b ""
"D:/Appdata/MiKTeX/tex/latex/amsmath/amsgen.sty" 1731152859 4173 d22509bc0c91281d991b2de7c88720dd ""
"D:/Appdata/MiKTeX/tex/latex/amsmath/amsmath.sty" 1731152860 88370 c780f23aea0ece6add91e09b44dca2cd ""
"D:/Appdata/MiKTeX/tex/latex/amsmath/amsopn.sty" 1731152860 4474 23ca1d3a79a57b405388059456d0a8df ""
"D:/Appdata/MiKTeX/tex/latex/amsmath/amstext.sty" 1731152860 2444 71618ea5f2377e33b04fb97afdd0eac2 ""
"D:/Appdata/MiKTeX/tex/latex/base/article.cls" 1738160243 20144 63d8bacaf52e5abf4db3bc322373e1d4 ""
"D:/Appdata/MiKTeX/tex/latex/base/inputenc.sty" 1738160243 5048 0270515b828149155424600fd2d58ac5 ""
"D:/Appdata/MiKTeX/tex/latex/base/size12.clo" 1738160243 8449 ffe4ba2166a344827c3a832d1d5e0a91 ""
"D:/Appdata/MiKTeX/tex/latex/geometry/geometry.cfg" 1578053545 1104 7ac475a4e3466b0b43e138e9356bda83 ""
"D:/Appdata/MiKTeX/tex/latex/geometry/geometry.sty" 1578053545 42759 9cf6c5257b1bc7af01a58859749dd37a ""
"D:/Appdata/MiKTeX/tex/latex/graphics/keyval.sty" 1730544106 2671 70891d50dac933918b827d326687c6e8 ""
"D:/Appdata/MiKTeX/tex/latex/l3backend/l3backend-pdftex.def" 1716480499 29785 9f93ab201fe5dd053afcc6c1bcf7d266 ""
"Models.aux" 1741606732 1429 8fbbeca065203156c4d218ad6b7c149b "pdflatex"
"Models.tex" 1741606731 4396 231b46402b7e7a479b713b2d10ce0d0d ""
"c:/Users/pedro/Desktop/Projects/source/Docs/Models.tex" 1741606731 4396 231b46402b7e7a479b713b2d10ce0d0d ""
(generated)
"Models.aux"
"Models.log"
"Models.pdf"
(rewritten before read)

View File

@ -1,109 +0,0 @@
PWD c:\Users\pedro\Desktop\Projects\source\Docs
INPUT C:\Users\pedro\AppData\Local\MiKTeX\miktex\data\le\pdftex\pdflatex.fmt
INPUT c:\Users\pedro\Desktop\Projects\source\Docs\Models.tex
OUTPUT Models.log
INPUT D:\Appdata\MiKTeX\tex\latex\base\article.cls
INPUT D:\Appdata\MiKTeX\tex\latex\base\article.cls
INPUT D:\Appdata\MiKTeX\tex\latex\base\size12.clo
INPUT D:\Appdata\MiKTeX\tex\latex\base\size12.clo
INPUT D:\Appdata\MiKTeX\tex\latex\base\size12.clo
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr12.tfm
INPUT D:\Appdata\MiKTeX\tex\latex\base\inputenc.sty
INPUT D:\Appdata\MiKTeX\tex\latex\base\inputenc.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsmath.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsmath.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsopn.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amstext.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amstext.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsgen.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsgen.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsbsy.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsbsy.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsmath\amsopn.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\amsfonts.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\amsfonts.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\amssymb.sty
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\amssymb.sty
INPUT D:\Appdata\MiKTeX\tex\latex\geometry\geometry.sty
INPUT D:\Appdata\MiKTeX\tex\latex\geometry\geometry.sty
INPUT D:\Appdata\MiKTeX\tex\latex\graphics\keyval.sty
INPUT D:\Appdata\MiKTeX\tex\latex\graphics\keyval.sty
INPUT D:\Appdata\MiKTeX\tex\generic\iftex\ifvtex.sty
INPUT D:\Appdata\MiKTeX\tex\generic\iftex\ifvtex.sty
INPUT D:\Appdata\MiKTeX\tex\generic\iftex\iftex.sty
INPUT D:\Appdata\MiKTeX\tex\generic\iftex\iftex.sty
INPUT D:\Appdata\MiKTeX\tex\latex\geometry\geometry.cfg
INPUT D:\Appdata\MiKTeX\tex\latex\geometry\geometry.cfg
INPUT D:\Appdata\MiKTeX\tex\latex\geometry\geometry.cfg
INPUT D:\Appdata\MiKTeX\tex\latex\l3backend\l3backend-pdftex.def
INPUT D:\Appdata\MiKTeX\tex\latex\l3backend\l3backend-pdftex.def
INPUT .\Models.aux
INPUT .\Models.aux
INPUT Models.aux
OUTPUT Models.aux
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr17.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmmi12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmsy10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmex10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\cmextra\cmex7.tfm
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsa.fd
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsa.fd
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsa.fd
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam7.tfm
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsb.fd
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsb.fd
INPUT D:\Appdata\MiKTeX\tex\latex\amsfonts\umsb.fd
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm7.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr17.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmbx12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmbx12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr8.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmr6.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmmi12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmmi8.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmmi6.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmsy10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmsy8.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmsy6.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmex10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\cmextra\cmex8.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\cmextra\cmex7.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam7.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm7.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmmi12.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmsy10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\cm\cmex10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm
INPUT D:\Appdata\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm
OUTPUT Models.pdf
INPUT C:\Users\pedro\AppData\Local\MiKTeX\fonts\map\pdftex\pdftex.map
INPUT Models.aux
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmbx12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmbx12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmex10.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmex10.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmmi12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmmi12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmmi8.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmmi8.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr12.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr17.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr17.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr6.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr6.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr8.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmr8.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmsy10.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmsy10.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmsy8.pfb
INPUT D:\Appdata\MiKTeX\fonts\type1\public\amsfonts\cm\cmsy8.pfb

Binary file not shown.

Binary file not shown.

View File

@ -1,83 +0,0 @@
% Archivo: modelos.tex
% Para compilar: pdflatex modelos.tex
\documentclass[12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath, amsfonts, amssymb}
\usepackage{geometry}
\geometry{a4paper, margin=2cm}
\begin{document}
\title{Modelos Físicos Empleados: Informe Detallado}
\author{Equipo de Desarrollo}
\date{\today}
\maketitle
\section{Introducción}
En este documento se recogen los fundamentos físicos y las fórmulas clave que se han empleado en las distintas partes de nuestro proyecto de simulación. Cubriremos aspectos como la autoinductancia de espiras, el cálculo de inductancia total en una bobina, la resistencia en cobre, la fuerza de rozamiento aerodinámico, la dinámica de proyectiles (con y sin rozamiento) y el cómputo de energía mecánica.
\section{Bobina e Inductancias}
\subsection{Autoinductancia de una espira}
La autoinductancia de un anillo de radio $r$ se calcula mediante una fórmula aproximada:
\begin{equation}
L_{\text{loop}} \approx \mu_{0}\,r\,\biggl[\ln\bigl(\tfrac{8r}{a_{\mathrm{eff}}}\bigr) - 2\biggr],
\end{equation}
\noindent donde $\mu_{0} = 4\pi \times 10^{-7}\,\mathrm{H/m}$ y $a_{\mathrm{eff}}$ es el radio efectivo que toma en cuenta el grosor del conductor.
\subsection{Inductancia mutua entre dos espiras coaxiales}
Para dos espiras coaxiales de radios $r_{1}$ y $r_{2}$, separadas una distancia axial $z$, se emplean integrales elípticas completas de primera ($K$) y segunda especie ($E$). La inductancia mutua $M$ se expresa:
\begin{equation}
M = \mu_{0}\,\sqrt{r_{1}r_{2}}\,\frac{(2 - k)\,K(k^{2}) - 2\,E(k^{2})}{k}, \quad\text{con}\quad k^{2} = \frac{4\,r_{1}\,r_{2}}{(r_{1}+r_{2})^{2} + z^{2}}.
\end{equation}
\subsection{Inductancia total de la bobina}
El total se obtiene sumando la autoinductancia de cada espira y la mutua entre pares:
\begin{equation}
L_{\text{total}} = \sum_{i} L_{\text{espira},i} + 2 \sum_{i<j} M_{ij}.
\end{equation}
Esta aproximación es de orden $O(N^2)$ al considerar todos los pares de espiras.
\subsection{Resistencia en el bobinado de cobre}
La longitud total del hilo, $\ell_{\mathrm{total}}$, se calcula como la suma de las circunferencias de cada espira. Luego:
\begin{equation}
R = \rho_{\mathrm{Cu}}\,\frac{\ell_{\mathrm{total}}}{A}, \quad \text{con}\quad A = \pi\,r_{\text{hilo}}^{2},
\end{equation}
\noindent donde $\rho_{\mathrm{Cu}} \approx 1.68\times 10^{-8}\,\Omega\cdot\mathrm{m}$.
\section{Rozamiento Aerodinámico: Coeficiente $b$}
Para simplificar el arrastre, se ha empleado un modelo lineal, $F_{\mathrm{drag}} = -b\,\vec{v}$. El coeficiente $b$ se relaciona con la forma tradicional de arrastre $\frac12\rho C_{D} A\,v^2$ a través de
\begin{equation}
b = 0.5\,\rho\,C_{D}\,A,
\end{equation}
\noindent donde $\rho$ es la densidad del aire, $C_{D}$ el coeficiente de forma (depende de la geometría) y $A$ el área frontal.
\section{Dinámica del Proyectil}
\subsection{Caso sin rozamiento}
El movimiento se reduce a un tiro parabólico clásico:
\begin{align}
a_{x} &= 0, && v_{x} = \text{cte}, \\[-3pt]
a_{y} &= -g, && v_{y}(t+\Delta t) = v_{y}(t) - g\,\Delta t.
\end{align}
La posición se actualiza integrando $x(t+\Delta t)=x(t)+v_{x}(t)\,\Delta t$, y análogamente para $y$.
\subsection{Caso con rozamiento lineal}
Incluimos la fuerza $F_{\mathrm{drag}} = -b\,\vec{v}$, por lo que:
\begin{align}
m\,a_{x} &= -b\,v_{x} \implies a_{x} = -\tfrac{b}{m}\,v_{x},\\
m\,a_{y} &= -m\,g - b\,v_{y} \implies a_{y} = -g - \tfrac{b}{m}\,v_{y}.
\end{align}
Luego se integra numéricamente (método de Euler) en pequeños pasos de tiempo $\Delta t$.
\section{Energía Mecánica}
En todo momento podemos calcular:
\begin{align}
E_{c} &= \tfrac12\,m\,(v_{x}^{2}+v_{y}^{2}),\\
E_{p} &= m\,g\,y, \quad\text{si } y \ge 0,\\
E_{\text{total}} &= E_{c} + E_{p}.
\end{align}
El arrastre disipa energía, de modo que $E_{\text{total}}$ no permanece constante si $b\neq0$.
\section{Conclusión}
Hemos revisado los principales modelos físicos que sustentan cada parte del proyecto: el diseño de la bobina (inductancia y resistencia), el rozamiento aerodinámico y la dinámica de un proyectil con o sin rozamiento. Estas ecuaciones y supuestos constituyen la base para la simulación y los cálculos de energías, alcance, y optimización del ángulo.
\end{document}

View File

@ -43,6 +43,7 @@ class MainApp:
# Pestaña 5: Nuevo TabPower
self.tab_power = TabPower(self.notebook, self.tab_coil, self.tab_sim)
self.notebook.add(self.tab_power.frame, text="Dimensionamiento eléctrico")
if __name__ == "__main__":
root = tk.Tk()

View File

@ -8,171 +8,181 @@ import math
class TabPower:
def __init__(self, notebook, tab_coil, tab_simulator):
"""
Pestaña para dimensionar C o V0 a partir de la energía mecánica deseada,
considerando n etapas/bobinas idénticas.
:param notebook: ttk.Notebook donde se añade esta pestaña
:param tab_coil: referencia a la instancia de TabCoil
:param tab_simulator: referencia a la instancia de TabSimulator (para leer E_target)
Pestaña que combina:
- Dimensionamiento (C o V0) por energía objetivo.
- Visualización/animación del proyectil pasando por n etapas.
"""
self.notebook = notebook
self.tab_coil = tab_coil
self.tab_simulator = tab_simulator
# Frame principal para la pestaña
# Frame principal
self.frame = tk.Frame(notebook)
self.frame.pack(fill="both", expand=True)
# Añadir la pestaña al notebook
notebook.add(self.frame, text="Power Stages (Energy-driven)")
# Layout: parte izquierda (entradas) y derecha (resultados / logs)
self.frame_left = tk.Frame(self.frame)
self.frame_left.pack(side="left", fill="y", padx=5, pady=5)
# Layout superior: panel de inputs y resultados
self.frame_top = tk.Frame(self.frame)
self.frame_top.pack(side="top", fill="x")
self.frame_right = tk.Frame(self.frame, bd=2, relief="groove")
self.frame_right.pack(side="right", fill="both", expand=True, padx=5, pady=5)
# Layout inferior: canvas animación + slider
self.frame_bottom = tk.Frame(self.frame)
self.frame_bottom.pack(side="bottom", fill="both", expand=True)
# ------------------------------
# Variables y widgets de entrada
# ------------------------------
# Modo de dimensionado
self.mode_var = tk.StringVar(value="Dimensionar C") # o "Dimensionar V0"
tk.Label(self.frame_left, text="Modo de Dimensionado:").pack(anchor="w")
self.combo_mode = ttk.Combobox(
self.frame_left, textvariable=self.mode_var,
values=["Dimensionar C", "Dimensionar V0"],
state="readonly"
)
# -----------------------------
# PARTE (A): INPUTS Y LOG (arriba)
# -----------------------------
self.frame_inputs = tk.Frame(self.frame_top)
self.frame_inputs.pack(side="left", fill="y", padx=5, pady=5)
self.frame_results = tk.Frame(self.frame_top, bd=2, relief="groove")
self.frame_results.pack(side="right", fill="both", expand=True, padx=5, pady=5)
# 1) Modo dimensionado
self.mode_var = tk.StringVar(value="Dimensionar C")
ttk.Label(self.frame_inputs, text="Modo Dimensionado:").pack(anchor="w")
self.combo_mode = ttk.Combobox(self.frame_inputs, textvariable=self.mode_var,
values=["Dimensionar C", "Dimensionar V0"], state="readonly")
self.combo_mode.bind("<<ComboboxSelected>>", self.on_mode_change)
self.combo_mode.pack(pady=2)
# Número de etapas
tk.Label(self.frame_left, text="Número de etapas (n):").pack(anchor="w")
# 2) Número de etapas
ttk.Label(self.frame_inputs, text="Número de etapas (n):").pack(anchor="w")
self.var_n = tk.IntVar(value=3)
tk.Entry(self.frame_left, textvariable=self.var_n, width=10).pack(pady=2)
tk.Entry(self.frame_inputs, textvariable=self.var_n, width=8).pack(pady=2)
# Capacidad (si se dimensiona V0, la capacit se fija)
tk.Label(self.frame_left, text="Capacidad (µF):").pack(anchor="w")
self.var_C = tk.DoubleVar(value=100.0) # ejemplo
self.entry_C = tk.Entry(self.frame_left, textvariable=self.var_C, width=10)
# 3) Capacidad
ttk.Label(self.frame_inputs, text="Capacidad (µF):").pack(anchor="w")
self.var_C = tk.DoubleVar(value=100.0)
self.entry_C = tk.Entry(self.frame_inputs, textvariable=self.var_C, width=8)
self.entry_C.pack(pady=2)
# Tensión inicial (si se dimensiona C, la tensión se fija)
tk.Label(self.frame_left, text="Tensión inicial (V):").pack(anchor="w")
self.var_V0 = tk.DoubleVar(value=200.0) # ejemplo
self.entry_V0 = tk.Entry(self.frame_left, textvariable=self.var_V0, width=10)
# 4) Tensión
ttk.Label(self.frame_inputs, text="Tensión (V):").pack(anchor="w")
self.var_V0 = tk.DoubleVar(value=200.0)
self.entry_V0 = tk.Entry(self.frame_inputs, textvariable=self.var_V0, width=8)
self.entry_V0.pack(pady=2)
# Tiempo de descarga aproximado
tk.Label(self.frame_left, text="Tiempo de etapa (ms):").pack(anchor="w")
self.var_time_ms = tk.DoubleVar(value=10.0) # 10 ms
tk.Entry(self.frame_left, textvariable=self.var_time_ms, width=10).pack(pady=2)
# 5) Tiempo por etapa
ttk.Label(self.frame_inputs, text="Tiempo/etapa (ms):").pack(anchor="w")
self.var_time_ms = tk.DoubleVar(value=10.0)
tk.Entry(self.frame_inputs, textvariable=self.var_time_ms, width=8).pack(pady=2)
# Botón dimensionar
tk.Button(self.frame_left, text="Dimensionar", command=self.dimensionar).pack(pady=5)
# Botones
ttk.Button(self.frame_inputs, text="Dimensionar", command=self.dimensionar).pack(pady=5)
ttk.Button(self.frame_inputs, text="Simular Movimiento", command=self.simular_movimiento).pack(pady=5)
# ------------------------------
# Sección derecha: Logs/Resultados
# ------------------------------
tk.Label(self.frame_right, text="Resultados:", font=("Arial",10,"bold")).pack(anchor="nw", padx=5, pady=5)
self.log_area = ScrolledText(self.frame_right, wrap="word", height=20, width=50)
# LOG de resultados
tk.Label(self.frame_results, text="Resultados", font=("Arial", 10, "bold")).pack(anchor="nw", padx=5, pady=5)
self.log_area = ScrolledText(self.frame_results, wrap="word", height=15, width=50)
self.log_area.pack(fill="both", expand=True, padx=5, pady=5)
self.log_area.configure(state="disabled")
# Forzar actualización inicial de estados de input
# -----------------------------
# PARTE (B): CANVAS ANIMACIÓN (abajo)
# -----------------------------
self.canvas = tk.Canvas(self.frame_bottom, bg="white")
self.canvas.pack(side="left", fill="both", expand=True)
# Panel con slider
self.frame_slider = tk.Frame(self.frame_bottom)
self.frame_slider.pack(side="right", fill="y", padx=5, pady=5)
tk.Label(self.frame_slider, text="Tiempo (ms):").pack()
self.slider_time = tk.Scale(self.frame_slider, from_=0, to=10, resolution=1,
orient=tk.VERTICAL, command=self.on_slider_move)
self.slider_time.pack(fill="y", expand=True)
# Variables internas
self.proyectil_id = None
self.trayectoria = [] # Lista (t, x)
self.t_total = 0.0
self.old_canvas_w = 1
self.old_canvas_h = 1
# Inicializar modo
self.on_mode_change()
# Responder a resize
self.canvas.bind("<Configure>", self.on_resize)
# ============== LOG FUNC =============
def log_message(self, txt, mode="info"):
self.log_area.config(state="normal")
if mode=="error":
self.log_area.insert("end", "ERROR: " + txt + "\n", "error")
else:
self.log_area.insert("end", txt + "\n", mode)
self.log_area.see("end")
self.log_area.config(state="disabled")
# ============== MODO DIMENSIONADO =============
def on_mode_change(self, event=None):
"""Habilita o deshabilita las entradas de C o V0 según el modo de dimensionado."""
mode = self.mode_var.get()
if mode == "Dimensionar C":
# Bloqueamos el field de C para escritura manual?
# O, según la lógica, convendría DESBLOQUEAR C si lo vamos a dimensionar?
# Típicamente: dimensionar C => el usuario fija V0
self.entry_C.config(state="disabled") # Se calculará
self.entry_V0.config(state="normal") # Se ingresa manualmente
# C se dimensiona => el user fija V0 => C inhabilitado
self.entry_C.config(state="disabled")
self.entry_V0.config(state="normal")
else:
# Dimensionar V0 => el usuario fija C
# Dimensionar V0 => user fija C => V0 inhabilitado
self.entry_C.config(state="normal")
self.entry_V0.config(state="disabled")
def log_message(self, text, mode="info"):
"""Inserta texto en el área de log."""
self.log_area.configure(state="normal")
if mode=="error":
self.log_area.insert("end", "ERROR: " + text + "\n", "error")
else:
self.log_area.insert("end", text + "\n", mode)
self.log_area.see("end")
self.log_area.configure(state="disabled")
# ============== DIMENSIONAR =============
def dimensionar(self):
"""
Lee la energía objetivo desde TabSimulator, y ajusta C o V0
para que la energía total (n etapas) se aproxime a E_target.
Lógica de bisección (resumida) para encontrar C o V0
que aporte la energía requerida en n etapas.
"""
# 0) Energía objetivo proveniente de TabSimulator
E_target_total = self.tab_simulator.get_energy_required()
if E_target_total <= 0:
self.log_message("E_target <= 0. Asegúrate de que TabSimulator calcule algo válido.", "error")
self.log_message("E_target <= 0; revisa TabSimulator.", "error")
return
# n (etapas)
n = self.var_n.get()
if n < 1:
self.log_message("El número de etapas debe ser >= 1", "error")
if n<1:
self.log_message("n<1 invalido.", "error")
return
# => La energía por etapa
E_target_stage = E_target_total / n
# 1) Leer inductancia y resistencia desde TabCoil
E_target_stage = E_target_total/n
# Leer L y R
try:
L_uH = float(self.tab_coil.var_L.get()) # microHenrios
R_coil = float(self.tab_coil.var_R.get()) # Ohms
L_uH = float(self.tab_coil.var_L.get())
R_coil = float(self.tab_coil.var_R.get())
except ValueError:
self.log_message("No se pudo leer L/R de TabCoil.", "error")
self.log_message("No se pudo leer L/R en TabCoil.", "error")
return
L_coil = L_uH * 1e-6 # Henrios
L_coil = L_uH * 1e-6
# h_c
try:
h_c_mm = float(self.tab_coil.var_h_c.get())
except ValueError:
self.log_message("No se pudo leer h_c de TabCoil.", "error")
self.log_message("No se pudo leer h_c en TabCoil.", "error")
return
h_c = h_c_mm / 1000.0 # m
h_c = h_c_mm/1000.0
# 2) Leer los datos de (C, V0, dt) (aunque uno de los dos se va a dimensionar)
# Tiempo
dt_ms = self.var_time_ms.get()
if dt_ms <= 0:
self.log_message("Tiempo de etapa (ms) inválido", "error")
if dt_ms<=0:
self.log_message("Tiempo/etapa <=0", "error")
return
T_total = dt_ms*1e-3
T_stage = dt_ms*1e-3
mode = self.mode_var.get()
# Obtenemos un valor 'fijo' para el que no dimensionamos
# y definimos la test_function acorde
if mode=="Dimensionar C":
# El user fija V0:
# El user ha fijado V0
try:
V0_fixed = float(self.var_V0.get())
if V0_fixed <= 0:
self.log_message("Tensión (V) inválida", "error")
if V0_fixed<=0:
self.log_message("Tensión <=0", "error")
return
except ValueError:
self.log_message("Tensión (V) inválida", "error")
self.log_message("Tensión no valida", "error")
return
# Rango de bisección en [Cmin, Cmax], µF
lower = 0.1
upper = 1e6
def test_function(C_uF):
E_stage = self.simular_descarga(
C_uF, V0_fixed, L_coil, R_coil, T_total, h_c
)
lower, upper = 0.1, 1e6
def test_fun(C_uF):
E_stage = self.simular_descarga(C_uF, V0_fixed, L_coil, R_coil, T_stage, h_c)
return E_stage
else:
@ -180,117 +190,257 @@ class TabPower:
try:
C_uF_fixed = float(self.var_C.get())
if C_uF_fixed<=0:
self.log_message("Capacidad (µF) inválida", "error")
self.log_message("Capacidad <=0", "error")
return
except ValueError:
self.log_message("Capacidad (µF) inválida", "error")
self.log_message("Capacidad no valida", "error")
return
# Rango de bisección en [1..5000] V, p. ej.
lower = 1.0
upper = 5000.0
def test_function(V0):
E_stage = self.simular_descarga(
C_uF_fixed, V0, L_coil, R_coil, T_total, h_c
)
lower, upper = 1.0, 5000.0
def test_fun(V0):
E_stage = self.simular_descarga(C_uF_fixed, V0, L_coil, R_coil, T_stage, h_c)
return E_stage
# Bisección
E_low = test_function(lower)
E_up = test_function(upper)
# Comprobación de que la energía buscada (E_target_stage) está entre E_low y E_up
E_low = test_fun(lower)
E_up = test_fun(upper)
if not ((E_low <= E_target_stage <= E_up) or (E_up <= E_target_stage <= E_low)):
self.log_message("ADVERTENCIA: El rango de bisección puede no encapsular la E_target. Ajusta límites.", "error")
self.log_message("Posible problema con rangos de bisección", "error")
iteration_count = 0
max_iter = 40
tol = 0.01*E_target_stage # 1% de la energía por etapa
best_param = 0.0
while iteration_count < max_iter:
mid = 0.5*(lower + upper)
E_mid = test_function(mid)
iteration_count=0
max_iter=40
tol=0.01*E_target_stage
best_param=0.0
while iteration_count<max_iter:
mid=0.5*(lower+upper)
E_mid = test_fun(mid)
diff = E_mid - E_target_stage
if abs(diff) < tol:
best_param = mid
if abs(diff)<tol:
best_param=mid
break
# Suponemos monotonicidad: +C => +E, +V0 => +E
if E_mid > E_target_stage:
upper = mid
# suponer monotonicidad
if E_mid>E_target_stage:
upper=mid
else:
lower = mid
iteration_count += 1
best_param = mid
lower=mid
iteration_count+=1
best_param=mid
E_final_stage = test_function(best_param)
E_final_total = E_final_stage * n
diff_total = E_final_total - E_target_total
# 3) Mostrar resultados
self.log_message("-"*40)
self.log_message(f"E_target_total = {E_target_total:.3f} J, n = {n}")
self.log_message(f"E_target_stage = {E_target_stage:.3f} J", "info")
E_stage_final = test_fun(best_param)
E_total_final = E_stage_final*n
diff_total = E_total_final - E_target_total
# Resultados
self.log_message("-"*40, "info")
self.log_message(f"E_target_total= {E_target_total:.3f} J => E_target_stage= {E_target_stage:.3f} J", "info")
if mode=="Dimensionar C":
self.log_message(f"Dimensionado de C => V0={V0_fixed:.1f} V fijo.", "info")
self.log_message(f"Capacidad final ~ {best_param:.2f} µF", "info")
self.log_message(f"MODO: Dimensionar C => V0= {V0_fixed:.1f} V fijo", "info")
self.log_message(f"C final= {best_param:.2f} µF", "info")
# guardamos en var_C
self.var_C.set(best_param)
else:
self.log_message(f"Dimensionado de V0 => C={self.var_C.get():.1f} µF fijo.", "info")
self.log_message(f"Tensión final ~ {best_param:.2f} V", "info")
self.log_message(f"MODO: Dimensionar V0 => C= {self.var_C.get():.1f} µF fijo", "info")
self.log_message(f"V0 final= {best_param:.2f} V", "info")
self.var_V0.set(best_param)
self.log_message(f"E_stage_final= {E_stage_final:.3f} J => E_total= {E_total_final:.3f} J (diff= {diff_total:.3f})", "info")
self.log_message("-"*40, "info")
self.log_message(f"E_result_stage ~ {E_final_stage:.3f} J => total ~ {E_final_total:.3f} J (diff={diff_total:.3f})", "info")
self.log_message("-"*40)
def simular_descarga(self, C_uF, V0, L_coil, R_coil, T_total, h_c):
"""
Modelo simplificado R-L(x)-C de una 'etapa' para un tiempo T_total.
Devuelve la energía consumida del condensador.
Reutilizamos la lógica de Euler para la descarga,
asumiendo L varía ~ 20% a lo largo x in [0..h_c].
"""
if C_uF<=0 or V0<=0 or T_total<=0:
def simular_descarga(self, C_uF, V0, L_coil, R_coil, T_stage, h_c):
"""Versión resumida de la descarga R-L(x)-C, devolviendo la energía consumida en 1 etapa."""
if C_uF<=0 or V0<=0 or T_stage<=0:
return 0.0
# Convertimos C a F
C = C_uF * 1e-6
# Asumimos L variable ~ 20% de variación
# Fijamos 20% de variación de L
C = C_uF*1e-6
L0 = 1.2*L_coil
Lf = 0.8*L_coil
def L_of_x(x):
return L0 + (Lf - L0)*(x/h_c) if h_c>1e-9 else L_coil
return L0 + (Lf-L0)*(x/h_c) if h_c>1e-9 else L_coil
def dLdx_of_x(x):
return (Lf - L0)/h_c if h_c>1e-9 else 0.0
return (Lf-L0)/h_c if h_c>1e-9 else 0.0
N_steps = 1000
dt = T_total/N_steps
# Integración Euler
N_steps=1000
dt = T_stage/N_steps
vCap = V0
i = 0.0
i=0.0
# sup vel cte => x(t)= (h_c/T_stage)*t
def x_of_t(t):
return (h_c / T_total)*t if T_total>1e-9 else 0.0
return (h_c/T_stage)*t if T_stage>1e-9 else 0.0
for step in range(N_steps+1):
x = x_of_t(step*dt)
Lx = L_of_x(x)
dLdt = dLdx_of_x(x)*(h_c / T_total)
dLdt = dLdx_of_x(x)*(h_c/T_stage)
if abs(Lx)>1e-12:
di_dt = (vCap - R_coil*i - i*dLdt)/Lx
else:
di_dt = 0.0
di_dt=0.0
i_new = i + di_dt*dt
i_new = i+di_dt*dt
vCap_new = vCap - (i/C)*dt
if vCap_new<0:
vCap_new=0.0
vCap_new=0
i = i_new
vCap = vCap_new
# Energía consumida ~ 1/2*C*(V0^2 - vCap^2)
E_consumida = 0.5*C*(V0**2 - vCap**2)
E_consumida= 0.5*C*(V0**2 - vCap**2)
if E_consumida<0:
E_consumida=0.0
return E_consumida
return E_consumida
# ============== SIMUL MOVIMIENTO ================
def simular_movimiento(self):
"""
Genera una trayectoria (t, x) con n etapas => n*h_c longitud total.
Asume un tiempo total= n*(tiempo por etapa).
Animación en canvas con slider de 0.. t_total(ms).
"""
self.trayectoria.clear()
try:
n = self.var_n.get()
h_c_mm = float(self.tab_coil.var_h_c.get())
dt_ms = self.var_time_ms.get()
except ValueError:
self.log_message("Valores invalidos para n, h_c, dt_ms", "error")
return
if n<1 or h_c_mm<=0 or dt_ms<=0:
self.log_message("Parametros invalidos en simular_movimiento", "error")
return
h_c = h_c_mm/1000.0
total_dist = n*h_c
self.t_total = n*(dt_ms*1e-3) # seg
# Generamos 200 puntos
Npts=200
dt = self.t_total/(Npts-1)
# vel cte => v= total_dist/t_total
if self.t_total<1e-9:
self.log_message("tiempo total ~0 => no se puede simular", "error")
return
v = total_dist/self.t_total
t=0.0
for i in range(Npts):
x = v*t
self.trayectoria.append((t, x))
t+=dt
# Ajustar slider => en ms => 0.. t_total*1000
self.slider_time.config(
from_=0, to=int(self.t_total*1000), resolution=1
)
self.slider_time.set(0)
self.log_message(f"Sim. movimiento => dist= {total_dist:.3f} m, t_total= {self.t_total:.3f} s", "info")
self.dibujar_escena()
def dibujar_escena(self):
"""Dibuja las bobinas (n rectangulos) en canvas y proyectil en pos inicial."""
self.canvas.delete("all")
w = self.canvas.winfo_width()
h = self.canvas.winfo_height()
if w<10 or h<10:
return
margin=30
usable_w = w-2*margin
y_base = h*0.5
n = self.var_n.get()
try:
h_c_mm = float(self.tab_coil.var_h_c.get())
except:
h_c_mm=50.0
h_c = h_c_mm/1000.0
total_dist = n*h_c
scale=0.0
if total_dist>1e-9:
scale= usable_w/total_dist
# dibujar n rectangulos
for stage in range(n):
x0_m = stage*h_c
x1_m = (stage+1)*h_c
x0_px= margin+ x0_m*scale
x1_px= margin+ x1_m*scale
self.canvas.create_rectangle(
x0_px, y_base-10, x1_px, y_base+10,
fill="orange", outline="black"
)
# proyectil => circulo
proj_r=5
x_init_px= margin
y_init_px= y_base
self.proyectil_id= self.canvas.create_oval(
x_init_px-proj_r, y_init_px-proj_r,
x_init_px+proj_r, y_init_px+proj_r,
fill="red"
)
def on_resize(self, event):
# Redibujamos la escena y recolocamos proyectil
self.dibujar_escena()
ms = self.slider_time.get()
self.mover_proyectil(ms)
def on_slider_move(self, val):
ms = float(val)
self.mover_proyectil(ms)
def mover_proyectil(self, ms):
"""Mueve el proyectil según el tiempo en ms => t=ms/1000,
busco x(t) en self.trayectoria (interp)."""
if not self.trayectoria:
return
t_req= ms*1e-3
# localizamos t_req en self.trayectoria
# simple => busco el frame mas cercano
N= len(self.trayectoria)
if t_req<= self.trayectoria[0][0]:
x_m= self.trayectoria[0][1]
elif t_req>= self.trayectoria[-1][0]:
x_m= self.trayectoria[-1][1]
else:
# busqueda lineal
for i in range(N-1):
t0, x0= self.trayectoria[i]
t1, x1= self.trayectoria[i+1]
if t_req>= t0 and t_req<= t1:
# interpolar
alpha= (t_req - t0)/(t1-t0) if abs(t1-t0)>1e-12 else 0.0
x_m= x0 + alpha*(x1-x0)
break
# convertir x_m => pix
w = self.canvas.winfo_width()
h = self.canvas.winfo_height()
margin=30
n = self.var_n.get()
try:
h_c_mm= float(self.tab_coil.var_h_c.get())
except:
h_c_mm=50.0
h_c= h_c_mm/1000.0
total_dist= n*h_c
usable_w= w-2*margin
scale=0.0
if total_dist>1e-9:
scale= usable_w/total_dist
x_px= margin+ x_m*scale
y_base= h*0.5
if self.proyectil_id is not None:
coords= self.canvas.coords(self.proyectil_id)
xc= (coords[0]+ coords[2])/2
yc= (coords[1]+ coords[3])/2
dx= x_px- xc
dy= y_base- yc
self.canvas.move(self.proyectil_id, dx, dy)