<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Longpost on Hill's Space</title><link>https://hill.pictures/tags/longpost/</link><description>Recent content in Longpost on Hill's Space</description><generator>Hugo</generator><language>en</language><managingEditor>hillexed@email.com (hillexed)</managingEditor><webMaster>hillexed@email.com (hillexed)</webMaster><lastBuildDate>Fri, 25 Oct 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://hill.pictures/tags/longpost/index.xml" rel="self" type="application/rss+xml"/><item><title>Not Real Numbers</title><link>https://hill.pictures/blog/2024-not-real-numbers/</link><pubDate>Fri, 25 Oct 2024 00:00:00 +0000</pubDate><author>hillexed@email.com (hillexed)</author><guid>https://hill.pictures/blog/2024-not-real-numbers/</guid><description>&lt;p>I rediscovered the &lt;a href="https://splasho.com/upgoer5/">xkcd up goer five text editor&lt;/a>, which lets you type using only the 1000 most common english words. So here&amp;rsquo;s my attempt at writing a description of something:&lt;/p>
&lt;h2 id="not-real-numbers">Not Real Numbers&lt;/h2>
&lt;p>We know how to do many number problems. Number problems with adding are easy. Adding the same number over and over is also easy. If you take a number and add it many times, the same number of times as that number, you get a box number.&lt;/p>
&lt;p>We can go forward from number to box number. Sometimes you want to go back from box number to smaller number. In fact, if you have a number problem with box numbers and adding over and over and adding, you learn in school a box problem answer form to do the problem. But part of the answer form says to go back from box numbers.&lt;/p>
&lt;p>A box number is always above the nothing number, or is the nothing number. Five boxed is twenty five, and so is five under nothing boxed. Even numbers under nothing, if you box them by adding them that many times, are above the nothing number.
But for some problems, the box problem answer form wants you to find a number where its box number is under nothing. Oh no!&lt;/p>
&lt;p>So the number people made up a new number, to imagine its box number is one under nothing. Some people were mad and called these new numbers not real. They were surprised when these not real numbers were good for doing problems! We only need to make up one new number to do all number problems with adding and adding over and over! Also, we can draw not real numbers by imagining they go in a different direction than real numbers.&lt;/p></description></item><item><title>Pifinder Perils</title><link>https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/</link><pubDate>Mon, 04 Dec 2023 15:29:08 +0000</pubDate><author>hillexed@email.com (hillexed)</author><guid>https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/</guid><description>


&lt;div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
	 

&lt;link rel="stylesheet" href="https://hill.pictures/css/hugo-easy-gallery.css" />










 
 
 



 

 
 
 
 
 
 
 






&lt;div class="box" >
 &lt;figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
 &lt;div class="img" style="background-image: url('/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png');" >

 
 &lt;img itemprop="thumbnail" src="https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png" alt="My Sliced PiFinder!" loading="lazy"/>
 

 

 &lt;/div>
 &lt;a href="https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/image.png" itemprop="contentUrl"

 data-pswp-width="684" data-pswp-height="385" data-thumbSrc="/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png" 

 

 >&lt;/a>
 &lt;/figure>
&lt;/div>













 
 
 



 

 
 
 
 
 
 
 






&lt;div class="box" >
 &lt;figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
 &lt;div class="img" style="background-image: url('/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png');" >

 
 &lt;img itemprop="thumbnail" src="https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png" alt="A 30s exposure taken by the Innomaker IMX462 camera on the PiFinder. It&amp;#39;s a field of stars over a washed-out white background of light pollution. It&amp;#39;s my first long-exposure astrophoto! There&amp;#39;s a dark smudge; it&amp;#39;s a bit of dust on the sensor." loading="lazy"/>
 

 

 &lt;/div>
 &lt;a href="https://hill.pictures/hadley/pifinder/3741817-pifinder-perils/image.png" itemprop="contentUrl"

 data-pswp-width="684" data-pswp-height="385" data-thumbSrc="/hadley/pifinder/3741817-pifinder-perils/image_hu11cba69ff7a751231f49313735389ae4_225051_9981a46c9e44d0bab0642611bc655eab.png" 

 

 >&lt;/a>
 &lt;/figure>
&lt;/div>


&lt;/div>

