<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jorge Martinez</title><link>https://jorgemartinez.space/</link><description>Recent content on Jorge Martinez</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 02 Nov 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://jorgemartinez.space/index.xml" rel="self" type="application/rss+xml"/><item><title>Soviet potato watercolor</title><link>https://jorgemartinez.space/posts/art/soviet-potato-watercolor/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/art/soviet-potato-watercolor/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>Update: 2025-11-23&lt;/strong>&lt;/p>
&lt;p>This watercolor has become very popular across my friends and relatives. Some
of them already offered me money. This artwork is not for sale yet.&lt;/p>&lt;/blockquote>
&lt;p>This is a replica I made of the artwork of an unknown author&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>. For good or
for bad, this is my best piece so far. I hope you appreciate the accuracy and
realism of the potato.&lt;/p>
&lt;p>&lt;img src="./img/soviet-potato-watercolor.jpg" alt="A potato wearing a traditional furry soviet hat.">&lt;/p></description></item><item><title>About AI gurus and their projects</title><link>https://jorgemartinez.space/posts/journal/about-ai-gurus-and-their-projects/</link><pubDate>Mon, 08 Sep 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/about-ai-gurus-and-their-projects/</guid><description>&lt;p>Since the release of ChatGPT, the hype on artificial intelligence (AI) has sky
rocketed. It is quite shocking to see how most developers and engineers are
now, all of a sudden, experts in this subject.&lt;/p>
&lt;p>What feels even more dangerous is to see companies approving every single
project that has AI on it. &lt;strong>These projects usually lack&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Leaders with the right technical background&lt;/li>
&lt;li>Scientific references and foundations&lt;/li>
&lt;li>Benchmarking&lt;/li>
&lt;li>Economic feasibility study&lt;/li>
&lt;/ul>
&lt;p>This is the best receipe for a failing project.&lt;/p></description></item><item><title>EuroSciPy 2025</title><link>https://jorgemartinez.space/posts/journal/euroscipy-2025/</link><pubDate>Mon, 25 Aug 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/euroscipy-2025/</guid><description>&lt;p>&lt;a href="https://euroscipy.org/">EuroSciPy 2025&lt;/a> was hosted in &lt;a href="https://www.krakow.pl/">Kraków, Poland&lt;/a>. Despite having attended
&lt;a href="https://www.scipy2022.scipy.org/">SciPyConf 2022&lt;/a>, &lt;a href="https://www.scipy2023.scipy.org/">SciPyConf 2023&lt;/a>, and &lt;a href="https://www.scipy2024.scipy.org/">SciPyConf 2024&lt;/a>, it was my first
time in this European event.&lt;/p>
&lt;p>This post covers my impressions and opinions about the event.&lt;/p>
&lt;h2 id="about-kraków">About Kraków&lt;/h2>
&lt;p>Kraków is an outstanding city. Despite being in the European Union, Poland
has its own currency, the &lt;a href="https://en.wikipedia.org/wiki/Polish_z%C5%82oty">Złoty&lt;/a>. The European Union imposes some minimum
requirements for its members and, as of today, Poland does not meet these. Read
&lt;a href="https://en.wikipedia.org/wiki/Poland_and_the_euro">Poland and the Euro&lt;/a> for more information about the topic.&lt;/p></description></item><item><title>Loup, mi mejor amigo</title><link>https://jorgemartinez.space/posts/journal/loup-mi-mejor-amigo-descansa-en-paz/</link><pubDate>Thu, 20 Feb 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/loup-mi-mejor-amigo-descansa-en-paz/</guid><description>&lt;blockquote>
&lt;p>El 19 de febrero de 2025, con una enorme tristeza, me despedí de Loup. Él fue
mi mejor amigo. Siempre estuvo. Nunca me falló. Pese a sus problemas de
salud, no me rendí. María tampoco lo hizo. Al fin y al cabo, Loup nos
presentó. Descansa en paz, amigo. Estoy seguro de que volveremos a vernos.&lt;/p>&lt;/blockquote>
&lt;p>Durante los últimos siete meses, Loup cambió su comportamiento sin motivo
aparente. Se volvió más temeroso, desconfiado y agresivo. Quienes lo conocíamos
sabíamos que algo iba mal.&lt;/p></description></item><item><title>Learning modern C</title><link>https://jorgemartinez.space/posts/programming/learning-modern-c-introduction/</link><pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/programming/learning-modern-c-introduction/</guid><description>&lt;p>For many years, I have tried to be as fluent as possible with the C programming
language. Despite being able to code simple routines, as soon as I face a
complex task, I fall back to Python.&lt;/p>
&lt;p>However, this time, I am taking learning C seriously. In fact, it is not about
learning C but also the latest good practices in the language.&lt;/p>
&lt;h2 id="why-learning-c">Why learning C?&lt;/h2>
&lt;p>I know, I know&amp;hellip;. Why learning C instead of a modern language like Zig, Go or Rust?
From my point of view, these are the benefits of learning C:&lt;/p></description></item><item><title>Kernel selection in Quarto</title><link>https://jorgemartinez.space/posts/tutorials/kernel-selection-in-quartotitle-of-the-post/</link><pubDate>Thu, 30 Jan 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/kernel-selection-in-quartotitle-of-the-post/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>Abstract&lt;/strong>&lt;/p>
&lt;p>This post explains how to install a custom Jupyter kernel so it can be used
later in Quarto. The final result is rendere by Hugo, ensuring up-to-date
content in your website.&lt;/p>&lt;/blockquote>
&lt;p>While reading about Quarto, I found that one can specify the kernel to be used,
see the &lt;a href="https://quarto.org/docs/computations/python.html#kernel-selection">kernel selection&lt;/a> section. This is possible thanks to &lt;a href="https://nbclient.readthedocs.io/en/latest/">nbclient&lt;/a>, which
executes the Jupyter notebooks.&lt;/p>
&lt;p>Therefore, I thought it would be interesting to explore other &lt;a href="https://gist.github.com/chronitis/682c4e0d9f663e85e3d87e97cd7d1624">available
kernels&lt;/a> in the Jupyter ecosystem. In particular, I wanted to explore the usage
of the &lt;a href="https://github.com/brendan-rius/jupyter-c-kernel">Jupyter C kernel&lt;/a>.&lt;/p></description></item><item><title>Los problemas con Loup</title><link>https://jorgemartinez.space/posts/journal/los-problemas-con-loup/</link><pubDate>Sat, 25 Jan 2025 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/los-problemas-con-loup/</guid><description>&lt;p>Escribo este post con mucha tristeza. Mi compañero y buen amigo Loup no se
encuentra bien&amp;hellip;&lt;/p>
&lt;p>Todo comenzó a finales de Octubre de 2024 cuando Loup tuvo una reacción muy
agresiva hacia mí. Loup jamás me había amenazado, así que lo que pensé es que
algo debía de dolerle mucho.&lt;/p>
&lt;p>Al día siguiente fuimos al veterianrio. Por precaución, le pusimos un bozal.
Loup no se quejó en ningún momento pero estaba muy tenso. El veterinario no
encontró nada concluyente.&lt;/p></description></item><item><title>Plotly support</title><link>https://jorgemartinez.space/posts/tutorials/plotly-support/</link><pubDate>Tue, 26 Nov 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/plotly-support/</guid><description>&lt;p>This website supports now &lt;a href="https://plotly.com/">plotly&lt;/a>. By using &lt;a href="https://quarto.org/">Quarto&lt;/a> for rendering the content
of a notebook, you can include plotly figures in your posts. The figures must
be saved as a JSON file. This file is then passed to the following shortcode:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-html" data-lang="html">&lt;span style="display:flex;">&lt;span>{{ $json := .Get &amp;#34;json&amp;#34; }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{{ $height := .Get &amp;#34;height&amp;#34; | default &amp;#34;200px&amp;#34; }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{{ if $json }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#062873;font-weight:bold">div&lt;/span> &lt;span style="color:#4070a0">id&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;{{ $json | htmlEscape }}&amp;#34;&lt;/span> &lt;span style="color:#4070a0">class&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;plotly&amp;#34;&lt;/span> &lt;span style="color:#4070a0">style&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;height:{{ $height | htmlEscape }}&amp;#34;&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#062873;font-weight:bold">div&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#062873;font-weight:bold">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(&lt;span style="color:#007020;font-weight:bold">function&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">var&lt;/span> plotId &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;{{ $json | htmlEscape }}&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">var&lt;/span> plotHeight &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;{{ $height | htmlEscape }}&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Plotly.d3.json(plotId, &lt;span style="color:#007020;font-weight:bold">function&lt;/span>(err, fig) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> (err) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> console.error(&lt;span style="color:#4070a0">&amp;#34;Error loading JSON for Plotly:&amp;#34;&lt;/span>, err);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Plotly.plot(plotId, fig.data, fig.layout, { responsive&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">true&lt;/span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>})();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#062873;font-weight:bold">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{{ else }}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#062873;font-weight:bold">p&lt;/span>&amp;gt;Error: No JSON URL provided for Plotly visualization.&amp;lt;/&lt;span style="color:#062873;font-weight:bold">p&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which can be used as follows:&lt;/p></description></item><item><title>Personal web components</title><link>https://jorgemartinez.space/posts/journal/personal-web-components/</link><pubDate>Sun, 20 Oct 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/personal-web-components/</guid><description>&lt;p>More than a year ago, I finalized the style of my personal website. It took me
several iterations. I had to re-learn HTML, CSS, and read lots of Hugo
docuemntation. However, the effort was worth it. Nowadays, it is very easy for
me to maintain my website.&lt;/p>
&lt;p>However, it contained some CSS bugs and the rendering in small screen media
could be improved.&lt;/p>
&lt;p>Therefore, I made the effort to fix these issues and truly consolidate the CSS
style. This post serves as a gallery of the different components that this
website ships with. Enjoy!&lt;/p></description></item><item><title>Introduction to evolutionary algorithms</title><link>https://jorgemartinez.space/posts/evolutionary-algorithms/introduction-to-evolutionary-algorithms/</link><pubDate>Wed, 11 Sep 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/evolutionary-algorithms/introduction-to-evolutionary-algorithms/</guid><description>&lt;p>&lt;strong>Evolutionary Algorithms&lt;/strong> (EA) are computer programs whose goal is to find the
(near-)optimal solution to a problem among a set of posible solutions by
mimicking natural selection.&lt;/p>
&lt;p>&lt;img src="https://cdn.pixabay.com/photo/2013/07/12/14/59/dna-149160_1280.png" alt="">&lt;/p>
&lt;p>Let&amp;rsquo;s dive a bit into previous definition!&lt;/p>
&lt;h2 id="computer-programs">Computer programs&lt;/h2>
&lt;p>Evolutionary algorithms are software routines. Therefore, they need to be
coded as any computer program.&lt;/p>
&lt;p>The reason why we make such distinction is because evolutionary algorithms are
a set of a broader subject named &lt;strong>Evolutionary Computation&lt;/strong> (EC), which is a
field of &lt;strong>Artificial intelligence&lt;/strong> (AI).&lt;/p></description></item><item><title>Computer Graphics with OpenGL</title><link>https://jorgemartinez.space/posts/tutorials/computer-graphics-with-opengl-part-i/</link><pubDate>Sun, 16 Jun 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/computer-graphics-with-opengl-part-i/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>Abstract&lt;/strong>&lt;/p>
&lt;p>In this series, we will dive into the fundamentals of computer graphics using
OpenGL. In this post, we explore the origin of the term. Then, the history of
OpenGL is revisited together with some fundamental ideas. Next, other graphics
APIs are presented. Finally, the software and hardware requirements for this
tutorial are listed.&lt;/p>&lt;/blockquote>
&lt;h2 id="computer-graphics">Computer graphics&lt;/h2>
&lt;p>The term &lt;strong>computer graphics&lt;/strong> was originally coined in 1960 by &lt;a href="https://en.wikipedia.org/wiki/William_Fetter">William A. Fetter&lt;/a> to
describe his work at &lt;a href="https://www.boeing.com/">The Boeing Company&lt;/a>.&lt;/p></description></item><item><title>Painting Tux on a beer jar</title><link>https://jorgemartinez.space/posts/journal/painting-tux-on-a-beer-jar/</link><pubDate>Fri, 31 May 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/painting-tux-on-a-beer-jar/</guid><description>&lt;p>My girlfriend thought it would be a good idea to do a pottery workshop. The idea
was to do something different from coding. Here is what I made:&lt;/p>
&lt;p>&lt;img src="img/beer_jar.jpg" alt="Tux on a beer jar">&lt;/p>
&lt;p>Being honest, it was &lt;a href="https://ponzeladesign.com/">Maria&lt;/a> the one who drew
&lt;a href="https://en.wikipedia.org/wiki/Tux_(mascot)">Tux&lt;/a> on the beer jar. I just had to
paint it and write the command at the bottom, naturally.&lt;/p></description></item><item><title>Script for converting PDF pages to images</title><link>https://jorgemartinez.space/posts/tutorials/script-for-converting-pdf-pages-to-images/</link><pubDate>Fri, 24 May 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/script-for-converting-pdf-pages-to-images/</guid><description>&lt;p>Recently, I created some beautiful images from a PDf document, see the image
below:&lt;/p>
&lt;p>&lt;img src="https://github.com/jorgepiloto/tfm/raw/main/fig/static/thumbnails.png" alt="PDF pages as images">&lt;/p>
&lt;p>Previous images were generated by taking a manual snapshot of every page and applying
them some shadow. This is not ideal. Every snapshot may have a different size
since I am manually cropping a region.&lt;/p>
&lt;p>Thus, I wondered if there was any program out there. This program should be able
to accept the path to a PDF file, the page number and the output file path where
the generated image can be saved.&lt;/p></description></item><item><title>My Final Masters Project</title><link>https://jorgemartinez.space/posts/journal/my-final-masters-project/</link><pubDate>Wed, 01 May 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/my-final-masters-project/</guid><description>&lt;p>Despite pursuing an MSc. in Astronomy and Astrophysics, I opted to delve into
astrodynamics and orbital mechanics for my final project. Fortunately, two
professors approached me with a fascinating topic involving targeting
interstellar interlopers. Here&amp;rsquo;s the gist of my project:&lt;/p>
&lt;p>&lt;a href="https://github.com/jorgepiloto/tfm/raw/main/report.pdf">&lt;em>Interstellar Interceptors. Mission design for rendezvous with objects in
hyperbolic orbits.&lt;/em>&lt;/a>&lt;/p>
&lt;div align="center">
 &lt;img style="display: inline;" width="200px" src="https://github.com/jorgepiloto/tfm/blob/main/fig/static/shots/2.png?raw=true" />
 &lt;img style="display: inline;" width="200px" src="https://github.com/jorgepiloto/tfm/blob/main/fig/static/shots/3.png?raw=true" />
 &lt;img style="display: inline;" width="200px" src="https://github.com/jorgepiloto/tfm/blob/main/fig/static/shots/4.png?raw=true" />
