Tab Power
This commit is contained in:
parent
b05a0ff729
commit
8bb059f4c4
14
Docs/Models.aux
Normal file
14
Docs/Models.aux
Normal file
@ -0,0 +1,14 @@
|
||||
\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}
|
58
Docs/Models.fdb_latexmk
Normal file
58
Docs/Models.fdb_latexmk
Normal file
@ -0,0 +1,58 @@
|
||||
# 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)
|
109
Docs/Models.fls
Normal file
109
Docs/Models.fls
Normal file
@ -0,0 +1,109 @@
|
||||
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
|
BIN
Docs/Models.pdf
Normal file
BIN
Docs/Models.pdf
Normal file
Binary file not shown.
BIN
Docs/Models.synctex.gz
Normal file
BIN
Docs/Models.synctex.gz
Normal file
Binary file not shown.
83
Docs/Models.tex
Normal file
83
Docs/Models.tex
Normal file
@ -0,0 +1,83 @@
|
||||
% 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}
|
@ -1,25 +1,21 @@
|
||||
# main.py
|
||||
|
||||
import sys
|
||||
sys.path.append("C:\\Users\\promerogomb\\Desktop\\Proyectos UNAV\\LaunchSim\\src")
|
||||
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
# Importamos las distintas pestañas
|
||||
# Ajusta la ruta si fuera necesario:
|
||||
# sys.path.append("C:\\Users\\pedro\\Desktop\\Projects\\source\\src")
|
||||
|
||||
from tabs.tab_simulator import TabSimulator
|
||||
from tabs.tab_search import TabSearch
|
||||
from tabs.tab_drag import TabDrag
|
||||
from tabs.tab_coil import TabCoil
|
||||
from tabs.tab_dynamic import TabDynamic
|
||||
from tabs.tab_power import TabPower
|
||||
|
||||
class MainApp:
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
self.master.title("LaunchSim")
|
||||
|
||||
# Intento de cargar icono (opcional)
|
||||
icon_path = os.path.join(os.path.dirname(__file__), "static", "icon.ico")
|
||||
if os.path.exists(icon_path):
|
||||
self.master.iconbitmap(icon_path)
|
||||
@ -44,10 +40,9 @@ class MainApp:
|
||||
# Pestaña 4: Bobinas
|
||||
self.tab_coil = TabCoil(self.notebook, self.tab_sim)
|
||||
self.notebook.add(self.tab_coil.frame, text="Bobinas")
|
||||
|
||||
# Pestaña 5: Cálculos dinámicos
|
||||
tab_dyn = TabDynamic(self.notebook, self.tab_coil, self.tab_sim, self.tab_drag)
|
||||
self.notebook.add(tab_dyn.frame, text="Cálculos Dinámicos")
|
||||
|
||||
# Pestaña 5: Nuevo TabPower
|
||||
self.tab_power = TabPower(self.notebook, self.tab_coil, self.tab_sim)
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
|
@ -1,47 +1,35 @@
|
||||
# src/geometry_viewer.py
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("TkAgg") # Para usar Tkinter como backend
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d import Axes3D # necesario para 3D
|
||||
from mpl_toolkits.mplot3d import Axes3D # Necesario para 3D
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
import numpy as np
|
||||
|
||||
def plot_geometry_in_frame(parent_frame, geom, p1, p2):
|
||||
"""
|
||||
Dibuja la geometría (Prisma/Cilindro/Esfera) en un Axes3D dentro
|
||||
de 'parent_frame' (un Frame de Tkinter). No abre ventana nueva.
|
||||
(Ejemplo de integración en tu TabDrag)
|
||||
"""
|
||||
|
||||
"""Dibuja distintas geometrías 3D en un Frame de Tkinter, sin abrir ventana nueva."""
|
||||
fig = plt.Figure(figsize=(4, 3), dpi=100)
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.set_title(f"{geom}", fontsize=10)
|
||||
|
||||
if geom == "Prisma cuadrado":
|
||||
lado = p1
|
||||
largo = p2
|
||||
lado, largo = p1, p2
|
||||
Xs = [0, lado, lado, 0, 0, lado, lado, 0]
|
||||
Ys = [0, 0, lado, lado, 0, 0, lado, lado]
|
||||
Zs = [0, 0, 0, 0, largo, largo, largo, largo]
|
||||
edges = [(0,1),(1,2),(2,3),(3,0),
|
||||
(4,5),(5,6),(6,7),(7,4),
|
||||
(0,4),(1,5),(2,6),(3,7)]
|
||||
for (i,j) in edges:
|
||||
edges = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),(0,4),(1,5),(2,6),(3,7)]
|
||||
for i,j in edges:
|
||||
ax.plot([Xs[i], Xs[j]], [Ys[i], Ys[j]], [Zs[i], Zs[j]], color='g')
|
||||
ax.set_xlim(0, max(lado,1))
|
||||
ax.set_ylim(0, max(lado,1))
|
||||
ax.set_zlim(0, max(largo,1))
|
||||
|
||||
elif geom == "Cilindro":
|
||||
r = p1
|
||||
h = p2
|
||||
r, h = p1, p2
|
||||
theta = np.linspace(0, 2*np.pi, 30)
|
||||
z = np.linspace(0, h, 30)
|
||||
theta_grid, z_grid = np.meshgrid(theta, z)
|
||||
X = r * np.cos(theta_grid)
|
||||
Y = r * np.sin(theta_grid)
|
||||
Z = z_grid
|
||||
T, Z = np.meshgrid(theta, z)
|
||||
X = r*np.cos(T)
|
||||
Y = r*np.sin(T)
|
||||
ax.plot_surface(X, Y, Z, color='cyan', alpha=0.5)
|
||||
ax.set_xlim(-r, r)
|
||||
ax.set_ylim(-r, r)
|
||||
@ -51,124 +39,65 @@ def plot_geometry_in_frame(parent_frame, geom, p1, p2):
|
||||
r = p1
|
||||
phi = np.linspace(0, np.pi, 30)
|
||||
theta = np.linspace(0, 2*np.pi, 30)
|
||||
phi_grid, theta_grid = np.meshgrid(phi, theta)
|
||||
X = r*np.sin(phi_grid)*np.cos(theta_grid)
|
||||
Y = r*np.sin(phi_grid)*np.sin(theta_grid)
|
||||
Z = r*np.cos(phi_grid)
|
||||
Phi, Theta = np.meshgrid(phi, theta)
|
||||
X = r*np.sin(Phi)*np.cos(Theta)
|
||||
Y = r*np.sin(Phi)*np.sin(Theta)
|
||||
Z = r*np.cos(Phi)
|
||||
ax.plot_surface(X, Y, Z, color='yellow', alpha=0.6)
|
||||
ax.set_xlim(-r, r)
|
||||
ax.set_ylim(-r, r)
|
||||
ax.set_zlim(-r, r)
|
||||
|
||||
else:
|
||||
# Geometría no reconocida
|
||||
ax.text2D(0.2, 0.5, "Geometría desconocida", transform=ax.transAxes)
|
||||
|
||||
# Borramos lo anterior en parent_frame y embebemos el nuevo canvas
|
||||
for child in parent_frame.winfo_children():
|
||||
child.destroy()
|
||||
|
||||
canvas = FigureCanvasTkAgg(fig, master=parent_frame)
|
||||
canvas_widget = canvas.get_tk_widget()
|
||||
canvas_widget.pack(fill="both", expand=True)
|
||||
canvas.draw()
|
||||
|
||||
|
||||
def plot_coil_in_frame(parent_frame,
|
||||
N=10,
|
||||
e_pvc=1.0,
|
||||
r_int=5.0,
|
||||
r_ext=8.0,
|
||||
h_c=10.0,
|
||||
d_cu=0.5):
|
||||
"""
|
||||
Dibuja un esquema 2D aproximado de la bobina en un Frame de Tkinter,
|
||||
basado en los parámetros:
|
||||
N : número total de vueltas
|
||||
e_pvc : espesor del PVC
|
||||
r_int : radio interior de la bobina
|
||||
r_ext : radio exterior total de la bobina
|
||||
h_c : altura total de la bobina
|
||||
d_cu : diámetro del conductor
|
||||
|
||||
Se calcula:
|
||||
r_capas = r_ext - (r_int + e_pvc)
|
||||
Luego se distribuyen N vueltas en 'num_capas' radiales, etc.
|
||||
"""
|
||||
|
||||
# 1) Cálculo de la anchura de bobinado
|
||||
def plot_coil_in_frame(parent_frame, N=10, e_pvc=1.0, r_int=5.0, r_ext=8.0, h_c=10.0, d_cu=0.5):
|
||||
"""Dibuja de forma aproximada una bobina en 2D (vista de corte)."""
|
||||
r_capas = r_ext - (r_int + e_pvc)
|
||||
if r_capas < 0:
|
||||
# Si ocurre un valor absurdo, forzamos a 0
|
||||
r_capas = 0.0
|
||||
if r_capas < 0: r_capas = 0.0
|
||||
|
||||
# 2) Creamos la figura y eje 2D
|
||||
fig = plt.Figure(figsize=(4, 3), dpi=100)
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_aspect("equal", adjustable="box")
|
||||
ax.set_title("Bobina (vista 2D)", fontsize=10)
|
||||
|
||||
# 3) Dibujar PVC como dos rectángulos "verticales"
|
||||
# Eje Y: 0 -> h_c
|
||||
# Izquierda: x ~ -(r_int + e_pvc) hasta -r_int
|
||||
# Derecha : x ~ (r_int) hasta (r_int + e_pvc)
|
||||
ax.fill_between(
|
||||
x=[-(r_int + e_pvc), -r_int],
|
||||
y1=0,
|
||||
y2=h_c,
|
||||
color="black",
|
||||
alpha=0.3
|
||||
)
|
||||
ax.fill_between(
|
||||
x=[r_int, r_int + e_pvc],
|
||||
y1=0,
|
||||
y2=h_c,
|
||||
color="black",
|
||||
alpha=0.3
|
||||
)
|
||||
# PVC a izquierda y derecha
|
||||
ax.fill_between(x=[-(r_int + e_pvc), -r_int], y1=0, y2=h_c, color="black", alpha=0.3)
|
||||
ax.fill_between(x=[r_int, r_int + e_pvc], y1=0, y2=h_c, color="black", alpha=0.3)
|
||||
|
||||
# 4) Cálculo de capas radiales y vueltas por capa
|
||||
num_capas = int(np.floor(r_capas / d_cu))
|
||||
if num_capas < 1:
|
||||
num_capas = 1
|
||||
|
||||
if num_capas < 1: num_capas = 1
|
||||
vueltas_por_capa = int(np.ceil(N / num_capas))
|
||||
|
||||
# 5) Distancia vertical entre vueltas en cada capa
|
||||
delta_h = h_c / (vueltas_por_capa + 1)
|
||||
|
||||
# Función auxiliar para dibujar un conductor (círculo)
|
||||
def dibuja_conductor(xc, yc, radio):
|
||||
circle = plt.Circle((xc, yc), radio, color="orange", fill=True)
|
||||
ax.add_patch(circle)
|
||||
|
||||
# 6) Bucle para colocar cada capa (izq/dcha)
|
||||
for i in range(num_capas):
|
||||
# X de la capa i, en lado izquierdo
|
||||
x_left = -(r_int + e_pvc + (2*i + 1) * (d_cu / 2))
|
||||
# ... y lado derecho
|
||||
x_right = +(r_int + e_pvc + (2*i + 1) * (d_cu / 2))
|
||||
|
||||
# Añadimos vueltas en altura
|
||||
for j in range(vueltas_por_capa):
|
||||
y = (j + 1) * delta_h
|
||||
if y > h_c:
|
||||
break # no dibujamos si excede la altura
|
||||
y = (j + 1)*delta_h
|
||||
if y > h_c: break
|
||||
dibuja_conductor(x_left, y, d_cu/2)
|
||||
dibuja_conductor(x_right, y, d_cu/2)
|
||||
|
||||
# 7) Ajustar los límites del dibujo en X e Y
|
||||
ax.set_xlim(-r_ext - d_cu, r_ext + d_cu)
|
||||
ax.set_ylim(0, h_c + d_cu)
|
||||
|
||||
ax.set_xlabel("Radio (X)")
|
||||
ax.set_ylabel("Altura (Y)")
|
||||
ax.grid(True)
|
||||
|
||||
# 8) Borramos lo anterior en parent_frame y embebemos esta figura
|
||||
for child in parent_frame.winfo_children():
|
||||
child.destroy()
|
||||
|
||||
canvas = FigureCanvasTkAgg(fig, master=parent_frame)
|
||||
canvas_widget = canvas.get_tk_widget()
|
||||
canvas_widget.pack(fill="both", expand=True)
|
||||
|
@ -1,335 +1,191 @@
|
||||
# src/tab_coil.py
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("TkAgg")
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
import numpy as np
|
||||
import math as m
|
||||
|
||||
# Se importan las integrales elípticas de SciPy para el cálculo preciso de la inductancia
|
||||
from scipy.special import ellipk, ellipe
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Constantes y funciones para el cálculo de inductancia
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
MU0 = 4.0e-7 * m.pi # Permeabilidad magnética del vacío (H/m)
|
||||
MU0 = 4.0e-7 * m.pi # Permeabilidad magnética vacío (H/m)
|
||||
RHO_CU = 1.68e-8 # Resistividad cobre (Ohm·m) a 20°C
|
||||
|
||||
def self_inductance_loop(r, a_eff=1e-4):
|
||||
"""
|
||||
Autoinductancia aproximada de un anillo fino de radio r.
|
||||
Emplea la fórmula: L ~ mu0*r * [ln(8r/a_eff) - 2].
|
||||
"""
|
||||
if r <= 0:
|
||||
return 0.0
|
||||
return MU0 * r * (m.log(8.0 * r / a_eff) - 2.0)
|
||||
if r <= 0: return 0.0
|
||||
return MU0*r*(m.log(8.0*r/a_eff) - 2.0)
|
||||
|
||||
def mutual_inductance_coaxial(r1, r2, z_dist):
|
||||
"""
|
||||
Inductancia mutua entre dos anillos coaxiales de radios r1 y r2,
|
||||
separados una distancia axial z_dist.
|
||||
Emplea las integrales elípticas de primera y segunda especie.
|
||||
"""
|
||||
if r1 < 1e-12 or r2 < 1e-12:
|
||||
return 0.0
|
||||
|
||||
k_sq = 4.0 * r1 * r2 / ((r1 + r2)**2 + z_dist**2)
|
||||
if k_sq > 1.0:
|
||||
# Caso no físico (puede ocurrir por redondeos), retornamos 0
|
||||
return 0.0
|
||||
|
||||
if r1 < 1e-12 or r2 < 1e-12: return 0.0
|
||||
k_sq = 4.0*r1*r2 / ((r1 + r2)**2 + z_dist**2)
|
||||
if k_sq > 1.0: return 0.0
|
||||
k = np.sqrt(k_sq)
|
||||
|
||||
# Caso casi degenerado (r1 ~ r2, z_dist ~ 0)
|
||||
if (1.0 - k_sq) < 1e-12:
|
||||
# Aproximación cuando r1 ≈ r2 y z ≈ 0
|
||||
return MU0 * min(r1, r2) * m.pi
|
||||
else:
|
||||
Kk = ellipk(k_sq)
|
||||
Ek = ellipe(k_sq)
|
||||
factor = MU0 * np.sqrt(r1 * r2)
|
||||
# Expresión: M = mu0 * sqrt(r1*r2)*((2-k)*K - 2E) / k
|
||||
return factor * ((2.0 - k) * Kk - 2.0 * Ek) / k
|
||||
if (1.0 - k_sq) < 1e-12:
|
||||
return MU0*min(r1, r2)*m.pi
|
||||
Kk, Ek = ellipk(k_sq), ellipe(k_sq)
|
||||
factor = MU0*np.sqrt(r1*r2)
|
||||
return factor*((2.0 - k)*Kk - 2.0*Ek)/k
|
||||
|
||||
def compute_coil_geometry(N_total, R_int, R_ext, espesor_plast, H, wire_d):
|
||||
"""
|
||||
Genera la lista de (r_i, z_i) para las N_total espiras, asumiendo
|
||||
que se llenan capas radiales y, en cada capa, vueltas a lo largo de la altura.
|
||||
Todos los parámetros deben estar en metros.
|
||||
"""
|
||||
radial_thickness = R_ext - (R_int + 2 * espesor_plast)
|
||||
radial_thickness = R_ext - (R_int + 2*espesor_plast)
|
||||
if radial_thickness <= 0:
|
||||
raise ValueError("No hay espacio radial para el bobinado.")
|
||||
|
||||
max_layers = int(np.floor(radial_thickness / wire_d))
|
||||
if max_layers < 1:
|
||||
raise ValueError("No cabe ni una capa con el grosor de hilo dado.")
|
||||
|
||||
max_turns_per_layer = int(np.floor(H / wire_d))
|
||||
if max_turns_per_layer < 1:
|
||||
raise ValueError("No cabe ni una vuelta en la altura dada.")
|
||||
|
||||
max_turns_possible = max_layers * max_turns_per_layer
|
||||
if max_turns_possible < N_total:
|
||||
raise ValueError(
|
||||
f"Con {max_layers} capas y {max_turns_per_layer} vueltas/capa "
|
||||
f"sólo se pueden alojar {max_turns_possible} espiras, "
|
||||
f"pero se solicitan {N_total}."
|
||||
)
|
||||
raise ValueError(f"Se necesitan {N_total} espiras, pero solo caben {max_turns_possible}.")
|
||||
|
||||
espiras = []
|
||||
vueltas_restantes = N_total
|
||||
layer_index = 0
|
||||
|
||||
while vueltas_restantes > 0 and layer_index < max_layers:
|
||||
r_layer = R_int + espesor_plast + layer_index * wire_d + wire_d / 2.0
|
||||
|
||||
if vueltas_restantes >= max_turns_per_layer:
|
||||
n_vueltas = max_turns_per_layer
|
||||
else:
|
||||
n_vueltas = vueltas_restantes
|
||||
|
||||
vueltas_rest = N_total
|
||||
layer_idx = 0
|
||||
while vueltas_rest > 0 and layer_idx < max_layers:
|
||||
r_layer = R_int + espesor_plast + layer_idx*wire_d + wire_d/2.0
|
||||
n_vueltas = max_turns_per_layer if vueltas_rest >= max_turns_per_layer else vueltas_rest
|
||||
for turn in range(n_vueltas):
|
||||
z_i = (turn + 0.5) * wire_d
|
||||
z_i = (turn + 0.5)*wire_d
|
||||
espiras.append((r_layer, z_i))
|
||||
|
||||
vueltas_restantes -= n_vueltas
|
||||
layer_index += 1
|
||||
|
||||
if vueltas_restantes > 0:
|
||||
raise ValueError(
|
||||
f"No se pudo colocar toda la bobina. "
|
||||
f"Faltan {vueltas_restantes} espiras por ubicar."
|
||||
)
|
||||
vueltas_rest -= n_vueltas
|
||||
layer_idx += 1
|
||||
if vueltas_rest > 0:
|
||||
raise ValueError(f"Faltan {vueltas_rest} espiras por ubicar.")
|
||||
|
||||
return espiras
|
||||
|
||||
def inductance_from_spiralist(espiras, wire_radius):
|
||||
"""
|
||||
Calcula la inductancia total (en henrios) a partir de la lista (r_i, z_i),
|
||||
sumando la autoinductancia de cada espira y la mutua entre pares (O(N^2)).
|
||||
"""
|
||||
"""Suma autoinductancias + mutuas (O(N^2))."""
|
||||
N = len(espiras)
|
||||
L_total = 0.0
|
||||
|
||||
for i in range(N):
|
||||
r_i, z_i = espiras[i]
|
||||
# Autoinductancia
|
||||
L_total += self_inductance_loop(r_i, wire_radius)
|
||||
# Mutua con las espiras j > i
|
||||
for j in range(i + 1, N):
|
||||
r_j, z_j = espiras[j]
|
||||
dist_z = abs(z_j - z_i)
|
||||
M_ij = mutual_inductance_coaxial(r_i, r_j, dist_z)
|
||||
L_total += 2.0 * M_ij
|
||||
|
||||
M_ij = mutual_inductance_coaxial(r_i, r_j, abs(z_j - z_i))
|
||||
L_total += 2.0*M_ij
|
||||
return abs(L_total)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Importamos la función para dibujar la bobina 2D (tal y como estaba).
|
||||
# -------------------------------------------------------------------
|
||||
# Importamos la función para dibujar la bobina
|
||||
from tabs.geometry_viewer import plot_coil_in_frame
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Definición de la clase TabCoil, añadiendo la ventana de logs y
|
||||
# el botón de cálculo de inductancia con mensajes de estado.
|
||||
# -------------------------------------------------------------------
|
||||
class TabCoil:
|
||||
def __init__(self, notebook, tab_simulator):
|
||||
"""
|
||||
Pestaña para representar la bobina en 2D y ajustar sus parámetros.
|
||||
Recibe:
|
||||
- notebook: el ttk.Notebook donde se añadirá
|
||||
- tab_simulator: referencia a TabSimulator, si se necesita comunicar
|
||||
"""
|
||||
self.notebook = notebook
|
||||
self.tab_simulator = tab_simulator
|
||||
|
||||
# Frame principal dentro del notebook
|
||||
self.frame = tk.Frame(notebook)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
# Layout principal: lado izq (parámetros) y lado der (dibujo)
|
||||
frame_left = tk.Frame(self.frame)
|
||||
frame_left.pack(side="left", fill="y", padx=5, pady=5)
|
||||
|
||||
frame_left = tk.Frame(self.frame); frame_left.pack(side="left", fill="y", padx=5, pady=5)
|
||||
frame_right = tk.Frame(self.frame, bd=2, relief="groove")
|
||||
frame_right.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# Este frame contendrá el canvas/figura para la bobina en 2D
|
||||
self.frame_2d = tk.Frame(frame_right)
|
||||
self.frame_2d.pack(fill="both", expand=True)
|
||||
|
||||
#
|
||||
# Sección Izquierda: parámetros de la bobina
|
||||
#
|
||||
tk.Label(frame_left, text="Parámetros de la Bobina:").pack(anchor="w")
|
||||
|
||||
# Variables
|
||||
self.var_N = tk.DoubleVar(value=500) # Número de espiras
|
||||
self.var_e_pvc = tk.DoubleVar(value=2) # Espesor del plástico [mm]
|
||||
self.var_r_int = tk.DoubleVar(value=4.035) # Radio interior [mm]
|
||||
self.var_r_ext = tk.DoubleVar(value=10.64) # Radio exterior [mm]
|
||||
self.var_h_c = tk.DoubleVar(value=53.12) # Altura de la bobina [mm]
|
||||
self.var_d_cu = tk.DoubleVar(value=0.5) # Diámetro conductor [mm]
|
||||
|
||||
# Variable para mostrar la inductancia (se mostrará en µH)
|
||||
self.var_N = tk.DoubleVar(value=500)
|
||||
self.var_e_pvc = tk.DoubleVar(value=2)
|
||||
self.var_r_int = tk.DoubleVar(value=4.035)
|
||||
self.var_r_ext = tk.DoubleVar(value=10.64)
|
||||
self.var_h_c = tk.DoubleVar(value=53.12)
|
||||
self.var_d_cu = tk.DoubleVar(value=0.5)
|
||||
self.var_L = tk.StringVar(value="")
|
||||
self.var_R = tk.StringVar(value="")
|
||||
|
||||
# Frame para agrupar los Entries
|
||||
self.frame_params = tk.Frame(frame_left, bd=1, relief="sunken")
|
||||
self.frame_params.pack(fill="x", pady=5)
|
||||
|
||||
# Función auxiliar para añadir filas (etiqueta + entry)
|
||||
def add_param_row(label_text, var, row, width=8):
|
||||
lbl = tk.Label(self.frame_params, text=label_text)
|
||||
lbl.grid(row=row, column=0, sticky="w", padx=5, pady=2)
|
||||
ent = tk.Entry(self.frame_params, textvariable=var, width=width)
|
||||
ent.grid(row=row, column=1, padx=5, pady=2)
|
||||
def add_param_row(lbl_txt, var, row, w=8):
|
||||
tk.Label(self.frame_params, text=lbl_txt).grid(row=row, column=0, sticky="w", padx=5, pady=2)
|
||||
tk.Entry(self.frame_params, textvariable=var, width=w).grid(row=row, column=1, padx=5, pady=2)
|
||||
|
||||
rowcount = 0
|
||||
add_param_row("N (vueltas):", self.var_N, rowcount); rowcount += 1
|
||||
add_param_row("e_pvc (mm):", self.var_e_pvc, rowcount); rowcount += 1
|
||||
add_param_row("r_int (mm):", self.var_r_int, rowcount); rowcount += 1
|
||||
add_param_row("r_ext (mm):", self.var_r_ext, rowcount); rowcount += 1
|
||||
add_param_row("h_c (mm):", self.var_h_c, rowcount); rowcount += 1
|
||||
add_param_row("d_cu (mm):", self.var_d_cu, rowcount); rowcount += 1
|
||||
add_param_row("N (vueltas):", self.var_N, rowcount); rowcount += 1
|
||||
add_param_row("e_pvc (mm):", self.var_e_pvc, rowcount); rowcount += 1
|
||||
add_param_row("r_int (mm):", self.var_r_int, rowcount); rowcount += 1
|
||||
add_param_row("r_ext (mm):", self.var_r_ext, rowcount); rowcount += 1
|
||||
add_param_row("h_c (mm):", self.var_h_c, rowcount); rowcount += 1
|
||||
add_param_row("d_cu (mm):", self.var_d_cu, rowcount); rowcount += 1
|
||||
|
||||
# Campo para ver la inductancia en microHenrios (µH).
|
||||
lblL = tk.Label(self.frame_params, text="Induct. (µH):")
|
||||
lblL.grid(row=rowcount, column=0, sticky="w", padx=5, pady=2)
|
||||
entL = tk.Entry(self.frame_params, textvariable=self.var_L, width=10, state="readonly")
|
||||
entL.grid(row=rowcount, column=1, padx=5, pady=2)
|
||||
tk.Label(self.frame_params, text="Induct. (µH):").grid(row=rowcount, column=0, sticky="w", padx=5, pady=2)
|
||||
tk.Entry(self.frame_params, textvariable=self.var_L, width=10, state="readonly").grid(row=rowcount, column=1, padx=5, pady=2)
|
||||
rowcount += 1
|
||||
|
||||
# Botones
|
||||
btn_refrescar = tk.Button(
|
||||
frame_left, text="Refrescar Vista 2D",
|
||||
command=self.refrescar_2d
|
||||
)
|
||||
btn_refrescar.pack(pady=10)
|
||||
tk.Label(self.frame_params, text="R (Ohms):").grid(row=rowcount, column=0, sticky="w", padx=5, pady=2)
|
||||
tk.Entry(self.frame_params, textvariable=self.var_R, width=10, state="readonly").grid(row=rowcount, column=1, padx=5, pady=2)
|
||||
rowcount += 1
|
||||
|
||||
btn_calc_L = tk.Button(
|
||||
frame_left, text="Calcular Inductancia",
|
||||
command=self.calcular_inductancia
|
||||
)
|
||||
btn_calc_L.pack(pady=5)
|
||||
tk.Button(frame_left, text="Refrescar Vista 2D", command=self.refrescar_2d).pack(pady=10)
|
||||
tk.Button(frame_left, text="Calcular", command=self.calcular_inductancia).pack(pady=5)
|
||||
|
||||
#
|
||||
# Sección de Logs
|
||||
#
|
||||
tk.Label(frame_left, text="Logs:").pack(anchor="w")
|
||||
self.log_area = ScrolledText(frame_left, wrap="word", height=8, width=35)
|
||||
self.log_area.pack(fill="x", padx=5, pady=5)
|
||||
self.log_area.configure(state="disabled")
|
||||
|
||||
# Definimos etiquetas de color
|
||||
self.log_area.tag_config("info", foreground="green")
|
||||
self.log_area.tag_config("error", foreground="red")
|
||||
|
||||
#
|
||||
# Figura y canvas para la bobina en 2D
|
||||
#
|
||||
self.fig = plt.Figure(figsize=(4,3), dpi=100)
|
||||
self.ax = self.fig.add_subplot(111)
|
||||
self.canvas_2d = FigureCanvasTkAgg(self.fig, master=self.frame_2d)
|
||||
self.canvas_2d_widget = self.canvas_2d.get_tk_widget()
|
||||
self.canvas_2d_widget.pack(fill="both", expand=True)
|
||||
|
||||
# Dibujamos la bobina inicial
|
||||
self.refrescar_2d()
|
||||
|
||||
def log_message(self, text, mode="info"):
|
||||
"""
|
||||
Inserta un mensaje en el log_area, en color definido por 'mode'.
|
||||
mode puede ser 'info' (verde) o 'error' (rojo).
|
||||
"""
|
||||
self.log_area.configure(state="normal")
|
||||
self.log_area.insert("end", text + "\n", mode)
|
||||
self.log_area.see("end")
|
||||
self.log_area.configure(state="disabled")
|
||||
|
||||
def refrescar_2d(self):
|
||||
"""
|
||||
Lee los valores de la bobina y llama a 'plot_coil_in_frame'
|
||||
para actualizar el dibujo en 2D.
|
||||
"""
|
||||
N_val = self.var_N.get()
|
||||
N_val = self.var_N.get()
|
||||
e_pvc_val = self.var_e_pvc.get()
|
||||
r_int_val = self.var_r_int.get()
|
||||
r_ext_val = self.var_r_ext.get()
|
||||
h_c_val = self.var_h_c.get()
|
||||
d_cu_val = self.var_d_cu.get()
|
||||
|
||||
# Limpiar el Axes
|
||||
h_c_val = self.var_h_c.get()
|
||||
d_cu_val = self.var_d_cu.get()
|
||||
self.ax.clear()
|
||||
|
||||
# Llamada a la función que inserta la figura en self.frame_2d
|
||||
plot_coil_in_frame(
|
||||
parent_frame=self.frame_2d,
|
||||
N=N_val,
|
||||
e_pvc=e_pvc_val,
|
||||
r_int=r_int_val,
|
||||
r_ext=r_ext_val,
|
||||
h_c=h_c_val,
|
||||
d_cu=d_cu_val
|
||||
)
|
||||
plot_coil_in_frame(self.frame_2d, N_val, e_pvc_val, r_int_val, r_ext_val, h_c_val, d_cu_val)
|
||||
|
||||
def calcular_inductancia(self):
|
||||
"""
|
||||
Cálculo de la inductancia a partir de los parámetros introducidos.
|
||||
Muestra el resultado en µH en el campo correspondiente y en la log_area.
|
||||
"""
|
||||
try:
|
||||
# Lectura de parámetros
|
||||
N_espiras = int(self.var_N.get())
|
||||
e_pvc_val = self.var_e_pvc.get()
|
||||
r_int_val = self.var_r_int.get()
|
||||
r_ext_val = self.var_r_ext.get()
|
||||
h_c_val = self.var_h_c.get()
|
||||
d_cu_val = self.var_d_cu.get()
|
||||
h_c_val = self.var_h_c.get()
|
||||
d_cu_val = self.var_d_cu.get()
|
||||
R_in, R_out = r_int_val/1000.0, r_ext_val/1000.0
|
||||
plast = e_pvc_val/1000.0
|
||||
altura = h_c_val/1000.0
|
||||
diam_hilo = d_cu_val/1000.0
|
||||
|
||||
# Conversión mm -> m
|
||||
R_in = r_int_val / 1000.0
|
||||
R_out = r_ext_val / 1000.0
|
||||
plast = e_pvc_val / 1000.0
|
||||
altura = h_c_val / 1000.0
|
||||
diam_hilo = d_cu_val / 1000.0
|
||||
|
||||
# Construir la geometría de la bobina
|
||||
espiras_xyz = compute_coil_geometry(
|
||||
N_total=N_espiras,
|
||||
R_int=R_in,
|
||||
R_ext=R_out,
|
||||
espesor_plast=plast,
|
||||
H=altura,
|
||||
wire_d=diam_hilo
|
||||
)
|
||||
|
||||
# Radio efectivo para autoinductancia
|
||||
wire_radius = diam_hilo / 2.0
|
||||
|
||||
# Calcular la inductancia total (en henrios)
|
||||
espiras_xyz = compute_coil_geometry(N_espiras, R_in, R_out, plast, altura, diam_hilo)
|
||||
wire_radius = diam_hilo/2.0
|
||||
L_h = inductance_from_spiralist(espiras_xyz, wire_radius)
|
||||
L_uH = L_h * 1e6
|
||||
L_uH = L_h*1e6
|
||||
|
||||
wire_length = 0.0
|
||||
for (r_i, _) in espiras_xyz:
|
||||
wire_length += 2.0*m.pi*r_i
|
||||
wire_area = m.pi*(wire_radius**2)
|
||||
R_coil = RHO_CU*(wire_length/wire_area)
|
||||
|
||||
# Actualizar variable de resultado
|
||||
self.var_L.set(f"{L_uH:.3f}")
|
||||
|
||||
# Log de éxito
|
||||
self.log_message("Inductancia calculada correctamente", "info")
|
||||
|
||||
self.var_R.set(f"{R_coil:.3f}")
|
||||
self.log_message(f"Inductancia: {L_uH:.3f} µH, Resistencia: {R_coil:.3f} Ω", "info")
|
||||
except ValueError as e:
|
||||
# Si hay algún problema con la geometría, etc.
|
||||
self.var_L.set("Error")
|
||||
self.var_R.set("Error")
|
||||
self.log_message(f"Error en cálculo: {str(e)}", "error")
|
||||
except Exception as ex:
|
||||
# Errores generales
|
||||
self.var_L.set("Error")
|
||||
self.log_message(f"Error inesperado: {str(ex)}", "error")
|
||||
self.var_R.set("Error")
|
||||
self.log_message(f"Error inesperado: {str(ex)}", "error")
|
||||
|
@ -1,7 +1,6 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import math
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("TkAgg")
|
||||
import matplotlib.pyplot as plt
|
||||
@ -11,66 +10,39 @@ import numpy as np
|
||||
|
||||
class TabDrag:
|
||||
def __init__(self, notebook, tab_simulator):
|
||||
"""
|
||||
Pestaña para calcular un coef. 'b' a partir de la geometría
|
||||
y exponer métodos para la reluctancia (l_fe y S_disp).
|
||||
"""
|
||||
self.notebook = notebook
|
||||
self.tab_simulator = tab_simulator
|
||||
|
||||
self.frame = tk.Frame(notebook)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
# Layout principal
|
||||
frame_left = tk.Frame(self.frame)
|
||||
frame_left.pack(side="left", fill="y", padx=5, pady=5)
|
||||
|
||||
frame_left = tk.Frame(self.frame); frame_left.pack(side="left", fill="y", padx=5, pady=5)
|
||||
frame_right = tk.Frame(self.frame, bd=2, relief="groove")
|
||||
frame_right.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
self.frame_3d = tk.Frame(frame_right); self.frame_3d.pack(fill="both", expand=True)
|
||||
|
||||
self.frame_3d = tk.Frame(frame_right)
|
||||
self.frame_3d.pack(fill="both", expand=True)
|
||||
|
||||
# Sección Izquierda: geometría + parámetros
|
||||
tk.Label(frame_left, text="Selecciona geometría:").pack(anchor="w")
|
||||
self.geometria_var = tk.StringVar(value="Prisma cuadrado")
|
||||
self.combo_geometrias = ttk.Combobox(
|
||||
frame_left, textvariable=self.geometria_var,
|
||||
values=["Prisma cuadrado", "Cilindro", "Esfera"],
|
||||
state="readonly"
|
||||
)
|
||||
self.combo_geometrias = ttk.Combobox(frame_left, textvariable=self.geometria_var,
|
||||
values=["Prisma cuadrado","Cilindro","Esfera"], state="readonly")
|
||||
self.combo_geometrias.pack(anchor="w", pady=5)
|
||||
|
||||
self.frame_parametros = tk.Frame(frame_left, bd=1, relief="sunken")
|
||||
self.frame_parametros.pack(fill="x", pady=5)
|
||||
|
||||
self.label_param1 = tk.Label(self.frame_parametros, text="Parámetro 1:")
|
||||
self.label_param1.grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
||||
self.entry_param1 = tk.Entry(self.frame_parametros, width=8)
|
||||
self.entry_param1.grid(row=0, column=1, padx=5, pady=2)
|
||||
self.entry_param1 = tk.Entry(self.frame_parametros, width=8); self.entry_param1.grid(row=0, column=1, padx=5, pady=2)
|
||||
self.entry_param1.insert(0, "1")
|
||||
|
||||
self.label_param2 = tk.Label(self.frame_parametros, text="Parámetro 2:")
|
||||
self.label_param2.grid(row=1, column=0, sticky="w", padx=5, pady=2)
|
||||
self.entry_param2 = tk.Entry(self.frame_parametros, width=8)
|
||||
self.entry_param2.grid(row=1, column=1, padx=5, pady=2)
|
||||
self.entry_param2 = tk.Entry(self.frame_parametros, width=8); self.entry_param2.grid(row=1, column=1, padx=5, pady=2)
|
||||
self.entry_param2.insert(0, "7")
|
||||
|
||||
self.label_result_b = tk.Label(frame_left, text="Coef (b): N/A", fg="blue")
|
||||
self.label_result_b.pack()
|
||||
tk.Button(frame_left, text="Calcular Coef. Rozamiento", command=self.calcular_coef_rozamiento).pack(pady=5)
|
||||
tk.Button(frame_left, text="Refrescar Vista 3D", command=self.refrescar_3d).pack(pady=10)
|
||||
|
||||
btn_calc_b = tk.Button(
|
||||
frame_left, text="Calcular Coef. Rozamiento",
|
||||
command=self.calcular_coef_rozamiento
|
||||
)
|
||||
btn_calc_b.pack(pady=5)
|
||||
|
||||
tk.Button(
|
||||
frame_left, text="Refrescar Vista 3D",
|
||||
command=self.refrescar_3d
|
||||
).pack(pady=10)
|
||||
|
||||
# Creamos la figura 3D
|
||||
self.fig = plt.Figure(figsize=(4,3), dpi=100)
|
||||
self.ax = self.fig.add_subplot(111, projection='3d')
|
||||
self.canvas_3d = FigureCanvasTkAgg(self.fig, master=self.frame_3d)
|
||||
@ -99,58 +71,42 @@ class TabDrag:
|
||||
self.label_param2.config(text="(no aplica):")
|
||||
self.entry_param2.delete(0, tk.END)
|
||||
self.entry_param2.config(state="disabled", fg="gray")
|
||||
else:
|
||||
self.label_param1.config(text="Parámetro 1:")
|
||||
self.label_param2.config(text="Parámetro 2:")
|
||||
|
||||
def refrescar_3d(self):
|
||||
# Dibuja la geometría en self.ax
|
||||
geom = self.geometria_var.get()
|
||||
try: p1 = float(self.entry_param1.get())
|
||||
except ValueError: p1 = 1.0
|
||||
try:
|
||||
p1 = float(self.entry_param1.get())
|
||||
except ValueError:
|
||||
p1 = 1.0
|
||||
try:
|
||||
if self.entry_param2.cget("state") != "disabled":
|
||||
p2 = float(self.entry_param2.get())
|
||||
else:
|
||||
p2 = 1.0
|
||||
p2 = float(self.entry_param2.get()) if self.entry_param2.cget("state") != "disabled" else 1.0
|
||||
except ValueError:
|
||||
p2 = 1.0
|
||||
|
||||
self.ax.clear()
|
||||
self.ax.set_axis_off()
|
||||
|
||||
if geom == "Prisma cuadrado":
|
||||
Xs = [0,p1,p1,0,0,p1,p1,0]
|
||||
Ys = [0,0,p1,p1,0,0,p1,p1]
|
||||
Zs = [0,0,0,0,p2,p2,p2,p2]
|
||||
edges = [(0,1),(1,2),(2,3),(3,0),
|
||||
(4,5),(5,6),(6,7),(7,4),
|
||||
(0,4),(1,5),(2,6),(3,7)]
|
||||
for (i,j) in edges:
|
||||
self.ax.plot([Xs[i],Xs[j]], [Ys[i],Ys[j]], [Zs[i],Zs[j]], color='orange')
|
||||
Xs=[0,p1,p1,0,0,p1,p1,0]
|
||||
Ys=[0,0,p1,p1,0,0,p1,p1]
|
||||
Zs=[0,0,0,0,p2,p2,p2,p2]
|
||||
edges=[(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),(0,4),(1,5),(2,6),(3,7)]
|
||||
for i,j in edges:
|
||||
self.ax.plot([Xs[i],Xs[j]],[Ys[i],Ys[j]],[Zs[i],Zs[j]],color='orange')
|
||||
self.ax.set_box_aspect((p1,p1,p2))
|
||||
elif geom == "Cilindro":
|
||||
import numpy as np
|
||||
r = p1
|
||||
h = p2
|
||||
r, h = p1, p2
|
||||
theta = np.linspace(0,2*math.pi,30)
|
||||
z = np.linspace(0,h,30)
|
||||
T,Z = np.meshgrid(theta,z)
|
||||
X = r*np.cos(T)
|
||||
Y = r*np.sin(T)
|
||||
X = r*np.cos(T); Y = r*np.sin(T)
|
||||
self.ax.plot_surface(X, Y, Z, color='orange', alpha=0.8)
|
||||
self.ax.set_box_aspect((2*r,2*r,h))
|
||||
elif geom == "Esfera":
|
||||
import numpy as np
|
||||
r = p1
|
||||
phi = np.linspace(0,math.pi,30)
|
||||
t = np.linspace(0,2*math.pi,30)
|
||||
phi_grid,t_grid = np.meshgrid(phi,t)
|
||||
X = r*np.sin(phi_grid)*np.cos(t_grid)
|
||||
Y = r*np.sin(phi_grid)*np.sin(t_grid)
|
||||
Z = r*np.cos(phi_grid)
|
||||
Phi,T = np.meshgrid(phi,t)
|
||||
X = r*np.sin(Phi)*np.cos(T)
|
||||
Y = r*np.sin(Phi)*np.sin(T)
|
||||
Z = r*np.cos(Phi)
|
||||
self.ax.plot_surface(X,Y,Z,color='orange',alpha=0.8)
|
||||
self.ax.set_box_aspect((2*r,2*r,2*r))
|
||||
else:
|
||||
@ -160,113 +116,63 @@ class TabDrag:
|
||||
self.canvas_3d.draw()
|
||||
|
||||
def calcular_coef_rozamiento(self):
|
||||
"""
|
||||
Calcula un coeficiente b ~ 0.5*rho*Cd*A
|
||||
"""
|
||||
geom = self.geometria_var.get()
|
||||
try:
|
||||
p1 = float(self.entry_param1.get())
|
||||
except:
|
||||
try: p1 = float(self.entry_param1.get())
|
||||
except:
|
||||
self.label_result_b.config(text="Error param1", fg="red")
|
||||
return
|
||||
|
||||
Cd=1.0
|
||||
A=1.0
|
||||
if geom=="Prisma cuadrado":
|
||||
try:
|
||||
p2=float(self.entry_param2.get())
|
||||
Cd, A = 1.0, 1.0
|
||||
if geom == "Prisma cuadrado":
|
||||
try: p2 = float(self.entry_param2.get())
|
||||
except:
|
||||
self.label_result_b.config(text="Error param2", fg="red")
|
||||
return
|
||||
Cd=1.15
|
||||
A=p1*p1
|
||||
elif geom=="Cilindro":
|
||||
try:
|
||||
p2=float(self.entry_param2.get())
|
||||
Cd, A = 1.15, p1*p1
|
||||
elif geom == "Cilindro":
|
||||
try: p2 = float(self.entry_param2.get())
|
||||
except:
|
||||
self.label_result_b.config(text="Error param2", fg="red")
|
||||
return
|
||||
Cd=0.82
|
||||
A=math.pi*(p1**2)
|
||||
elif geom=="Esfera":
|
||||
Cd=0.47
|
||||
A=math.pi*(p1**2)
|
||||
|
||||
rho=1.225
|
||||
b_calc=0.5*rho*Cd*A
|
||||
Cd, A = 0.82, math.pi*(p1**2)
|
||||
elif geom == "Esfera":
|
||||
Cd, A = 0.47, math.pi*(p1**2)
|
||||
|
||||
rho = 1.225
|
||||
b_calc = 0.5*rho*Cd*A
|
||||
self.label_result_b.config(text=f"Coef (b) ~ {b_calc:.4f}", fg="blue")
|
||||
self.tab_simulator.set_b_value(b_calc)
|
||||
|
||||
def get_l_fe(self):
|
||||
"""
|
||||
Devuelve la 'altura' l_fe (m) según la geometría:
|
||||
- Prisma cuadrado: param2 (Longitud)
|
||||
- Cilindro: param2 (Altura)
|
||||
- Esfera: 2*radio (diámetro)
|
||||
"""
|
||||
geom = self.geometria_var.get()
|
||||
|
||||
try:
|
||||
p1 = float(self.entry_param1.get())
|
||||
except ValueError:
|
||||
p1 = 1.0
|
||||
|
||||
try: p1 = float(self.entry_param1.get())
|
||||
except ValueError: p1 = 1.0
|
||||
l_fe_val = 1.0
|
||||
|
||||
if geom == "Prisma cuadrado":
|
||||
try:
|
||||
l_fe_val = float(self.entry_param2.get())
|
||||
except ValueError:
|
||||
l_fe_val = 1.0
|
||||
|
||||
try: l_fe_val = float(self.entry_param2.get())
|
||||
except ValueError: l_fe_val = 1.0
|
||||
elif geom == "Cilindro":
|
||||
try:
|
||||
l_fe_val = float(self.entry_param2.get())
|
||||
except ValueError:
|
||||
l_fe_val = 1.0
|
||||
|
||||
try: l_fe_val = float(self.entry_param2.get())
|
||||
except ValueError: l_fe_val = 1.0
|
||||
elif geom == "Esfera":
|
||||
# Usamos 2*r (diámetro). Ajusta si prefieres 0
|
||||
l_fe_val = 2.0 * p1
|
||||
|
||||
l_fe_val = 2.0*p1
|
||||
return l_fe_val
|
||||
|
||||
def get_projectile_section_for_reluctance(self, tab_coil):
|
||||
"""
|
||||
Devuelve S_disp (m^2) para la fórmula de reluctancia:
|
||||
- Si la geometría es "Esfera", => S_disp = 2 * S_bob
|
||||
con S_bob = pi * (r_ext^2)
|
||||
(r_ext lo lees de tab_coil en mm, conv a m)
|
||||
- En otros casos => área frontal real (p1^2 si prisma, pi*r^2 si cilindro).
|
||||
"""
|
||||
geom = self.geometria_var.get()
|
||||
|
||||
# 1) Leer r_ext de tab_coil (en mm) => m
|
||||
try:
|
||||
r_ext_mm = float(tab_coil.var_r_ext.get())
|
||||
except:
|
||||
r_ext_mm = 10.0
|
||||
try: r_ext_mm = float(tab_coil.var_r_ext.get())
|
||||
except: r_ext_mm = 10.0
|
||||
r_ext_m = r_ext_mm / 1000.0
|
||||
|
||||
# S_bob = pi*(r_ext_m^2)
|
||||
s_bob = math.pi*(r_ext_m**2)
|
||||
|
||||
# 2) Leer p1
|
||||
try:
|
||||
p1 = float(self.entry_param1.get())
|
||||
except ValueError:
|
||||
p1 = 1.0
|
||||
try: p1 = float(self.entry_param1.get())
|
||||
except ValueError: p1 = 1.0
|
||||
|
||||
if geom == "Esfera":
|
||||
# S_disp = 2*S_bob
|
||||
return 2.0 * s_bob
|
||||
return 2.0*s_bob
|
||||
elif geom == "Prisma cuadrado":
|
||||
# Sección frontal ~ p1 x p1
|
||||
return (p1**2)
|
||||
return p1**2
|
||||
elif geom == "Cilindro":
|
||||
# Sección frontal ~ pi*(radio^2) = pi*(p1^2)
|
||||
return math.pi*(p1**2)
|
||||
else:
|
||||
# Por defecto, algo por si la geo no es conocida
|
||||
return 1.0
|
||||
|
@ -1,307 +0,0 @@
|
||||
# src/tab_dynamic.py
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
import math
|
||||
import numpy as np
|
||||
|
||||
MU0 = 4.0e-7 * math.pi
|
||||
|
||||
class TabDynamic:
|
||||
def __init__(self, notebook, tab_coil=None, tab_simulator=None, tab_drag=None):
|
||||
"""
|
||||
Pestaña que:
|
||||
- Obtiene el trabajo W desde tab_simulator.
|
||||
- Calcula la reluctancia media con la integración en x in [0, h_c].
|
||||
- Usa el modelo W = ((N I)^2 * h_c)/(4 mu0 S_fe (sumR_avg)^2) para hallar I.
|
||||
- Ofrece dimensionamiento de fuente (condensador / DC).
|
||||
"""
|
||||
self.notebook = notebook
|
||||
self.tab_coil = tab_coil
|
||||
self.tab_simulator = tab_simulator
|
||||
self.tab_drag = tab_drag
|
||||
|
||||
self.frame = tk.Frame(notebook)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
# Panel Izq: Botones / parámetros
|
||||
left_frame = tk.Frame(self.frame)
|
||||
left_frame.pack(side="left", fill="y", padx=5, pady=5)
|
||||
|
||||
# Panel Der: Logs
|
||||
right_frame = tk.Frame(self.frame, bd=2, relief="groove")
|
||||
right_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# Sección 1: Importar Config
|
||||
import_frame = tk.LabelFrame(left_frame, text="1) Importar Config")
|
||||
import_frame.pack(fill="x", pady=5)
|
||||
|
||||
btn_import = tk.Button(import_frame, text="Importar Config", command=self.importar_config)
|
||||
btn_import.pack(pady=5)
|
||||
|
||||
self.var_W_mech = tk.StringVar(value="")
|
||||
tk.Label(import_frame, text="W (J):").pack(anchor="w")
|
||||
tk.Entry(import_frame, textvariable=self.var_W_mech, width=12, state="readonly").pack(pady=2)
|
||||
|
||||
self.W_mech = 0.0 # energía mecánica importada
|
||||
|
||||
# Sección 2: Calcular Reluctancia Media
|
||||
rel_frame = tk.LabelFrame(left_frame, text="2) Reluctancia Media")
|
||||
rel_frame.pack(fill="x", pady=5)
|
||||
|
||||
self.var_sumR_avg = tk.StringVar(value="")
|
||||
tk.Button(rel_frame, text="Calcular Rel. Media", command=self.calcular_reluctancia_media).pack(pady=5)
|
||||
tk.Label(rel_frame, text="sum(R)_avg:").pack(anchor="w")
|
||||
tk.Entry(rel_frame, textvariable=self.var_sumR_avg, width=15, state="readonly").pack(pady=2)
|
||||
|
||||
self.sumR_avg = 0.0 # valor en el interior
|
||||
|
||||
# Sección 3: Calcular Corriente
|
||||
work_frame = tk.LabelFrame(left_frame, text="3) Corriente Necesaria")
|
||||
work_frame.pack(fill="x", pady=5)
|
||||
|
||||
self.var_S_fe = tk.DoubleVar(value=1e-4) # Sección en la zona férrea
|
||||
tk.Label(work_frame, text="S_fe (m²):").grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
||||
tk.Entry(work_frame, textvariable=self.var_S_fe, width=10).grid(row=0, column=1, padx=5, pady=2)
|
||||
|
||||
tk.Button(work_frame, text="Calcular Corriente", command=self.calcular_corriente_trabajo).grid(row=1, column=0, columnspan=2, pady=5)
|
||||
|
||||
self.var_I_req = tk.StringVar(value="")
|
||||
tk.Label(work_frame, text="I_req (A):").grid(row=2, column=0, sticky="w", padx=5, pady=2)
|
||||
tk.Entry(work_frame, textvariable=self.var_I_req, width=10, state="readonly").grid(row=2, column=1, padx=5, pady=2)
|
||||
|
||||
# Sección 4: Dimensionar Fuente
|
||||
fuente_frame = tk.LabelFrame(left_frame, text="4) Dimensionar Fuente")
|
||||
fuente_frame.pack(fill="x", pady=5)
|
||||
|
||||
tk.Button(fuente_frame, text="Dimensionar Capacitor", command=self.dimensionar_condensador).pack(pady=5)
|
||||
tk.Button(fuente_frame, text="Fuente DC minima", command=self.dimensionar_fuente_dc).pack(pady=5)
|
||||
|
||||
self.var_C_sug = tk.StringVar(value="")
|
||||
self.var_V_sug = tk.StringVar(value="")
|
||||
self.var_Vdc_min = tk.StringVar(value="")
|
||||
|
||||
tk.Label(fuente_frame, text="C (F) ~").pack(anchor="w")
|
||||
tk.Entry(fuente_frame, textvariable=self.var_C_sug, width=15, state="readonly").pack(pady=2)
|
||||
|
||||
tk.Label(fuente_frame, text="Vcap (V) ~").pack(anchor="w")
|
||||
tk.Entry(fuente_frame, textvariable=self.var_V_sug, width=15, state="readonly").pack(pady=2)
|
||||
|
||||
tk.Label(fuente_frame, text="V_DC min (V) ~").pack(anchor="w")
|
||||
tk.Entry(fuente_frame, textvariable=self.var_Vdc_min, width=15, state="readonly").pack(pady=2)
|
||||
|
||||
# LOGS
|
||||
tk.Label(right_frame, text="Logs:").pack(anchor="w")
|
||||
self.log_area = ScrolledText(right_frame, wrap="word", height=25, width=50)
|
||||
self.log_area.pack(fill="both", expand=True)
|
||||
self.log_area.configure(state="disabled")
|
||||
self.log_area.tag_config("info", foreground="green")
|
||||
self.log_area.tag_config("error", foreground="red")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Log helper
|
||||
# --------------------------------------------------------
|
||||
def log_message(self, msg, mode="info"):
|
||||
self.log_area.configure(state="normal")
|
||||
self.log_area.insert("end", msg + "\n", mode)
|
||||
self.log_area.see("end")
|
||||
self.log_area.configure(state="disabled")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 1) Importar Config (energía W)
|
||||
# --------------------------------------------------------
|
||||
def importar_config(self):
|
||||
"""
|
||||
Lee la energía mecánica W de tab_simulator y la guarda en self.W_mech
|
||||
"""
|
||||
try:
|
||||
if not self.tab_simulator:
|
||||
raise ValueError("No hay tab_simulator para leer W.")
|
||||
w_val = self.tab_simulator.get_energy_required()
|
||||
if w_val<=0:
|
||||
raise ValueError("Energía <= 0.")
|
||||
self.W_mech = w_val
|
||||
self.var_W_mech.set(f"{w_val:.3f}")
|
||||
self.log_message(f"Importado W={w_val:.3f} J", "info")
|
||||
|
||||
except Exception as e:
|
||||
self.log_message(f"Error importar_config: {e}", "error")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 2) Calcular Reluctancia Media (sum(R)_avg)
|
||||
# => integrando la ecuación sum_R(x)= ...
|
||||
# --------------------------------------------------------
|
||||
def calcular_reluctancia_media(self):
|
||||
try:
|
||||
if not self.tab_coil:
|
||||
raise ValueError("Falta referencia a tab_coil.")
|
||||
if not self.tab_drag:
|
||||
raise ValueError("Falta referencia a tab_drag.")
|
||||
|
||||
# 1) Leer h_c, N, r_int (en mm->m)
|
||||
h_c_m = float(self.tab_coil.var_h_c.get())/1000.0
|
||||
if h_c_m<=0:
|
||||
raise ValueError("h_c_m <=0.")
|
||||
|
||||
r_int_m = float(self.tab_coil.var_r_int.get())/1000.0
|
||||
if r_int_m<=0:
|
||||
raise ValueError("r_int_m <=0.")
|
||||
|
||||
# 2) l_fe => de tab_drag
|
||||
l_fe_m = self.tab_drag.get_l_fe() # en metros
|
||||
if l_fe_m<0:
|
||||
raise ValueError("l_fe<0?")
|
||||
|
||||
# 3) s_disp => get_projectile_section_for_reluctance
|
||||
s_disp = self.tab_drag.get_projectile_section_for_reluctance(self.tab_coil)
|
||||
if s_disp<=0:
|
||||
raise ValueError("s_disp <=0.")
|
||||
|
||||
# 4) s_c= pi*r_int^2
|
||||
s_c = math.pi*(r_int_m**2)
|
||||
if s_c<=0:
|
||||
raise ValueError("s_c<=0.")
|
||||
|
||||
# 5) Definir sumR(x)
|
||||
def sumR_of_x(x):
|
||||
# sum_R = h_c/(mu0*s_disp)
|
||||
# + (h_c + l_fe - x)/(mu0*s_disp)
|
||||
# + (h_c - x)/(mu0*s_c)
|
||||
return (
|
||||
(h_c_m/(MU0*s_disp))
|
||||
+ ((h_c_m + l_fe_m - x)/(MU0*s_disp))
|
||||
+ ((h_c_m - x)/(MU0*s_c))
|
||||
)
|
||||
|
||||
# 6) Integración en [0..h_c_m]
|
||||
n_steps=200
|
||||
dx = h_c_m/n_steps
|
||||
area_sum=0.0
|
||||
x_curr=0.0
|
||||
for _ in range(n_steps):
|
||||
x_mid= x_curr+0.5*dx
|
||||
val_mid= sumR_of_x(x_mid)
|
||||
area_sum+= val_mid
|
||||
x_curr+=dx
|
||||
area_sum*= dx
|
||||
|
||||
sumR_avg= area_sum/h_c_m
|
||||
if sumR_avg<=1e-30:
|
||||
raise ValueError("sumR_avg nulo o negativo.")
|
||||
|
||||
self.sumR_avg= sumR_avg
|
||||
self.var_sumR_avg.set(f"{sumR_avg:.3e}")
|
||||
self.log_message(f"Reluctancia media= {sumR_avg:.3e} H^-1", "info")
|
||||
|
||||
except ValueError as ve:
|
||||
self.sumR_avg=0.0
|
||||
self.var_sumR_avg.set("")
|
||||
self.log_message(f"Error calc Reluctancia media: {ve}", "error")
|
||||
except Exception as ex:
|
||||
self.sumR_avg=0.0
|
||||
self.var_sumR_avg.set("")
|
||||
self.log_message(f"Excepción calc Reluct. media: {ex}", "error")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 3) Calcular Corriente, con W = ((N I)^2 * h_c)/(4 mu0 S_fe (sumR_avg)^2)
|
||||
# --------------------------------------------------------
|
||||
def calcular_corriente_trabajo(self):
|
||||
try:
|
||||
if self.W_mech<=0:
|
||||
raise ValueError("No hay W_mech positivo. Importa config primero.")
|
||||
if self.sumR_avg<=0:
|
||||
raise ValueError("sumR_avg<=0. Calcula la Reluctancia media antes.")
|
||||
|
||||
# 1) Leer h_c (mm->m), N, etc. de tab_coil
|
||||
h_c_m = float(self.tab_coil.var_h_c.get())/1000.0
|
||||
N_val = float(self.tab_coil.var_N.get())
|
||||
if h_c_m<=0 or N_val<=0:
|
||||
raise ValueError("Parámetros de bobina inválidos.")
|
||||
|
||||
# 2) Leer S_fe
|
||||
S_fe_val= self.var_S_fe.get()
|
||||
if S_fe_val<=0:
|
||||
raise ValueError("S_fe <=0")
|
||||
|
||||
# 3) Tomamos sumR_avg, W
|
||||
sumR_avg= self.sumR_avg
|
||||
W= self.W_mech
|
||||
|
||||
# 4) W= ((N I)^2 * h_c)/(4 mu0 S_fe (sumR_avg)^2)
|
||||
# => (N I)^2= [4 mu0 S_fe (sumR_avg)^2 W]/h_c
|
||||
top= 4.0*MU0*S_fe_val*(sumR_avg**2)* W
|
||||
bottom= h_c_m
|
||||
if bottom<=1e-30:
|
||||
raise ValueError("h_c_m=0?")
|
||||
|
||||
NI_sq= top/bottom
|
||||
if NI_sq<=0:
|
||||
raise ValueError("Resultado (N I)^2 <=0, no válido.")
|
||||
|
||||
NI= math.sqrt(NI_sq)
|
||||
I_req= NI/ N_val
|
||||
|
||||
self.var_I_req.set(f"{I_req:.3f}")
|
||||
self.log_message(f"I necesaria= {I_req:.3f} A", "info")
|
||||
|
||||
except ValueError as ve:
|
||||
self.var_I_req.set("")
|
||||
self.log_message(f"Error calc I trabajo: {ve}", "error")
|
||||
except Exception as ex:
|
||||
self.var_I_req.set("")
|
||||
self.log_message(f"Excepción calc I trabajo: {ex}", "error")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 4) Dimensionar la fuente (ejemplo condensador y DC)
|
||||
# --------------------------------------------------------
|
||||
def dimensionar_condensador(self):
|
||||
"""
|
||||
Ejemplo: suponer que la energía total W -> (1/2)C V^2
|
||||
W_marg = W_mech * factor... etc.
|
||||
"""
|
||||
try:
|
||||
W= self.W_mech
|
||||
if W<=0:
|
||||
raise ValueError("Primero importa config. W<=0?")
|
||||
|
||||
# suponer un factor "pérdidas"
|
||||
eff=0.7
|
||||
# E capacitor= W/ eff
|
||||
E_cap= W/eff
|
||||
# asumes C= 1mF => V= sqrt(2 E_cap/C)
|
||||
# o V= 200 => C= 2E_cap/V^2, etc.
|
||||
# Aquí arbitrariamente p.ej C=1e-3 y calculamos V
|
||||
C_val= 1e-3
|
||||
Vcalc= math.sqrt(2.0*E_cap/C_val)
|
||||
|
||||
self.var_C_sug.set(f"{C_val:.3g}")
|
||||
self.var_V_sug.set(f"{Vcalc:.1f}")
|
||||
self.log_message(f"Caps: C=1mF => V~{Vcalc:.1f} V (descarga)", "info")
|
||||
|
||||
except ValueError as ve:
|
||||
self.log_message(f"Error dimensionar_condensador: {ve}", "error")
|
||||
except Exception as ex:
|
||||
self.log_message(f"Excepción dimensionar_condensador: {ex}", "error")
|
||||
|
||||
def dimensionar_fuente_dc(self):
|
||||
"""
|
||||
Ejemplo simple: V_dc >= R_coil * I_req
|
||||
R_coil en ohms => la obtendrías o la metes manual.
|
||||
"""
|
||||
try:
|
||||
Ireq_str= self.var_I_req.get()
|
||||
Ireq= float(Ireq_str)
|
||||
if Ireq<=0:
|
||||
raise ValueError("I_req <=0? Calcula la corriente primero.")
|
||||
|
||||
# supongamos R_coil= 2 ohms (ejemplo)
|
||||
R_coil= 2.0
|
||||
V_dc= R_coil* Ireq
|
||||
self.var_Vdc_min.set(f"{V_dc:.1f}")
|
||||
self.log_message(f"Fuente DC~ {V_dc:.1f} V (min. ohmico)", "info")
|
||||
|
||||
except ValueError as ve:
|
||||
self.log_message(f"Error dimensionar_fuente_dc: {ve}", "error")
|
||||
except Exception as ex:
|
||||
self.log_message(f"Excepción dimensionar_fuente_dc: {ex}", "error")
|
296
src/tabs/tab_power.py
Normal file
296
src/tabs/tab_power.py
Normal file
@ -0,0 +1,296 @@
|
||||
# tab_power.py
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
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)
|
||||
"""
|
||||
self.notebook = notebook
|
||||
self.tab_coil = tab_coil
|
||||
self.tab_simulator = tab_simulator
|
||||
|
||||
# Frame principal para la pestaña
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
# ------------------------------
|
||||
# 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"
|
||||
)
|
||||
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")
|
||||
self.var_n = tk.IntVar(value=3)
|
||||
tk.Entry(self.frame_left, textvariable=self.var_n, width=10).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)
|
||||
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)
|
||||
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)
|
||||
|
||||
# Botón dimensionar
|
||||
tk.Button(self.frame_left, text="Dimensionar", command=self.dimensionar).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)
|
||||
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
|
||||
self.on_mode_change()
|
||||
|
||||
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
|
||||
else:
|
||||
# Dimensionar V0 => el usuario fija C
|
||||
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")
|
||||
|
||||
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.
|
||||
"""
|
||||
# 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")
|
||||
return
|
||||
|
||||
# n (etapas)
|
||||
n = self.var_n.get()
|
||||
if n < 1:
|
||||
self.log_message("El número de etapas debe ser >= 1", "error")
|
||||
return
|
||||
|
||||
# => La energía por etapa
|
||||
E_target_stage = E_target_total / n
|
||||
|
||||
# 1) Leer inductancia y resistencia desde TabCoil
|
||||
try:
|
||||
L_uH = float(self.tab_coil.var_L.get()) # microHenrios
|
||||
R_coil = float(self.tab_coil.var_R.get()) # Ohms
|
||||
except ValueError:
|
||||
self.log_message("No se pudo leer L/R de TabCoil.", "error")
|
||||
return
|
||||
L_coil = L_uH * 1e-6 # Henrios
|
||||
|
||||
# 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")
|
||||
return
|
||||
h_c = h_c_mm / 1000.0 # m
|
||||
|
||||
# 2) Leer los datos de (C, V0, dt) (aunque uno de los dos se va a dimensionar)
|
||||
dt_ms = self.var_time_ms.get()
|
||||
if dt_ms <= 0:
|
||||
self.log_message("Tiempo de etapa (ms) inválido", "error")
|
||||
return
|
||||
T_total = 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:
|
||||
try:
|
||||
V0_fixed = float(self.var_V0.get())
|
||||
if V0_fixed <= 0:
|
||||
self.log_message("Tensión (V) inválida", "error")
|
||||
return
|
||||
except ValueError:
|
||||
self.log_message("Tensión (V) inválida", "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
|
||||
)
|
||||
return E_stage
|
||||
|
||||
else:
|
||||
# Dimensionar V0 => user fija C
|
||||
try:
|
||||
C_uF_fixed = float(self.var_C.get())
|
||||
if C_uF_fixed<=0:
|
||||
self.log_message("Capacidad (µF) inválida", "error")
|
||||
return
|
||||
except ValueError:
|
||||
self.log_message("Capacidad (µF) inválida", "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
|
||||
)
|
||||
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
|
||||
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")
|
||||
|
||||
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)
|
||||
diff = E_mid - E_target_stage
|
||||
if abs(diff) < tol:
|
||||
best_param = mid
|
||||
break
|
||||
# Suponemos monotonicidad: +C => +E, +V0 => +E
|
||||
if E_mid > E_target_stage:
|
||||
upper = mid
|
||||
else:
|
||||
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")
|
||||
|
||||
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")
|
||||
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"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:
|
||||
return 0.0
|
||||
|
||||
# Convertimos C a F
|
||||
C = C_uF * 1e-6
|
||||
# Asumimos L variable ~ 20% de variación
|
||||
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
|
||||
def dLdx_of_x(x):
|
||||
return (Lf - L0)/h_c if h_c>1e-9 else 0.0
|
||||
|
||||
N_steps = 1000
|
||||
dt = T_total/N_steps
|
||||
vCap = V0
|
||||
i = 0.0
|
||||
|
||||
def x_of_t(t):
|
||||
return (h_c / T_total)*t if T_total>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)
|
||||
|
||||
if abs(Lx)>1e-12:
|
||||
di_dt = (vCap - R_coil*i - i*dLdt)/Lx
|
||||
else:
|
||||
di_dt = 0.0
|
||||
|
||||
i_new = i + di_dt*dt
|
||||
vCap_new = vCap - (i/C)*dt
|
||||
if vCap_new<0:
|
||||
vCap_new=0.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)
|
||||
if E_consumida<0:
|
||||
E_consumida=0.0
|
||||
return E_consumida
|
@ -1,68 +1,38 @@
|
||||
# tab_search.py
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import math
|
||||
import matplotlib
|
||||
|
||||
matplotlib.use("TkAgg")
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
|
||||
class TabSearch:
|
||||
def __init__(self, notebook, tab_simulator):
|
||||
"""
|
||||
Tercera pestaña: 'Búsqueda de ángulo'.
|
||||
Recibe 'tab_simulator' para leer la config en la pestaña 1:
|
||||
- masa (m)
|
||||
- coef de rozamiento (b)
|
||||
- altura h0
|
||||
- si hay drag o no
|
||||
- alcance (si el modo es "Alcance (m)")
|
||||
"""
|
||||
self.notebook = notebook
|
||||
self.tab_sim = tab_simulator # referencia a la pestaña de simulación
|
||||
|
||||
self.tab_sim = tab_simulator
|
||||
self.frame = tk.Frame(notebook, width=280)
|
||||
self.frame.pack(side="left", fill="both", expand=True)
|
||||
# Fijamos ancho y desactivamos propagación para la columna
|
||||
self.frame.pack_propagate(False)
|
||||
|
||||
# -- Layout principal: izquierda (botones + info), derecha (gráfico) --
|
||||
self.frame_left = tk.Frame(self.frame, width=240)
|
||||
self.frame_left.pack(side="left", fill="y", padx=5, pady=5)
|
||||
self.frame_left.pack_propagate(False)
|
||||
|
||||
self.frame_right = tk.Frame(self.frame)
|
||||
self.frame_right.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# 1) Botones e info en la parte izquierda
|
||||
tk.Button(self.frame_left, text="Importar config", command=self.import_config).pack(pady=5)
|
||||
tk.Button(self.frame_left, text="Ejecutar búsqueda", command=self.run_search).pack(pady=5)
|
||||
|
||||
# Etiquetas para mostrar la config importada
|
||||
self.label_m = tk.Label(self.frame_left, text="Masa: ??? kg", bg="white")
|
||||
self.label_m.pack(pady=2, anchor="w")
|
||||
self.label_m = tk.Label(self.frame_left, text="Masa: ??? kg", bg="white"); self.label_m.pack(pady=2, anchor="w")
|
||||
self.label_b = tk.Label(self.frame_left, text="b: ???", bg="white"); self.label_b.pack(pady=2, anchor="w")
|
||||
self.label_h0 = tk.Label(self.frame_left, text="h0: ??? m", bg="white"); self.label_h0.pack(pady=2, anchor="w")
|
||||
self.label_drag = tk.Label(self.frame_left, text="Drag: ???", bg="white"); self.label_drag.pack(pady=2, anchor="w")
|
||||
self.label_x_target = tk.Label(self.frame_left, text="X_target: ???", bg="white"); self.label_x_target.pack(pady=2, anchor="w")
|
||||
|
||||
self.label_b = tk.Label(self.frame_left, text="b: ???", bg="white")
|
||||
self.label_b.pack(pady=2, anchor="w")
|
||||
|
||||
self.label_h0 = tk.Label(self.frame_left, text="h0: ??? m", bg="white")
|
||||
self.label_h0.pack(pady=2, anchor="w")
|
||||
|
||||
self.label_drag = tk.Label(self.frame_left, text="Drag: ???", bg="white")
|
||||
self.label_drag.pack(pady=2, anchor="w")
|
||||
|
||||
self.label_x_target = tk.Label(self.frame_left, text="X_target: ???", bg="white")
|
||||
self.label_x_target.pack(pady=2, anchor="w")
|
||||
|
||||
# Label para mostrar el ángulo/vel. hallados tras la búsqueda
|
||||
self.label_result = tk.Label(self.frame_left, text="Resultado:\n...", fg="green", bg="white")
|
||||
self.label_result.pack(pady=10, fill="x")
|
||||
|
||||
# 2) Creamos figure + canvas a la derecha (para dibujar la curva)
|
||||
self.fig = plt.Figure(figsize=(4, 3), dpi=100)
|
||||
self.fig = plt.Figure(figsize=(4,3), dpi=100)
|
||||
self.ax = self.fig.add_subplot(111)
|
||||
self.ax.set_xlabel("Ángulo (grados)")
|
||||
self.ax.set_ylabel("Vel. requerida (m/s)")
|
||||
@ -71,149 +41,85 @@ class TabSearch:
|
||||
self.canvas_widget = self.canvas.get_tk_widget()
|
||||
self.canvas_widget.pack(fill="both", expand=True)
|
||||
|
||||
# Variables donde guardaremos la config importada
|
||||
self.m = 1.0 # masa
|
||||
self.b = 0.0 # coef rozamiento
|
||||
self.h0 = 0.0 # altura
|
||||
self.m, self.b, self.h0 = 1.0, 0.0, 0.0
|
||||
self.has_drag = False
|
||||
self.x_target = None # Alcance importado, si está en modo "Alcance (m)"
|
||||
self.x_target = None
|
||||
|
||||
def import_config(self):
|
||||
"""
|
||||
Lee la config actual de la pestaña 1 (simulador):
|
||||
- masa
|
||||
- b (si hay rozamiento)
|
||||
- h0
|
||||
- check_rozamiento => has_drag
|
||||
- y si param es "Alcance (m)", tomamos ese v0 como x_target
|
||||
"""
|
||||
try:
|
||||
# Masa
|
||||
self.m = float(self.tab_sim.entry_masa.get())
|
||||
|
||||
# Rozamiento
|
||||
if self.tab_sim.check_rozamiento.get():
|
||||
self.has_drag = True
|
||||
self.b = float(self.tab_sim.entry_b.get())
|
||||
else:
|
||||
self.has_drag = False
|
||||
self.b = 0.0
|
||||
|
||||
# Altura
|
||||
self.h0 = float(self.tab_sim.entry_h0.get())
|
||||
|
||||
# Alcance si param== "Alcance (m)"
|
||||
if self.tab_sim.parametro_var.get() == "Alcance (m)":
|
||||
self.x_target = float(self.tab_sim.entry_v0.get())
|
||||
else:
|
||||
self.x_target = None
|
||||
|
||||
# Actualizamos en pantalla
|
||||
self.label_m.config(text=f"Masa: {self.m:.2f} kg")
|
||||
self.label_b.config(text=f"b: {self.b:.4f}")
|
||||
self.label_h0.config(text=f"h0: {self.h0:.2f} m")
|
||||
self.label_drag.config(text=f"Drag: {self.has_drag}")
|
||||
|
||||
if self.x_target is not None:
|
||||
self.label_x_target.config(text=f"X_target: {self.x_target:.2f} m")
|
||||
else:
|
||||
self.label_x_target.config(text="X_target: (No hay)")
|
||||
|
||||
self.label_result.config(text="Config importada OK", bg="lightgreen", fg="black")
|
||||
|
||||
except ValueError:
|
||||
self.label_result.config(text="Error al leer config", bg="red", fg="white")
|
||||
self.x_target = None
|
||||
|
||||
def run_search(self):
|
||||
"""
|
||||
Barre ángulos de 0..90 y busca la velocidad mínima
|
||||
para alcanzar la distancia x_target (si existe).
|
||||
Si no hay x_target (porque el modo era Vel(m/s)),
|
||||
mostramos error.
|
||||
"""
|
||||
if self.x_target is None:
|
||||
# Si el usuario está en modo "Velocidad (m/s)" en la pestaña 1,
|
||||
# no tenemos 'x_target' válido. Lógica:
|
||||
self.label_result.config(
|
||||
text="Error: en la pestaña 1 no se eligió 'Alcance (m)'",
|
||||
bg="red", fg="white"
|
||||
)
|
||||
self.label_result.config(text="Error: en la pestaña 1 no se eligió 'Alcance (m)'", bg="red", fg="white")
|
||||
return
|
||||
|
||||
X_target = self.x_target # usar la que importamos
|
||||
angulos = []
|
||||
velocidades = []
|
||||
best_angle = None
|
||||
best_v = 1e9
|
||||
|
||||
# Recorremos ángulos de 0..90
|
||||
X_target = self.x_target
|
||||
angulos, velocidades = [], []
|
||||
best_angle, best_v = None, 1e9
|
||||
for angle_deg in range(0, 91):
|
||||
v0_min = 0.0
|
||||
v0_max = 300.0
|
||||
final_v = 0.0
|
||||
|
||||
# Bisección
|
||||
v0_min, v0_max, final_v = 0.0, 300.0, 0.0
|
||||
for _ in range(100):
|
||||
guess = 0.5*(v0_min + v0_max)
|
||||
guess = 0.5*(v0_min+v0_max)
|
||||
dist = self.simular_dist(guess, angle_deg, self.has_drag)
|
||||
if abs(dist - X_target) < 0.1:
|
||||
final_v = guess
|
||||
break
|
||||
if dist < X_target:
|
||||
v0_min = guess
|
||||
else:
|
||||
v0_max = guess
|
||||
if dist < X_target: v0_min = guess
|
||||
else: v0_max = guess
|
||||
final_v = guess
|
||||
|
||||
angulos.append(angle_deg)
|
||||
velocidades.append(final_v)
|
||||
|
||||
if final_v < best_v:
|
||||
best_v = final_v
|
||||
best_angle = angle_deg
|
||||
best_v, best_angle = final_v, angle_deg
|
||||
|
||||
# Dibujamos
|
||||
self.ax.clear()
|
||||
self.ax.set_xlabel("Ángulo (grados)")
|
||||
self.ax.set_ylabel("Vel. requerida (m/s)")
|
||||
self.ax.plot(angulos, velocidades, color="red")
|
||||
|
||||
# Marcamos el punto mínimo
|
||||
self.ax.plot([best_angle], [best_v], marker="o", markersize=8, color="green")
|
||||
self.ax.plot([best_angle],[best_v], marker="o", markersize=8, color="green")
|
||||
self.canvas.draw()
|
||||
|
||||
self.label_result.config(
|
||||
text=f"Mín. en {best_angle}°, v0={best_v:.2f} m/s",
|
||||
bg="white", fg="green"
|
||||
)
|
||||
self.label_result.config(text=f"Mín. en {best_angle}°, v0={best_v:.2f} m/s", bg="white", fg="green")
|
||||
|
||||
def simular_dist(self, v0_guess, angle_deg, drag):
|
||||
"""
|
||||
Integra la distancia horizontal partiendo de (0,h0)
|
||||
hasta y<=0. Devuelve x final. Usa self.m, self.b, self.h0, drag (bool).
|
||||
"""
|
||||
dt = 0.01
|
||||
x = 0.0
|
||||
y = self.h0
|
||||
x, y = 0.0, self.h0
|
||||
alpha = math.radians(angle_deg)
|
||||
vx = v0_guess * math.cos(alpha)
|
||||
vy = v0_guess * math.sin(alpha)
|
||||
|
||||
vx = v0_guess*math.cos(alpha)
|
||||
vy = v0_guess*math.sin(alpha)
|
||||
while True:
|
||||
if drag:
|
||||
ax = -(self.b / self.m) * vx
|
||||
ay = -9.8 - (self.b / self.m) * vy
|
||||
ax = -(self.b/self.m)*vx
|
||||
ay = -9.8 - (self.b/self.m)*vy
|
||||
else:
|
||||
ax = 0.0
|
||||
ay = -9.8
|
||||
|
||||
ax, ay = 0.0, -9.8
|
||||
vx += ax*dt
|
||||
vy += ay*dt
|
||||
x += vx*dt
|
||||
y += vy*dt
|
||||
|
||||
if y <= 0:
|
||||
break
|
||||
|
||||
x += vx*dt
|
||||
y += vy*dt
|
||||
if y <= 0: break
|
||||
return x
|
||||
|
@ -5,137 +5,77 @@ import math
|
||||
class TabSimulator:
|
||||
def __init__(self, notebook):
|
||||
self.frame = tk.Frame(notebook)
|
||||
|
||||
self.m = 0.0 # Masa
|
||||
self.h0 = 0.0 # Altura inicial (0..2m)
|
||||
self.m = 0.0
|
||||
self.h0 = 0.0
|
||||
self.trayectoria = []
|
||||
self.t_final = 0
|
||||
self.proyectil = None
|
||||
self.vel_text = None
|
||||
self.current_canvas_width = 1
|
||||
self.current_canvas_height = 1
|
||||
self.energy_required = 0.0
|
||||
|
||||
# ============ Estructura general ============
|
||||
# TOP
|
||||
self.frame_top = tk.Frame(self.frame)
|
||||
self.frame_top.pack(side="top", fill="x", padx=5, pady=5)
|
||||
self.frame_top = tk.Frame(self.frame); self.frame_top.pack(side="top", fill="x", padx=5, pady=5)
|
||||
self.frame_middle = tk.Frame(self.frame); self.frame_middle.pack(side="top", fill="both", expand=True)
|
||||
self.frame_bottom = tk.Frame(self.frame); self.frame_bottom.pack(side="bottom", fill="both", expand=False)
|
||||
|
||||
# MIDDLE (canvas)
|
||||
self.frame_middle = tk.Frame(self.frame)
|
||||
self.frame_middle.pack(side="top", fill="both", expand=True)
|
||||
self.frame_slider_and_energy = tk.Frame(self.frame_bottom); self.frame_slider_and_energy.pack(side="left", fill="y", padx=5, pady=5)
|
||||
self.frame_slider = tk.Frame(self.frame_slider_and_energy); self.frame_slider.pack(side="top", fill="x", padx=5, pady=5)
|
||||
self.frame_energy = tk.Frame(self.frame_slider_and_energy, bd=2, relief="groove"); self.frame_energy.pack(side="bottom", fill="both", padx=5, pady=5)
|
||||
self.frame_log = tk.Frame(self.frame_bottom); self.frame_log.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# BOTTOM (slider + energía + log)
|
||||
self.frame_bottom = tk.Frame(self.frame)
|
||||
self.frame_bottom.pack(side="bottom", fill="both", expand=False)
|
||||
|
||||
# - Izquierda: slider + energía
|
||||
self.frame_slider_and_energy = tk.Frame(self.frame_bottom)
|
||||
self.frame_slider_and_energy.pack(side="left", fill="y", padx=5, pady=5)
|
||||
|
||||
self.frame_slider = tk.Frame(self.frame_slider_and_energy)
|
||||
self.frame_slider.pack(side="top", fill="x", padx=5, pady=5)
|
||||
|
||||
self.frame_energy = tk.Frame(self.frame_slider_and_energy, bd=2, relief="groove")
|
||||
self.frame_energy.pack(side="bottom", fill="both", expand=False, padx=5, pady=5)
|
||||
|
||||
# - Derecha: log
|
||||
self.frame_log = tk.Frame(self.frame_bottom)
|
||||
self.frame_log.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# ============ Fila superior de widgets ============
|
||||
tk.Label(self.frame_top, text="Parámetro:").grid(row=0, column=0, sticky="w")
|
||||
self.parametro_var = tk.StringVar()
|
||||
self.combo_param = ttk.Combobox(
|
||||
self.frame_top, textvariable=self.parametro_var,
|
||||
values=["Velocidad (m/s)", "Alcance (m)"], width=15
|
||||
)
|
||||
self.combo_param.grid(row=0, column=1, padx=5)
|
||||
self.combo_param.current(0)
|
||||
|
||||
self.entry_v0 = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_v0.grid(row=0, column=2, padx=5)
|
||||
self.combo_param = ttk.Combobox(self.frame_top, textvariable=self.parametro_var, values=["Velocidad (m/s)","Alcance (m)"], width=15)
|
||||
self.combo_param.grid(row=0, column=1, padx=5); self.combo_param.current(0)
|
||||
|
||||
self.entry_v0 = tk.Entry(self.frame_top, width=8); self.entry_v0.grid(row=0, column=2, padx=5)
|
||||
tk.Label(self.frame_top, text="Ángulo (grados):").grid(row=0, column=3, sticky="w")
|
||||
self.entry_alpha = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_alpha.grid(row=0, column=4, padx=5)
|
||||
self.entry_alpha = tk.Entry(self.frame_top, width=8); self.entry_alpha.grid(row=0, column=4, padx=5)
|
||||
|
||||
tk.Label(self.frame_top, text="Masa (kg):").grid(row=0, column=5, sticky="w")
|
||||
self.entry_masa = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_masa.grid(row=0, column=6, padx=5)
|
||||
self.entry_masa = tk.Entry(self.frame_top, width=8); self.entry_masa.grid(row=0, column=6, padx=5)
|
||||
|
||||
self.check_rozamiento = tk.BooleanVar()
|
||||
self.check_rozamiento.set(False)
|
||||
self.chk = tk.Checkbutton(
|
||||
self.frame_top, text="Incluir rozamiento",
|
||||
variable=self.check_rozamiento,
|
||||
command=self.on_toggle_rozamiento
|
||||
)
|
||||
self.check_rozamiento = tk.BooleanVar(); self.check_rozamiento.set(False)
|
||||
self.chk = tk.Checkbutton(self.frame_top, text="Incluir rozamiento", variable=self.check_rozamiento, command=self.on_toggle_rozamiento)
|
||||
self.chk.grid(row=0, column=7, padx=15)
|
||||
|
||||
tk.Label(self.frame_top, text="Coef. (b):").grid(row=0, column=8, sticky="e")
|
||||
self.entry_b = tk.Entry(self.frame_top, width=8, state="disabled")
|
||||
self.entry_b.grid(row=0, column=9, padx=5)
|
||||
self.entry_b = tk.Entry(self.frame_top, width=8, state="disabled"); self.entry_b.grid(row=0, column=9, padx=5)
|
||||
|
||||
# Altura inicial (0..2)
|
||||
tk.Label(self.frame_top, text="Altura (m):").grid(row=0, column=10, sticky="e")
|
||||
self.entry_h0 = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_h0.grid(row=0, column=11, padx=5)
|
||||
self.entry_h0.insert(0, "0.0") # por defecto
|
||||
self.entry_h0 = tk.Entry(self.frame_top, width=8); self.entry_h0.grid(row=0, column=11, padx=5)
|
||||
self.entry_h0.insert(0, "0.0")
|
||||
|
||||
# Botón Calcular
|
||||
self.button_calcular = tk.Button(
|
||||
self.frame_top, text="Calcular",
|
||||
command=self.calcular_trayectoria
|
||||
)
|
||||
self.button_calcular = tk.Button(self.frame_top, text="Calcular", command=self.calcular_trayectoria)
|
||||
self.button_calcular.grid(row=0, column=12, padx=10)
|
||||
|
||||
# Cajitas para pos X e Y
|
||||
tk.Label(self.frame_top, text="Pos X (m):").grid(row=0, column=13, padx=5)
|
||||
self.entry_pos_x = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_pos_x.grid(row=0, column=14, padx=5)
|
||||
self.entry_pos_x = tk.Entry(self.frame_top, width=8); self.entry_pos_x.grid(row=0, column=14, padx=5)
|
||||
|
||||
tk.Label(self.frame_top, text="Pos Y (m):").grid(row=0, column=15, padx=5)
|
||||
self.entry_pos_y = tk.Entry(self.frame_top, width=8)
|
||||
self.entry_pos_y.grid(row=0, column=16, padx=5)
|
||||
self.entry_pos_y = tk.Entry(self.frame_top, width=8); self.entry_pos_y.grid(row=0, column=16, padx=5)
|
||||
|
||||
# ============ Canvas ============
|
||||
self.canvas = tk.Canvas(self.frame_middle, bg="white")
|
||||
self.canvas.pack(fill="both", expand=True)
|
||||
self.canvas.bind("<Configure>", self.on_resize)
|
||||
|
||||
# ============ Slider tiempo ============
|
||||
self.slider_time = tk.Scale(
|
||||
self.frame_slider, from_=0, to=1, resolution=0.01,
|
||||
orient=tk.HORIZONTAL, label="Tiempo (s):",
|
||||
command=self.actualizar_posicion
|
||||
)
|
||||
self.slider_time = tk.Scale(self.frame_slider, from_=0, to=1, resolution=0.01, orient=tk.HORIZONTAL, label="Tiempo (s):", command=self.actualizar_posicion)
|
||||
self.slider_time.pack(fill="x")
|
||||
|
||||
# ============ Cuadro energía ============
|
||||
tk.Label(self.frame_energy, text="Energía mecánica", font=("Arial", 10, "bold")).pack()
|
||||
tk.Label(self.frame_energy, text="Energía mecánica", font=("Arial",10,"bold")).pack()
|
||||
self.label_Ec = tk.Label(self.frame_energy, text="Ec: 0.0 J"); self.label_Ec.pack(anchor="w", padx=5)
|
||||
self.label_Ep = tk.Label(self.frame_energy, text="Ep: 0.0 J"); self.label_Ep.pack(anchor="w", padx=5)
|
||||
self.label_Etot = tk.Label(self.frame_energy, text="E_total: 0.0 J"); self.label_Etot.pack(anchor="w", padx=5)
|
||||
self.label_Esobredim = tk.Label(self.frame_energy, text="E_total x1.15: 0.0 J"); self.label_Esobredim.pack(anchor="w", padx=5)
|
||||
|
||||
self.label_Ec = tk.Label(self.frame_energy, text="Ec: 0.0 J")
|
||||
self.label_Ec.pack(anchor="w", padx=5)
|
||||
|
||||
self.label_Ep = tk.Label(self.frame_energy, text="Ep: 0.0 J")
|
||||
self.label_Ep.pack(anchor="w", padx=5)
|
||||
|
||||
self.label_Etot = tk.Label(self.frame_energy, text="E_total: 0.0 J")
|
||||
self.label_Etot.pack(anchor="w", padx=5)
|
||||
|
||||
self.label_Esobredim = tk.Label(self.frame_energy, text="E_total x1.15: 0.0 J")
|
||||
self.label_Esobredim.pack(anchor="w", padx=5)
|
||||
|
||||
self.energy_required = 0.0
|
||||
|
||||
# Logger
|
||||
self.text_log = tk.Text(self.frame_log, height=2, state="normal")
|
||||
self.text_log.pack(fill="both", expand=True)
|
||||
|
||||
def set_log_message(self, mensaje, bg_color="white", fg_color="black"):
|
||||
def set_log_message(self, msg, bg_color="white", fg_color="black"):
|
||||
self.text_log.config(state="normal", bg=bg_color, fg=fg_color)
|
||||
self.text_log.delete("1.0", tk.END)
|
||||
self.text_log.insert(tk.END, mensaje + "\n")
|
||||
self.text_log.insert(tk.END, msg + "\n")
|
||||
self.text_log.config(state="disabled")
|
||||
|
||||
def set_b_value(self, new_b):
|
||||
@ -157,7 +97,6 @@ class TabSimulator:
|
||||
self.dibujar_trayectoria()
|
||||
|
||||
def calcular_trayectoria(self):
|
||||
# 1) Lee alpha, masa
|
||||
try:
|
||||
alpha_deg = float(self.entry_alpha.get())
|
||||
self.m = float(self.entry_masa.get())
|
||||
@ -165,26 +104,19 @@ class TabSimulator:
|
||||
self.set_log_message("Error: revisa (ángulo, masa).", "red", "white")
|
||||
return
|
||||
|
||||
# 2) Altura
|
||||
try:
|
||||
h0_val = float(self.entry_h0.get())
|
||||
except ValueError:
|
||||
h0_val = 0.0
|
||||
if h0_val < 0: h0_val = 0.0
|
||||
if h0_val > 2: h0_val = 2.0
|
||||
try: h0_val = float(self.entry_h0.get())
|
||||
except ValueError: h0_val = 0.0
|
||||
if h0_val<0: h0_val=0.0
|
||||
if h0_val>2: h0_val=2.0
|
||||
self.h0 = h0_val
|
||||
|
||||
# 3) Validar ángulo
|
||||
if alpha_deg < 0 or alpha_deg > 90:
|
||||
if alpha_deg<0 or alpha_deg>90:
|
||||
self.set_log_message("Introduce un ángulo entre 0 y 90", "red","white")
|
||||
return
|
||||
|
||||
alpha_rad = math.radians(alpha_deg)
|
||||
|
||||
# 4) Leer param (Vel/Alcance)
|
||||
modo = self.parametro_var.get()
|
||||
try:
|
||||
valor = float(self.entry_v0.get())
|
||||
try: valor = float(self.entry_v0.get())
|
||||
except ValueError:
|
||||
self.set_log_message("Error en el valor (vel/alcance).", "red","white")
|
||||
return
|
||||
@ -193,35 +125,26 @@ class TabSimulator:
|
||||
if modo == "Velocidad (m/s)":
|
||||
v0 = valor
|
||||
else:
|
||||
# Modo alcance
|
||||
X_final = valor
|
||||
if not self.check_rozamiento.get():
|
||||
v0_min=0.0
|
||||
v0_max=1000.0
|
||||
v0_min, v0_max = 0.0, 1000.0
|
||||
for _ in range(100):
|
||||
guess=0.5*(v0_min+v0_max)
|
||||
dist_alcanzado = self.simular_dist_sin_rozamiento(guess, alpha_rad, self.h0)
|
||||
if abs(dist_alcanzado - X_final)<0.1:
|
||||
v0=guess
|
||||
break
|
||||
if dist_alcanzado<X_final:
|
||||
v0_min=guess
|
||||
else:
|
||||
v0_max=guess
|
||||
guess = 0.5*(v0_min+v0_max)
|
||||
dist_alc = self.simular_dist_sin_rozamiento(guess, alpha_rad, self.h0)
|
||||
if abs(dist_alc - X_final)<0.1:
|
||||
v0=guess; break
|
||||
if dist_alc<X_final: v0_min=guess
|
||||
else: v0_max=guess
|
||||
v0=guess
|
||||
else:
|
||||
v0_min=0.0
|
||||
v0_max=1000.0
|
||||
v0_min, v0_max = 0.0, 1000.0
|
||||
for _ in range(100):
|
||||
guess=0.5*(v0_min+v0_max)
|
||||
dist_alcanzado = self.simular_dist_con_rozamiento(guess, alpha_rad, self.h0)
|
||||
if abs(dist_alcanzado - X_final)<0.1:
|
||||
v0=guess
|
||||
break
|
||||
if dist_alcanzado<X_final:
|
||||
v0_min=guess
|
||||
else:
|
||||
v0_max=guess
|
||||
guess = 0.5*(v0_min+v0_max)
|
||||
dist_alc = self.simular_dist_con_rozamiento(guess, alpha_rad, self.h0)
|
||||
if abs(dist_alc - X_final)<0.1:
|
||||
v0=guess; break
|
||||
if dist_alc<X_final: v0_min=guess
|
||||
else: v0_max=guess
|
||||
v0=guess
|
||||
|
||||
if v0<=0:
|
||||
@ -230,13 +153,11 @@ class TabSimulator:
|
||||
|
||||
b=0.0
|
||||
if self.check_rozamiento.get():
|
||||
try:
|
||||
b = float(self.entry_b.get())
|
||||
try: b = float(self.entry_b.get())
|
||||
except:
|
||||
self.set_log_message("Coef rozamiento no válido", "red","white")
|
||||
return
|
||||
|
||||
# 5) Integramos la trayectoria
|
||||
self.canvas.delete("all")
|
||||
self.trayectoria.clear()
|
||||
self.proyectil=None
|
||||
@ -246,40 +167,28 @@ class TabSimulator:
|
||||
t=0.0
|
||||
x=0.0
|
||||
y=self.h0
|
||||
vx = v0*math.cos(alpha_rad)
|
||||
vy = v0*math.sin(alpha_rad)
|
||||
vx=v0*math.cos(alpha_rad)
|
||||
vy=v0*math.sin(alpha_rad)
|
||||
|
||||
if not self.check_rozamiento.get():
|
||||
# sin rozamiento
|
||||
while True:
|
||||
self.trayectoria.append((t,x,y,vx,vy))
|
||||
vy_new = vy-9.8*dt
|
||||
x_new = x+vx*dt
|
||||
y_new = y+vy*dt
|
||||
t+=dt
|
||||
vx= vx
|
||||
vy= vy_new
|
||||
x= x_new
|
||||
y= y_new
|
||||
if y<=0 and t>0.01:
|
||||
break
|
||||
vy_new = vy - 9.8*dt
|
||||
x_new = x + vx*dt
|
||||
y_new = y + vy*dt
|
||||
t+=dt; vx= vx; vy= vy_new; x= x_new; y= y_new
|
||||
if y<=0 and t>0.01: break
|
||||
else:
|
||||
# con rozamiento lineal
|
||||
while True:
|
||||
self.trayectoria.append((t,x,y,vx,vy))
|
||||
ax=-(b/self.m)*vx
|
||||
ay=-9.8-(b/self.m)*vy
|
||||
vx_new = vx+ax*dt
|
||||
vy_new = vy+ay*dt
|
||||
x_new = x+vx_new*dt
|
||||
y_new = y+vy_new*dt
|
||||
t+=dt
|
||||
vx= vx_new
|
||||
vy= vy_new
|
||||
x= x_new
|
||||
y= y_new
|
||||
if y<=0 and t>0.01:
|
||||
break
|
||||
ax = -(b/self.m)*vx
|
||||
ay = -9.8 - (b/self.m)*vy
|
||||
vx_new = vx + ax*dt
|
||||
vy_new = vy + ay*dt
|
||||
x_new = x + vx_new*dt
|
||||
y_new = y + vy_new*dt
|
||||
t+=dt; vx= vx_new; vy= vy_new; x= x_new; y= y_new
|
||||
if y<=0 and t>0.01: break
|
||||
|
||||
self.t_final = t
|
||||
self.slider_time.config(from_=0, to=self.t_final)
|
||||
@ -296,16 +205,11 @@ class TabSimulator:
|
||||
vy=v0_guess*math.sin(alpha_rad)
|
||||
t=0.0
|
||||
while True:
|
||||
vy_new= vy-9.8*dt
|
||||
x_new= x+ vx*dt
|
||||
y_new= y+ vy*dt
|
||||
t+=dt
|
||||
vx= vx
|
||||
vy= vy_new
|
||||
x= x_new
|
||||
y= y_new
|
||||
if y<=0 and t>0.01:
|
||||
break
|
||||
vy_new = vy - 9.8*dt
|
||||
x_new = x + vx*dt
|
||||
y_new = y + vy*dt
|
||||
t+=dt; vx= vx; vy= vy_new; x= x_new; y= y_new
|
||||
if y<=0 and t>0.01: break
|
||||
return x
|
||||
|
||||
def simular_dist_con_rozamiento(self, v0_guess, alpha_rad, h0):
|
||||
@ -315,10 +219,8 @@ class TabSimulator:
|
||||
vx=v0_guess*math.cos(alpha_rad)
|
||||
vy=v0_guess*math.sin(alpha_rad)
|
||||
t=0.0
|
||||
try:
|
||||
b=float(self.entry_b.get())
|
||||
except:
|
||||
b=0.1
|
||||
try: b=float(self.entry_b.get())
|
||||
except: b=0.1
|
||||
while True:
|
||||
ax=-(b/self.m)*vx
|
||||
ay=-9.8-(b/self.m)*vy
|
||||
@ -326,33 +228,22 @@ class TabSimulator:
|
||||
vy_new= vy+ay*dt
|
||||
x_new= x+vx_new*dt
|
||||
y_new= y+vy_new*dt
|
||||
t+=dt
|
||||
vx= vx_new
|
||||
vy= vy_new
|
||||
x= x_new
|
||||
y= y_new
|
||||
if y<=0 and t>0.01:
|
||||
break
|
||||
t+=dt; vx= vx_new; vy= vy_new; x= x_new; y= y_new
|
||||
if y<=0 and t>0.01: break
|
||||
return x
|
||||
|
||||
def dibujar_trayectoria(self):
|
||||
if not self.trayectoria:
|
||||
return
|
||||
|
||||
if not self.trayectoria: return
|
||||
min_x = min(pt[1] for pt in self.trayectoria)
|
||||
max_x = max(pt[1] for pt in self.trayectoria)
|
||||
min_y = min(pt[2] for pt in self.trayectoria)
|
||||
max_y = max(pt[2] for pt in self.trayectoria)
|
||||
|
||||
rx = max_x - min_x
|
||||
ry = max_y - min_y
|
||||
rx, ry = max_x - min_x, max_y - min_y
|
||||
if rx<1e-9: rx=1.0
|
||||
if ry<1e-9: ry=1.0
|
||||
|
||||
w = self.current_canvas_width
|
||||
h = self.current_canvas_height
|
||||
w, h = self.current_canvas_width, self.current_canvas_height
|
||||
margen=20
|
||||
scale = min((w-2*margen)/rx, (h-2*margen)/ry)
|
||||
scale = min((w-2*margen)/rx,(h-2*margen)/ry)
|
||||
|
||||
self.canvas.delete("all")
|
||||
pts=[]
|
||||
@ -360,29 +251,25 @@ class TabSimulator:
|
||||
sx = margen + (xx - min_x)*scale
|
||||
sy = (h - margen) - (yy - min_y)*scale
|
||||
pts.append((sx, sy))
|
||||
|
||||
for i in range(len(pts)-1):
|
||||
x1,y1=pts[i]
|
||||
x2,y2=pts[i+1]
|
||||
self.canvas.create_line(x1,y1,x2,y2,fill="blue")
|
||||
|
||||
if pts:
|
||||
x0,y0 = pts[0]
|
||||
x0,y0=pts[0]
|
||||
r=5
|
||||
self.proyectil = self.canvas.create_oval(x0-r,y0-r, x0+r,y0+r, fill="red")
|
||||
self.proyectil = self.canvas.create_oval(x0-r,y0-r,x0+r,y0+r, fill="red")
|
||||
self.vel_text = self.canvas.create_text(x0+15,y0, text="v=0.00 m/s", fill="black")
|
||||
|
||||
self.scale=scale
|
||||
self.margen=margen
|
||||
self.min_x=min_x
|
||||
self.min_y=min_y
|
||||
|
||||
self.actualizar_energia(0)
|
||||
|
||||
def actualizar_posicion(self, val):
|
||||
if not self.trayectoria or not self.proyectil:
|
||||
return
|
||||
|
||||
if not self.trayectoria or not self.proyectil: return
|
||||
t_slider = float(val)
|
||||
if t_slider<=0:
|
||||
x_real=self.trayectoria[0][1]
|
||||
@ -397,74 +284,54 @@ class TabSimulator:
|
||||
else:
|
||||
tiempos=[p[0] for p in self.trayectoria]
|
||||
idx=0
|
||||
while idx<len(self.trayectoria)-1 and tiempos[idx+1]<t_slider:
|
||||
idx+=1
|
||||
while idx<len(self.trayectoria)-1 and tiempos[idx+1]<t_slider: idx+=1
|
||||
x_real=self.trayectoria[idx][1]
|
||||
y_real=self.trayectoria[idx][2]
|
||||
vx_real=self.trayectoria[idx][3]
|
||||
vy_real=self.trayectoria[idx][4]
|
||||
|
||||
# Pos en canvas
|
||||
w = self.current_canvas_width
|
||||
h = self.current_canvas_height
|
||||
w, h = self.current_canvas_width, self.current_canvas_height
|
||||
sx = self.margen + (x_real - self.min_x)*self.scale
|
||||
sy = (h - self.margen) - (y_real - self.min_y)*self.scale
|
||||
|
||||
coords_act = self.canvas.coords(self.proyectil)
|
||||
x_c=(coords_act[0]+coords_act[2])/2
|
||||
y_c=(coords_act[1]+coords_act[3])/2
|
||||
dx=sx-x_c
|
||||
dy=sy-y_c
|
||||
x_c = (coords_act[0]+coords_act[2])/2
|
||||
y_c = (coords_act[1]+coords_act[3])/2
|
||||
dx, dy = sx - x_c, sy - y_c
|
||||
self.canvas.move(self.proyectil, dx, dy)
|
||||
|
||||
vel = math.sqrt(vx_real**2 + vy_real**2)
|
||||
if self.vel_text:
|
||||
coords_text=self.canvas.coords(self.vel_text)
|
||||
xt=coords_text[0]
|
||||
yt=coords_text[1]
|
||||
dx_t=sx-xt
|
||||
dy_t=(sy+15)-yt
|
||||
coords_text = self.canvas.coords(self.vel_text)
|
||||
xt, yt = coords_text[0], coords_text[1]
|
||||
dx_t, dy_t = sx - xt, (sy+15) - yt
|
||||
self.canvas.move(self.vel_text, dx_t, dy_t)
|
||||
self.canvas.itemconfig(self.vel_text, text=f"v={vel:.2f} m/s")
|
||||
|
||||
# Pos X e Y (en metros, no en pixeles)
|
||||
self.entry_pos_x.delete(0, tk.END)
|
||||
self.entry_pos_x.insert(0, f"{x_real:.2f}")
|
||||
|
||||
self.entry_pos_y.delete(0, tk.END)
|
||||
self.entry_pos_y.insert(0, f"{y_real:.2f}")
|
||||
|
||||
self.actualizar_energia(t_slider)
|
||||
|
||||
def actualizar_energia(self, t_slider):
|
||||
if not self.trayectoria:
|
||||
return
|
||||
|
||||
if not self.trayectoria: return
|
||||
if t_slider<=0:
|
||||
vx_=self.trayectoria[0][3]
|
||||
vy_=self.trayectoria[0][4]
|
||||
x_=self.trayectoria[0][1]
|
||||
y_=self.trayectoria[0][2]
|
||||
vx_, vy_ = self.trayectoria[0][3], self.trayectoria[0][4]
|
||||
x_, y_ = self.trayectoria[0][1], self.trayectoria[0][2]
|
||||
elif t_slider>=self.t_final:
|
||||
vx_=self.trayectoria[-1][3]
|
||||
vy_=self.trayectoria[-1][4]
|
||||
x_=self.trayectoria[-1][1]
|
||||
y_=self.trayectoria[-1][2]
|
||||
vx_, vy_ = self.trayectoria[-1][3], self.trayectoria[-1][4]
|
||||
x_, y_ = self.trayectoria[-1][1], self.trayectoria[-1][2]
|
||||
else:
|
||||
tiempos=[p[0] for p in self.trayectoria]
|
||||
idx=0
|
||||
while idx<len(self.trayectoria)-1 and tiempos[idx+1]<t_slider:
|
||||
idx+=1
|
||||
vx_=self.trayectoria[idx][3]
|
||||
vy_=self.trayectoria[idx][4]
|
||||
x_=self.trayectoria[idx][1]
|
||||
y_=self.trayectoria[idx][2]
|
||||
tiempos = [p[0] for p in self.trayectoria]
|
||||
idx = 0
|
||||
while idx<len(self.trayectoria)-1 and tiempos[idx+1]<t_slider: idx+=1
|
||||
vx_, vy_ = self.trayectoria[idx][3], self.trayectoria[idx][4]
|
||||
x_, y_ = self.trayectoria[idx][1], self.trayectoria[idx][2]
|
||||
|
||||
Ec = 0.5*self.m*(vx_**2 + vy_**2)
|
||||
Ep=0.0
|
||||
if y_>0:
|
||||
Ep=self.m*9.8*y_
|
||||
E_tot=Ec+Ep
|
||||
Ep = self.m*9.8*y_ if y_>0 else 0.0
|
||||
E_tot = Ec + Ep
|
||||
self.energy_required = E_tot
|
||||
E_sobredim=1.15*E_tot
|
||||
|
||||
@ -472,6 +339,6 @@ class TabSimulator:
|
||||
self.label_Ep.config(text=f"Ep: {Ep:.2f} J")
|
||||
self.label_Etot.config(text=f"E_total: {E_tot:.2f} J")
|
||||
self.label_Esobredim.config(text=f"E_total x1.15: {E_sobredim:.2f} J")
|
||||
|
||||
|
||||
def get_energy_required(self):
|
||||
return self.energy_required
|
||||
return self.energy_required
|
||||
|
Loading…
Reference in New Issue
Block a user