&lt;p>I&amp;rsquo;m building a PiFinder! It uses a camera to take pictures of the sky, connected to a raspberry pi which uses a database of stars to tell you where in the sky your telescope is pointing. But a PiFinder is $550 new. A stock pifinder uses the newest and most expensive options for pis and cameras, and when I looked at the parts list, I thought: I can build something similar for a fifth of the price! And so began the quest to create the Sliced PiFinder: a DIY PiFinder made from an used raspberry pi for a small slice of the cost. &lt;a href="https://cohost.org/hillexed/post/3661910-pifinder-is-almost-p">Previously, I bought a $33 camera and 3D printed a custom enclosure for it.&lt;/a>&lt;/p>
&lt;p>On the one hand, if I can successfully modify this project so I don&amp;rsquo;t need expensive parts, I save $400! On the other hand, my hubris has led to consequences.&lt;/p>
&lt;p>This is a longpost about those consequences and the shenanigans I found to work around them.&lt;/p>
&lt;p>(None of this would be possible if the pifinder wasn&amp;rsquo;t open source, so thanks to the creator brickbots, who has given me tons of help!)&lt;/p>
&lt;h1 id="problem-1-camera-fov">Problem 1: Camera FoV&lt;/h1>
&lt;p>We live in a world where I use software written by a space agency across the ocean for free. What a world we live in.&lt;/p>
&lt;p>Once the PiFinder takes a picture of the sky, it needs to use a database of stars to figure out where in the sky the camera was looking when the picture was taken. That process is called &amp;ldquo;plate solving&amp;rdquo; because we used to take star pictures on photographic plates.&lt;/p>
&lt;p>How do you plate solve? Amazingly, that&amp;rsquo;s the easy part! The European Space Agency released an open source python library called &lt;a href="https://github.com/esa/tetra3">&amp;ldquo;tetra3&amp;rdquo;&lt;/a> that does it all for you! All you need to know is&amp;hellip; hmmmm&amp;hellip; the exact position of every single star in the sky your camera can see.&lt;/p>
&lt;p>Amazingly, you can just download a database of every single bright star in the sky! Astronomers have been making star catalogs for thousands of years, including launching space telescopes like Gaia devoted to doing nothing but star cataloging. The PiFinder software downloads a database of stars automatically!&lt;/p>
&lt;p>However, for tetra3 to plate solve, it needs to know your camera&amp;rsquo;s field of view (FoV), so it knows how far apart various stars will look to the camera. Different lenses change FoV. The PiFinder software expects you to have a Raspberry pi HQ Camera ($50) and 25mm lens ($20), which has a FoV of 10.2. I&amp;rsquo;m using an IMX462 camera ($33) and 12mm lens from a different camera ($10), so how do I figure out my FoV?&lt;/p>
&lt;p>One option is math. I tried pointing the camera at Orion, taking a picture of the screen, measuring the distance in pixels between stars in orion, and comparing that to the known angular distance. That gave me an estimate of 15 degrees of FoV. Unfortunately, I didn&amp;rsquo;t know at the time that the the picture was wider than the screen.&lt;/p>
&lt;p>Then I realized I was using a raspberry pi and could access the raw hardware. I SSHed in, used the command line to take a picture, then downloaded the raw .png. Now I knew what the software was looking at. I uploaded it to nova.astrometry.net, which plate solved my image and told me not just the exact coordinates of where I was looking (and the constellation!) but also the image&amp;rsquo;s FoV.&lt;/p>
&lt;p>(Also, just for fun, I used it to take my first ever long exposure shot of 30s, and was blown away by the number of stars visible. Image above!)&lt;/p>
&lt;p>Another complication: images are 2D. Does tetra3&amp;rsquo;s FoV input mean horizontal FoV or vertical FoV or diagonal FoV? I didn&amp;rsquo;t see it anywhere in the documentation. (It was horizontal)&lt;/p>
&lt;p>In the end, my new IMX462 camera with 12mm lens had about 26deg of horizontal FoV. I could buy a new lens to reduce that down to the PiFinder software&amp;rsquo;s expected 10.2 degrees&amp;hellip; but instead brickbots helped me write software to crop the image down to 10.2 degrees. Now plate solving works!&lt;/p>
&lt;h1 id="problem-2-altaz">Problem 2: Alt/Az&lt;/h1>
&lt;p>Let&amp;rsquo;s say you want to see the andromeda galaxy. How do you know if it&amp;rsquo;s is visible in your night sky right now or not? Should you look north or east or south to find it?&lt;/p>
&lt;p>Since the earth is a sphere, &amp;ldquo;down&amp;rdquo; changes from place to place. Since north is perpendicular to down, that means north/south/east/west look at different parts of the sky based on your place on earth.&lt;/p>
&lt;p>Thankfully, if you know your location on Earth and the time, since Earth rotates once per 24 hours you can do some math to figure out where to look relative to your local north and down directions. Those coordinates are called &amp;ldquo;altitude&amp;rdquo; and &amp;ldquo;azimuth&amp;rdquo;.&lt;/p>
&lt;p>The PiFinder gets your location on Earth and the time from a $50 GPS USB dongle. But that&amp;rsquo;s expensive and my phone already has a GPS. If I can modify the software so I can enter my coordinates and the time, I save $50.&lt;/p>
&lt;p>It turns out parts of the software don&amp;rsquo;t work unless it knows your coordinates, but instead of showing an error message some features simply do nothing. I edited the software&amp;rsquo;s config file to add my GPS coordinates&amp;hellip; but coordinates in the config file are never used, since all previous pifinders have had GPSes and got coordinates from there. So I had to modify the existing &amp;ldquo;GPS_fake.py&amp;rdquo; (which previously did nothing) to actually send fake GPS and time messages.&lt;/p>
&lt;p>And because computing altitude and azimuth isn&amp;rsquo;t complicated enough: the raspberry pi has no internal clock, so every time it starts up the time is wrong (unless it can connect to wifi and download the time using NTP). This works for me at home, but if I want to take this to dark places, I eventually need to program a screen that lets you enter the coordinates and time on the device itself!&lt;/p>
&lt;h1 id="problem-3-gyroscope-fusion-failures">Problem 3: Gyroscope Fusion Failures&lt;/h1>
&lt;p>If you move a telescope with a PiFinder left or right, changing your azimuth, its screen should move the star display left or right quickly. That&amp;rsquo;s hard for a few reasons.&lt;/p>
&lt;p>First, the Pifinder needs long exposures to capture dim stars. If it moves during a long exposure, the image it captures will have stars that look like smeared lines instead of dots and it can&amp;rsquo;t plate solve. That means if you bump the device, there&amp;rsquo;s a few seconds of delay before the camera can lock on to the sky again.&lt;/p>
&lt;p>To get quicker feedback during unsuccessful pictures, the PiFinder uses an intertial measurement unit (IMU) chip, which combines a gyroscope (measures changes in angle), an accelerometer (measures acceleration, including gravity), and a magnetometer (measures Earth&amp;rsquo;s magnetic field). The PiFinder uses a fancy $30 IMU chip called the BNO055, which has a tiny processor that computes the chip&amp;rsquo;s current altitude and azimuth 100+ times a second.&lt;/p>
&lt;p>However, I scavenged an &lt;a href="https://www.adafruit.com/product/5543">LSM6DS3TR-C + LIS3MDL&lt;/a> IMU from a different project with the exact same sensors: magnetometer, gyroscope, accelerometer. Surely, I thought, I could write some code to compute altitude/azimuth from those sensors and save $30!&lt;/p>
&lt;p>Combining data from the 3 sensors is called &amp;ldquo;sensor fusion&amp;rdquo;. It&amp;rsquo;s incredibly hard. Thankfully, I&amp;rsquo;m not the first to study sensor fusion (drone builders want it too) and there&amp;rsquo;s two main sensor fusion algorithms which already exist, named mahony and madgwick. Adafruit has an AHRS library which implements both&amp;hellip; in C++, but I was using python. Eventually I found an implementation of both and downloaded it, loaded in my sensor readings&amp;hellip; and spinning the device 90 degrees didn&amp;rsquo;t change the output by 90 degrees. Why?&lt;/p>
&lt;h3 id="problem-1-axis-remapping">Problem #1: Axis remapping!&lt;/h3>
&lt;p>In my PiFinder, the IMU is oriented so the sensor&amp;rsquo;s +Y direction is the one the camera is pointing towards, but I had a hard time figuring that out because the chip was buried inside a circuit board and the code has many options to switch coordinates based on how a PiFinder is mounted on a telescope.&lt;/p>
&lt;h3 id="problem-2-units">Problem #2: Units&lt;/h3>
&lt;p>The Madgwick filter expected the gyro&amp;rsquo;s inputs to be in degrees/second when they were in radians per second. No problem, I can just multiply them by 180/pi. Then I took a look at the Madgwick code, and it requests degrees because it multiplies the numbers by pi/180 to turn them back into radians. Aaargh.&lt;/p>
&lt;h3 id="problem-3-calibration">Problem #3: Calibration!&lt;/h3>
&lt;p>Magnetometers and gyroscopes will drift - if they output a range, say, 4 units wide, instead of getting sensor readings from -2 to 2, your actual readings might be shifted so you read values from 0.5 to 3.5. Magnetometer readings shift in a similar way for a different reason: Earth&amp;rsquo;s magnetic field is different everywhere. You can take many many readings and average them to find the true zero point, then subtract that from all future readings to make zero the middle point.&lt;/p>
&lt;p>For a gyroscope, those readings need to be taken while the gyroscope isn&amp;rsquo;t moving. I realized I could use the accelerometer for that - if the accelerometer&amp;rsquo;s gravity direction isn&amp;rsquo;t changing, I can use that to know I&amp;rsquo;m not moving and grab some gyroscope readings. Once you integrate the gyroscope over time, it looked pretty stable, with only around 0.3 degrees of error.&lt;/p>
&lt;p>For a magnetometer, spin it around as much as possible, and the unchanging 3D vector of earth&amp;rsquo;s magnetic field, as measured by the magnetometer&amp;rsquo;s 3 axes, should trace out a sphere! Then you can use the center of that sphere as your reference zero point.&lt;/p>
&lt;p>Engineers did what engineers do in overly specific fields and made up magnetometer calibration number jargon. The values for center of the sphere (which should be at (0,0,0) but usually isn&amp;rsquo;t) are called the &amp;ldquo;hard-iron offsets&amp;rdquo;. But you can get fancier: if you have a magnet (or some other electrical device) near the magnetometer, it might make its own magnetic field and skew one axis at a time, and that skew is called the &amp;ldquo;soft-iron offset&amp;rdquo;. They&amp;rsquo;re just coefficients in a calibration matrix!&lt;/p>
&lt;p>The simplest way to calibrate a magnetometer is to ignore soft-iron entirely, keep track of maximum and minimum values in all 3 magnetometer axes as you spin the device around every which way, assume the hard-iron offset is the average of the max and min, and subtract that calibrated value every time you read from the magnetometer in the future.&lt;/p>
&lt;p>I had to do all that calibration myself. The output still didn&amp;rsquo;t work very well. The sensor fusion algorithm&amp;rsquo;s output reported altitude and azimuth axes which didn&amp;rsquo;t measure down correctly. Why wasn&amp;rsquo;t it working?&lt;/p>
&lt;h3 id="problem-4-every-single-magdwick-filter-ever-was-wrong-3-years-ago">Problem #4: EVERY SINGLE MAGDWICK FILTER EVER WAS WRONG 3 YEARS AGO&lt;/h3>
&lt;p>According to &lt;a href="https://github.com/RideBeeline/madgwick-investigation/blob/main/README.md">this research by someone named Mark Uckermann from a British bike GPS startup,&lt;/a>, apparently almost every Magdwick library has a subtle bug in the code compared to the original paper, invisible if you use a small enough &amp;ldquo;beta&amp;rdquo; parameter. Oops. That was 3 years ago, and I think my library has fixed it? But I can&amp;rsquo;t tell since the variable names are slightly different.&lt;/p>
&lt;h3 id="problem-5-speed">Problem #5: Speed&lt;/h3>
&lt;p>The sensor fusion algorithms involve lots of math. Doing these calculations myself in python (along with the camera and plate solving and everything else the pifinder was doing) took up so much time that on my pi 3, the IMU code was only able to read values from the sensors around 12 times a second. The sensors were updating 104 times a second. That meant I was losing tons of info, including updates of &amp;ldquo;how much the angle shifted since the last time you checked&amp;rdquo; data from the gyroscope. I could configure the sensors to slower speed&amp;hellip; But the gyroscope+accelerometer could only go to discrete values like 12.5 Hz while the magnetometer could only go to different discrete values, like 10 Hz. Aargh.&lt;/p>
&lt;p>After all that&amp;hellip; turning my device 90 degrees still wouldn&amp;rsquo;t make the azimuth output change by 90 degrees. Aaaaargh.&lt;/p>
&lt;p>But wait. If my sensor fusion algorithm wasn&amp;rsquo;t working&amp;hellip; maybe it was a problem in the sensor fusion algorithm code? The accelerometer tells me the direction of gravity - maybe I can compute azimuth. The IMU can tell me where magnetic north is. If I measure both&amp;hellip; maybe there&amp;rsquo;s a way to compute altitude and azimuth directly? Maybe, just maybe, if I sat down and did a ton of galaxy brain math, I&amp;rsquo;d be able to use my own scavenged IMU instead of buying the pifinder&amp;rsquo;s recommended BNO055 for $30!&lt;/p>
&lt;p>Oh yeah. The BNO055 chip is $30. It does calibration for you.&lt;/p>
&lt;p>I gave up, desoldered my old IMU, bought a BNO055, and soldered it in.&lt;/p>
&lt;p>It better work.&lt;/p></description></item><item><title>I'M MAD AT AMERICAN PIPES</title><link>https://hill.pictures/blog/pipenonsense/</link><pubDate>Wed, 10 May 2023 18:39:09 +0000</pubDate><author>hillexed@email.com (hillexed)</author><guid>https://hill.pictures/blog/pipenonsense/</guid><description>&lt;p>I have a 1/2&amp;quot; diameter hole. I want to put a pipe inside the hole. What size PVC pipe fits into a 1/2&amp;quot; hole?&lt;/p>
&lt;p>Did you guess 1/2&amp;quot; pipe? WRONG. &amp;ldquo;1/2 inch pipe&amp;rdquo; isn&amp;rsquo;t half an inch big on the outside. It&amp;rsquo;s closer to 7/8&amp;quot;. You might think &amp;ldquo;ah so the 1/2&amp;rdquo; refers to the inner diameter&amp;quot;. Wrong. The inner diameter of 1/2&amp;quot; PVC pipe is legally required to be 0.602 inches, according to the Schedule 40 standard. Want a pipe with outer diameter 1/2&amp;quot;? The closest size of PVC pipe is of course 1/4&amp;quot; PVC pipe. But even that won&amp;rsquo;t quite work, because, 1/4&amp;quot; pipe has outer diameter 0.54&amp;quot;, which is more than 0.5&amp;quot; and way more than the 0.25&amp;quot; in the name of the pipe. &lt;b>PIPE SIZES ARE ALL LIES&lt;/b>&lt;/p>
&lt;p>Guess how I found out? I&amp;rsquo;m building &lt;a href="https://www.printables.com/model/224383-astronomical-telescope-hadley-an-easy-assembly-hig">a telescope&lt;/a>, and as part of that I&amp;rsquo;m building a mount. I bought a cheap collection of 3 pipes off the internet, but I needed a fourth, so I went to the hardware store to buy some pipe. Guess who learned a $6.99 lesson in nonsensical standards&amp;hellip;&lt;/p>
&lt;h1 id="chamomile-comments">Chamomile comments:&lt;/h1>
&lt;p>Wow, this is way more infuriating than 2x4&amp;rsquo;s actually being 1.5x3.5 inches.&lt;/p>
&lt;h1 id="porglezomp-addition">Porglezomp addition:&lt;/h1>
&lt;p>Your comment made me think &amp;ldquo;oh well i know why 2x4s are like that at least that makes sense&amp;rdquo; and then I realized that there was probably a reason for this one too, so I went and figured that out:&lt;/p>
&lt;p>You see, a 2x4 is 1.5&amp;quot;x3.5&amp;quot;. And I thought &amp;ldquo;well, at least that has a good reason from a century ago,&amp;rdquo; but then I realized that this probably has one of those too. So let&amp;rsquo;s look into it.&lt;/p>
&lt;h2 id="dimensional-lumber">Dimensional Lumber&lt;/h2>
&lt;p>A 2x4 is 1.5&amp;quot;x3.5&amp;quot;. So why is it named like that? Well, a century ago a 2x4 referred to a 2x4 of green lumber—it was commonly cut and shipped green. And then it would shrink as it dried, and be planed to a precise size on-site. Eventually, we got better at making and transporting dry lumber, but stuck to those post-drying actual sizes for the same nominal sizes. For compatibility. So it&amp;rsquo;s the fault of compatibility with how things were manufactured over a century ago. It made sense at the time.
Nominal Pipe Size&lt;/p>
&lt;blockquote>
&lt;p>You might think &amp;ldquo;ah so the 1/2&amp;rdquo; refers to the inner diameter&amp;quot;. Wrong. The inner diameter of 1/2&amp;quot; PVC pipe is legally required to be 0.602 inches, according to the Schedule 40 standard.&lt;/p>
&lt;/blockquote>
&lt;p>Well it turns out this is also the same thing. When nominal pipe sizes were standardized, they referred to the inner diameter of the pipe, but those pipes had thicker walls. Now we build stronger pipes that have thinner walls, but the dimensions were still standardized and named for those older inner diameters, and now they&amp;rsquo;re all nonsense again.&lt;/p>
&lt;blockquote>
&lt;p>The reason for the discrepancy for NPS 1⁄8 to 12 inches is that these NPS values were originally set to give the same inside diameter (ID) based on wall thicknesses standard at the time. However, as the set of available wall thicknesses evolved, the ID changed and NPS became only indirectly related to ID and OD.&lt;/p>
&lt;/blockquote>
&lt;p>Source: &lt;a href="https://en.wikipedia.org/wiki/Nominal_Pipe_Size#Application">https://en.wikipedia.org/wiki/Nominal_Pipe_Size#Application&lt;/a>&lt;/p>
&lt;p>This standard is from 1927 / 1939 / 1949 ?? so it is more annoying than the lumber standard, to me.
Diamètre Nominal&lt;/p>
&lt;blockquote>
&lt;p>I&amp;rsquo;M MAD AT AMERICAN PIPES&lt;/p>
&lt;/blockquote>
&lt;p>Good news! European pipes are no better! &lt;a href="http://www.piping-engineering.com/nominal-pipe-size-nps-nominal-bore-nb-outside-diameter-od.html">A 15mm pipe has a 21.3mm outer diameter&lt;/a>, and the inner diameter also completely depends on the material.&lt;/p>
&lt;p>Source: &lt;a href="http://www.piping-engineering.com/nominal-pipe-size-nps-nominal-bore-nb-outside-diameter-od.html">http://www.piping-engineering.com/nominal-pipe-size-nps-nominal-bore-nb-outside-diameter-od.html&lt;/a>&lt;/p></description></item><item><title>How Microprocessors Make Music</title><link>https://hill.pictures/blog/192722-how-microprocessors/</link><pubDate>Sat, 12 Nov 2022 18:11:52 +0000</pubDate><author>hillexed@email.com (hillexed)</author><guid>https://hill.pictures/blog/192722-how-microprocessors/</guid><description>
&lt;link rel="stylesheet" href="https://hill.pictures/css/hugo-easy-gallery.css" />