&lt;/div>
&lt;p>It has been a nightmare to working and stuying at the same time. I&amp;rsquo;ve missed out
on so much: Christmas, birthdays, weekends, you name it! Lacking time to
properly rest and recover during the weekend was very stressful.&lt;/p></description></item><item><title>Bases de Datos - Actividad Guiada II</title><link>https://jorgemartinez.space/posts/astronomy/bases-de-datos-actividad-guiada-ii/</link><pubDate>Sun, 28 Jan 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/bases-de-datos-actividad-guiada-ii/</guid><description>&lt;h2 id="introducción">Introducción&lt;/h2>
&lt;p>El informe recoge los objetivos, método y resultados de la Actividad Guiada II para la asignatura de Bases de Datos. Esta actividad se centra en la indetificación de enanas marrones mediante la correlación de diferentes catálogos astronómicos. Se han utilizado los programas Aladin y Topcat para el estudio. En relación a los catálogos, se han utilizado 2MASS y SDSS.&lt;/p>
&lt;h2 id="método">Método&lt;/h2>
&lt;p>El objetivo de la actividad es la identificación de enanas marrones mediante la comparación cruzada de catálogos astronómicos.&lt;/p></description></item><item><title>Bases de Datos - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/bases-de-datos-actividad-guiada-i/</link><pubDate>Mon, 22 Jan 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/bases-de-datos-actividad-guiada-i/</guid><description>&lt;h2 id="introducción-y-objetivo">Introducción y objetivo&lt;/h2>
&lt;p>Esta actividad aborda el uso de la herramienta Aladin, un atlas interactivo del cielo desarrollado por el Centro de Datos Astronómicos de Estrasburgo (CDS). El objetivo principal es familiarizarse con las funciones básicas de Aladin y aplicarlas al análisis de la galaxia NGC 2997.&lt;/p>
&lt;h2 id="método">Método&lt;/h2>
&lt;p>Para analizar la galaxia NGC 2997 se han establecido los siguientes pasos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Búsqueda y descarga de imágenes:&lt;/strong> se ha utilizado la base de datos NED para localizar y descargar imágenes de NGC 2997 en diversas bandas desde el telescopio Danés.&lt;/li>
&lt;li>&lt;strong>Carga de imágenes en Aladin:&lt;/strong> se ha llevado a cabo la carga de las imágenes descargadas en la herramienta Aladin.&lt;/li>
&lt;li>&lt;strong>Calibración astrométrica:&lt;/strong> se ha realizado la calibración astrométrica utilizando estrellas de referencia.&lt;/li>
&lt;li>&lt;strong>Descripción morfológica global:&lt;/strong> se ha analizado la morfología de NGC 2997 en diferentes filtros, destacando características específicas como el bulbo, el disco y las bandas de polvo.&lt;/li>
&lt;li>&lt;strong>Creación de imágenes RGB:&lt;/strong> se han combinado diferentes filtros para generar imágenes compuestas en color verdadero. Esto resalta propiedades globales de la galaxia y de su estructura.&lt;/li>
&lt;li>&lt;strong>Superposición de observaciones de radio:&lt;/strong> se ha integrado datos de radio a 1.4 GHz mediante la superposición de un mapa de contornos en una imagen compuesta RGB.&lt;/li>
&lt;/ol>
&lt;p>Estos pasos combinados brindan una metodología integral para analizar NGC 2997, desde la obtención de datos hasta la visualización y la interpretación detallada de las distintas características de la galaxia.&lt;/p></description></item><item><title>Running Threejs in my website</title><link>https://jorgemartinez.space/posts/tutorials/running-threejs-in-my-website/</link><pubDate>Sat, 13 Jan 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/running-threejs-in-my-website/</guid><description>&lt;p>In this tutorial, I am going to show you how to use
&lt;a href="https://threejs.org/">Three.js&lt;/a> in your &lt;a href="https://gohugo.io/">Hugo&lt;/a> website. By
the end of this tutorial, you should be able to get interactive scenes like this
one:&lt;/p>







&lt;div id="threejs-container-f117" class="thumbnail">&lt;/div>


&lt;script type="importmap">
 {
 "imports": {
 "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
 "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
 }
 }
&lt;/script>


&lt;script type="module" src="viewer.js">&lt;/script>

