{"@attributes":{"version":"2.0"},"channel":{"title":"Jacobo Tarr\u00edo","link":"https:\/\/jacobo.tarrio.org\/","description":{},"item":[{"title":"Project Spite","link":"https:\/\/jacobo.tarrio.org\/2022\/project-spite.html","description":"<p>I saw my first demo in 1993 or so. It was magical: I would run a program, and a\ndisplay of shapes, effects, and music would appear on my computer, all rendered\nin real time. One day, after watching one of those demos, still humming its\nmusic, I started my editor to try and code a simple 3D cube, when my little\nbrother said:<\/p>\n<blockquote>\n<p>\u201cOh, so <em>you<\/em> think you&rsquo;re going to be able to code <em>that<\/em>&hellip;\u201d<\/p>\n<\/blockquote>\n<p>He was 10 or 11 and probably just wanted me to get off the computer so he could\nplay a game, but it hurt me a little at the time.<\/p>\n<p>So, of course, nearly 30 years later I still remember it and I dedicate this\nproject to that memory.<\/p>\n<p>Last April I realized that, one year from now, it will be the 30th anniversary\nof <a href=\"https:\/\/en.wikipedia.org\/wiki\/Second_Reality\">Second Reality<\/a>, and thought\nthat:<\/p>\n<ol>\n<li>It would be cool if someone would write an HTML5 remake for 2023.<\/li>\n<li>Hey, I&rsquo;m someone.<\/li>\n<\/ol>\n<p>So I started working on it.<\/p>\n<p>Now, about a month later I realized that I had never written a demo before,\nit was taking me a long time to get something going, that I have little to\nno idea what the names of the different effects in the demo are, much less how\nto code them, and that I don&rsquo;t have enough artistic ability to redo the drawings\nfor a 1080p or 4k resolution world.<\/p>\n<p>Therefore, I decided to complete the intro, put a ribbon on it, release it\nalong with the <a href=\"https:\/\/github.com\/jtarrio\/spite\">source code<\/a>, and see if\nanyone would like to build upon it.<\/p>\n<p><a href=\"..\/assets\/2022\/spite\/index.html\">You can see the demo here.<\/a><\/p>\n<p>You can exit the demo at any time pressing Enter or \u201cQ\u201d. It works at a 1080p\nresolution, fullscreen. It should work in a computer but also on a phone or\ntablet.<\/p>\n<p>Many of the assets were reused or adapted from the original Second Reality demo,\nwhose source code was published 10 year ago, for the 20th anniversary. I\nrepainted the planet landscape so it doesn&rsquo;t look all blocky and upscaled.<\/p>\n<p>The music is played in real time from the original S3M, of course; I wrote the\nplayer myself, though it is pretty much an \u201conly needs to be able to play\none song\u201d affair.<\/p>\n<p>Let me know what you think!<\/p>\n","pubDate":"Mon, 04 Jul 2022 00:00:00 +0000"},{"title":"Do not overuse primitive data types","link":"https:\/\/jacobo.tarrio.org\/2021\/do-not-overuse-primitive-data-types.html","description":"<p>Ask a modern programmer to design a program for you, and they&rsquo;ll soon give you a diagram full of classes\nnamed <code>Customer<\/code> and <code>Employee<\/code> and <code>User<\/code> and <code>UserDAO<\/code> and <code>ConnectionProvider<\/code> and <code>ConnectionProviderFactory<\/code>\nand <code>AbstractConnectionProviderFactoryImpl<\/code>.<\/p>\n<p>Ok, ok, you&rsquo;re right: only Java programmers will get so mired down in this taxonomy hell, but it is true\nthat, nowadays, every programmer knows how to use some basic object orientation principles. Even with non-OOP\nlanguages, coders will create and use <code>struct<\/code>s or <code>record<\/code>s or <code>Type<\/code>s or whatever mechanism the language provides\nto create new data types.<\/p>\n<p>However, even though programmers in 2021 tend to consider ourselves wiser and more enlightened than our\nforebears, our programs still contain vestiges of the olden days when Fortran and COBOL and BASIC only gave us a\nsmall set of primitive data types. Every time we want to store a primary key for a database record, we put it in\na <code>long<\/code> or <code>string<\/code>-typed variable. If we want to pass a timestamp to a function, we use an <code>int<\/code> or <code>long<\/code>\nargument. What about a function that returns a UUID? We use a <code>string<\/code>. A text string that we just got from the\nuser? A text string that is ready to insert in an HTML document? Both, <code>string<\/code>s.<\/p>\n<p>We use the same data types, over and over again, for things as different from one another as primary keys from\nseveral tables, timestamps in seconds and milliseconds, URIs, and UUIDs. This is an extremely common practice\nthat is, also, an extremely common cause of bugs. Who hasn&rsquo;t ever written some code that tries to look up a\nrecord in the wrong table, or that compares seconds and microseconds, or that inserts an unescaped text string\nin an HTML document?<\/p>\n<p>In this post, I describe three strategies to deal with those objects, avoid mistakes like the ones above, and\navoid introducing bugs.<\/p>\n<h1>Use new types for new entities<\/h1>\n<p>Have you heard of the <a href=\"http:\/\/web.archive.org\">Internet Archive<\/a>? It&rsquo;s a non-profit organization that wants to\ncreate an &ldquo;Internet Library&rdquo; by archiving every webpage in the world, to preserve them so we can look at them\nin the future.<\/p>\n<p>As you might guess, the Internet Archive does a lot of work with URIs. Every webpage has its own URI and also\ncontains other URIs for all the other webpages it links to. In the Archive, webpages are indexed by their URIs\nso users can easily access them. In summary, for the Internet Archive, URIs are its currency.<\/p>\n<p>Programmers are always tempted to store URIs in variables of type <code>string<\/code>. We see a sequence of alphanumeric\ncharacters and we think: &ldquo;ooh, that&rsquo;s easy: it&rsquo;s a text string, therefore it&rsquo;s of type <code>string<\/code>.&rdquo; However, URIs\naren&rsquo;t just text strings. URIs have certain rules they must follow at all times: they must have a certain format,\nthere are some characters that are not valid, there are escape sequences, etc.<\/p>\n<p>When we store a URI in a <code>string<\/code>-typed variable, the system knows nothing about those rules. The variable might\ncontain a malformed URI, or even something that is no URI at all, and nothing would warn us until we try to use\nit and something breaks. Therefore, we need to write (and always use) code that, every time we assign a new value\nto the variable, will validate and normalize it. Should we fail to do that every single time, we would expose\nourselves to a multitude of possible programming errors. We might not be able to find the page the user is looking\nfor, we might open our page to an XSS attack, or something in between.<\/p>\n<p>To solve this problem most effectively, we need to acknowledge the fact that a URI is not a text string that we\ncan handle using only <code>string<\/code>-typed variables; we need to use a specific data type that will know and enforce\nall the rules and format of a URI. This <code>URI<\/code> data type must provide operations to convert a text string into an\nobject of type <code>URI<\/code> by applying all the validation and normalization rules, and it must also provide operations\nto extract the different parts of the URI and get a representation of the URI in the form of a text string.<\/p>\n<p>Most modern programming languages provide a class named <code>URI<\/code> in their standard library, so we only need to\nremember to use it instead of <code>string<\/code> anywhere that we deal with URIs.<\/p>\n<p>The Internet Archive&rsquo;s home page has a text box where the user can write a URI to see what that URI contained\nat a time in the past. The web server receives the web request, which contains the URI in the form of a string;\nthe first thing the web server does is to convert that string into an object of type <code>URI<\/code>. From this point on,\nthe URI is validated and formatted, so the server can just pass it along to the storage subsystem, which can\nretrieve the content of the webpage.<\/p>\n<hr>\n<p>Sometimes we can&rsquo;t use classes provided by our standard library, so we have to write them ourselves. For example,\nseveral years ago, I worked in a project that used a database that stored objects that were indexed through an\nidentifier that followed a <code>HHHHHHHH-V<\/code> format, where <code>H<\/code> was a hexadecimal digit, and <code>V<\/code> was a version number\nwith one or more decimal digits.<\/p>\n<p>The original authors of the system hadn&rsquo;t agreed on a single way to handle those identifiers. In some parts of the\nprogram, the identifier was stored in a <code>string<\/code> variable. In other parts, the identifier was split in two:\na <code>string<\/code> that contained the hexadecimal digits, and an <code>int<\/code> that contained the version number. Our code often\nlooked a bit like this:<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> List&lt;String&gt; <span style=\"color:#c34e00\">findLinkedIds<\/span>(String id)\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">throws<\/span> IllegalArgumentException, NotFoundException {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">if<\/span> (!isValid(id)) {\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">throw<\/span> <span style=\"color:#00f\">new<\/span> IllegalArgumentException(<span style=\"color:#009c00\">&#34;id&#34;<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>    }\n<\/span><\/span><span style=\"display:flex;\"><span>    String hexa = extractHexa(id);\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">int<\/span> version = extractVersion(id);\n<\/span><\/span><span style=\"display:flex;\"><span>    Record record = lookupRecord(hexa, version);\n<\/span><\/span><span style=\"display:flex;\"><span>    List&lt;String&gt; linkedIds = <span style=\"color:#00f\">new<\/span> ArrayList&lt;&gt;();\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">for<\/span> (Record linked : record.getLinkedRecords()) {\n<\/span><\/span><span style=\"display:flex;\"><span>        linkedIds.add(linked.getHexa() + <span style=\"color:#009c00\">&#34;-&#34;<\/span> + linked.getVersion());\n<\/span><\/span><span style=\"display:flex;\"><span>    }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">return<\/span> linkedIds;\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> Record <span style=\"color:#c34e00\">lookupRecord<\/span>(String hexa, <span style=\"color:#00f\">int<\/span> version) {\n<\/span><\/span><span style=\"display:flex;\"><span>    Query query = createQuery(<span style=\"color:#009c00\">&#34;SELECT * FROM Records WHERE hexa=?, version=?&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>                              hexa, version);\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">return<\/span> transform(query.execute());\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><p>As you can see, it was a mess. We spent half of our time validating identifiers and splitting them in two and\nreassembling them and chasing bugs caused by places where we had forgotten to validate or where we had passed\nan identifier when we needed just the hexadecimal part or vice versa.<\/p>\n<p>The solution to this problem came after we admitted that an identifier was a new type and not just a text\nstring, and we created a new class for storing and handling identifiers.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">class<\/span> <span style=\"color:#007575\">Identifier<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> String hexa;\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> <span style=\"color:#00f\">int<\/span> version;\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> <span style=\"color:#c34e00\">Identifier<\/span>(String hexa, <span style=\"color:#00f\">int<\/span> version) {\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">this<\/span>.hexa = hexa;\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">this<\/span>.version = version;\n<\/span><\/span><span style=\"display:flex;\"><span>    }\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> String <span style=\"color:#c34e00\">getHexa<\/span>() { <span style=\"color:#00f\">return<\/span> hexa; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">int<\/span> <span style=\"color:#c34e00\">getVersion<\/span>() { <span style=\"color:#00f\">return<\/span> version; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> String <span style=\"color:#c34e00\">toString<\/span>() { <span style=\"color:#00f\">return<\/span> hexa + <span style=\"color:#009c00\">&#34;-&#34;<\/span> + version; }\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">static<\/span> Identifier <span style=\"color:#c34e00\">create<\/span>(String id) <span style=\"color:#00f\">throws<\/span> IllegalArgumentException {\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#f00;font-style:italic\">\/\/ Validate and extract the parts of the identifier\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>        ...\n<\/span><\/span><span style=\"display:flex;\"><span>        String hexa = ...;\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">int<\/span> version = ...;\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Identifier(hexa, version);\n<\/span><\/span><span style=\"display:flex;\"><span>    }\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><p>And, with this, we could simplify our code, avoid the constant validations, and handle identifiers consistently\nthroughout the program, removing hundreds of lines of code and opportunities for bugs.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> List&lt;Identifier&gt; <span style=\"color:#c34e00\">findLinkedIds<\/span>(Identifier id)\n<\/span><\/span><span style=\"display:flex;\"><span>        <span style=\"color:#00f\">throws<\/span> NotFoundException {\n<\/span><\/span><span style=\"display:flex;\"><span>    Record record = lookupRecord(id);\n<\/span><\/span><span style=\"display:flex;\"><span>    List&lt;Identifier&gt; linkedIds = <span style=\"color:#00f\">new<\/span> ArrayList&lt;&gt;();\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">for<\/span> (Record linked : record.getLinkedRecords()) {\n<\/span><\/span><span style=\"display:flex;\"><span>        linkedIds.add(linked.getIdentifier());\n<\/span><\/span><span style=\"display:flex;\"><span>    }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">return<\/span> linkedIds;\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> Record <span style=\"color:#c34e00\">lookupRecord<\/span>(Identifier id)\n<\/span><\/span><span style=\"display:flex;\"><span>    Query query = createQuery(<span style=\"color:#009c00\">&#34;SELECT * FROM Records WHERE hexa=?, version=?&#34;<\/span>,\n<\/span><\/span><span style=\"display:flex;\"><span>                              identifier.getHexa(), identifier.getVersion());\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">return<\/span> transform(query.execute());\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><h1>Use different types for different entities<\/h1>\n<p>In the world of databases, there are two types of people: those who use <code>INT<\/code>s for primary keys, and those who use\nUUIDs. Whatever type of person your DBA is, your code is going to be full of variables, all belonging to the same\ntype, that contain primary keys for several different tables.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">long<\/span> idPost = getLongParam(<span style=\"color:#009c00\">&#34;idPost&#34;<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">long<\/span> idComment = getLongParam(<span style=\"color:#009c00\">&#34;idComment&#34;<\/span>);\n<\/span><\/span><span style=\"display:flex;\"><span>Comment comment = loadComment(idPost, idComment);\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">long<\/span> idUser = comment.userId();\n<\/span><\/span><span style=\"display:flex;\"><span>User user = loadUser(idPost);\n<\/span><\/span><span style=\"display:flex;\"><span>outputTemplate(<span style=\"color:#009c00\">&#34;comment&#34;<\/span>, user, comment);\n<\/span><\/span><\/code><\/pre><p>This could be an excerpt from the source code of a CMS: a function that handles the response to an HTTP <code>GET<\/code>\noperation to display a comment to a post. This function receives a post identifier and a comment identifier, loads\nthe comment and the data of the user who posted the comment, and outputs them as HTML to display them in the browser.<\/p>\n<p>The database uses integer numbers for the primary keys, and the language stores them in variables of type <code>long<\/code>.\nThere is one key for the post, another for the comment, and yet another one for the user. And, since all three\nvariables have the same type, it&rsquo;s very easy to mix them up without noticing. In fact, the code above has a bug.\nHow long does it take you to find it?<\/p>\n<p>Even though all primary keys are stored in variables belonging to the same type, they are not interchangeable. If\nwe pass a user id to a function that expects a post id, this function will give us an incorrect result and we will\nnot notice this slip-up until we run the program and we notice the error. Or, even worse, until a user sees it and\nnotifies us. Or, worst of all, we never realize and end up with corrupted data.<\/p>\n<p>If those keys are not interchangeable in practice, they shouldn&rsquo;t be interchangeable in the code either. We can\nachieve this very easily using different classes for each different table&rsquo;s primary keys.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span>IdPost idPost = <span style=\"color:#00f\">new<\/span> IdPost(getLongParam(<span style=\"color:#009c00\">&#34;idPost&#34;<\/span>));\n<\/span><\/span><span style=\"display:flex;\"><span>IdComment idComment = <span style=\"color:#00f\">new<\/span> IdComment(getLongParam(<span style=\"color:#009c00\">&#34;idComment&#34;<\/span>));\n<\/span><\/span><span style=\"display:flex;\"><span>Comment comment = loadComment(idPost, idComment);\n<\/span><\/span><span style=\"display:flex;\"><span>IdUser idUser = comment.userId();\n<\/span><\/span><span style=\"display:flex;\"><span>User user = loadUser(idPost); <span style=\"color:#f00;font-style:italic\">\/\/ The compiler throws an error here.\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>outputTemplate(<span style=\"color:#009c00\">&#34;comment&#34;<\/span>, user, comment);\n<\/span><\/span><\/code><\/pre><p>It&rsquo;s extremely easy to create these classes in most modern programming languages. For example, in Java we can\ncreate a base class that incorporates all the functionality and then add one new line of code for each new type.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">abstract<\/span> <span style=\"color:#00f\">class<\/span> <span style=\"color:#007575\">LongId<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> <span style=\"color:#00f\">long<\/span> id;\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#c34e00\">LongId<\/span>(<span style=\"color:#00f\">long<\/span> id) { <span style=\"color:#00f\">this<\/span>.id = id; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">long<\/span> <span style=\"color:#c34e00\">getId<\/span>() { <span style=\"color:#00f\">return<\/span> id; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> String <span style=\"color:#c34e00\">toString<\/span>() { <span style=\"color:#00f\">return<\/span> getClass().getSimpleName() + <span style=\"color:#009c00\">&#34;=&#34;<\/span> + id; }\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> IdPost <span style=\"color:#00f\">extends<\/span> LongId { <span style=\"color:#00f\">public<\/span> <span style=\"color:#c34e00\">IdPost<\/span>(<span style=\"color:#00f\">long<\/span> id) { <span style=\"color:#00f\">super<\/span>(id); } }\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> IdComment <span style=\"color:#00f\">extends<\/span> LongId { <span style=\"color:#00f\">public<\/span> <span style=\"color:#c34e00\">IdComment<\/span>(<span style=\"color:#00f\">long<\/span> id) { <span style=\"color:#00f\">super<\/span>(id); } }\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> IdUser <span style=\"color:#00f\">extends<\/span> LongId { <span style=\"color:#00f\">public<\/span> <span style=\"color:#c34e00\">IdUser<\/span>(<span style=\"color:#00f\">long<\/span> id) { <span style=\"color:#00f\">super<\/span>(id); } }\n<\/span><\/span><\/code><\/pre><hr>\n<p>This technique is also useful in data processing tasks, where we might handle data in several different\nstages of processing and we don&rsquo;t want to mix them up. The most common use nowadays is in avoiding XSS\nin web apps.<\/p>\n<p>Many web apps need to receive a text string from the user, process it in some way, and finally display\nit in a web page. If you aren&rsquo;t careful, you will just stick that text string directly in the HTML and\ncreate an XSS attack; at a minimum, you need to <em>escape<\/em> that text string before inserting it into the\nHTML. Sometimes, web app programmers will lose track of their strings and will escape a string twice, and\nthen users will see weird stuff like &ldquo;r&amp;eacute;sum&amp;eacute;&rdquo; instead of &ldquo;r\u00e9sum\u00e9&rdquo;. Other times, they will\nuse the wrong type of escape, such as a SQL escape, and users will end up seeing &ldquo;O\\'Connell&rdquo; instead\nof &ldquo;O'Connell&rdquo;.<\/p>\n<p>To avoid this problem, many modern web frameworks don&rsquo;t let programmers insert a <code>string<\/code> directly.\nInstead, they force us to use special types that contain already-escaped text strings. We can create an\ninstance of one of those types from an unescaped string, and there is no way to make a mistake from that\npoint on: wherever we use that class, it contains a string that is already escaped and ready to insert\ninto an HTML page.<\/p>\n<h1>Use a single type for a single entity<\/h1>\n<p>It&rsquo;s very common to use <code>int<\/code>s and <code>long<\/code>s to represent time intervals. However, we haven&rsquo;t agreed on\nwhich unit to use. The Unix operating system uses seconds, but the Java and JavaScript programming languages\nuse milliseconds. I&rsquo;ve worked on systems that used microseconds and nanoseconds. Very often, we need to\nuse different units in different parts of the same program, depending on who wrote the code or which\nfunction takes the argument.<\/p>\n<p>The problem is that all those time intervals are represented as a plain <code>long<\/code>, without any kind of\nindication of the time unit being used, so it&rsquo;s very easy to pass a number of milliseconds to a function\nthat expects seconds, or subtract microseconds from nanoseconds, or perform other similar nonsensical\noperations by mistake.<\/p>\n<p>We might be tempted to try to solve this problem by creating a new type for each unit. A class <code>Seconds<\/code>\nwould store an interval measured in seconds, a class <code>Milliseconds<\/code> would store another interval measured\nin milliseconds, and so forth. In this way, we could not mix different units without the compiler giving\nus an error.<\/p>\n<p>The problem is that, very frequently, we need to convert between different units. Sometimes we have a\nfunction that returns seconds, which we have to pass to another function that expects milliseconds, so\nwe need to perform a conversion. Other times, we have to combine two intervals that could be measured in\ndifferent units. We might try to solve this by adding conversion and addition and subtraction functions\nfor each pair of units; however, with only four units, it would yield <em>forty-four<\/em> functions in total.\nThat&rsquo;s a lot of functions.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">class<\/span> <span style=\"color:#007575\">Seconds<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Milliseconds <span style=\"color:#c34e00\">milliseconds<\/span>() { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Microseconds <span style=\"color:#c34e00\">microseconds<\/span>() { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Nanoseconds <span style=\"color:#c34e00\">nanoseconds<\/span>() { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">plus<\/span>(Seconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">plus<\/span>(Milliseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">plus<\/span>(Microseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">plus<\/span>(Nanoseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">minus<\/span>(Seconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">minus<\/span>(Milliseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">minus<\/span>(Microseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Seconds <span style=\"color:#c34e00\">minus<\/span>(Nanoseconds other) { <span style=\"color:#00f\">return<\/span> ... }\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\">\/\/ Do this three more times for Milliseconds, Microseconds, and Nanoseconds\n<\/span><\/span><\/span><\/code><\/pre><p>Frankly, this option is not sustainable. The amount of code we need to write as soon as we need to add\none more unit or one more operation will quickly become gargantuan, and, moreover, most of it will\nconsist of conversion operations.<\/p>\n<p>The actual solution comes from realizing that the entity we are creating data types for is not the number\nof seconds, milliseconds, or microseconds. <em>The entity is the time interval<\/em>, and all those units are only\ndifferent ways to measure it. We don&rsquo;t need to create a new type for each time unit; we only need to create\na single type for time intervals, which contain all the operations we need to express those intervals in\nthe appropriate units.<\/p>\n<p>For example, we could have a type <code>Interval<\/code> that contains a variable for the length of the interval in a\nconvenient unit, and that also contains functions to express that interval in seconds, milliseconds,\nmicroseconds, and nanoseconds, along with other functions to perform the reverse conversion.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">class<\/span> <span style=\"color:#007575\">Interval<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> <span style=\"color:#00f\">double<\/span> value;\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">private<\/span> <span style=\"color:#c34e00\">Interval<\/span>(<span style=\"color:#00f\">double<\/span> value) { <span style=\"color:#00f\">this<\/span>.value = value; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f00;font-style:italic\">\/\/ Constructors\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">static<\/span> Interval <span style=\"color:#c34e00\">fromSeconds<\/span>(<span style=\"color:#00f\">long<\/span> seconds) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(seconds); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">static<\/span> Interval <span style=\"color:#c34e00\">fromMilliseconds<\/span>(<span style=\"color:#00f\">long<\/span> milliseconds) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(milliseconds \/ 1e3); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">static<\/span> Interval <span style=\"color:#c34e00\">fromMicroseconds<\/span>(<span style=\"color:#00f\">long<\/span> microseconds) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(microseconds \/ 1e6); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">static<\/span> Interval <span style=\"color:#c34e00\">fromNanoseconds<\/span>(<span style=\"color:#00f\">long<\/span> nanoseconds) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(nanoseconds \/ 1e9); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f00;font-style:italic\">\/\/ Conversions\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">long<\/span> <span style=\"color:#c34e00\">seconds<\/span>() { <span style=\"color:#00f\">return<\/span> (<span style=\"color:#00f\">long<\/span>) value; }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">long<\/span> <span style=\"color:#c34e00\">milliseconds<\/span>() { <span style=\"color:#00f\">return<\/span> (<span style=\"color:#00f\">long<\/span>) (value * 1e3); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">long<\/span> <span style=\"color:#c34e00\">microseconds<\/span>() { <span style=\"color:#00f\">return<\/span> (<span style=\"color:#00f\">long<\/span>) (value * 1e6); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> <span style=\"color:#00f\">long<\/span> <span style=\"color:#c34e00\">nanoseconds<\/span>() { <span style=\"color:#00f\">return<\/span> (<span style=\"color:#00f\">long<\/span>) (value * 1e9); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#f00;font-style:italic\">\/\/ Operations\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>    <span style=\"color:#00f\">public<\/span> Interval <span style=\"color:#c34e00\">plus<\/span>(Interval other) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(value + other.value); }\n<\/span><\/span><span style=\"display:flex;\"><span>    <span style=\"color:#00f\">public<\/span> Interval <span style=\"color:#c34e00\">minus<\/span>(Interval other) { <span style=\"color:#00f\">return<\/span> <span style=\"color:#00f\">new<\/span> Interval(value - other.value); }\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><p>And that&rsquo;s all the code we need to represent time intervals measured in four different units! Moreover, if\nwe ever needed to add more units, we would only need to add two functions.<\/p>\n<p>Now we could use this class to replace, in our code, all those <code>long<\/code>s expressed in indeterminate units,\nand remove all the chances for mistakes and bugs.<\/p>\n<pre tabindex=\"0\" style=\"background-color:#fff;\"><code><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\">\/\/ Old and busted.\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\">\/\/ Is the timeout in seconds? Milliseconds? Fortnights?\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>Record <span style=\"color:#c34e00\">lookupRecord<\/span>(Identifier id, <span style=\"color:#00f\">long<\/span> timeout) { ... }\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\">\/\/ New hotness.\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#f00;font-style:italic\"><\/span>Record <span style=\"color:#c34e00\">lookupRecord<\/span>(Identifier id, Interval timeout) { ... }\n<\/span><\/span><\/code><\/pre><p>Many modern programming languages provide a class like <code>Interval<\/code> in their standard library. For example,\nJava has the <code>java.time<\/code> package, which provides the <code>Duration<\/code> class (along with another class, called\n<code>Instant<\/code>, which represents a particular moment in time.) The C++ language provides the <code>std::chrono<\/code>\nnamespace, with its classes <code>duration<\/code> and <code>time_point<\/code>. For other languages, there may be a third-party\nlibrary that provides it. Always use those classes; never use <code>long<\/code>s for time.<\/p>\n<h1>Conclusion<\/h1>\n<p>Sometimes, we resort to using primitive data types without thinking, when it might end up causing us\nterrible headaches. We should stop to think whether that number we are storing in that <code>long<\/code> is\n<em>really<\/em> just a number, or whether what&rsquo;s in that <code>string<\/code> is <em>only<\/em> a text string, and create and\nuse new data types whenever we find that they have some kind of nuance or restriction or invariant.<\/p>\n<p>We humans know we cannot add two cherries and three oranges together. However, if computers only\nsee two <code>long<\/code>s or two <code>double<\/code>s, they will happily add them and divide them. It falls on us, humans\nwho know that different entities belong to different types, to tell the computer about this difference\nby using different data types for each.<\/p>\n<p>And, finally, we humans know that 60 seconds is the same thing as a minute, or that 5,000 meters are\n5 kilometers; however, the computer only sees one variable that says &ldquo;60&rdquo; and another that says &ldquo;1&rdquo;,\nor a &ldquo;5000&rdquo; value and another &ldquo;5&rdquo;. It&rsquo;s on us to tell the computer that both things are the same.<\/p>\n<p>Next time you think that it would be cool if you could &ldquo;annotate&rdquo; or &ldquo;mark&rdquo; a number or a text string\nto treat it especially, try creating and using a new data type. I&rsquo;m sure that it will make your\nprograms more reliable and easier to read and modify.<\/p>\n","pubDate":"Tue, 06 Jul 2021 00:00:00 +0000"},{"title":"The bandwidth of a Morse Code signal","link":"https:\/\/jacobo.tarrio.org\/2020\/bandwidth-of-a-cw-signal.html","description":"<p>In most countries, you need to pass an examination to get an amateur radio\nlicense. In the US, the question pool is public, so a big part of studying\nfor the license consists of going through the whole question pool and making\nsure you know the answers to every question. One of them tripped me for a bit:<\/p>\n<pre><code>T8A11\nWhat is the approximate maximum bandwidth required to transmit a CW signal?\n    A. 2.4 kHz\n    B. 150 Hz\n    C. 1000 Hz\n    D. 15 kHz\n<\/code><\/pre>\n<p>(To normal people, a CW signal is a &ldquo;beeping Morse code signal&rdquo;.)<\/p>\n<p>Now, a CW signal is pretty much a sinusoidal wave, and I knew that a pure\nsinusoidal wave takes a tiny, tiny bandwidth, so I could eliminate answers\n&ldquo;A&rdquo;, &ldquo;C&rdquo;, and &ldquo;D&rdquo; straight away. That left &ldquo;B&rdquo;, but I didn&rsquo;t know why it\nwould be the right answer, so I had to think about it for a while.<\/p>\n<p>It is true that a sinusoidal signal takes very little bandwidth. If a CW\nsignal consisted just of a steady sinusoidal carrier that never turned off\nand on, it would indeed have an extremely low bandwidth, only limited by the\ntransmitting oscillator&rsquo;s stability.<\/p>\n<p>However, a CW signal is not a steady sinusoidal; it is modulated. In\nparticular, it is modulated by turning it off and on according to a message\nthat&rsquo;s encoded using Morse code. This modulation causes the CW signal to\nhave a bigger bandwidth than a steady carrier.<\/p>\n<p>As an extreme, we can imagine that we want to transmit a series of Morse\ndots at 30 words per minute. That would be equivalent to switching the\ncarrier on and off 25 times every second. That&rsquo;s a 12.5-Hz signal that,\nwhen modulated, requires a 25-Hz bandwidth at the <em>very<\/em> minimum (12.5 Hz\non each sideband). In practice, the required bandwidth would be higher,\ndepending on how abruptly the carrier was switched on and off.<\/p>\n<hr>\n<p>To demonstrate it, I wrote a widget to simulate a CW signal being received by\na CW radio with a filter centered at 600 Hz. It can play the received signal\non your speakers and show its frequency spectrum on your screen. The top half\ndisplays an instantaneous chart (with horizontal lines every 40 dB), while\nthe bottom displays a waterfall plot. The vertical lines indicate the\nfrequency of the received signal, with dashed lines every 500 Hz and\ncontinuous lines every 1000 Hz.<\/p>\n<p>Let&rsquo;s first look at (and listen to) a transmitter that produces a signal with\nvery abrupt off\/on and on\/off transitions. Go ahead and press &ldquo;Play&rdquo;:<\/p>\n<iframe src=\"..\/assets\/2020\/morsewidget\/widget.html\" width=\"200\" height=\"300\" frameborder=\"0\" style=\"display: block; margin: auto;\"><\/iframe>\n<p>As you can see, when the carrier is steady on, the signal does not use much\nbandwidth; it is when the signal switches on or off that it uses a lot of\nbandwidth. This bandwidth usage depends on how sudden the on\/off transitions\nare. Above, the switches were instantaneous, so the signal uses a lot of\nbandwidth, which is not good.<\/p>\n<p>To avoid using so much bandwidth, many radio transmitters ramp the signal\nup and down over 5 milliseconds instead of cutting it on and off. This\nlowers the bandwidth usage without really affecting the sound. You can check\nit out by pressing &ldquo;Play&rdquo; on the widget below:<\/p>\n<iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#envelope=sine\" width=\"200\" height=\"300\" frameborder=\"0\" style=\"display: block; margin: auto;\"><\/iframe>\n<p>That&rsquo;s not the whole story, however. The widgets above simulate a radio with\na receive filter, so they don&rsquo;t show the whole bandwidth that&rsquo;s used by\nthe signals. The widgets below had their filters removed, so they can\nshow how much bandwidth is really used in each case. The one on the left is\nthe original signal that switches on and off suddenly, while the one on the\nright is the modified signal with 5-millisecond transitions:<\/p>\n<div style=\"margin: auto; width: fit-content;\">\n  <iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#nofilter=true\" width=\"200\" height=\"300\" frameborder=\"0\"><\/iframe>\n  <iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#envelope=sine&nofilter=true\" width=\"200\" height=\"300\" frameborder=\"0\"><\/iframe>\n<\/div>\n<p>The difference is undeniable. On the left, the on\/off transitions appear\nas broadband energy spikes that are almost as powerful as the signal itself\nacross the whole band. On the right, the spikes still produce quite a bit of\npower, but it occupies a smaller bandwidth and their power goes down faster\nas they get further from the central frequency.<\/p>\n<p>The huge amount of power produced by sudden on\/off switches causes an annoying\neffect, called &ldquo;key clicks&rdquo;. Press &ldquo;Play&rdquo; on the widget below to hear it:<\/p>\n<iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#frequency=3400\" width=\"200\" height=\"300\" frameborder=\"0\" style=\"display: block; margin: auto;\"><\/iframe>\n<p>In this widget, there is a signal being transmitted 2800 Hz above where we\nare listening, so it&rsquo;s outside of what the filter will let through and we\ncan&rsquo;t hear it. However, the filter lets through some of the energy that comes\nfrom the carrier being switched on and off, and it can be heard as clicks.<\/p>\n<p>These &ldquo;key clicks&rdquo; are very annoying since they could even drown out real\nsignals and they can often be heard quite far away from the signal&rsquo;s\nfrequency, so if your transmitter produces clicks, other amateur radio\noperators will be quick to find you to tell you what they think of your\nradio transmitter.<\/p>\n<p>On a transmitter with a 5-millisecond ramp time there isn&rsquo;t so much power\noutside of the signal, so the filter doesn&rsquo;t let so much energy through\nand there are no clicks. Press &ldquo;Play&rdquo; on the widget below to hear the result:<\/p>\n<iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#envelope=sine&frequency=3400\" width=\"200\" height=\"300\" frameborder=\"0\" style=\"display: block; margin: auto;\"><\/iframe>\n<hr>\n<p>This is the part where you can do your own experiments with key clicks.\nHere are two widgets you can try: the left one is clicky, while the right\none is not clicky. Both have a slider you can use to modify the signal&rsquo;s\nfrequency and see where the clicks are present, or where the signal is\nlouder than the clicks.<\/p>\n<div style=\"margin: auto; width: fit-content;\">\n  <iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#slider=true\" width=\"200\" height=\"300\" frameborder=\"0\"><\/iframe>\n  <iframe src=\"..\/assets\/2020\/morsewidget\/widget.html#envelope=sine&slider=true\" width=\"200\" height=\"300\" frameborder=\"0\"><\/iframe>\n<\/div>\n<p>Found anything interesting? Let me know what you think!<\/p>\n","pubDate":"Wed, 13 May 2020 00:00:00 +0000"},{"title":"My most memorable bug","link":"https:\/\/jacobo.tarrio.org\/2020\/my-most-memorable-bug.html","description":"<p>It will be ten years this month since my team and I briefly thought\nwe would be involved in an international diplomatic incident.<\/p>\n<p>It was May 2010, and I had recently moved from the Google office in Dublin,\nIreland, to the Mountain View, California, headquarters to join the Gadget\nServer team.<\/p>\n<p>If you remember iGoogle, you surely also remember Gadgets. iGoogle was\nGoogle&rsquo;s customizable web portal. It had some windows called\n&ldquo;Gadgets&rdquo; that users could choose and place anywhere on their iGoogle\npage. There were Gadgets for reading email, the news, or the weather\nforecast, and there even was a Gadget to convert between measurement units\n(meters to feet, pints to liters, etc.) Our team worked on the system that\ndisplayed Gadgets to users.<\/p>\n<p>One day, we got a bug report claiming that, in Taiwan, the unit conversion\nGadget didn&rsquo;t appear in the traditional Chinese characters\nused in Taiwan but in the simplified Chinese characters used in\nmainland China.<\/p>\n<p>In those days, Google and China weren&rsquo;t going through the best moment in\ntheir relationship: some months before, Google had uncovered a series of\nhacking attacks from China and announced that it would close its\nspecially censored search engine for China. From that moment on, Chinese\nusers would access the regular uncensored search engine. China was not\nhappy about this, and Google was not pleased either.<\/p>\n<p>With so much tension between Google and China, learning that users in Taiwan\nhad started to see a Gadget as if they were in mainland China added the Gadget Server\nteam to the list of unhappy people. Might someone in China be intercepting\nGoogle traffic destined for Taiwan? We hoped not, and even though we didn&rsquo;t\nexpect it to be the case, we needed to find out what was happening.<\/p>\n<hr>\n<p>The first step when you get a bug report is to try to reproduce it: you\ncan&rsquo;t investigate a bug you can&rsquo;t see. However, any way I tried it, I\ncouldn&rsquo;t reproduce it. I would send requests to see the Gadget as a\nTaiwanese user, but I would always see it in traditional Chinese characters\nas if there were no bugs. I tried it every which way with no results.<\/p>\n<p>Then, I sent the same request to every server at the same time\nand checked if there were any differences between them. Our service got\nrequests from all over the world, so we had servers spread throughout the\nplanet; user requests would arrive automatically at their nearest\nserver. When I did my tests, my requests went to a US server,\nbut requests from Taiwan would go to a server in Asia. In theory,\nall servers were identical, but what if it turned out they weren&rsquo;t?<\/p>\n<p>I wrote a web page that sent requests directly to every server, loaded\nit on my browser, and saw that some servers gave different\nanswers. Most servers in Europe and America responded with the Gadget\nin traditional Chinese characters, which was the correct result; however,\nmost servers in Asia responded in simplified Chinese.<\/p>\n<p>To add to the mystery, not all servers in each location gave the same\nresult. Some gave the correct answer, others gave the wrong answer, and\nthe ratio between one and the other differed depending on the location.<\/p>\n<p>After much testing, I realized there was an apparent memory effect.\nI would send a request to display the Gadget in simplified Chinese; then,\nfor the next few minutes, the servers would always respond in simplified\nChinese whenever I sent requests for traditional Chinese. It also happened\nthe other way around: I would send a request for traditional Chinese, and\nthe servers would keep responding to simplified Chinese requests\nin traditional Chinese.<\/p>\n<p>This phenomenon explained why most servers in Asia responded in simplified Chinese: most\nChinese-speaking users live in China and use simplified characters. Most\nrequests coming from China went to servers in Asia, so they mostly received\nsimplified Chinese requests. Then, the servers would get &ldquo;stuck&rdquo; in simplified\nChinese for a few minutes; whenever they got a traditional Chinese\nrequest, they would give a simplified Chinese response.<\/p>\n<p>After figuring this out, I felt a huge relief since it meant that\na Nation-State-level traffic interception action hadn&rsquo;t caused the problem.\nInstead, it was just a regular, run-of-the-mill programming error.\nNevertheless, it still needed to be fixed, and the symptoms suggested that\na caching problem caused it.<\/p>\n<hr>\n<p>Developers used XML files to define gadgets. Gadgets could also have translations into\nseveral languages, which developers stored in separate XML files\n(one file for each language), and the Gadget definition file had a list that told which\ntranslation file goes with which language.<\/p>\n<p>Every time someone wanted to see a Gadget, the server had to download its\nXML definition file, parse it, download the required translation file,\nand parse it, too. Some Gadgets had millions of users,\nso the server would need to download and parse the same files over and over again.\nTo avoid that, the Gadget Server had a cache.<\/p>\n<p>A cache is a data structure that stores the result of an operation to\navoid having to perform that operation repeatedly. In the case of the Gadget Server,\nthe cache stored previously downloaded and parsed XML files. Whenever the\nserver needed a file, it checked if it was already stored there\nand ready for use. Otherwise, the server would\ndownload and parse the file and cache the result to avoid doing that work again later.<\/p>\n<p>My initial theory was that, somehow, the cache could be mixing up the\ntraditional and simplified Chinese translation files. I spent several\ndays inspecting the code and the cached contents, but I couldn&rsquo;t\nsee any problem. As far as I could tell, the XML file cache&rsquo;s\nimplementation was correct and worked flawlessly.\nI would have sworn that the Gadget couldn&rsquo;t be\ndisplayed in the wrong language if I hadn&rsquo;t seen it myself.<\/p>\n<hr>\n<p>While I inspected the code, I also tried reproducing the problem\non my workstation. Production servers would get &ldquo;stuck&rdquo; on simplified\nor traditional Chinese for a few minutes; however, this never happened when\nI ran the server on my workstation: I would send mixed requests and get\nmixed responses. Therefore, I couldn&rsquo;t reproduce the bug in\na controlled environment.<\/p>\n<p>That&rsquo;s why I made a drastic decision: I would attach a debugger to a\nserver in the production network and reproduce the bug there.<\/p>\n<p>Surely enough, I wouldn&rsquo;t do it on a <em>production<\/em> server. At the time, we owned\nseveral types of servers, not just production servers, that received regular\nuser requests. We also had sandbox servers with no external users;\ninstead, they were there so that iGoogle and other Gadget-using services\ncould perform tests without affecting users. I wouldn&rsquo;t attach a\ndebugger to a production server and risk affecting external users; I would\ndo it on a sandbox server.<\/p>\n<p>I chose a sandbox server, prepared it, attached a debugger to it, reproduced\nthe bug, investigated it, and, finally, cleaned up and left everything the\nway it was before. After my investigation, I confirmed that, just as I&rsquo;d\nthought, it was a caching problem, but not the caching problem I had expected.<\/p>\n<p>According to my theory, the program would go to the cache to get the\nfile with the traditional Chinese translation, and it would return\nthe wrong file. I wanted to set a breakpoint before the\nXML file request and see what happened. Surprisingly, the caching system worked\ncorrectly: the program requested the traditional Chinese translation file\nand that&rsquo;s what the cache provided.\nThe problem had to be somewhere else.<\/p>\n<p>After getting the translation, the program applied it to the Gadget. In\ntranslated Gadgets, the definition file didn&rsquo;t include any text in any\nlanguage; instead, it had placeholders that the server would\nreplace with text from the translation file. That&rsquo;s what happened:\nthe server took the XML definition file, looked for the placeholders,\nand wherever one appeared, replaced it with the corresponding\ntext in traditional Chinese script.<\/p>\n<p>The next step was to parse the resulting XML file.<\/p>\n<p>The unit conversion Gadget had many users, many of whom used the\ntraditional Chinese translation. To serve them, after replacing the\nplaceholders with Chinese text, the server would have to parse the same\nresulting XML file over and over again. Since the server would have to\nparse the same XML several times a day, it used a cache to avoid doing\nall that redundant work. And I had no idea that this cache existed!<\/p>\n<p>That was the cache that gave the wrong result: it would get\nan XML file with traditional Chinese text and return the result of\nparsing the same XML file but with simplified Chinese text.<\/p>\n<p>Now, I needed to figure out why that happened.<\/p>\n<hr>\n<p>Caches work by associating a key with a value. For example, the first cache\nI talked about in this story, which avoided downloading and\nparsing the same XML files repeatedly, used the file&rsquo;s URL as the key and\nthe parsed file as the value.<\/p>\n<p>This new cache used a byte array representation of the XML file as its key. The server called the\n<a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/api\/java\/lang\/String.html#getBytes()\"><code>String.getBytes()<\/code><\/a>\nfunction to obtain this representation; this function converts a text string to a byte array using the default\nencoding.<\/p>\n<p>On my workstation, the default encoding was UTF-8. This encoding converts\neach Chinese character into two or three bytes. For example, UTF-8 represents\nthe string &ldquo;\u4f60\u597d&rdquo; as the bytes <code>{0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd}<\/code>.<\/p>\n<p>On the servers, however, the default encoding was US-ASCII, a very\nold encoding (1963) that only supports the characters used in the English\nlanguage, so it can&rsquo;t encode Chinese characters. Whenever <code>getBytes()<\/code>\nfinds a character it can&rsquo;t encode, it replaces it with a question mark.\nTherefore, the string &ldquo;\u4f60\u597d&rdquo; is encoded as &ldquo;??&rdquo;.<\/p>\n<p>That&rsquo;s where the problem was: when the server, which used US-ASCII,\ngenerated a key, it would consist of an XML file with every Chinese\ncharacter replaced with a question mark. Since traditional and simplified\nChinese translations used the same number of characters, even if the\ncharacters were different, the keys always turned out identical, so the\nserver would use the value in the cache, even if it happened to be for the wrong\nChinese script.<\/p>\n<p>This problem wasn&rsquo;t reproducible on my workstation since it used UTF-8,\nwhich supports Chinese characters. Therefore, the keys differed,\nand the cache would return the correct value.<\/p>\n<p>After several weeks of trying this and that, inspecting the code, fighting\nthe cache, and, finally, taking desperate measures, the solution for this\nbug was to fix all calls to <code>getBytes()<\/code> so they&rsquo;d use the UTF-8 encoding\nexplicitly.<\/p>\n<hr>\n<p>This story started as an international spy plot and ended by changing\na function to specify the encoding. I guess it&rsquo;s a bit of an anticlimactic\nending. Still, at least all the team members were happy not to have to\ntestify for the US Congress.<\/p>\n<p>This episode taught me the importance of always specifying\nthe parameters the program depends on and never making anything\nimplicit or dependent on the environment. Our server had a bug because\nit relied on a different configuration parameter in our\nworkstations and production; if we had specified the UTF-8 encoding\nexplicitly from the beginning, this would have never happened to us.<\/p>\n<p>And I wouldn&rsquo;t have an interesting story to tell.<\/p>\n","pubDate":"Sat, 02 May 2020 00:00:00 +0000"},{"title":"Una antena casera de onda corta para radioaficionados","link":"https:\/\/jacobo.tarrio.org\/2020\/antena-de-onda-corta.html","description":"<p>Un d\u00eda, en el verano de 2002, estaba navegando por Internet y ca\u00ed en una\np\u00e1gina web en la que un radioaficionado describ\u00eda sus experimentos haciendo\nrebotar ondas de radio en la Luna. Esa p\u00e1gina me tuvo cautivo durante varias\nhoras viendo fotos de sus <a href=\"http:\/\/web.archive.org\/web\/20200429021909\/https:\/\/hb9q.ch\/2018\/\">gigantescas antenas<\/a>,\nv\u00eddeos en los que enviaban c\u00f3digo Morse y recib\u00edan el eco\n<a href=\"https:\/\/www.youtube.com\/watch?v=JbruflE27_8\">dos segundos y medio despu\u00e9s<\/a>,\ne informes sobre c\u00f3mo dos radioaficionados en extremos opuestos del mundo\nse comunicaban usando la Luna como reflector.<\/p>\n<p>Como soy un pedazo de friki, durante los siguientes d\u00edas pens\u00e9 en lo\ninteresante que ser\u00eda sacarme la licencia de radioaficionado e instalar\nuna peque\u00f1a estaci\u00f3n de radio en casa (no tan potente como para hacer rebotar\nse\u00f1ales en la Luna, pero s\u00ed para comunicarme con otros pa\u00edses). Sin embargo,\naqu\u00e9l no era el mejor momento para esas ideas: estaba intentando terminar la\ncarrera y ten\u00eda varias asignaturas pendientes para setiembre, por lo que mis\npadres no ver\u00edan con buenos ojos esa nueva distracci\u00f3n. Adem\u00e1s, entonces era\nnecesario aprender c\u00f3digo Morse para obtener la licencia. Por no hablar de la\nantena; seguro que necesitar\u00eda una antena muy grande. Quita, quita&hellip;<\/p>\n<p><img src=\"..\/assets\/2020\/torre-antenas.jpg\" alt=\"Foto de una torre de antenas de radioaficionado\"><span class=\"imageCaption\">Una torre de antenas de radioaficionado construida sobre una casa en Palo\nAlto (California). En lo m\u00e1s alto tiene una antena omnidireccional para VHF\ny UHF; debajo, un dipolo, posiblemente para 20 metros; debajo del dipolo,\nuna antena direccional tipo Yagi, probablemente para 10 metros. El dipolo y\nla antena Yagi se pueden girar con un motor para orientarlas hacia otras\nantenas distantes.<\/span>\n<\/p>\n<p>Varios a\u00f1os despu\u00e9s, en 2013 y 2014, se populariz\u00f3 el uso de descodificadores\nde TDT como receptores de radio definidos por software (SDR). Estos\ndescodificadores tienen un modo especial en el que pueden recibir ondas de\nradio, digitalizarlas y pasarlas al ordenador para que \u00e9ste las procese.\nCon este sistema es muy f\u00e1cil experimentar con radio y, en muy poco tiempo,\nmucha gente hizo programas para escuchar la radio, recibir transmisiones\nADS-B procedentes de aviones, recibir fotos meteorol\u00f3gicas directamente del\nsat\u00e9lite, etc.<\/p>\n<p>Pronto cay\u00f3 en mis manos uno de estos descodificadores e hice una <a href=\"https:\/\/github.com\/google\/radioreceiver\">app de\nChrome para escuchar la radio<\/a>.\nNo tard\u00e9 mucho tiempo en a\u00f1adirle la capacidad de escuchar transmisiones de\nradioaficionados y, poco tiempo despu\u00e9s, se me ocurri\u00f3 otra vez la idea de\nsacarme la licencia. Me compr\u00e9 los libros, estudi\u00e9 (ya no era necesario\naprender Morse) y en 2015 obtuve licencias en los EEUU y en Espa\u00f1a.\nAhora realizo contactos por onda corta\n<a href=\"https:\/\/drive.google.com\/open?id=1eq3c8n1HzD49JQFdfDwgEW1igC9HGmoi&amp;usp=sharing\">con un mont\u00f3n de radioaficionados<\/a>,\ny, para mi alivio, no he tenido que instalar una antena gigantesca.<\/p>\n<p>La mayor\u00eda de los radioaficionados no nos podemos permitir tener una torre\ncomo la de la foto de arriba (\u00a1ojal\u00e1!). Muchos vivimos en pisos de alquiler,\no los c\u00f3digos urban\u00edsticos no permiten este tipo de estructuras, o no tenemos\nespacio, o&hellip; miles de razones. Por este motivo, la historia de la\nradioafici\u00f3n es tambi\u00e9n la historia de la b\u00fasqueda de antenas compactas\nque se puedan utilizar en espacios limitados.<\/p>\n<p>Durante estos a\u00f1os he probado a construir y utilizar varias antenas. Cuando\nviv\u00eda en California viv\u00eda en un bajo con un balc\u00f3n cubierto, as\u00ed que constru\u00ed\nuna <a href=\"https:\/\/photos.app.goo.gl\/kzt8jhXNR1NUp1vu7\">antena de bucle magn\u00e9tico<\/a>.\nEstas antenas son muy compactas pero tambi\u00e9n son engorrosas; tienen muy poco\nancho de banda, as\u00ed que es necesario reajustarlas cada vez que uno cambia\nde frecuencia.<\/p>\n<p>Despu\u00e9s de mudarme a Nueva York tuve un patio de 6 por 6 metros con un \u00e1rbol,\nas\u00ed que me constru\u00ed una antena vertical hecha con cables el\u00e9ctricos. Esta\nantena es multibanda, port\u00e1til y f\u00e1cil de utilizar, y es la que os voy a\ndescribir en este art\u00edculo.<\/p>\n<h1>Descripci\u00f3n de la antena<\/h1>\n<p>Mi antena vertical consta de varios elementos: un <em>radiador<\/em>, seis <em>radiales<\/em>,\nun <em>acoplador<\/em>, un <em>unun<\/em> y, por supuesto, un cable coaxial que la conecta al\ntransmisor de radio.<\/p>\n<p><img src=\"..\/assets\/2020\/antena-vertical-casera.svg\" alt=\"Diagrama de mi antena vertical\"><\/p>\n<p>El radiador es el componente que asciende verticalmente y da el nombre de\n&ldquo;vertical&rdquo; a la antena. Est\u00e1 formado por un cable el\u00e9ctrico de unos 15 metros\nde largo. Cualquier cable el\u00e9ctrico sirve, pero es conveniente elegir uno que\nsea ligero y resistente, ya que va a estar colgado de un \u00e1rbol y va a sufrir\nla acci\u00f3n del viento y del sol.<\/p>\n<p>Los radiales son seis cables el\u00e9ctricos de cinco metros de largo que forman\nel &ldquo;plano de tierra&rdquo; de la antena. Todos ellos reposan directamente en el\nsuelo, est\u00e1n conectados en un solo punto y luego salen en l\u00ednea recta en\ntodas las direcciones, como los radios de una rueda de bicicleta. Igual\nque para el radiador, cualquier cable sirve.<\/p>\n<p>El acoplador hace ajustes para que la diferencia de impedancia entre la\nantena y la l\u00ednea de transmisi\u00f3n no afecte negativamente a la radio. El\nacoplador es opcional, pero entonces tendr\u00e9is que cortar el radiador a una\nlongitud espec\u00edfica para que funcione en una banda. El acoplador os\npermite utilizar la antena en las bandas de 20, 40 y 60 metros, entre otras.<\/p>\n<p>Finalmente, el <em>unun<\/em> se encarga de eliminar interferencias de la l\u00ednea\nde transmisi\u00f3n. Al transmitir, la antena induce corrientes en el cable\ncoaxial que viene de la radio y el acoplador detecta esas corrientes, piensa\nque son debidas a un desajuste en la antena, e intenta arreglarlo, con lo\nque se desajusta por completo. El <em>unun<\/em> elimina esas corrientes antes de\nque lleguen al acoplador, por lo que \u00e9ste funciona correctamente.<\/p>\n<p><span class=\"multipleImgs\"><img src=\"..\/assets\/2020\/cable-de-antena.jpg\" alt=\"El cable que uso para el radiador\"><img src=\"..\/assets\/2020\/conexiones-acoplador.jpg\" alt=\"Conexiones del radiador y radiales al acoplador\"><img src=\"..\/assets\/2020\/antena.jpg\" alt=\"Acoplador, unun, radiales\"><\/span><span class=\"imageCaption\"><em>Primera foto<\/em>: el cable que utilizo para el radiador. Es un cable trenzado\nde acero revestido de cobre dise\u00f1ado espec\u00edficamente para antenas port\u00e1tiles,\npero sirve cualquier tipo de cable que sea lo suficientemente ligero y\nresistente.\n<br>\n<em>Segunda foto<\/em>: detalle de la conexi\u00f3n del radiador y los radiales al\nacoplador. Utilizo un adaptador de bornes para conectar la antena; el\nradiador est\u00e1 conectado al borne rojo y los radiales al borne negro.\nEl adaptador de bornes es opcional: podr\u00eda haber enchufado el radiador\ndirectamente al centro del conector &ldquo;Ant&rdquo; y los radiales al tornillo &ldquo;Gnd&rdquo;.\n<br>\n<em>Tercera foto<\/em>: el acoplador\n(<a href=\"https:\/\/ldgelectronics.com\/index.php\/products\/zero-power\/z-11pro\/\">LDG Z11 Pro II<\/a>,\ncaja negra con botones grises) y el <em>unun<\/em>\n(<a href=\"https:\/\/ldgelectronics.com\/index.php\/products\/accessories\/baluns\/\">LDG RU-1:1<\/a>,\ncaja azul) despu\u00e9s de haber hecho todas las conexiones. Los radiales est\u00e1n\nextendidos en todas las direcciones. La caja negra con la etiqueta blanca\ncontiene 8 pilas tipo AA para proporcionar 12 voltios al acoplador.<\/span>\n\n\n<\/p>\n<h1>C\u00f3mo utilizar la antena<\/h1>\n<p>Para usar esta antena es necesario colgar el radiador de un \u00e1rbol, un poste\no una estructura similar. Preferiblemente, esa estructura deber\u00eda estar hecha\nde un material no conductor. Por ejemplo, una ca\u00f1a de pescar hecha de\nfibra de vidrio servir\u00eda, pero no una ca\u00f1a de pescar hecha de fibra de\ncarbono.<\/p>\n<p>Para colgar la antena yo utilizo un sedal y un peso de plomo. Primero ato\nel plomo al sedal, lo voleo como si fuera una honda y lo arrojo por encima\nde la copa del \u00e1rbol. Con un poco de suerte, el plomo ir\u00e1 suficientemente\nalto, no chocar\u00e1 con ninguna rama y aterrizar\u00e1 al otro lado del \u00e1rbol.<\/p>\n<p>Hay algo de peligro de que el sedal se enrede en las ramas del \u00e1rbol.\nEn mi experiencia, esto ocurre principalmente si interfiero con el vuelo\ndel plomo. Si lo lanzo y luego no toco el sedal hasta que el plomo llegue\nal suelo, casi siempre ir\u00e1 todo bien. Si lo lanzo y luego agarro el sedal para\nintentar controlar el vuelo del plomo, hay mucho peligro de que el plomo\nhaga un extra\u00f1o alrededor de una rama y se quede atascado.<\/p>\n<p>Despu\u00e9s de haber arrojado el sedal por encima del \u00e1rbol puedo ir al plomo,\ndesatarlo del sedal, atar la punta del radiador al sedal y luego izar el\nradiador. Despu\u00e9s de esto s\u00f3lo queda conectar el radiador y los radiales al\nacoplador, \u00e9ste al <em>unun<\/em> y finalmente conectarlo al transmisor con un cable\ncoaxial.<\/p>\n<p>Los dibujos de debajo muestran distintas posibles configuraciones de la\nantena que pod\u00e9is usar dependiendo de cu\u00e1nto espacio teng\u00e1is, d\u00f3nde est\u00e9n\nvuestros \u00e1rboles, etc. Fijaos en que un extremo del sedal est\u00e1 atado al\nradiador pero el otro extremo no est\u00e1 atado a ning\u00fan punto fijo, sino a un\npeso que lo mantiene tenso pero lo permite oscilar. Esto es importante\nporque el \u00e1rbol y el radiador van a sufrir los embates del viento y se\nmenear\u00e1n en todas direcciones. Si at\u00e1is el sedal, es posible que, al\nmoverse, tire del radiador demasiado fuerte y lo rompa. Al usar un peso\nque oscila libremente, no hay peligro de que esto ocurra.<\/p>\n<p>Si vuestro radiador es m\u00e1s pesado que el m\u00edo, es muy probable que un sedal no\nsea lo suficientemente fuerte para izarlo. En ese caso deber\u00edais usar un\ncordel m\u00e1s resistente; por ejemplo, una l\u00ednea de arborista con su\ncorrespondiente peso.<\/p>\n<p><span class=\"multipleImgs\"><img src=\"..\/assets\/2020\/esquema-antena-vertical-1.svg\" alt=\"Radiador colgado de un \u00e1rbol alto\"><img src=\"..\/assets\/2020\/esquema-antena-vertical-2.svg\" alt=\"Radiador colgado de un \u00e1rbol distante\"><img src=\"..\/assets\/2020\/esquema-antena-vertical-3.svg\" alt=\"Radiador con una pata colgante\"><img src=\"..\/assets\/2020\/esquema-antena-vertical-4.svg\" alt=\"Radiador en zig-zag\"><\/span><span class=\"imageCaption\">En condiciones ideales, con un \u00e1rbol suficientemente alto y suficientemente\ncerca, el radiador colgar\u00e1 verticalmente o casi verticalmente (primera figura).\nNo siempre tenemos condiciones ideales, as\u00ed que a veces hay que hacer\nadaptaciones. Por ejemplo, si el \u00e1rbol est\u00e1 un poco lejos, podemos inclinar\nel radiador (segunda figura). Si el \u00e1rbol no es lo bastante alto, tal vez\ntengamos que pasar parte del radiador por encima de la rama y dejarlo\ncolgando de \u00e9sta (tercera figura). En ciertos casos, tal vez tengamos que\ndejar que el radiador zigzaguee por todo el sitio (cuarta figura).<\/span>\n\n\n\n<\/p>\n<p>Los radiales deber\u00edan salir de debajo del radiador y extenderse en l\u00ednea\nrecta y en todas direcciones. Si no ten\u00e9is espacio, tal vez necesit\u00e9is\napa\u00f1\u00e1roslas de una u otra manera. Al final, lo m\u00e1s importante es que\nel plano de tierra sea lo m\u00e1s tupido, sim\u00e9trico y uniforme posible,\na\u00f1adiendo radiales y alarg\u00e1ndolos donde pod\u00e1is o acort\u00e1ndolos donde no os\nquede m\u00e1s remedio.<\/p>\n<p><span class=\"multipleImgs\"><img src=\"..\/assets\/2020\/radiales-1.svg\" alt=\"Seis radiales\"><img src=\"..\/assets\/2020\/radiales-2.svg\" alt=\"Plegando los radiales en un espacio limitado\"><img src=\"..\/assets\/2020\/radiales-3.svg\" alt=\"A\u00f1adiendo radiales para aprovechar el espacio\"><\/span><span class=\"imageCaption\">Lo ideal ser\u00eda distribuir los radiales de una forma sim\u00e9trica y uniforme\n(primera figura). Si el espacio es limitado, siempre pod\u00e9is doblar y plegar\nlos radiales un poco para adaptarlos al sitio, aunque es conveniente que\nvayan lo m\u00e1s rectos posible (segunda figura). Ante la duda, lo mejor es\nponer muchos radiales para llenar el espacio disponible (tercera figura).<\/span>\n\n\n<\/p>\n<p>Dependiendo de la configuraci\u00f3n del radiador y de los radiales, el patr\u00f3n\nde radiaci\u00f3n de la antena tendr\u00e1 una u otra forma, as\u00ed que ser\u00eda in\u00fatil\ntratar de caracterizarlo en esta p\u00e1gina web, pero en general, la m\u00e1xima\nganancia tiende a ser perpendicular al radiador y, si los radiales no son\nsim\u00e9tricos, habr\u00e1 menos ganancia en la direcci\u00f3n en la que haya menos\nradiales o \u00e9stos sean menos densos.<\/p>\n<p>Por lo tanto, si quer\u00e9is una antena omnidireccional con buen DX, tratad\nde colgar el radiador m\u00e1s vertical que pod\u00e1is y colocad los radiales lo\nm\u00e1s sim\u00e9tricos que pod\u00e1is.<\/p>\n<h1>Conclusiones<\/h1>\n<p>He estado utilizando y mejorando poco a poco esta antena desde que me\nmud\u00e9 a Nueva York. Como est\u00e1 hecha con cables el\u00e9ctricos, es muy port\u00e1til,\nas\u00ed que he podido utilizarla en mi patio en Brooklyn (6 por 6 metros\ncon un \u00e1rbol de unos 8 metros) y tambi\u00e9n en la casa de mis suegros en\nConnecticut (much\u00edsimo espacio y un \u00e1rbol de unos 12 metros). Gracias al\nacoplador, puedo utilizarla en las bandas de 20, 40 y 60 metros, as\u00ed\ncomo otras bandas a las que no presto mucha atenci\u00f3n.<\/p>\n<p>En Nueva York casi siempre hago un zig-zag con el radiador y tengo que\ndoblar los radiales un poco. Hay edificios justo al norte de la antena,\nas\u00ed que no puedo recibir en esa direcci\u00f3n. Adem\u00e1s, siendo Nueva York, hay\nun mont\u00f3n de ruido el\u00e9ctrico. A\u00fan as\u00ed, he podido comunicarme con Brasil y\ncon Polonia usando 50 vatios.<\/p>\n<p>En Connecticut el radiador va completamente extendido pero inclinado hacia\nel norte, y el suelo tambi\u00e9n hace una ligera pendiente ascendente hacia el\nnoroeste, por lo que tambi\u00e9n me es dif\u00edcil hacer contactos en esas\ndirecciones. Sin embargo, consigo muchos contactos con las islas del Caribe\ny con Europa. Mis contactos m\u00e1s lejanos son Serbia y Argentina, tambi\u00e9n con\n50 vatios, aunque he podido, en ocasiones, oir estaciones australianas.<\/p>\n<p><img src=\"..\/assets\/2020\/contactos.png\" alt=\"Contactos realizados con mi antena vertical a d\u00eda de hoy\"><span class=\"imageCaption\">Contactos realizados con mi antena vertical. Los puntos azules son contactos\nrealizados desde Brooklyn; los puntos marrones son contactos realizados desde\nConnecticut. Tambi\u00e9n pod\u00e9is ver el\n<a href=\"https:\/\/drive.google.com\/open?id=1eq3c8n1HzD49JQFdfDwgEW1igC9HGmoi&amp;usp=sharing\">mapa actualizado con los \u00faltimos datos<\/a>.\n(Mapa proporcionado por Google).<\/span>\n<\/p>\n<p>En el futuro cercano tengo pensado duplicar el n\u00famero de radiales, de seis a\ndoce. Esto deber\u00eda mejorar el rendimiento de mi antena en 1 o 2 decibelios.\nTambi\u00e9n tengo inter\u00e9s en probar un poste extensible de fibra de vidrio (una\n&ldquo;ca\u00f1a de pescar&rdquo;) que me permita extender el radiador verticalmente sin\nnecesidad de un \u00e1rbol.<\/p>\n<p>\u00a1No dud\u00e9is en poneros en contacto conmigo si ten\u00e9is preguntas o sugerencias,\no si quer\u00e9is planear un contacto por radio!<\/p>\n","pubDate":"Wed, 29 Apr 2020 00:00:00 +0000"}]}}