&lt;div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
	 

&lt;/div>

&lt;p>So I&amp;rsquo;ve been working on trying to make a little Arduino powered synthesizer recently! Turns out it&amp;rsquo;s hard and sound is complicated and now I&amp;rsquo;m cursed with knowledge. The only way to get rid of curses is to dilute them, so here&amp;rsquo;s everything I&amp;rsquo;ve learned about how to make sound on an Arduino.&lt;/p>
&lt;h3 id="speakers">Speakers&lt;/h3>
&lt;p>A speaker takes some electricity and moves a little flat thing either outwards or inwards depending on the voltage, which pushes on air and makes sound. They convert changing electricity to changing sound waves. For a microprocessor to make sound, it needs to change voltage really fast so the speaker makes the right sound wave.&lt;/p>
&lt;h3 id="the-simplest-square-sound">The Simplest Square Sound&lt;/h3>
&lt;p>Computers use zeroes and ones. Those are usually voltages: a zero is a low voltage, and an one is a high voltage. Any Arduino pin can easily output a zero or one, in the form of either 0 volts (&amp;ldquo;0V&amp;rdquo;) or 5 volts (&amp;ldquo;5V&amp;rdquo;). However, not all pins can output the values in between. Thankfully, there&amp;rsquo;s one easy type of sound wave you can output using only 0V and 5V: a square wave at maximum volume, which switches between 0V and 5V at a high frequency. Arduinos even have a built-in tone() function to do that, which uses a timer (timer1, which I&amp;rsquo;ll get to later) to manually switch whatever pin you want between 0V and 5V really fast in software. Unfortunately, to output other types of waves (like sine waves), or even just control a wave&amp;rsquo;s volume so your wave can fade out over time, you need to output values between 5V and 0V. For that you need PWM.&lt;/p>
&lt;h3 id="all-the-other-sounds-pwm">All the other sounds: PWM&lt;/h3>
&lt;p>PWM works like this. Let&amp;rsquo;s say you want to output 1 volt. Unfortunately, an Arduino physically cannot output 1 volt - it can only output 100% of 5V or 0% of 5V, with no in between. If only you could just output 20% of 5V. The arduino can&amp;rsquo;t output 20% of 5V, but it can output 5V, 20% of the time. If you switch between 5V and 0V really fast, outputting 0V the other 80% of the time, it averages out to 1V! That&amp;rsquo;s Pulse Width Modulation (PWM): switching between 5V and 0V really fast in order to output signals which are, on average, any voltage you want between 5V and 0V. (To do the averaging, usually a resistor and capacitor are placed in between the output and the speaker to act as an &amp;ldquo;RC filter&amp;rdquo;.)&lt;/p>
&lt;h2 id="timing-is-everything">Timing is everything&lt;/h2>
&lt;p>An Arduino has hardware built into the chip to do PWM. But that hardware is only built into certain pins of the chip. That&amp;rsquo;s because in order to switch between 5V and 0V fast enough to average out, you need a very fast and accurate timer to tell you when to switch. The ATMega328 chip which most Arduinos use has 3 timers built into it, so you can run 3 PWM outputs at different speeds at once if you really wanted to.&lt;/p>
&lt;p>To control PWM, you&amp;rsquo;re really controlling the timers, and so you need to set specific bits in specific arcanely-named all-caps acronym variables like OCR1A which you can only understand by looking up &amp;ldquo;ATMega328 datasheet&amp;rdquo; and banging your head against a wall.&lt;/p>
&lt;p>Timer 0 is used for the system functions millis() and micros(), so it&amp;rsquo;s encouraged not to use it if you have the option. Timer1 is a 16-bit timer, which means its PWM pins can output any multiple of 5/(2^16) volts at once. However, it can only output to digital pin 9 or pin 10 (hardware-chip-footprint-numbers 15 and 16). Timer2 is an 8-bit timer, which means it has less resolution than Timer1 and can only output 2^8 different values between 5V and 0V. It outputs on digital pin 11 or digital pin 5 (hardware-chip-footprint-numbers 17 and 11). That&amp;rsquo;s worse for audio, because you want smoother waves. Use timer 1.&lt;/p>
&lt;h2 id="pwm-has-some-rough-edges">PWM has some rough edges&lt;/h2>
&lt;p>Let&amp;rsquo;s say I want to output a sine wave at a frequency &lt;code>a&lt;/code>. Mathematically, I want to output the function &lt;code>sin(at)&lt;/code>, where &lt;code>t&lt;/code> increases over time. Mathematical sine waves are smooth, with infinite resolution. Unfortunately, sine waves created by computers are really just numbers, and those numbers have a fixed resolution.&lt;/p>
&lt;p>Let&amp;rsquo;s say we&amp;rsquo;re using an 8-bit PWM. In order to use PWM and output your wave, you must tell the PWM an integer between 0 and 2^8 = 255, and it will interpret 0 means 0V and 255 means 5V, and anything in between is treated as a fraction with 255 on the bottom: 20 means 5 * 20/255 volts. The inputs are converted to integers. That means even if your wave is continuous, any values in between any two integers such as 4 and 5 will be rounded to either 4 or 5. Even if the math says your wave should output the value sin(1) * 255 = 4.45, that number will be rounded down to 4 and output 0.078V instead of 0.087 V.&lt;/p>
&lt;p>(Math note: sin(at) is centered at y=0 and ranges from y=-1 to y=1. To properly fit sin(at) into the range 0 to 255, you really want to output &lt;code>sin(at) * 128 + 127&lt;/code> so that the center point is 127, the center of the 0-255 output range.)&lt;/p>
&lt;p>The 8-bit timer forces us to round our numbers to 8-bit integers (0-255), and that causes some effects you can hear. Specifically, the rounding might sound like the wave is &amp;ldquo;tinny&amp;rdquo;, or has some extra buzzing. Some people like that sound because it reminds them of the old days of 8-bit computers and will add that effect into music on purpose, which is called &amp;ldquo;bitcrushing&amp;rdquo;.&lt;/p>
&lt;p>In general, squeezing a wave into k bits will distort the sound less and less the bigger k is. 8-bit is pretty noticeable. 16-bit lets the values range from 0 to 2^16, and there&amp;rsquo;s basically no distortion. 16-bit audio is CD quality. Avoid 8-bit PWM for audio output and use something with more bits if you care about audio quality.&lt;/p>
&lt;h2 id="multiple-notes-at-once-thats-not-in-the-budget">Multiple notes at once? That&amp;rsquo;s not in the budget&lt;/h2>
&lt;p>A piano can play more than one note at the same time. I want my microprocessor to be able to do that too!&lt;/p>
&lt;p>Let&amp;rsquo;s say I&amp;rsquo;m stuck with my 8-bit timer, and I want to output two notes at once. Thankfully, it&amp;rsquo;s not too bad: to hear two things at once, just add up the waves. Then, output &lt;code>sin(at) + sin(bt)&lt;/code>. That&amp;rsquo;s just a number, so I can output it fine using PWM, right?&lt;/p>
&lt;p>Unfortunately, &lt;code>sin(at)&lt;/code> and &lt;code>sin(bt)&lt;/code> both range from -1 to 1, so adding them together could give me a number anywhere from -2 to 2. That means if I used the same formula as before to convert the waves into the 0-255 range, &lt;code>wave(t) * 128 + 127&lt;/code>, I&amp;rsquo;d get a value from −129 to 383, which is outside the valid 8-bit timer range 0 to 255. Whoops.&lt;/p>
&lt;p>To fix this, instead of outputting &lt;code>sin(at) + sin(bt)&lt;/code>, shrink the wave vertically so it ranges from -1 to 1 by dividing by 2. Output &lt;code>(sin(at) + sin(bt))/2&lt;/code>. Perfect.&lt;/p>
&lt;p>You can even generalize this: to play eight notes at once, add up the 8 individual waves and divide by 8, and your signal will stay between -1 and 1. If you want your Arduino to be able to play up to 8 waves at once, you&amp;rsquo;ll want to output &lt;code>(wave1 + wave2 + wave3 + wave4 + wave5 + wave6 + wave 7 + wave8)/8&lt;/code>. If you convert that into the range 0-255, it&amp;rsquo;ll be &lt;code>(wave1 + wave2 + wave3 + wave4 + wave5 + wave6 + wave 7 + wave8)/8 * 255 + 127&lt;/code>. If you&amp;rsquo;re not playing all 8 notes at once, some of those waves might be 0 the entire time and not some form of sin(t).&lt;/p>
&lt;p>However, if you program that into your microprocessor, then even just one note might sound very distorted! Why?&lt;/p>
&lt;p>Remember, the less bits you have to store a wave, the more distorted it&amp;rsquo;ll sound. If sin(x) can take any value between -1 and 1, it&amp;rsquo;s converted to 8-bit number that&amp;rsquo;s rounded to any value between 0 and 255. But if 8 notes can be played at once, and we divided by 8, we&amp;rsquo;re effectively adding waves of the form &lt;code>sin(x)/8&lt;/code>, which is rounded to an integer between 0 and 32.&lt;/p>
&lt;p>This rounding has two effects: first, using the same speaker, our original 0-255 wave moves the speaker eight times more distance compared to an 0-32 wave, so the new wave sounds much quieter*. (This problem once tricked me into thinking my code stopped working, when really it was just too quiet to hear.) Second, rounding a wave to one of 32 values adds a LOT more distortion than rounding to one of 256 values, and you can hear that distortion very strongly.&lt;/p>
&lt;p>*the wave is eight times weaker, but we hear volume logarithmically so it only sounds log_2(8) = 3 times weaker.&lt;/p>
&lt;p>You could think of this as a &amp;ldquo;bit budget&amp;rdquo;: you can either spend your 0-255 range on one high quality wave, or give each wave 1/8th of the total range to represent them so that you can add up to 8 waves at once at the cost of bitcrushing each individual wave.&lt;/p>
&lt;p>Use 16-bit outputs. That way you can play multiple waves at once without running into the bitcrushing problem as much, because an 0-65536 range divided by 8 is still a very high quality 0-8192 range. You&amp;rsquo;ll still have to make the speaker louder to counteract the volume loss from dividing your waves to play multiple notes at once, however.&lt;/p>
&lt;h2 id="also-theres-intense-time-pressure">Also, there&amp;rsquo;s intense time pressure&lt;/h2>
&lt;p>Humans can hear waves up to 20kHz, which means if you want to be able to output all possible waves a human can hear, your microcontroller needs to choose which value to output 40,000 times a second. That includes doing any math to compute sin(x), adding the waves from multiple notes at once, etc. It&amp;rsquo;s not too bad if you only play one note at once - in fact, for a square wave you can simply use a timer - but if you want to play multiple notes at once, time starts to become a problem.&lt;/p>
&lt;p>An Arduino runs at a 16mHz clock speed by default. That means whatever code you write to choose which 0-255 or 0-65536 value to output, that code needs to involve less than 16000000 / 40000 = 400 machine instructions. Your function needs to be FAST. If it takes too long, you&amp;rsquo;ll get distortion!&lt;/p>
&lt;p>Writing fast code is hard. I&amp;rsquo;ve successfully gotten up to 4 waves added together on an Arduino, but but as soon as I tried adding a few more additions to bring it up to 6 waves at a time, my code took too long to compute, lagged, and made weird digital noises instead of my crisp waves. Want to compute (wave1 + wave2 + wave3)/3? Too slow. The ATMega chip doesn&amp;rsquo;t have a division instruction built in, so dividing by 3 takes as much time as a good fraction of the rest of my code combined.&lt;/p>
&lt;p>A library called Mozzi solves the gotta-go-fast problem by having its fast function be &amp;ldquo;read one value from the precomputed buffer and output it&amp;rdquo;, then updating that precomputed buffer only once every 256 function calls. Another technique for outputting waves quickly is called &amp;ldquo;wavetable synthesis&amp;rdquo;, where you save many values of sin(x) for various x in a table 1024 entries long, and that way accessing table[x] is very quick. Other projects go fast by abandoning the Arduino&amp;rsquo;s ATMega328 chip for an even faster computer, such as a Teensy. In fact, the device you&amp;rsquo;re reading this on is much faster than either of those, and that&amp;rsquo;s why people usually make music with computers nowadays.&lt;/p>
&lt;h2 id="in-summary">In summary&lt;/h2>
&lt;p>Computers are made of workarounds upon workarounds upon workarounds, modern processor speeds are a miracle we take for granted, and I&amp;rsquo;d say music was a mistake but actually the cool noises you get at the end are worth it.&lt;/p></description></item></channel></rss>