&lt;p>Use your mouse rotate around previous scene. Use the scrolling wheel to zoom in
or out.&lt;/p>
&lt;h2 id="main-objective">Main objective&lt;/h2>
&lt;p>The main objectives are:&lt;/p>
&lt;ul>
&lt;li>Using Three.js without using &lt;a href="https://www.npmjs.com/">npm package registry&lt;/a>&lt;/li>
&lt;li>Easily include a JS file inside a Markdown post&lt;/li>
&lt;/ul>
&lt;h2 id="avoiding-to-install-threejs">Avoiding to install Three.js&lt;/h2>
&lt;p>It is possible to install Three.js by using a &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network">Content Delivery Network
(CDN)&lt;/a>. These are
servers hosting different files. In this case, these files are the ones for
Three.js. From the &lt;a href="https://threejs.org/docs/#manual/en/introduction/Installation">official installation
guidelines&lt;/a>, it
looks like &lt;a href="https://unpkg.com/">https://unpkg.com/&lt;/a> is the desired CDN.&lt;/p></description></item><item><title>Cosmología - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/cosmologa-actividad-guiada-i/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/cosmologa-actividad-guiada-i/</guid><description>&lt;h2 id="introducción">Introducción&lt;/h2>
&lt;p>El objetivo del presente trabajo es investigar la relación existente entre el corrimiento al rojo y la distancia de un cuerpo en el Universo. Para ello, se han calculado los datos medios fotométricos de diferentes cúmulos de galaxias. Obtenidas las magnitudes medias en las principales bandas del espectro electromagnético, se ha representado su valor frente al corrimiento al rojo para cada cúmulo de galaxias.&lt;/p>
&lt;p>Todos los datos han sido procesados con Python, empleando herramientas open-source. El código se adjunta junto con las explicaciones para asegurar una reproducibilidad de los resultados. Además, reduce el número de errores humanos y permite escalar de forma rápida el número de resultados.&lt;/p></description></item><item><title>Cosmología - Actividad Guiada II</title><link>https://jorgemartinez.space/posts/astronomy/cosmologa-actividad-guiada-ii/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/cosmologa-actividad-guiada-ii/</guid><description>&lt;h2 id="introducción">Introducción&lt;/h2>
&lt;p>En el presente trabajo se estudia la relación entre los diferentes parámetros cosmológicos y su influencia en las ecuaciones cosmológicas. Mediante la variación de sus valores, es posible obtener relaciones y predecir de qué formas se comportará un modelo de universo.&lt;/p>
&lt;h2 id="metodología">Metodología&lt;/h2>
&lt;p>Para la resolución de los distintos apartados se ha utilizado las ecuaciones definidas en la &lt;a href="https://www.astro.ucla.edu/~wright/CosmoCalc.html">web de CosmoCalc&lt;/a>, ver &lt;a href="https://www.astro.ucla.edu/~wright/CosmoCalc.html">https://www.astro.ucla.edu/~wright/CosmoCalc.html&lt;/a>.&lt;/p>
&lt;p>No obstante, para presentar de forma clara la solución a los ejercicios, se ha actualizado el script de Python proporcionado por James Schombert. Dicho script puede encontrarse en la siguiente dirección &lt;a href="https://www.astro.ucla.edu/~wright/CC.python">https://www.astro.ucla.edu/~wright/CC.python&lt;/a>. Nótese que el código utiliza Python 2. Esta version ha sido abandonada en favor de Python 3.&lt;/p></description></item><item><title>Astronomía Óptica e Infrarroja - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/astronoma-ptica-e-infrarroja-actividad-guiada-i/</link><pubDate>Fri, 22 Dec 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/astronoma-ptica-e-infrarroja-actividad-guiada-i/</guid><description>&lt;h2 id="análisis-del-archivo-jhc001fits">Análisis del archivo jhc001.fits&lt;/h2>
&lt;img src="index_files/figure-markdown_strict/cell-3-output-1.png" width="1563" height="449" />
&lt;p>&lt;strong>Tipo espectral: O&lt;/strong>&lt;/p>
&lt;p>Las líneas de Balmer del hidrógeno (H) y líneas de helio (He-I y He-II) están presentes. La presencia de líneas de helio neutro (He-I) indica una temperatura de fotosfera alta. Esto podría indicar que se trata de una estrella de clase A o B. Dado que también se observan líneas de helio ionizado (He-II), esto sugiere temperaturas aún más altas, que son típicas de estrellas de tipo espectral O.&lt;/p></description></item><item><title>Asteroides a l'ast con ASTERIA</title><link>https://jorgemartinez.space/posts/astronomy/asteroides-a-last-con-asteria/</link><pubDate>Sat, 25 Nov 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/asteroides-a-last-con-asteria/</guid><description>&lt;p>¿Sabías que algunos asteroides cercanos a la Tierra pueden desviarse de su
órbita debido a que rotan y se calientan? ¡Como un pollo a l&amp;rsquo;ast! Modelar este
tipo de asteroides resultaba complicado hasta ahora. Gracias al software
&lt;a href="https://github.com/Fenu24/D-NEAs">ASTERIA&lt;/a>, es posible simular y analizar de
forma rápida este tipo de asteroides.&lt;/p>
&lt;p>&lt;img src="img/banner.png" alt="">&lt;/p>
&lt;h2 id="asteroides-a-last">¿Asteroides a l&amp;rsquo;ast?&lt;/h2>
&lt;p>Por asteroides a l&amp;rsquo;ast nos referimos a asteroides que rotan sobre sí mismos
mientras son calentados por una estrella.&lt;/p></description></item><item><title>Radioastronomía - Actividad Guiada II</title><link>https://jorgemartinez.space/posts/astronomy/radioastronoma-actividad-guiada-ii/</link><pubDate>Sun, 01 Oct 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/radioastronoma-actividad-guiada-ii/</guid><description>&lt;h2 id="resumen">Resumen&lt;/h2>
&lt;p>Este informe recoge los resultados de la actividad guiada II para la asignatura de Radioastronomía del Máster en &lt;a href="https://www.universidadviu.com/es/master-astronomia-astrofisica">Astronomía y Astrofísica de la Universidad Internacional de Valencia&lt;/a>. Se ha utilizado &lt;a href="https://github.com/onsala-space-observatory/APSYNSIM">Apsynsim&lt;/a> para simular los problemas propuestos y obtener las soluciones.&lt;/p>
&lt;h2 id="respuesta-de-un-interferómetro-a-una-fuente-puntual">Respuesta de un interferómetro a una fuente puntual&lt;/h2>
&lt;p>&lt;strong>1. Investiga la relación entre la anchura de las franjas de la PSF y la separación de las antenas.&lt;/strong>&lt;/p>
&lt;p>La tabla 1 representa la relación entre el número de antenas, su distancia y la anchura de las franjas.&lt;/p></description></item><item><title>Astrofísica Estelar - Actividad Guiada II</title><link>https://jorgemartinez.space/posts/astronomy/astrofsica-estelar-actividad-guiada-ii/</link><pubDate>Sun, 24 Sep 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/astrofsica-estelar-actividad-guiada-ii/</guid><description>&lt;h2 id="soluciones">Soluciones&lt;/h2>
&lt;p>&lt;strong>Frecuencias fundamentales.&lt;/strong>
Los espectros de amplitud vienen dados por la &lt;a href="#fig-amplitude" class="quarto-xref">Figure 1&lt;/a>:&lt;/p>
&lt;ul>
&lt;li>Frecuencia primaria: 22.79 ciclos por día (c/d) con una amplitud de 2.50.&lt;/li>
&lt;li>Frecuencia secundaria: 21.71 ciclos por día (c/d) con una amplitud de 0.21.&lt;/li>
&lt;/ul>
&lt;p>La frecuencia primaria es mayor que la frecuencia secundaria en 22.79 c/d frente a 21.71 c/d, lo que significa que la estrella está experimentando oscilaciones más rápidas a la frecuencia primaria.&lt;/p>
&lt;p>En términos de amplitud, la amplitud de la frecuencia primaria es mucho mayor (2.50) en comparación con la amplitud de la frecuencia secundaria (0.21). Esto indica que las oscilaciones a la frecuencia primaria son mucho más pronunciadas o intensas que las oscilaciones a la frecuencia secundaria.&lt;/p></description></item><item><title>Using Quarto in my website</title><link>https://jorgemartinez.space/posts/tutorials/using-quarto-in-my-website/</link><pubDate>Sat, 16 Sep 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/using-quarto-in-my-website/</guid><description>&lt;p>Some time ago, I shared a post on my website detailing the fascinating fusion of Hugo and Jupyter Notebooks, which you can explore &lt;a href="https://jorgemartinez.space/posts/tutorials/hugo-with-jupyter-and-jupytext/">here&lt;/a>. This unique combination has proven to be a game-changer for me, enabling me to tackle my astronomy assignments while effortlessly generating content for my website, all within the seamless confines of Jupyter Lab.&lt;/p>
&lt;p>However, this innovative approach is not without its challenges. I&amp;rsquo;ve encountered several issues along the way, including:&lt;/p></description></item><item><title>Hacking GitHub Large File Storage (LFS)</title><link>https://jorgemartinez.space/posts/tutorials/hacking-github-large-file-storage-lfs/</link><pubDate>Tue, 05 Sep 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/hacking-github-large-file-storage-lfs/</guid><description>&lt;p>Today, I had to work with various
&lt;a href="https://fits.gsfc.nasa.gov/fits_documentation.html">FITS&lt;/a> files. These files
can be very large in size. Even if I knew this, I decided to set up a
repository for tracking my work.&lt;/p>
&lt;p>The only measure that I took was to compress all of them inside a
&lt;code>fits.tar.gz&lt;/code> file. Then, I added this file using &lt;a href="https://git-lfs.com/">git lfs&lt;/a>
and pushed the commit to GitHub. Here is the result:&lt;/p>
&lt;p>&lt;img src="img/github-lfs-limit.png" alt="A screenshot from my GitHub account indicating the LFS limit has been reached">&lt;/p></description></item><item><title>Astrofísica Extragaláctica - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/astrofsica-extragalctica-actividad-guiada-i/</link><pubDate>Sun, 03 Sep 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/astrofsica-extragalctica-actividad-guiada-i/</guid><description>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous">&lt;/script>
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous" data-relocate-top="true">&lt;/script>
&lt;script type="application/javascript">define('jquery', [],function() {return window.jQuery;})&lt;/script>
&lt;!-- #region -->
&lt;p>El presente informe contiene las soluciones a los ejercicios propuestos en la Actividad Guiada I de la asignatura &amp;ldquo;Astrofísica Extragaláctica&amp;rdquo;.&lt;/p>
&lt;p>El informe presentan dos casos prácticos relacionados con la astronomía. En el primero, se utiliza el Telescopio Espacial Hubble para calcular la distancia a la galaxia M100 mediante el método de las Cefeidas, acompañado de la creación de un diagrama que representa la relación entre el período y la luminosidad de estas estrellas variables. En el segundo caso, se emplea la herramienta Aladin para determinar la distancia a la galaxia M31 y se compara esta medición con datos previamente publicados en artículos científicos de Astronomía y Astrofísica, nuevamente generando un diagrama Periodo-Luminosidad. Finalmente, se destaca que, a partir de las distancias calculadas a estas galaxias, se obtiene una estimación de la constante de Hubble y se aproxima la edad del Universo, proporcionando una perspectiva valiosa sobre la medición de distancias cósmicas y la comprensión del universo en su conjunto.&lt;/p></description></item><item><title>Astrofísica Estelar - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/astrofsica-estelar-actividad-guiada-i/</link><pubDate>Sat, 22 Jul 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/astrofsica-estelar-actividad-guiada-i/</guid><description>&lt;p>El presente informe contiene las soluciones a los ejercicios propuestos en la Actividad Guiada I de la asignatura &lt;em>Astrofísica Estelar&lt;/em>.&lt;/p>
&lt;p>En particular, el trabajo se enfoca en la ubicación precisa de objetos estelares en el diagrama HR mediante la aplicación de modelos de interior estelar. Dichos modelos han sido generados utilizando &lt;a href="https://github.com/MESAHub/mesa">Modules for Experiments in Stellar Astrophysics (MESA)&lt;/a>.&lt;/p>
&lt;p>Dentro de los modelos generados se han simulado dos casos de observación: uno con melaticidad solar y otro con metalicidad subsolar. Para cada caso se proporciona una tabla resumen y un diagrama Hertzsprung-Russell (HR) con las isocrónas. Junto con el diagrama HR se han incluído las trazas proporcionadas por las observables (propiedades observadas) de la estrella. Finalmente, se resuelven diferentes cuestiones relacionadas con el tipo de estrella y su fase evolutiva.&lt;/p></description></item><item><title>Sistema solar - Actividad Guiada II - B</title><link>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-ii-b/</link><pubDate>Sat, 24 Jun 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-ii-b/</guid><description>&lt;h2 id="resumen">Resumen&lt;/h2>
&lt;p>El presente informe contiene las soluciones al ejercicio propuesto en la Actividad Guiada II - B de la asignatura &amp;ldquo;Sistema Solar&amp;rdquo;. Se ha seleccionado el artículo titulado &lt;em>Uracil in the carbonaceous asteroid (162173) Ryugu&lt;/em>.&lt;/p>
&lt;p>El análisis de las muestras obtenidas del asteroide revelan la presencia de la uracilo, una de las cuatro bases nitrogenadas que forman parte del ácido ribonucleico (ARN). Conocida la presencia de este compuesto, los autores del artículo establecen la hipótesis de la llegada de material orgánico a nuestro planeta a través de asteroides durante las primeras etapas del mundo.&lt;/p></description></item><item><title>Sistema solar - Actividad Guiada II - A</title><link>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-ii-a/</link><pubDate>Tue, 20 Jun 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-ii-a/</guid><description>&lt;h2 id="resumen">Resumen&lt;/h2>
&lt;p>El presente informe contiene las soluciones al ejercicio propuesto en la Actividad Guiada II - A de la asignatura &amp;ldquo;Sistema Solar&amp;rdquo;. Se ha seleccionado el experimento 8 de la colección de &lt;em>Mars in a Box&lt;/em>. El experimento analiza las temperaturas durante el verano y el invierno en el hemisferio boreal de Marte. Los resultados parecen invertidos al principio pero son justificados a través de la excentricidad de la órbita marciana, la distancai del planeta al Sol y su ténue atmósfera.&lt;/p></description></item><item><title>Astronomía e instrumentación clásica - Actividad Guiada II</title><link>https://jorgemartinez.space/posts/astronomy/astronoma-e-instrumentacin-clsica-actividad-guiada-ii/</link><pubDate>Sun, 18 Jun 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/astronoma-e-instrumentacin-clsica-actividad-guiada-ii/</guid><description>&lt;p>El presente informe recoge las soluciones a los ejercicios propuestos por la actividad guiada II de la asignatura &lt;em>Astronomía e instrumentación clásica&lt;/em>.&lt;/p>
&lt;h2 id="soluciones-a-los-ejercicios-propuestos">Soluciones a los ejercicios propuestos&lt;/h2>
&lt;p>Esta sección recoge los resultados y soluciones a los ejercicios propuestos por la actividad.&lt;/p>
&lt;p>Para resolver los ejercicios se ha utilizado el lenguaje de programacion Python. Todo el codigo generado se incluye en cada apartado del ejercicio.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">from&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">astropy.coordinates&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> Angle
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">astropy.units&lt;/span> &lt;span style="color:#007020;font-weight:bold">as&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">u&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">numpy&lt;/span> &lt;span style="color:#007020;font-weight:bold">as&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">np&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">angle_to_hms&lt;/span>(angle):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> angle &lt;span style="color:#666">=&lt;/span> Angle(angle)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hms &lt;span style="color:#666">=&lt;/span> angle&lt;span style="color:#666">.&lt;/span>hms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#4070a0">f&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>&lt;span style="color:#007020">int&lt;/span>(hms&lt;span style="color:#666">.&lt;/span>h)&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0">h &lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>&lt;span style="color:#007020">int&lt;/span>(hms&lt;span style="color:#666">.&lt;/span>m)&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0">min &lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>hms&lt;span style="color:#666">.&lt;/span>s&lt;span style="color:#70a0d0">:&lt;/span>&lt;span style="color:#4070a0">.0f&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0">s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">repr_angle&lt;/span>(angle):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#4070a0">f&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>angle&lt;span style="color:#666">.&lt;/span>to(u&lt;span style="color:#666">.&lt;/span>rad)&lt;span style="color:#70a0d0">:&lt;/span>&lt;span style="color:#4070a0">.2f&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0"> = &lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>angle&lt;span style="color:#666">.&lt;/span>to(u&lt;span style="color:#666">.&lt;/span>deg)&lt;span style="color:#70a0d0">:&lt;/span>&lt;span style="color:#4070a0">.2f&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0"> = &lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>angle_to_hms(angle)&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">from_Ah_to_Hdelta&lt;/span>(A, h, latitude):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> M &lt;span style="color:#666">=&lt;/span> np&lt;span style="color:#666">.&lt;/span>arctan(np&lt;span style="color:#666">.&lt;/span>tan(np&lt;span style="color:#666">.&lt;/span>pi &lt;span style="color:#666">/&lt;/span> &lt;span style="color:#40a070">2&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>rad &lt;span style="color:#666">-&lt;/span> h) &lt;span style="color:#666">*&lt;/span> np&lt;span style="color:#666">.&lt;/span>cos(A))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> latitude_minus_M &lt;span style="color:#666">=&lt;/span> latitude &lt;span style="color:#666">-&lt;/span> M
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> H_raw &lt;span style="color:#666">=&lt;/span> np&lt;span style="color:#666">.&lt;/span>arctan((np&lt;span style="color:#666">.&lt;/span>tan(A) &lt;span style="color:#666">*&lt;/span> np&lt;span style="color:#666">.&lt;/span>sin(M)) &lt;span style="color:#666">/&lt;/span> np&lt;span style="color:#666">.&lt;/span>cos(latitude_minus_M))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> A &lt;span style="color:#666">&amp;lt;&lt;/span> &lt;span style="color:#40a070">180&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> H &lt;span style="color:#666">=&lt;/span> H_raw &lt;span style="color:#666">+&lt;/span> (&lt;span style="color:#40a070">360&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg &lt;span style="color:#007020;font-weight:bold">if&lt;/span> H_raw &lt;span style="color:#666">&amp;lt;&lt;/span> &lt;span style="color:#40a070">0&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg &lt;span style="color:#007020;font-weight:bold">else&lt;/span> &lt;span style="color:#40a070">0&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> H &lt;span style="color:#666">=&lt;/span> H_raw &lt;span style="color:#666">-&lt;/span> (&lt;span style="color:#40a070">360&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg &lt;span style="color:#007020;font-weight:bold">if&lt;/span> H_raw &lt;span style="color:#666">&amp;gt;&lt;/span> &lt;span style="color:#40a070">180&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg &lt;span style="color:#007020;font-weight:bold">else&lt;/span> &lt;span style="color:#40a070">0&lt;/span> &lt;span style="color:#666">*&lt;/span> u&lt;span style="color:#666">.&lt;/span>deg)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> delta &lt;span style="color:#666">=&lt;/span> np&lt;span style="color:#666">.&lt;/span>arctan(np&lt;span style="color:#666">.&lt;/span>tan(latitude_minus_M) &lt;span style="color:#666">*&lt;/span> np&lt;span style="color:#666">.&lt;/span>cos(H))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> H, delta
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">from_Hdelta_to_absolute&lt;/span>(H, delta, theta):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> theta &lt;span style="color:#666">-&lt;/span> H, delta
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="ejercicio-1---solución">Ejercicio 1 - Solución&lt;/h3>
&lt;blockquote>
&lt;p>Calcular las coordenadas ecuatoriales absolutas de un astro que a las 12h 09m 09s de tiempo sidéreo tiene unas coordenadas horizontales de a = 153◦ 31′ 02′′ y h = 67◦ 50′ 56′′. Dato: φ = −40◦ 50′ 30′′.&lt;/p></description></item><item><title>Exoplanetas y Astrobiología - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/exoplanetas-y-astrobiologa-actividad-guiada-i/</link><pubDate>Wed, 14 Jun 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/exoplanetas-y-astrobiologa-actividad-guiada-i/</guid><description>&lt;p>El presente informe recoge el estudio y resultados propuestos por la actividad I de la asignatura &lt;em>Exoplanetas y Astrobiología&lt;/em>.&lt;/p>
&lt;p>En este informe se presenta un resumen del estudio realizado sobre exoplanetas, centrándose en la familiarización con la terminología propia de este campo y el análisis de datos reales para la caracterización de un planeta en particular. Se analizó la curva de luz durante el tránsito del planeta, que proporcionó información sobre la interacción entre el planeta y su estrella madre. Además, se examinó la curva de velocidad radial para comprender los efectos gravitacionales del planeta en la estrella. Mediante estos análisis, se calcularon parámetros físicos clave del planeta, como su masa, tamaño y período orbital. Posteriormente, se discutieron las propiedades del planeta, incluyendo su posición dentro o fuera de la zona de habitabilidad, con el objetivo de evaluar su potencial para albergar vida. Este estudio contribuye al conocimiento de la diversidad de exoplanetas y a la búsqueda de posibles entornos habitables en el universo.&lt;/p></description></item><item><title>Sistema solar - Actividad Guiada I</title><link>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-i/</link><pubDate>Sat, 03 Jun 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/sistema-solar-actividad-guiada-i/</guid><description>&lt;p>El presente informe contiene las soluciones a los ejercicios propuestos en la Actividad Guiada I de la asignatura &amp;ldquo;Sistema Solar&amp;rdquo;.&lt;/p>
&lt;p>Los ejercicios propuestos plantean el reto de encontrar el tipo de asteroide, conociendo su espectro. Dicho espectro se obtiene a partir de un archivo Flexible Image Transport System (FITS). Conocidas las 24 clases de referencia establecidas por DeMeo [1], es posible identificar a qué clase pertenece un asteroide. El problema propone resolver un total de 11 asteroides.&lt;/p></description></item><item><title>Classic astronomy - Homework I</title><link>https://jorgemartinez.space/posts/astronomy/classic-astronomy-homework-i/</link><pubDate>Sat, 20 May 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/astronomy/classic-astronomy-homework-i/</guid><description>&lt;p>This report collects the solutions to the proposed exercises for the &lt;strong>Activida
Guiada I&lt;/strong> of the subject &lt;strong>Astronomia classica e instrumentacion
astronomica&lt;/strong>. All exercises have been solved using the &lt;a href="https://stellarium.org/es/">Stellarium Astronomy
Software&lt;/a> with the following configuration:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Stellarium version:&lt;/strong> 0.20.4&lt;/li>
&lt;li>&lt;strong>Azimuth from South:&lt;/strong> true&lt;/li>
&lt;/ul>
&lt;h2 id="about-stellarium">About Stellarium&lt;/h2>
&lt;p>Stellarium is a popular open-source planetarium software that allows users to
explore and visualize the night sky in real-time. It provides a realistic and
immersive experience by rendering a 3D representation of the sky, complete with
stars, constellations, planets, nebulae, and other celestial objects.&lt;/p></description></item><item><title>Learning SPICE at ESAC</title><link>https://jorgemartinez.space/posts/journal/learning-spice-at-esac/</link><pubDate>Sat, 29 Apr 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/learning-spice-at-esac/</guid><description>&lt;p>The last &lt;a href="https://naif.jpl.nasa.gov/naif/">SPICE&lt;/a> training session was held at &lt;a href="https://www.esa.int/About_Us/ESAC">ESAC&lt;/a>, in Madrid, and I had the pleasure to attend it.&lt;/p>
&lt;figure>
&lt;img src="img/attendees_cropped.jpg" alt="Attendes photo" />
&lt;figcaption aria-hidden="true">Attendes photo&lt;/figcaption>
&lt;/figure>
&lt;h2 id="what-is-spice">What is SPICE?&lt;/h2>
&lt;p>SPICE (Spacecraft Planet Instrument C-matrix Events) is a software toolkit developed by NASA&amp;rsquo;s Navigation and Ancillary Information Facility (NAIF) for space mission design, navigation, and science data analysis. It provides a set of software libraries and tools for manipulating and visualizing space mission geometry, spacecraft instrument pointing, and other related information.&lt;/p></description></item><item><title>From open source contributor to Ansys sofware developer</title><link>https://jorgemartinez.space/posts/journal/from-open-source-contributor-to-ansys-sofware-developer/</link><pubDate>Mon, 17 Apr 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/from-open-source-contributor-to-ansys-sofware-developer/</guid><description>&lt;p>It all started back in university when I discovered the world of coding and
started working on my own software projects.&lt;/p>
&lt;p>As my interest in coding grew, I began contributing to various open source
projects, including &lt;a href="https://github.com/poliastro/poliastro">poliastro&lt;/a>.
Contributing to the open source community allowed me to improve my coding skills
and learn from experienced developers all around the world. It was an incredible
opportunity to grow and to help others do the same.&lt;/p></description></item><item><title>Sobre el futuro</title><link>https://jorgemartinez.space/posts/journal/sobre-el-futuro/</link><pubDate>Thu, 16 Mar 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/sobre-el-futuro/</guid><description>&lt;p>Hoy cumplo 26 años. El futuro que se me presenta, como a muchos otros jóvenes es
nefasto.&lt;/p>
&lt;p>Nos quieren hacer creer que la culpa de la inflación es de la guerra entre Rusia
y Ucrania. La culpa real es de los bancos centrales, los cuales se han dedicado
a imprimir dinero sin ningún tipo de control. Ahora sufrimos las consecuencias
en forma de subidas de interés.&lt;/p>
&lt;p>La nacionalidad se regala a cambio de votos. Jamás había habido tantos
ministerios y el gasto público es el mayor de toda la historia.&lt;/p></description></item><item><title>Project Euler: solution to problem 002</title><link>https://jorgemartinez.space/posts/project-euler/project-euler-solution-to-problem-002/</link><pubDate>Wed, 15 Mar 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/project-euler/project-euler-solution-to-problem-002/</guid><description>&lt;div class="pb-0 mb-5 alert alert-secondary" role="alert">
 &lt;p>Problem 2&lt;/p>
 &lt;p>
 Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89... By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
 &lt;/p>
&lt;/div>

&lt;h2 id="mathematical-background">Mathematical background&lt;/h2>
&lt;p>The Fibonaci sequence is defined as:&lt;/p></description></item><item><title>Project Euler: solution to problem 001</title><link>https://jorgemartinez.space/posts/project-euler/project-euler-solution-to-problem-001/</link><pubDate>Tue, 14 Mar 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/project-euler/project-euler-solution-to-problem-001/</guid><description>&lt;div class="pb-0 mb-5 alert alert-secondary" role="alert">
 &lt;p>Problem 1&lt;/p>
 &lt;p>
 If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
 &lt;/p>
&lt;/div>

&lt;h2 id="mathematical-background">Mathematical background&lt;/h2>
&lt;p>The multiple of a number is defined such that:&lt;/p>
&lt;p>$$
b = n \cdot a
$$&lt;/p>
&lt;p>In previous expression:&lt;/p>
&lt;ul>
&lt;li>The integer number $a$ is named the &lt;em>divisor&lt;/em>.&lt;/li>
&lt;li>The integer number $b$ is named the &lt;em>multiple&lt;/em>.&lt;/li>
&lt;li>The integer number $n$ is named the &lt;em>multiplier&lt;/em>.&lt;/li>
&lt;/ul>
&lt;h2 id="the-modulus-and-the-remainder-are-not-the-same">The modulus and the remainder are not the same!&lt;/h2>
&lt;p>Let us clarify the difference between &lt;em>modulus&lt;/em> and &lt;em>remainder&lt;/em>. These two
mathematical concepts are not the same. In addition, they are treated
differently by different programming languages.&lt;/p></description></item><item><title>A new website theme</title><link>https://jorgemartinez.space/posts/journal/a-new-website-theme/</link><pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/a-new-website-theme/</guid><description>&lt;p>During the last years, I have been iterating over and over on multiple static
website generators and various custom themes for my personal webpage.&lt;/p>
&lt;p>About a year a go I migrated my website from Pelican to &lt;a href="https://gohugo.io/">Hugo&lt;/a>. Hugo is blazing
fast and highly customizable.&lt;/p>
&lt;p>At the same time I migrated to Hugo, I decided to create a custom theme that I
named &lt;a href="https://github.com/jorgepiloto/unbloated">unbloated&lt;/a>. The simplicity of this theme was one of my favorite things.
However, the theme was not responsive at all, meaning that when seen using a
smartphone, everything look a bit distorded.&lt;/p></description></item><item><title>Programando en streaming</title><link>https://jorgemartinez.space/posts/journal/programando-en-streaming/</link><pubDate>Mon, 26 Dec 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/programando-en-streaming/</guid><description>&lt;p>Hace unos meses que llevo dándole vueltas a la idea de hacer streaming
mientras trabajo en proyectos de software y por fin me he decidido a hacerlo.&lt;/p>
&lt;p>¿Qué opinas de la plantilla para los directos? La he generado usando &lt;a href="https://inkscape.org/">Inkscape&lt;/a>.&lt;/p>
&lt;p>&lt;img src="img/streaming.png" alt="Mi layout para streaming">&lt;/p>
&lt;h2 id="dónde-ver-el-streaming">Dónde ver el streaming?&lt;/h2>
&lt;p>El streaming se emitirá en mi canal de YouTube. El enlace a la lista de streams
es el siguiente &lt;a href="https://www.youtube.com/@ingenierodeaviones/streams">https://www.youtube.com/@ingenierodeaviones/streams&lt;/a>.&lt;/p>
&lt;h2 id="cuándo-ver-el-streaming">Cuándo ver el streaming?&lt;/h2>
&lt;p>Todos los domingos de 18:00 a 20:00 hora local de Madrid. La emisión comenzará
el 8 de Enero de 2023.&lt;/p></description></item><item><title>Fin del curso</title><link>https://jorgemartinez.space/posts/compumaticas/fin-del-curso/</link><pubDate>Sat, 02 Jul 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/fin-del-curso/</guid><description>&lt;!-- #region -->
&lt;p>Los cursos de compumáticas I y II han finalizado, dando el paso a los campamentos de verano en la Escuela de Pensamiento Matemático.&lt;/p>
&lt;p>&lt;img src="img/escuela_epm.jpg" alt="Vista de la EPM">&lt;/p>
&lt;p>Durante todos estos meses, hemos aprendido los fundamentos del lenguaje de Python a través de proyectos sencillos. Muchos de estos proyectos estaban relacionados con la modelización matemática. Otros en cambio, nos han servido para aprender las herramientas básicas del desarrollo de software.&lt;/p></description></item><item><title>Calendario Lunar con Python</title><link>https://jorgemartinez.space/posts/compumaticas/calendario-lunar-con-python/</link><pubDate>Sat, 07 May 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/calendario-lunar-con-python/</guid><description>&lt;p>En el presente post vamos a aprender como generar un calendario lunar. Un
calendario lunar nos muestra las fases de la Luna a lo largo del año. El
Calendario funciona de la siguiente forma:&lt;/p>
&lt;ol>
&lt;li>Indicamos el año para el cual deseamos generar el calendario.&lt;/li>
&lt;li>El programa utiliza una plantilla HTML y rellena cada día de cada mes del año
con una imagen de la fase lunar correspondiente.&lt;/li>
&lt;/ol>
&lt;h2 id="atacando-el-problema">Atacando el problema&lt;/h2>
&lt;p>Para resolver el problema, lo mejor es dividirlo en otros problemas más pequeños. Así pues, podemos identificar los sguientes sub-problemas:&lt;/p></description></item><item><title>Hugo with Jupyter and Jupytext</title><link>https://jorgemartinez.space/posts/tutorials/hugo-with-jupyter-and-jupytexttitle-of-the-post/</link><pubDate>Sun, 10 Apr 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/hugo-with-jupyter-and-jupytexttitle-of-the-post/</guid><description>&lt;p>This post was written using &lt;a href="https://daringfireball.net/projects/markdown/">Markdown&lt;/a>. All its Python code snnipets are interpreted and their output is captured. You don&amp;rsquo;t believe me? See the code below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">from&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">datetime&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> datetime
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">matplotlib.pyplot&lt;/span> &lt;span style="color:#007020;font-weight:bold">as&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">plt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>plt&lt;span style="color:#666">.&lt;/span>plot(&lt;span style="color:#007020">list&lt;/span>(&lt;span style="color:#007020">range&lt;/span>(&lt;span style="color:#40a070">10&lt;/span>)), &lt;span style="color:#4070a0">&amp;#34;-bo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>plt&lt;span style="color:#666">.&lt;/span>title(&lt;span style="color:#4070a0">f&lt;/span>&lt;span style="color:#4070a0">&amp;#34;Last update: &lt;/span>&lt;span style="color:#70a0d0">{&lt;/span>datetime&lt;span style="color:#666">.&lt;/span>now()&lt;span style="color:#666">.&lt;/span>strftime(&lt;span style="color:#4070a0">&amp;#39;%Y-%m-&lt;/span>&lt;span style="color:#70a0d0">%-d&lt;/span>&lt;span style="color:#4070a0">&amp;#39;&lt;/span>)&lt;span style="color:#70a0d0">}&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>plt&lt;span style="color:#666">.&lt;/span>show()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;img src="index_files/figure-markdown_strict/cell-2-output-1.png" width="632" height="431" />
&lt;p>Do you see the date in the title of the figure? It is today&amp;rsquo;s date. However, it does not match the publish date of this post. This is becasue my website gets rendered every day at 00:00. Thus, the date in the figure&amp;rsquo;s title gets updated every day too.&lt;/p></description></item><item><title>Generando cuadrados mágicos</title><link>https://jorgemartinez.space/posts/compumaticas/generando-cuadrados-mgicos/</link><pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/generando-cuadrados-mgicos/</guid><description>&lt;p>Se entiende por cuadrado mágico aquella matriz de tamaño $n \times n$ que presenta la propiedad de que sus filas, columnas y diagonales suman lo mismo. Dicha suma se conoce con el nombre de &lt;strong>número mágico&lt;/strong> y se representa por la letra $M$.&lt;/p>
&lt;p>Los cuadrados mágicos han sido asociados a temas esotéricos desde que se tiene conocimiento de los mismos y pueden encontrarse en numerosas construcciones, tal y como sucede en la &lt;a href="https://sagradafamilia.org/es/home">Sagrada Familia&lt;/a>:&lt;/p></description></item><item><title>Space Invaders con Python y Pygame</title><link>https://jorgemartinez.space/posts/compumaticas/space-invaders-con-python-y-pygame/</link><pubDate>Mon, 31 Jan 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/space-invaders-con-python-y-pygame/</guid><description>&lt;p>Vamos a diseñar el popular juego conocido como &amp;ldquo;Space Invaders&amp;rdquo; utilizando para ello la librería &lt;a href="https://www.pygame.org/news">Pygame&lt;/a>.&lt;/p>
&lt;figure>
&lt;img src="https://www.impericon.com/780x240x90/media/impericon/header/entertainment/space_invaders/20200714_space_invaders_header_mobile@2x.jpg" alt="Space Invaders logo" />
&lt;figcaption aria-hidden="true">Space Invaders logo&lt;/figcaption>
&lt;/figure>
&lt;p>No vamos a complicar mucho el juego: bastará con crear una matriz de aliens con forma cuadrada que poco a poco se vayan acercando al límite inferior de la pantalla. En este mismo lugar se encuentra el tanque, modelado como un rectángulo sencillo. El tanque puede disparar misiles sencillos, también modelados como rectángulos.&lt;/p></description></item><item><title>Noche estrellada con Python turtle</title><link>https://jorgemartinez.space/posts/compumaticas/noche-estrellada-con-python-turtle/</link><pubDate>Sun, 23 Jan 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/noche-estrellada-con-python-turtle/</guid><description>&lt;p>Una de las cosas que más me gustan es ver las estrellas por la noche. Un cielo
de color negro lleno de pequeños puntos blancos me resulta algo precioso. ¿Y si
dibujamos una noche estrellada con Python y turtle?&lt;/p>
&lt;p>Como siempre, el código fuente&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> puedes encontrarlo al final del post.&lt;/p>
&lt;h2 id="atacando-el-problema">Atacando el problema&lt;/h2>
&lt;p>Para visualizar mejor el problema, imaginemos un grupo de estrellas dispuestas en el cielo nocturno:&lt;/p>
&lt;figure>
&lt;img src="img/stars_background_auxiliary.png" alt="Esquema noche estrellada" />
&lt;figcaption aria-hidden="true">Esquema noche estrellada&lt;/figcaption>
&lt;/figure>
&lt;p>A lo largo del tiempo, diversas culturas han agrupado las estrellas en formas conocidas bajo el nombre de constelaciones. En la imagen anterior se muestra la popular Osa Mayor. Nuestro objetivo es dibujar solamente las estrellas, no las líneas o las constelaciones.&lt;/p></description></item><item><title>Dibujando el logo de los Juegos Olímpicos con Python turtle</title><link>https://jorgemartinez.space/posts/compumaticas/dibujando-el-logo-de-los-juegos-olmpicos-con-python-turtle/</link><pubDate>Thu, 20 Jan 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/dibujando-el-logo-de-los-juegos-olmpicos-con-python-turtle/</guid><description>&lt;p>Vamos a ver cómo podemos dibujar el logo de los Juegos Olímpicos con Python
turtle. Sin embargo vamos a añadir una condición: el logo debe de estar
proporcionado al radio de los anillos que lo forman.&lt;/p>
&lt;p>Como siempre, el código fuente&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> puedes encontrarlo al final del post.&lt;/p>
&lt;h2 id="atacando-el-problema">Atacando el problema&lt;/h2>
&lt;p>El logo de los Juegos Olímpicos consta de un total de cinco circunferencias o
anillos dispuestoss en dos filas: tres arriba y dos abajo.&lt;/p></description></item><item><title>Presentación del curso</title><link>https://jorgemartinez.space/posts/compumaticas/presentacin-del-curso/</link><pubDate>Sat, 15 Jan 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/compumaticas/presentacin-del-curso/</guid><description>&lt;p>Querido/a estudiante,&lt;/p>
&lt;p>mi nombre es Jorge Martínez Garrido y soy uno de los profesores de la asignatura
de &lt;a href="https://pensamientomatematico.es/cursos/compumaticas/">Compumáticas&lt;/a> en la
&lt;a href="https://pensamientomatematico.es/">Escuela de Pensamiento Matemático&lt;/a>.&lt;/p>
&lt;p>El objetivo fundamental de la asignatura es educar la mente para que aprenda a
resolver complejos problemas mediante la ayuda de las computadoras y las
matemáticas.&lt;/p>
&lt;p>Si bien los problemas que se presentan a lo largo del curso son de diversa
naturaleza, todos ellos tienen una parte matemática. Algunos de los temas que
trataremos son los siguientes:&lt;/p></description></item><item><title>Modern OpenGL with Python - Part 0</title><link>https://jorgemartinez.space/posts/tutorials/modern-opengl-with-python-part-0/</link><pubDate>Sat, 01 Jan 2022 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/tutorials/modern-opengl-with-python-part-0/</guid><description>&lt;p>Computer graphics is, from my point of view, one of the most interesting
subjects within software development. In the end, no matter which is your field
of study, you will end up dealing with computer graphics in one way or another.&lt;/p>
&lt;p>For example, in the case of the Python ecosystem, you are likely to have used
&lt;a href="https://matplotlib.org/">matplotlib&lt;/a>. However, you may have noticed that it
limits as soon as you start asking it for more complex scenes or animations. As
an example consider the following problem:&lt;/p></description></item><item><title>Having fun on Christmas Holidays</title><link>https://jorgemartinez.space/posts/journal/having-fun-on-christmas-holidays/</link><pubDate>Sat, 25 Dec 2021 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/having-fun-on-christmas-holidays/</guid><description>&lt;p>Christmas time has come again and, to be honest, I do not like it too much. I am
not sure why I have this feeling against Christmas but that&amp;rsquo;s how I feel.&lt;/p>
&lt;p>Nevertheless, Loup and I are having a lot of fun during these days. Even if the
weather is cloudy, it&amp;rsquo;s not cold outside. Loup and I needed to have long
walks through the mountains and their meadows&amp;hellip; These lasts months were a
little bit difficult for both.&lt;/p></description></item><item><title>Migrating to Arch Linux</title><link>https://jorgemartinez.space/posts/journal/migrating-to-arch-linux/</link><pubDate>Wed, 24 Nov 2021 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/posts/journal/migrating-to-arch-linux/</guid><description>&lt;p>It&amp;rsquo;s been around six years since I accidentally installed a Linux distro.
However, it was a fortunate mistake&amp;hellip; My knowledge on various topics such us
programming, system administration and others has dramatically increased. But
now, I feel like it is time to move a step further by switching to &lt;a href="https://archlinux.org/">Arch
Linux&lt;/a>!&lt;/p>
&lt;p>&lt;img src="img/arch_linux_logo.png" alt="Arch Linux logo">&lt;/p>
&lt;h2 id="from-ubuntu-to-endeavouros">From Ubuntu to EndeavourOS&lt;/h2>
&lt;p>The first Linux flavor I tasted was &lt;a href="https://ubuntu.com/">Ubuntu&lt;/a>, a distro
supported by &lt;a href="https://archlinux.org/">Canonical&lt;/a>. Ubuntu is a very popular
distro for newcomers to the Linux world as it is easy to install, configure and
use.&lt;/p></description></item><item><title>About</title><link>https://jorgemartinez.space/about.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/about.html</guid><description>&lt;h2 id="about">About&lt;/h2>
&lt;p>I am Jorge Martinez Garrido, an aerospace engineer and software developer with a
keen interest in astrodynamics and scientific computing.&lt;/p>



&lt;div>
 &lt;img src="../img/me/portrait.png" alt="Portrait of Jorge Martinez Garrido. Author is Maria Moreno Poncela." class="img-fluid">
 &lt;figcaption class="caption">Portrait of Jorge Martinez Garrido. Author is Maria Moreno Poncela.&lt;/figcaption>
&lt;/div>


&lt;p>The subsequent sections depict significant events from my life, which have
shaped my present persona. May the forthcoming lines provide you with a deeper
insight into my perspective on the world.&lt;/p></description></item><item><title>Applications</title><link>https://jorgemartinez.space/applications.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/applications.html</guid><description>&lt;h2 id="applications">Applications&lt;/h2>
&lt;p>Interactive tools built to run entirely in the browser — no server, no data sent anywhere.&lt;/p>
&lt;style>
 .app-card {
 display: flex;
 flex-direction: column;
 gap: 0.5rem;
 width: 220px;
 padding: 1rem;
 border: thin solid var(--black);
 box-shadow: 2px 2px var(--darkgray);
 background: var(--white);
 color: var(--black);
 text-decoration: none;
 transition: background 0.2s, color 0.2s;
 }
 .app-card:hover {
 background: var(--black);
 color: var(--white);
 }
 .app-card:hover .app-tag { background: var(--white); color: var(--black); }
 .app-card:visited { color: var(--black); }
 .app-card:hover:visited { color: var(--white); }
 .app-title { font-weight: bold; text-transform: uppercase; font-size: 0.95rem; }
 .app-desc { font-size: 0.78rem; line-height: 1.4; }
 .app-tag {
 align-self: flex-start;
 font-size: 0.65rem;
 text-transform: uppercase;
 letter-spacing: 0.05em;
 border: thin solid var(--black);
 padding: 0.1rem 0.3rem;
 margin-top: auto;
 }
&lt;/style>
&lt;div class="collection">
 &lt;a class="app-card" href="https://jorgemartinez.space/applications/mortage-simulator.html">
 &lt;span class="app-title">Mortgage Simulator&lt;/span>
 &lt;span class="app-desc">Monthly payments, amortization schedule and cost breakdown. Supports fixed and two-period fixed→variable rate mortgages.&lt;/span>
 &lt;span class="app-tag">Finance&lt;/span>
 &lt;/a>
 &lt;a class="app-card" href="https://jorgemartinez.space/applications/atmosphere-simulator.html">
 &lt;span class="app-title">Atmosphere Simulator&lt;/span>
 &lt;span class="app-desc">ISA model calculator for temperature, pressure, density and speed of sound at any altitude up to 86 km. Supports ISA±ΔT offsets.&lt;/span>
 &lt;span class="app-tag">Aerospace&lt;/span>
 &lt;/a>
&lt;/div></description></item><item><title>Atmosphere Simulator</title><link>https://jorgemartinez.space/applications/atmosphere-simulator.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/applications/atmosphere-simulator.html</guid><description>&lt;h2 id="isa-atmosphere-simulator">ISA Atmosphere Simulator&lt;/h2>
&lt;p>The &lt;strong>International Standard Atmosphere (ISA)&lt;/strong> defines how temperature, pressure, density, and speed of sound vary with altitude. It is the reference model used in aviation, aerospace engineering, and meteorology.&lt;/p>
&lt;style>
 fieldset { margin: 0.5rem 2.5rem; max-width: 100%; box-sizing: border-box; }
 .param-grid {
 display: grid;
 grid-template-columns: 1fr auto;
 gap: 0.4rem 0.75rem;
 align-items: center;
 }
 .param-grid label {
 width: auto;
 margin-bottom: 0;
 font-size: 0.85rem;
 white-space: nowrap;
 }
 .param-grid .param-right {
 display: flex;
 align-items: center;
 gap: 0.5rem;
 min-width: 0;
 }
 .param-grid input[type="range"] {
 -webkit-appearance: none;
 appearance: none;
 width: clamp(60px, 20vw, 140px);
 height: 4px;
 background: var(--darkgray);
 border: none;
 border-radius: 0;
 outline: none;
 cursor: pointer;
 padding: 0;
 }
 .param-grid input[type="range"]::-webkit-slider-thumb {
 -webkit-appearance: none;
 width: 14px;
 height: 14px;
 background: var(--black);
 border-radius: 0;
 cursor: pointer;
 }
 .param-grid input[type="range"]::-moz-range-thumb {
 width: 14px;
 height: 14px;
 background: var(--black);
 border-radius: 0;
 border: none;
 cursor: pointer;
 }
 .param-grid input[type="number"] {
 width: 90px;
 border: thin solid var(--black);
 background: var(--white);
 font-family: var(--font-family-code);
 font-size: 0.85rem;
 padding: 0.15rem 0.3rem;
 text-align: right;
 }
 .param-grid .param-hint {
 font-size: 0.75rem;
 color: #666;
 grid-column: 2;
 text-align: right;
 margin-top: -0.3rem;
 }
 .param-divider {
 grid-column: 1 / -1;
 border-top: thin solid var(--darkgray);
 margin: 0.2rem 0;
 }
 /* --- Results --- */
 #summary { display: none; margin: 1rem 1rem 0; }
 .stat-grid {
 display: grid;
 grid-template-columns: repeat(4, 1fr);
 gap: 0.4rem;
 margin-bottom: 0.4rem;
 }
 @media (max-width: 600px) { .stat-grid { grid-template-columns: 1fr 1fr; } }
 .stat-card {
 border: thin solid var(--black);
 border-left: 3px solid var(--black);
 background: var(--white);
 padding: 0.65rem 0.9rem;
 display: flex;
 flex-direction: column;
 gap: 0.15rem;
 }
 .stat-card.stat-hero {
 grid-column: 1 / -1;
 flex-direction: row;
 align-items: center;
 justify-content: space-between;
 gap: 0.5rem;
 }
 .stat-label {
 font-size: 0.65rem;
 text-transform: uppercase;
 letter-spacing: 0.07em;
 color: #888;
 }
 .stat-value { font-size: 1.15rem; font-weight: bold; color: var(--black); line-height: 1.1; }
 .stat-hero .stat-value { font-size: 1.5rem; }
 .stat-sub { font-size: 0.75rem; color: #555; margin-top: 0.15rem; }
 .stat-layer {
 font-size: 0.65rem;
 font-style: italic;
 color: #777;
 margin-top: 0.1rem;
 }
 /* layer badge */
 .layer-badge {
 display: inline-block;
 font-size: 0.65rem;
 font-weight: bold;
 padding: 0.05rem 0.35rem;
 text-transform: uppercase;
 letter-spacing: 0.05em;
 background: var(--white);
 color: var(--black);
 border: thin solid var(--black);
 margin-left: 0.4rem;
 vertical-align: middle;
 }
 /* charts 2x2 */
 #charts-grid {
 display: grid;
 grid-template-columns: 1fr 1fr;
 gap: 0;
 margin-top: 0.5rem;
 }
 @media (max-width: 600px) { #charts-grid { grid-template-columns: 1fr; } }
 /* ISA reference table */
 details.isa-details { margin: 0 1rem 1rem; }
 details.isa-details > summary {
 cursor: pointer;
 padding: 0.45rem 0.75rem;
 background: var(--lightgray);
 font-weight: bold;
 font-size: 0.85rem;
 text-transform: uppercase;
 letter-spacing: 0.05em;
 list-style: none;
 user-select: none;
 }
 details.isa-details > summary::before { content: '▶ '; font-size: 0.65rem; }
 details.isa-details[open] > summary::before { content: '▼ '; }
 details.isa-details > div { overflow-x: auto; max-height: 400px; }
 #isaTable th { background: var(--black); color: var(--white); padding: 0.3rem 0.5rem; position: sticky; top: 0; font-size: 0.8rem; }
 #isaTable td { padding: 0.2rem 0.5rem; font-size: 0.8rem; }
 #isaTable tr:nth-child(even) td { background: var(--lightgray); }
 #isaTable tr.highlight-row td { background: #d0d0d0; font-weight: bold; }
&lt;/style>
&lt;fieldset>
 &lt;legend>Atmosphere Parameters&lt;/legend>
 &lt;div class="param-grid">
 &lt;label for="altitude">Altitude (m)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="altitudeSlider" min="0" max="86000" step="100" value="0" oninput="syncFromSlider('altitude'); compute()">
 &lt;input type="number" id="altitude" value="0" min="0" max="86000" step="100" oninput="syncFromNumber('altitude'); compute()">
 &lt;/div>
 &lt;span>&lt;/span>
 &lt;span class="param-hint" id="altFtHint">0 ft&lt;/span>
 &lt;label for="deltaISA">ΔT&lt;sub>ISA&lt;/sub> (°C offset)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="deltaISASlider" min="-40" max="40" step="1" value="0" oninput="syncFromSlider('deltaISA'); compute()">
 &lt;input type="number" id="deltaISA" value="0" min="-40" max="40" step="1" oninput="syncFromNumber('deltaISA'); compute()">
 &lt;/div>
 &lt;span>&lt;/span>
 &lt;span class="param-hint" id="deltaHint">ISA standard day&lt;/span>
 &lt;/div>
&lt;/fieldset>
&lt;div id="summary">&lt;/div>
&lt;div id="charts-grid">
 &lt;div id="chartTemp">&lt;/div>
 &lt;div id="chartPressure">&lt;/div>
 &lt;div id="chartDensity">&lt;/div>
 &lt;div id="chartSound">&lt;/div>
&lt;/div>
&lt;details class="isa-details">
 &lt;summary>ISA Reference Table&lt;/summary>
 &lt;div>
 &lt;table id="isaTable" style="width:100%; border-collapse:collapse;">&lt;/table>
 &lt;/div>
&lt;/details>
&lt;script>
// ── ISA Layer definitions (ICAO standard atmosphere, up to 86 km) ──────────
// Each layer: [base altitude m, base temperature K, lapse rate K/m, name]
const ISA_LAYERS = [
 [ 0, 288.15, -0.0065, 'Troposphere'],
 [ 11000, 216.65, 0.0000, 'Tropopause'],
 [ 20000, 216.65, 0.0010, 'Stratosphere I'],
 [ 32000, 228.65, 0.0028, 'Stratosphere II'],
 [ 47000, 270.65, 0.0000, 'Stratopause'],
 [ 51000, 270.65, -0.0028, 'Mesosphere I'],
 [ 71000, 214.65, -0.0020, 'Mesosphere II'],
 [ 86000, 186.87, 0.0000, 'Mesopause'], // sentinel
];

const G = 9.80665; // m/s²
const R = 287.05287; // J/(kg·K)
const GAMMA = 1.4;
const P0 = 101325.0; // Pa at sea level

// Pre-compute base pressures for each layer
const layerBasePressure = [P0];
for (let i = 1; i &lt; ISA_LAYERS.length; i++) {
 const [hb, Tb, L] = ISA_LAYERS[i - 1];
 const [hTop] = ISA_LAYERS[i];
 const dh = hTop - hb;
 const Pb = layerBasePressure[i - 1];
 if (Math.abs(L) &lt; 1e-10) {
 layerBasePressure.push(Pb * Math.exp(-G * dh / (R * Tb)));
 } else {
 const Ttop = Tb + L * dh;
 layerBasePressure.push(Pb * Math.pow(Ttop / Tb, -G / (L * R)));
 }
}

function isaPoint(h, deltaT) {
 // Clamp
 h = Math.max(0, Math.min(h, 86000));
 // Find layer
 let layerIdx = 0;
 for (let i = 1; i &lt; ISA_LAYERS.length - 1; i++) {
 if (h >= ISA_LAYERS[i][0]) layerIdx = i; else break;
 }
 const [hb, Tb, L, name] = ISA_LAYERS[layerIdx];
 const Pb = layerBasePressure[layerIdx];
 const dh = h - hb;
 let T_std, P;
 if (Math.abs(L) &lt; 1e-10) {
 T_std = Tb;
 P = Pb * Math.exp(-G * dh / (R * Tb));
 } else {
 T_std = Tb + L * dh;
 P = Pb * Math.pow(T_std / Tb, -G / (L * R));
 }
 const T = T_std + (deltaT || 0); // ISA+ΔT
 const rho = P / (R * T);
 const a = Math.sqrt(GAMMA * R * T);
 return { T, T_std, P, rho, a, layerIdx, layerName: name };
}

// Build profile arrays (every 500 m)
function buildProfile(deltaT) {
 const step = 500;
 const alts = [], temps = [], pressures = [], densities = [], sounds = [];
 for (let h = 0; h &lt;= 86000; h += step) {
 const pt = isaPoint(h, deltaT);
 alts.push(h);
 temps.push(pt.T - 273.15); // °C
 pressures.push(pt.P / 100); // hPa
 densities.push(pt.rho);
 sounds.push(pt.a);
 }
 return { alts, temps, pressures, densities, sounds };
}

// Build reference table rows every 1000 m
function buildTable(deltaT, highlightH) {
 let rows = `&lt;tr>
 &lt;th>Alt (m)&lt;/th>&lt;th>Alt (ft)&lt;/th>&lt;th>Layer&lt;/th>
 &lt;th>T_ISA (°C)&lt;/th>&lt;th>T (°C)&lt;/th>
 &lt;th>P (hPa)&lt;/th>&lt;th>ρ (kg/m³)&lt;/th>&lt;th>a (m/s)&lt;/th>
 &lt;/tr>`;
 for (let h = 0; h &lt;= 86000; h += 1000) {
 const pt = isaPoint(h, deltaT);
 const isHL = Math.abs(h - Math.round(highlightH / 1000) * 1000) &lt; 500;
 const cls = isHL ? 'highlight-row' : '';
 rows += `&lt;tr class="${cls}">
 &lt;td>${h.toLocaleString()}&lt;/td>
 &lt;td>${Math.round(h * 3.28084).toLocaleString()}&lt;/td>
 &lt;td>${pt.layerName}&lt;/td>
 &lt;td>${(pt.T_std - 273.15).toFixed(2)}&lt;/td>
 &lt;td>${(pt.T - 273.15).toFixed(2)}&lt;/td>
 &lt;td>${(pt.P / 100).toFixed(2)}&lt;/td>
 &lt;td>${pt.rho.toFixed(4)}&lt;/td>
 &lt;td>${pt.a.toFixed(2)}&lt;/td>
 &lt;/tr>`;
 }
 document.getElementById('isaTable').innerHTML = rows;
}

function compute() {
 const h = parseFloat(document.getElementById('altitude').value) || 0;
 const deltaT = parseFloat(document.getElementById('deltaISA').value) || 0;

 updateHints(h, deltaT);

 const pt = isaPoint(h, deltaT);
 const T_C = pt.T - 273.15;
 const T_std_C = pt.T_std - 273.15;
 const P_hPa = pt.P / 100;
 const a_kt = pt.a * 1.94384; // m/s → knots

 // ── Summary ─────────────────────────────────────────────────────────────
 const summaryEl = document.getElementById('summary');
 summaryEl.style.display = 'block';

 const hFt = (h * 3.28084).toFixed(0);
 summaryEl.innerHTML = `
 &lt;div class="stat-grid">
 &lt;div class="stat-card stat-hero">
 &lt;div>
 &lt;span class="stat-label">Altitude&lt;/span>
 &lt;div class="stat-value">${h.toLocaleString()} m &amp;nbsp;/&amp;nbsp; ${parseInt(hFt).toLocaleString()} ft&lt;/div>
 &lt;div class="stat-layer">Layer: ${pt.layerName} &lt;span class="layer-badge">ISA ${deltaT >= 0 ? '+' : ''}${deltaT}&lt;/span>&lt;/div>
 &lt;/div>
 &lt;/div>
 &lt;div class="stat-card">
 &lt;span class="stat-label">Temperature&lt;/span>
 &lt;span class="stat-value">${T_C.toFixed(2)} °C&lt;/span>
 &lt;span class="stat-sub">${pt.T.toFixed(2)} K&lt;/span>
 &lt;span class="stat-sub">ISA std: ${T_std_C.toFixed(2)} °C&lt;/span>
 &lt;/div>
 &lt;div class="stat-card">
 &lt;span class="stat-label">Pressure&lt;/span>
 &lt;span class="stat-value">${P_hPa.toFixed(2)} hPa&lt;/span>
 &lt;span class="stat-sub">${pt.P.toFixed(1)} Pa&lt;/span>
 &lt;span class="stat-sub">${(pt.P / P0 * 100).toFixed(3)}% of P₀&lt;/span>
 &lt;/div>
 &lt;div class="stat-card">
 &lt;span class="stat-label">Density&lt;/span>
 &lt;span class="stat-value">${pt.rho.toFixed(4)} kg/m³&lt;/span>
 &lt;span class="stat-sub">${(pt.rho / 1.225 * 100).toFixed(2)}% of ρ₀&lt;/span>
 &lt;/div>
 &lt;div class="stat-card">
 &lt;span class="stat-label">Speed of Sound&lt;/span>
 &lt;span class="stat-value">${pt.a.toFixed(2)} m/s&lt;/span>
 &lt;span class="stat-sub">${a_kt.toFixed(1)} kt&lt;/span>
 &lt;span class="stat-sub">${(pt.a / 340.294 * 100).toFixed(2)}% of a₀&lt;/span>
 &lt;/div>
 &lt;/div>`;

 // ── Profile data ─────────────────────────────────────────────────────────
 const prof = buildProfile(deltaT);
 const cfg = { responsive: true, displayModeBar: false };

 // Vertical dashed line at selected altitude
 const hLine = shape => ({
 type: 'line', x0: shape[0], x1: shape[1],
 y0: h, y1: h, xref: 'paper', yref: 'y',
 line: { color: '#000', width: 1.5, dash: 'dot' }
 });

 const altAxis = { title: 'Altitude (m)', tickformat: ',.0f', range: [0, 86000] };
 const layout = (title, xTitle, xRange) => ({
 title, height: 320,
 font: { family: 'Mono, monospace', size: 11 },
 paper_bgcolor: '#ffffff', plot_bgcolor: '#ffffff',
 margin: { t: 40, r: 20, b: 50, l: 70 },
 legend: { orientation: 'h', y: -0.2 },
 xaxis: { title: xTitle, range: xRange },
 yaxis: altAxis,
 shapes: [hLine([0, 1])],
 annotations: [{
 x: 0.98, xref: 'paper', y: h, yref: 'y',
 text: `${h.toLocaleString()} m`, showarrow: false,
 xanchor: 'right', font: { size: 9, color: '#333' }
 }]
 });

 // Also show ISA std profile when ΔT ≠ 0
 const tracesTemp = [
 { x: prof.temps, y: prof.alts, mode: 'lines', name: deltaT !== 0 ? `ISA${deltaT >= 0 ? '+' : ''}${deltaT}` : 'Temperature',
 line: { color: '#000', width: 2 } }
 ];
 if (deltaT !== 0) {
 const profStd = buildProfile(0);
 tracesTemp.push({ x: profStd.temps, y: profStd.alts, mode: 'lines', name: 'ISA std',
 line: { color: '#aaa', width: 1.5, dash: 'dash' } });
 }

 Plotly.react('chartTemp', tracesTemp,
 layout('Temperature', 'T (°C)', [-100, 30]), cfg);

 Plotly.react('chartPressure', [{
 x: prof.pressures, y: prof.alts, mode: 'lines', name: 'Pressure',
 line: { color: '#000', width: 2 }, fill: 'tozerox', fillcolor: 'rgba(0,0,0,0.05)'
 }], layout('Pressure', 'P (hPa)', [0, 1030]), cfg);

 Plotly.react('chartDensity', [{
 x: prof.densities, y: prof.alts, mode: 'lines', name: 'Density',
 line: { color: '#000', width: 2 }, fill: 'tozerox', fillcolor: 'rgba(0,0,0,0.05)'
 }], layout('Density', 'ρ (kg/m³)', [0, 1.4]), cfg);

 Plotly.react('chartSound', [{
 x: prof.sounds, y: prof.alts, mode: 'lines', name: 'Speed of Sound',
 line: { color: '#000', width: 2 }
 }], layout('Speed of Sound', 'a (m/s)', [270, 360]), cfg);

 // ── Reference table ───────────────────────────────────────────────────────
 buildTable(deltaT, h);
}

function syncFromSlider(id) {
 document.getElementById(id).value = document.getElementById(id + 'Slider').value;
}

function syncFromNumber(id) {
 const v = parseFloat(document.getElementById(id).value);
 if (!isNaN(v)) document.getElementById(id + 'Slider').value = v;
}

function updateHints(h, deltaT) {
 document.getElementById('altFtHint').textContent =
 Math.round(h * 3.28084).toLocaleString() + ' ft';
 document.getElementById('deltaHint').textContent =
 deltaT === 0 ? 'ISA standard day' :
 deltaT > 0 ? `ISA+${deltaT} (hot day)` : `ISA${deltaT} (cold day)`;
}

document.addEventListener('DOMContentLoaded', compute);
&lt;/script></description></item><item><title>Mortage Simulator</title><link>https://jorgemartinez.space/applications/mortage-simulator.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/applications/mortage-simulator.html</guid><description>&lt;h2 id="mortgage-simulator">Mortgage Simulator&lt;/h2>
&lt;style>
 fieldset { margin: 0.5rem 2.5rem; max-width: 100%; box-sizing: border-box; }
 .param-grid {
 display: grid;
 grid-template-columns: 1fr auto;
 gap: 0.4rem 0.75rem;
 align-items: center;
 }
 .param-grid label {
 width: auto;
 margin-bottom: 0;
 font-size: 0.85rem;
 white-space: nowrap;
 }
 .param-grid .param-right {
 display: flex;
 align-items: center;
 gap: 0.5rem;
 min-width: 0;
 }
 .param-grid input[type="range"] {
 -webkit-appearance: none;
 appearance: none;
 width: clamp(60px, 20vw, 140px);
 height: 4px;
 background: var(--darkgray);
 border: none;
 border-radius: 0;
 outline: none;
 cursor: pointer;
 padding: 0;
 }
 .param-grid input[type="range"]::-webkit-slider-thumb {
 -webkit-appearance: none;
 width: 14px;
 height: 14px;
 background: var(--black);
 border-radius: 0;
 cursor: pointer;
 }
 .param-grid input[type="range"]::-moz-range-thumb {
 width: 14px;
 height: 14px;
 background: var(--black);
 border-radius: 0;
 border: none;
 cursor: pointer;
 }
 .param-grid input[type="number"] {
 width: 90px;
 border: thin solid var(--black);
 background: var(--white);
 font-family: var(--font-family-code);
 font-size: 0.85rem;
 padding: 0.15rem 0.3rem;
 text-align: right;
 }
 .param-grid .param-hint {
 font-size: 0.75rem;
 color: #666;
 grid-column: 2;
 text-align: right;
 margin-top: -0.3rem;
 }
 .param-divider {
 grid-column: 1 / -1;
 border-top: thin solid var(--darkgray);
 margin: 0.2rem 0;
 }
 #calcBtn {
 grid-column: 1 / -1;
 margin-top: 0.5rem;
 margin-bottom: 0.5rem;
 padding: 0.5rem;
 font-family: var(--font-family-code);
 font-size: 1rem;
 font-weight: bold;
 text-transform: uppercase;
 background: var(--white);
 color: var(--black);
 border: thin solid var(--black);
 cursor: pointer;
 letter-spacing: 0.1em;
 transition: background 0.2s, color 0.2s;
 }
 #calcBtn:hover {
 background: var(--black);
 color: var(--white);
 }
 /* --- Results --- */
 #summary { display: none; margin: 1rem 1rem 0; }
 .stat-grid {
 display: grid;
 grid-template-columns: repeat(3, 1fr);
 gap: 0.4rem;
 margin-bottom: 0.4rem;
 }
 @media (max-width: 500px) { .stat-grid { grid-template-columns: 1fr 1fr; } }
 /* --- shared card base --- */
 .stat-card, .stat-period-card, .stat-transition {
 border: thin solid var(--black);
 border-left: 3px solid var(--black);
 background: var(--white);
 padding: 0.65rem 0.9rem;
 }
 /* column layout (regular cards) */
 .stat-card { display: flex; flex-direction: column; gap: 0.15rem; }
 /* row layout + full-width (hero, period cards, transition) */
 .stat-card.stat-hero, .stat-period-card, .stat-transition {
 grid-column: 1 / -1;
 display: flex;
 flex-direction: row;
 align-items: center;
 justify-content: space-between;
 gap: 0.5rem;
 }
 /* unified label */
 .stat-label {
 font-size: 0.65rem;
 text-transform: uppercase;
 letter-spacing: 0.07em;
 color: #888;
 }
 /* unified value */
 .stat-value { font-size: 1.15rem; font-weight: bold; color: var(--black); line-height: 1.1; }
 .stat-hero .stat-value { font-size: 1.7rem; }
 .stat-period-card .stat-value { font-size: 1.4rem; font-weight: bold; color: var(--black); line-height: 1.1; }
 /* period card description sub-line */
 .stat-desc { font-size: 0.72rem; color: #666; margin-top: 0.2rem; }
 /* savings / hint text */
 .stat-savings { font-size: 0.68rem; color: #888; margin-top: 0.05rem; }
 /* transition row */
 .stat-transition { font-size: 0.78rem; }
 .stat-transition strong { font-size: 0.95rem; }
 /* charts */
 #charts-row {
 display: grid;
 grid-template-columns: 1fr 1fr;
 gap: 0;
 }
 @media (max-width: 500px) { #charts-row { grid-template-columns: 1fr; } }
 /* two-period toggle */
 .param-toggle {
 grid-column: 1 / -1;
 display: flex;
 align-items: center;
 gap: 0.5rem;
 padding: 0.3rem 0;
 }
 .param-toggle input[type="checkbox"] {
 width: auto;
 cursor: pointer;
 accent-color: var(--black);
 }
 .param-toggle label {
 font-size: 0.85rem;
 cursor: pointer;
 margin: 0;
 white-space: nowrap;
 }
 .period-badge {
 display: inline-block;
 font-size: 0.65rem;
 font-weight: bold;
 padding: 0.05rem 0.25rem;
 text-transform: uppercase;
 letter-spacing: 0.05em;
 margin-right: 0.2rem;
 vertical-align: middle;
 }
 .period-1 { background: var(--white); color: var(--black); border: thin solid var(--black); }
 .period-2 { background: var(--white); color: var(--black); border: thin solid var(--black); }
 .two-period-row { display: none; }
 .show-two-period label.two-period-row { display: block; }
 .show-two-period div.two-period-row { display: flex; }
 /* amortization table */
 details.amort-details { margin: 0 1rem 1rem; }
 details.amort-details > summary {
 cursor: pointer;
 padding: 0.45rem 0.75rem;
 background: var(--lightgray);
 font-weight: bold;
 font-size: 0.85rem;
 text-transform: uppercase;
 letter-spacing: 0.05em;
 list-style: none;
 user-select: none;
 }
 details.amort-details > summary::before { content: '▶ '; font-size: 0.65rem; }
 details.amort-details[open] > summary::before { content: '▼ '; }
 details.amort-details > div { overflow-x: auto; max-height: 400px; }
 #amortTable th { background: var(--black); color: var(--white); padding: 0.3rem 0.5rem; position: sticky; top: 0; }
 #amortTable td { padding: 0.2rem 0.5rem; }
 #amortTable tr.p1-row:nth-child(even) td { background: var(--lightgray); }
 #amortTable tr.p2-row td { background: #e0e0e0; }
 #amortTable tr.p2-row:nth-child(even) td { background: #d4d4d4; }
 #amortTable tr.p2-start td { border-top: 2px solid var(--black); }
&lt;/style>
&lt;fieldset>
 &lt;legend>Loan Parameters&lt;/legend>
 &lt;div class="param-grid">
 &lt;label for="housePrice">House price (€)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="housePriceSlider" min="50000" max="2000000" step="1000" value="200000" oninput="syncFromSlider('housePrice'); simulate()">
 &lt;input type="number" id="housePrice" value="200000" min="50000" max="2000000" step="1000" oninput="syncFromNumber('housePrice'); simulate()">
 &lt;/div>
 &lt;label for="downPayment">Down payment (€)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="downPaymentSlider" min="0" max="500000" step="1000" value="100000" oninput="syncFromSlider('downPayment'); simulate()">
 &lt;input type="number" id="downPayment" value="100000" min="0" max="500000" step="1000" oninput="syncFromNumber('downPayment'); simulate()">
 &lt;/div>
 &lt;span>&lt;/span>
 &lt;span class="param-hint" id="downHint">50.0% of price&lt;/span>
 &lt;label for="loanTerm">Loan term (years)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="loanTermSlider" min="1" max="50" step="1" value="30" oninput="syncFromSlider('loanTerm'); simulate()">
 &lt;input type="number" id="loanTerm" value="30" min="1" max="50" step="1" oninput="syncFromNumber('loanTerm'); simulate()">
 &lt;/div>
 &lt;div class="param-toggle">
 &lt;input type="checkbox" id="twoPeriod" onchange="toggleTwoPeriod()">
 &lt;label for="twoPeriod">Two-period mortgage (fixed → variable rate)&lt;/label>
 &lt;/div>
 &lt;label id="interestRateLabel" for="interestRate">Interest rate (% / yr)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="interestRateSlider" min="0.1" max="15" step="0.05" value="1.75" oninput="syncFromSlider('interestRate'); simulate()">
 &lt;input type="number" id="interestRate" value="1.75" min="0.1" max="15" step="0.05" oninput="syncFromNumber('interestRate'); simulate()">
 &lt;/div>
 &lt;label class="two-period-row" for="fixedPeriod">Fixed rate period (years)&lt;/label>
 &lt;div class="two-period-row param-right">
 &lt;input type="range" id="fixedPeriodSlider" min="1" max="29" step="1" value="5" oninput="syncFromSlider('fixedPeriod'); simulate()">
 &lt;input type="number" id="fixedPeriod" value="5" min="1" max="29" step="1" oninput="syncFromNumber('fixedPeriod'); simulate()">
 &lt;/div>
 &lt;label class="two-period-row" for="variableRate">Variable rate (% / yr)&lt;/label>
 &lt;div class="two-period-row param-right">
 &lt;input type="range" id="variableRateSlider" min="0.1" max="15" step="0.05" value="4.5" oninput="syncFromSlider('variableRate'); simulate()">
 &lt;input type="number" id="variableRate" value="4.5" min="0.1" max="15" step="0.05" oninput="syncFromNumber('variableRate'); simulate()">
 &lt;/div>
 &lt;div class="param-divider">&lt;/div>
 &lt;div class="param-divider">&lt;/div>
 &lt;label for="extraPayment">Extra payment (€ / mo)&lt;/label>
 &lt;div class="param-right">
 &lt;input type="range" id="extraPaymentSlider" min="0" max="2000" step="50" value="0" oninput="syncFromSlider('extraPayment'); simulate()">
 &lt;input type="number" id="extraPayment" value="0" min="0" max="2000" step="50" oninput="syncFromNumber('extraPayment'); simulate()">
 &lt;/div>
 &lt;button id="calcBtn" onclick="simulate()">Calculate&lt;/button>
 &lt;/div>
&lt;/fieldset>
&lt;div id="summary">&lt;/div>
&lt;div id="charts">
 &lt;div id="chartBalance">&lt;/div>
 &lt;div id="chartPayment" style="display:none">&lt;/div>
 &lt;div id="charts-row">
 &lt;div id="chartBreakdown">&lt;/div>
 &lt;div id="chartAnnual">&lt;/div>
 &lt;/div>
&lt;/div>
&lt;details class="amort-details">
 &lt;summary>Amortization Schedule&lt;/summary>
 &lt;div>
 &lt;table id="amortTable" style="font-size:0.8rem; width:100%;">&lt;/table>
 &lt;/div>
&lt;/details>
&lt;script>
function simulate() {
 const price = parseFloat(document.getElementById('housePrice').value) || 0;
 const down = parseFloat(document.getElementById('downPayment').value) || 0;
 const rate1 = parseFloat(document.getElementById('interestRate').value) || 0;
 const termYears = parseInt(document.getElementById('loanTerm').value) || 0;
 const extra = parseFloat(document.getElementById('extraPayment').value) || 0;
 const twoP = document.getElementById('twoPeriod').checked;
 const fixedYrs = twoP ? Math.min(parseInt(document.getElementById('fixedPeriod').value) || 1, termYears - 1) : termYears;
 const rate2 = twoP ? (parseFloat(document.getElementById('variableRate').value) || 0) : rate1;

 const principal = price - down;
 if (principal &lt;= 0 || rate1 &lt;= 0 || termYears &lt;= 0) return;

 const n = termYears * 12;
 const n1 = fixedYrs * 12;
 const n2 = n - n1;
 const r1 = rate1 / 100 / 12;
 const r2 = rate2 / 100 / 12;

 // Period 1 payment: scheduled for full loan term at r1
 const mp1 = principal * r1 * Math.pow(1 + r1, n) / (Math.pow(1 + r1, n) - 1);
 // Baseline interest with no extra payment (single-period, r1 only) for savings hint
 const baseInterest = mp1 * n - principal;

 // --- Amortization ---
 let balance = principal;
 const months = [], balances = [], interests = [], principals = [], mPayments = [];
 const annualData = {};
 let totalInterest = 0, actualMonths = 0;

 for (let m = 1; m &lt;= (twoP ? n1 : n) &amp;&amp; balance > 0; m++) {
 const ic = balance * r1;
 let pc = mp1 - ic + extra;
 if (pc > balance) pc = balance;
 balance -= pc;
 if (balance &lt; 0.01) balance = 0;
 totalInterest += ic; actualMonths = m;
 months.push(m); balances.push(+balance.toFixed(2));
 interests.push(+ic.toFixed(2)); principals.push(+pc.toFixed(2));
 mPayments.push(+(mp1 + extra).toFixed(2));
 const yr = Math.ceil(m / 12);
 if (!annualData[yr]) annualData[yr] = { interest: 0, principal: 0 };
 annualData[yr].interest += ic; annualData[yr].principal += pc;
 if (balance === 0) break;
 }

 const balance1 = balance;
 let mp2 = 0;

 if (twoP &amp;&amp; n2 > 0 &amp;&amp; balance1 > 0) {
 mp2 = r2 > 0 ? balance1 * r2 * Math.pow(1 + r2, n2) / (Math.pow(1 + r2, n2) - 1) : balance1 / n2;
 for (let m = n1 + 1; m &lt;= n &amp;&amp; balance > 0; m++) {
 const ic = balance * r2;
 let pc = mp2 - ic + extra;
 if (pc > balance) pc = balance;
 balance -= pc;
 if (balance &lt; 0.01) balance = 0;
 totalInterest += ic; actualMonths = m;
 months.push(m); balances.push(+balance.toFixed(2));
 interests.push(+ic.toFixed(2)); principals.push(+pc.toFixed(2));
 mPayments.push(+(mp2 + extra).toFixed(2));
 const yr = Math.ceil(m / 12);
 if (!annualData[yr]) annualData[yr] = { interest: 0, principal: 0 };
 annualData[yr].interest += ic; annualData[yr].principal += pc;
 if (balance === 0) break;
 }
 }

 const yearsActual = (actualMonths / 12).toFixed(1);
 const interestRatio = (totalInterest / (principal + totalInterest) * 100).toFixed(1);
 const transitionYear = twoP &amp;&amp; n2 > 0 &amp;&amp; balance1 > 0 ? fixedYrs : null;

 // --- Summary ---
 const summaryEl = document.getElementById('summary');
 summaryEl.style.display = 'block';

 let heroInner = '';
 if (!twoP || n2 &lt;= 0 || balance1 &lt;= 0) {
 heroInner =
 `&lt;div>&lt;span class="stat-label">Monthly Payment&lt;/span>` +
 `${extra > 0 ? `&lt;div class="stat-savings" style="margin-top:0.3rem">+ ${fmt(extra)} extra &amp;nbsp;=&amp;nbsp; ${fmt(mp1 + extra)} / mo total&lt;/div>` : ''}&lt;/div>` +
 `&lt;span class="stat-value">${fmt(mp1)}&lt;/span>`;
 }

 const extraSaving = !twoP &amp;&amp; extra > 0
 ? `&lt;span class="stat-savings">saves ${fmt(baseInterest - totalInterest)} vs no extra&lt;/span>` : '';

 const bottomCards =
 `&lt;div class="stat-card">&lt;span class="stat-label">Total Interest&lt;/span>&lt;span class="stat-value">${fmt(totalInterest)}&lt;/span>&lt;span class="stat-savings">${interestRatio}% of total paid&lt;/span>&lt;/div>` +
 `&lt;div class="stat-card">&lt;span class="stat-label">Total Paid&lt;/span>&lt;span class="stat-value">${fmt(principal + totalInterest)}&lt;/span>&lt;/div>` +
 `&lt;div class="stat-card">&lt;span class="stat-label">Payoff In&lt;/span>&lt;span class="stat-value">${yearsActual} yrs&lt;/span>${extra > 0 ? `&lt;span class="stat-savings">vs ${termYears} scheduled&lt;/span>` : ''}${extraSaving}&lt;/div>` +
 `&lt;div class="stat-card">&lt;span class="stat-label">Down Payment&lt;/span>&lt;span class="stat-value">${fmt(down)}&lt;/span>&lt;span class="stat-savings">${((down/price)*100).toFixed(1)}% of price&lt;/span>&lt;/div>`;

 if (twoP &amp;&amp; n2 > 0 &amp;&amp; balance1 > 0) {
 summaryEl.innerHTML =
 `&lt;div class="stat-grid">` +
 `&lt;div class="stat-period-card">` +
 `&lt;div>` +
 `&lt;div class="stat-label">Fixed Period&lt;/div>` +
 `&lt;div class="stat-desc">${fixedYrs} years at ${rate1}%&lt;/div>` +
 `&lt;/div>` +
 `&lt;div style="text-align:right">` +
 `&lt;div class="stat-label">Monthly Payment&lt;/div>` +
 `&lt;div class="stat-value">${fmt(mp1)}&lt;/div>` +
 `&lt;/div>` +
 `&lt;/div>` +
 `&lt;div class="stat-period-card">` +
 `&lt;div>` +
 `&lt;div class="stat-label">Variable Period&lt;/div>` +
 `&lt;div class="stat-desc">${n2 / 12} years at ${rate2}%&lt;/div>` +
 `&lt;/div>` +
 `&lt;div style="text-align:right">` +
 `&lt;div class="stat-label">Monthly Payment&lt;/div>` +
 `&lt;div class="stat-value">${fmt(mp2)}&lt;/div>` +
 `&lt;/div>` +
 `&lt;/div>` +
 `&lt;div class="stat-transition">` +
 `&lt;span>Balance at rate change &amp;nbsp;(after ${fixedYrs} yrs)&lt;/span>` +
 `&lt;strong>${fmt(balance1)}&lt;/strong>` +
 `&lt;/div>` +
 `&lt;/div>` +
 `&lt;div class="stat-grid" style="margin-top:0.4rem">${bottomCards}&lt;/div>`;
 } else {
 summaryEl.innerHTML =
 `&lt;div class="stat-grid">` +
 `&lt;div class="stat-card stat-hero">${heroInner}&lt;/div>` +
 `&lt;div class="stat-card">&lt;span class="stat-label">Loan Amount&lt;/span>&lt;span class="stat-value">${fmt(principal)}&lt;/span>&lt;/div>` +
 bottomCards +
 `&lt;/div>`;
 }

 // --- Charts helpers ---
 const yearLabels = months.map(m => +(m / 12).toFixed(2));
 const cfg = plotlyConfig();

 const boundaryShape = transitionYear ? [{
 type: 'line', x0: transitionYear, x1: transitionYear, y0: 0, y1: 1,
 yref: 'paper', line: { color: '#888', width: 1.5, dash: 'dot' }
 }] : [];
 const boundaryNote = transitionYear ? [{
 x: transitionYear, y: 0.96, yref: 'paper', xanchor: 'left',
 text: ` ${rate1}% → ${rate2}%`, showarrow: false,
 font: { size: 10, color: '#555' }
 }] : [];

 // Chart 1: Balance over time
 Plotly.react('chartBalance', [{
 x: yearLabels, y: balances, mode: 'lines', name: 'Remaining balance',
 line: { color: '#000', width: 2 }
 }], {
 title: 'Remaining Balance Over Time',
 xaxis: { title: 'Year', tickformat: '.0f' },
 yaxis: { title: '€', tickformat: ',.0f' },
 shapes: boundaryShape, annotations: boundaryNote, ...plotlyLayout(300)
 }, cfg);

 // Chart 2: Monthly payment (only in two-period mode — the step change is meaningful)
 const chartPaymentEl = document.getElementById('chartPayment');
 if (transitionYear) {
 chartPaymentEl.style.display = '';
 Plotly.react('chartPayment', [{
 x: yearLabels, y: mPayments, mode: 'lines', name: 'Monthly payment',
 line: { color: '#000', width: 2, shape: 'hv' },
 fill: 'tozeroy', fillcolor: 'rgba(0,0,0,0.06)'
 }], {
 title: 'Monthly Payment Over Time',
 xaxis: { title: 'Year', tickformat: '.0f' },
 yaxis: { title: '€', tickformat: ',.0f' },
 shapes: boundaryShape, annotations: boundaryNote, ...plotlyLayout(280)
 }, cfg);
 } else {
 chartPaymentEl.style.display = 'none';
 }

 // Chart 3: Payment breakdown pie
 Plotly.react('chartBreakdown', [{
 labels: ['Principal', 'Total Interest'],
 values: [+principal.toFixed(2), +totalInterest.toFixed(2)],
 type: 'pie',
 marker: { colors: ['#000', '#bbb'] },
 textinfo: 'label+percent',
 hovertemplate: '%{label}: €%{value:,.0f}&lt;extra>&lt;/extra>'
 }], { title: 'Payment Breakdown', ...plotlyLayout(260) }, cfg);

 // Chart 4: Annual principal vs interest stacked bar
 const years = Object.keys(annualData).map(Number);
 const barBoundary = transitionYear ? [{
 type: 'line', x0: transitionYear - 0.5, x1: transitionYear - 0.5, y0: 0, y1: 1,
 yref: 'paper', line: { color: '#888', width: 1.5, dash: 'dot' }
 }] : [];
 Plotly.react('chartAnnual', [
 { x: years, y: years.map(y => +annualData[y].principal.toFixed(2)), type: 'bar', name: 'Principal', marker: { color: '#000' } },
 { x: years, y: years.map(y => +annualData[y].interest.toFixed(2)), type: 'bar', name: 'Interest', marker: { color: '#bbb' } }
 ], {
 title: 'Annual Principal vs Interest',
 barmode: 'stack',
 xaxis: { title: 'Year', dtick: 1 },
 yaxis: { title: '€', tickformat: ',.0f' },
 shapes: barBoundary, ...plotlyLayout(260)
 }, cfg);

 // --- Amortization table ---
 const rateCol = twoP ? '&lt;th>Rate&lt;/th>' : '';
 let rows = `&lt;tr>&lt;th>Month&lt;/th>${rateCol}&lt;th>Payment (€)&lt;/th>&lt;th>Principal (€)&lt;/th>&lt;th>Interest (€)&lt;/th>&lt;th>Balance (€)&lt;/th>&lt;/tr>`;
 for (let i = 0; i &lt; months.length; i++) {
 const isP2 = twoP &amp;&amp; months[i] > n1;
 const isStart = twoP &amp;&amp; months[i] === n1 + 1;
 const cls = isStart ? 'p2-row p2-start' : isP2 ? 'p2-row' : 'p1-row';
 const rateCell = twoP ? `&lt;td>${isP2 ? rate2 : rate1}%&lt;/td>` : '';
 rows += `&lt;tr class="${cls}">&lt;td>${months[i]}&lt;/td>${rateCell}&lt;td>${(interests[i]+principals[i]).toFixed(2)}&lt;/td>&lt;td>${principals[i].toFixed(2)}&lt;/td>&lt;td>${interests[i].toFixed(2)}&lt;/td>&lt;td>${balances[i].toFixed(2)}&lt;/td>&lt;/tr>`;
 }
 document.getElementById('amortTable').innerHTML = rows;
}

function toggleTwoPeriod() {
 const checked = document.getElementById('twoPeriod').checked;
 document.querySelector('.param-grid').classList.toggle('show-two-period', checked);
 document.getElementById('interestRateLabel').textContent =
 checked ? 'Fixed rate (% / yr)' : 'Interest rate (% / yr)';
 if (checked) {
 const termYears = parseInt(document.getElementById('loanTerm').value) || 30;
 const fp = document.getElementById('fixedPeriod');
 const fps = document.getElementById('fixedPeriodSlider');
 fp.max = fps.max = termYears - 1;
 if (parseFloat(fp.value) >= termYears) fp.value = fps.value = Math.max(1, termYears - 1);
 }
 simulate();
}

function syncFromSlider(id) {
 document.getElementById(id).value = document.getElementById(id + 'Slider').value;
 if (id === 'downPayment') updateDownHint();
}

function syncFromNumber(id) {
 const input = document.getElementById(id);
 const slider = document.getElementById(id + 'Slider');
 const v = parseFloat(input.value);
 if (!isNaN(v)) slider.value = v;
 if (id === 'downPayment' || id === 'housePrice') updateDownHint();
 if (id === 'loanTerm') {
 const fp = document.getElementById('fixedPeriod');
 const fps = document.getElementById('fixedPeriodSlider');
 fp.max = fps.max = Math.max(1, (v || 30) - 1);
 if (parseFloat(fp.value) >= v) fp.value = fps.value = Math.max(1, v - 1);
 }
}

function updateDownHint() {
 const price = parseFloat(document.getElementById('housePrice').value) || 0;
 const down = parseFloat(document.getElementById('downPayment').value) || 0;
 const pct = price > 0 ? (down / price * 100).toFixed(1) : '0.0';
 document.getElementById('downHint').textContent = pct + '% of price';
}

function fmt(n) {
 return '€' + n.toLocaleString('en-IE', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function plotlyLayout(height) {
 return {
 height: height || 300,
 font: { family: 'Mono, monospace', size: 12 },
 paper_bgcolor: '#ffffff', plot_bgcolor: '#ffffff',
 margin: { t: 40, r: 20, b: 50, l: 60 },
 legend: { orientation: 'h', y: -0.2 }
 };
}

function plotlyConfig() {
 return { responsive: true, displayModeBar: false };
}

document.addEventListener('DOMContentLoaded', simulate);
&lt;/script></description></item><item><title>Projects</title><link>https://jorgemartinez.space/projects.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/projects.html</guid><description>&lt;h2 id="projects">Projects&lt;/h2>
&lt;p>In my free time, I enjoy collaborating with open-source projects and exploring
new technologies that can benefit both my personal growth and the company I
work for.&lt;/p>
&lt;p>This page showcases a variety of projects I contribute to. Links to their source
code are provided. Explore them and see what interests you.&lt;/p>
&lt;p>For a complete overview of my work and activity, visit &lt;a href="https://github.com/jorgepiloto">my GitHub profile&lt;/a>.&lt;/p>
&lt;br>
&lt;h3 id="poliastro-astrodynamics-in-python">poliastro: astrodynamics in Python&lt;/h3>
&lt;p>poliastro is an open source (MIT) pure Python library for interactive
Astrodynamics and Orbital Mechanics, with a focus on ease of use, speed, and
quick visualization. It provides a simple and intuitive API, and handles
physical quantities with units.&lt;/p></description></item><item><title>Publications</title><link>https://jorgemartinez.space/publications.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/publications.html</guid><description>&lt;h2 id="publications">Publications&lt;/h2>
&lt;p>This page contains all my publications. All of them can be freely downloaded.
Data and source code are freely accessible ensuring reproducible results.&lt;/p>
&lt;p>If you encounter any problems when downloading those or find any errata, please
&lt;a href="mailto:contact@jorgemartinez.space">contact me&lt;/a>.&lt;/p>
&lt;br>
&lt;h3 id="a-critical-review-on-the-state-of-the-art-of-lamberts-problem">A critical review on the state-of-the-art of Lambert&amp;rsquo;s problem&lt;/h3>
&lt;p>&lt;img src="./publications/img/icatt_critical_review_lamberts_problem.png" alt="A critical review on the state-of-the-art of Lambert&amp;rsquo;s problem">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>@inproceedings{martinez2021_icatt,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> author = {Jorge Martínez and Manuel Sanjurjo},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> title = {A critical review on the state-of-the-art of Lambert&amp;#39;s problem solvers},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> booktitle = {International Conference on Astrodynamics Tools and Techniques (ICATT)},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> year = {2021}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>

&lt;p align="center">
 &lt;a class="tag" href="https://github.com/lamberthub/lamberthub/raw/main/art/icatt_report.pdf">
 &lt;b>Download this article&lt;/b>
 &lt;/a>
&lt;/p></description></item><item><title>Search Results</title><link>https://jorgemartinez.space/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/search/</guid><description/></item><item><title>Tags</title><link>https://jorgemartinez.space/tags/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jorgemartinez.space/tags/</guid><description/></item></channel></rss>