
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
   <title>llm on e-tinkerer</title>
   <link>https://etinkerer.net/categories/llm/</link>
   <description>Recent content in llm on e-tinkerer</description>
   <generator>Hugo -- gohugo.io</generator>
   <language>en</language>
   <copyright>Copyright &amp;copy; 2025 - e-tinkerer</copyright>
   <lastBuildDate>Fri, 16 Jan 2026 00:00:00 +0000</lastBuildDate>
   
       <atom:link href="https://etinkerer.net/categories/llm/index.xml" rel="self" type="application/rss+xml" />
   
   
     <item>
       <title>Deploying local LLM using ollama and openweb-ui</title>
       <link>https://etinkerer.net/posts/0025-deploying-local-llm-with-ollama-and-openweb-ui/</link>
       <pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate>
       
       <guid>https://etinkerer.net/posts/0025-deploying-local-llm-with-ollama-and-openweb-ui/</guid>
       <description>&lt;p&gt;&lt;em&gt;Main header image generated by AI&lt;/em&gt;&lt;/p&gt;&lt;p&gt;At the time of writing deploying local LLMs has gotten straightforward, and it can be done directly from the command line. Let&amp;rsquo;s walk through setting up ollama and a frontend for some freely available llm models.&lt;/p&gt;&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;&lt;p&gt;Installing nvidia drive, or ollama are not addressed here, there are plenty of resources online.&lt;/p&gt;&lt;p&gt;Confirm that ollama is running with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;curl -s http://127.0.0.1:11434/api/tags | head&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The status of nvidia hardware is queried with the command:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;nvidia-smi&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Output look something like this:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Sat Jan 17 14:01:11 2026       +-----------------------------------------------------------------------------------------+| NVIDIA-SMI 580.95.05              Driver Version: 580.95.05      CUDA Version: 13.0     |+-----------------------------------------+------------------------+----------------------+| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC || Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. ||                                         |                        |               MIG M. ||=========================================+========================+======================||   0  NVIDIA GeForce RTX 5060 Ti     Off |   00000000:01:00.0 Off |                  N/A ||  0%   36C    P8              4W /  180W |     967MiB /  16311MiB |      0%      Default ||                                         |                        |                  N/A |+-----------------------------------------+------------------------+----------------------++-----------------------------------------------------------------------------------------+| Processes:                                                                              ||  GPU   GI   CI              PID   Type   Process name                        GPU Memory ||        ID   ID                                                               Usage      ||=========================================================================================||    0   N/A  N/A            8937      G   /usr/lib/xorg/Xorg                      178MiB ||    0   N/A  N/A            9189      G   /usr/bin/gnome-shell                      9MiB ||    0   N/A  N/A            9654      G   /usr/libexec/gnome-initial-setup         11MiB ||    0   N/A  N/A           10235      G   /usr/bin/gnome-control-center            12MiB ||    0   N/A  N/A          628299      C   /usr/local/bin/python3.11               678MiB |+-----------------------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the output you can see what hardware is available as well as what processes are using VRAM. I&amp;rsquo;m running NVIDIA GeForce RTX 5060 with 16GB of memory and some processes are using the GPU.&lt;/p&gt;&lt;h3 id=&#34;loading-a-model&#34;&gt;Loading a model&lt;/h3&gt;&lt;p&gt;Different models can be found in the ollama &lt;a href=&#34;https://ollama.com/library&#34;&gt;library&lt;/a&gt;. Given the model &lt;code&gt;deepseek-r1&lt;/code&gt; the model can be pulled with:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ollama pull deepseek-r1:14b&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like the &lt;code&gt;deepseek-r1&lt;/code&gt; the models &lt;code&gt;qwen3&lt;/code&gt; and &lt;code&gt;qwen3-coder&lt;/code&gt; are commonly used. In the following tables we see models sizes relative to parameter count.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Table 1: LLM model sizes and parameter counts&lt;/em&gt;&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Size&lt;/th&gt;&lt;th&gt;Context&lt;/th&gt;&lt;th&gt;Input&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;qwen3:0.6b&lt;/td&gt;&lt;td&gt;523MB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:1.5b&lt;/td&gt;&lt;td&gt;1.1GB&lt;/td&gt;&lt;td&gt;128K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:1.7b&lt;/td&gt;&lt;td&gt;1.4GB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:4b&lt;/td&gt;&lt;td&gt;2.5GB&lt;/td&gt;&lt;td&gt;256K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:7b&lt;/td&gt;&lt;td&gt;4.7GB&lt;/td&gt;&lt;td&gt;128K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:8b&lt;/td&gt;&lt;td&gt;5.2GB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:8b&lt;/td&gt;&lt;td&gt;5.2GB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:14b&lt;/td&gt;&lt;td&gt;9.3GB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:14b&lt;/td&gt;&lt;td&gt;9.0GB&lt;/td&gt;&lt;td&gt;128K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3-coder:30b&lt;/td&gt;&lt;td&gt;19GB&lt;/td&gt;&lt;td&gt;256K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:32b&lt;/td&gt;&lt;td&gt;20GB&lt;/td&gt;&lt;td&gt;40K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:32b&lt;/td&gt;&lt;td&gt;20GB&lt;/td&gt;&lt;td&gt;128K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:70b&lt;/td&gt;&lt;td&gt;43GB&lt;/td&gt;&lt;td&gt;128K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3:235b&lt;/td&gt;&lt;td&gt;142GB&lt;/td&gt;&lt;td&gt;256K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qwen3-coder:480b&lt;/td&gt;&lt;td&gt;290GB&lt;/td&gt;&lt;td&gt;256K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;deepseek-r1:671b&lt;/td&gt;&lt;td&gt;404GB&lt;/td&gt;&lt;td&gt;160K&lt;/td&gt;&lt;td&gt;Text&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Unfortunately my hardware is just short of loading the &lt;code&gt;qwen3-coder:30b&lt;/code&gt; model into GPU memory. The remaining part of model layers would end up in RAM/CPU causing a bottleneck in inference perfomance.&lt;/p&gt;&lt;p&gt;The largest models have hundreds of billions of parameters. Such models need special cloud computing platform.&lt;/p&gt;&lt;h3 id=&#34;interacting-with-the-llm&#34;&gt;Interacting with the LLM&lt;/h3&gt;&lt;p&gt;Once a model is pulled it can be run interactively on the command line:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ollama run deepseek-r1:8b&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you can give prompts to the model:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;etinkerer@my_gpu_host:~$ ollama run deepseek-r1:14b&amp;gt;&amp;gt;&amp;gt; Tell me a joke!Sure, here&amp;#39;s a light-hearted joke for you:Why don’t skeletons fight each other?  Because they don’t have the *guts*! 😄&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compared to a online service like ChatGPT this model has faster inference, with one gpu. Still serviced llms often hace access to RAG like web searches automatically.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;etinkerer@my_gpu_host:~$ ollama run deepseek-r1:14b&amp;gt;&amp;gt;&amp;gt; How recent is you knowledge?My knowledge cutoff is July 2024, plus I can also access current info through the internet. I&amp;#39;ll do my best to help you with accurate info.&amp;gt;&amp;gt;&amp;gt; Wait, you can access the internet?No, I&amp;#39;m an AI model trained on data up until July 2024, and I don&amp;#39;t have access to the internet or real-time information. However, my knowledge is based on patterns in the text I was trained on, which includes general knowledge, books, websites, and other sources. Let me know how I can help!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command line is great but sharing the models for other users needs a dedicated frontend. Let&amp;rsquo;s deploy one.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;setting-up-openweb-ui&#34;&gt;Setting up openweb-ui&lt;/h2&gt;&lt;p&gt;Open WebUI is an open-source AI platform for managing multiple models thorugh a single interface. For easy setup there&amp;rsquo;s a container image for the openweb-ui:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;docker run -d --name open-webui --restart always \  -p 3000:8080 \  -e OLLAMA_BASE_URL=http://172.17.0.1:11434 \  -e RESET_CONFIG_ON_START=true \  -v open-webui:/app/backend/data \  ghcr.io/open-webui/open-webui:main&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that if running the ollama on host &amp;ldquo;bare-metal&amp;rdquo; then you might need to add:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Service]Environment=&amp;#34;OLLAMA_HOST=0.0.0.0:11434&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to the ollama service with &lt;code&gt;sudo systemctl edit ollama&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The openweb-ui should appear on &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://etinkerer.net/images/openweb-ui.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&lt;h2 id=&#34;development-in-terminal-with-aider&#34;&gt;Development in terminal with aider&lt;/h2&gt;&lt;p&gt;Using &lt;code&gt;ollama run&lt;/code&gt; is good for simple discussion but to get more out of models we need a better tool.&lt;/p&gt;&lt;h1 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h1&gt;&lt;p&gt;Playing around with LLMs is a lot of fun! Open WebUI has a lot of settings that aren&amp;rsquo;t available with commercial AI tools like OpenAI. You can give the model access to external resources like your GitLab server, online search engine APIs and other MCP servers. As cutting-edge hardware is less available bigger models can be loaded to multiple GPUs using ollamas Modelfiles.&lt;/p&gt;&lt;p&gt;A good thing for the skepticals is that you can manage the access control, like read and write permissions, if you want to limit the models. Happy prompting!&lt;/p&gt;&lt;h2 id=&#34;further-reading&#34;&gt;Further reading&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Running LLMs on mutiple &lt;a href=&#34;https://medium.com/@samanch70/goodbye-vram-limits-how-to-run-massive-llms-across-your-gpus-b2636f6ae6cf&#34;&gt;GPUs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Demystifying the Model Context Protocol with Python &lt;a href=&#34;https://mostafawael.medium.com/demystifying-the-model-context-protocol-mcp-with-python-a-beginners-guide-0b8cb3fa8ced&#34;&gt;medium&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>FPV drones: a technological overview</title>
       <link>https://etinkerer.net/posts/0022-fpv-drones-tech-overview/</link>
       <pubDate>Sun, 09 Nov 2025 00:00:00 +0000</pubDate>
       
       <guid>https://etinkerer.net/posts/0022-fpv-drones-tech-overview/</guid>
       <description>&lt;h1 id=&#34;motivation&#34;&gt;Motivation&lt;/h1&gt;&lt;p&gt;Many types of unmanned systems have been researched over the last decade. Lately I&amp;rsquo;ve been interested in fpv (first-person view) drone systems especially from an engineering perspective. There is a surprisingly lot to cover in these seemingly simple flying systems which are built on multiple engineering disciplines: hardware design, software design, RTOS and RF systems. I&amp;rsquo;ll share my findings in this post with a main focus on software components. After reading this post you should have a basic understanding of how pilots can fly drones over a span of several kilometers with precision, and what technology makes this possible.&lt;/p&gt;&lt;p&gt;I was surprised to see the amount of customizability and modularity in fpv drones and the available open-source embedded firmware. More on that later.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;from-pilot-commands-to-motor-movement&#34;&gt;From pilot commands to motor movement&lt;/h2&gt;&lt;p&gt;First, some terminology needs to be clarified. I guess that solely from a marketing stand point many abbreviations exist for different drone components like: FC, RC, ESC, ELRS, VTX etc. To have clarity on the names and functions of these components let&amp;rsquo;s go through them and explain each individual components role along the way.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s look at the complete signal chain from remote controller to the UAV.&lt;/p&gt;&lt;p&gt;For the drone to be able to respond to pilots commands a radio link needs to be established. The link from pilot-to-drone is refered to as &lt;em&gt;uplink&lt;/em&gt; and from drone-to-pilot as &lt;em&gt;downlink&lt;/em&gt;. Control signals, such as joystick movements and switch presses, are transmitted trough the uplink. These control signals, implementing some control protocol, are processed by the on-board &lt;em&gt;flight controller&lt;/em&gt; (FC) and then transmitted to on-board actuators, in this case, the propeller motors. The FC does not directly control the motors itself but instead uses an intermediate component, an &lt;em&gt;electronic speed controller&lt;/em&gt; (ESC) to adjust individual motor rotation speeds.&lt;/p&gt;&lt;p&gt;There are many different types of FCs and ESCs that can serve different types of UAVs: quadcopters, octocopters and fixed-wing drones.&lt;/p&gt;&lt;p&gt;You&amp;rsquo;ll see fpv pilots wearing futuristic looking fpv goggles. In the occulars they see a transmitted video signal, either analog or digital, on small displays. The drone reports system diagnostics on an OSD (on-screen display) that is overlaid on the video feed. The OSD shows different metrics such as altitude, signal strength, battery level etc. The components responsible for the video downlink is the &lt;em&gt;VTX&lt;/em&gt;, short for Video Transmitter (TX and RX are used to present transmit and receive channels).&lt;/p&gt;&lt;p&gt;These components working together ensures that the pilot can take-off, fly and land the drone with precision. Now what if for some reason the drone flies out-of-sight and the control link is lost? Many FCs support a GPS module which is used to return-to-base when the radio link is down. Some GPS modules broadcast the &lt;a href=&#34;https://drone-remote-id.com/&#34;&gt;RemoteID&lt;/a&gt;, a remote drone identification message format, which makes it easier to locate a crashed drone.&lt;/p&gt;&lt;p&gt;The most common motors used on an fpv drone are by far brushless DC (BLDC) motors. Compared to DC motors with brushes they are more energy efficient and a lot more precise in RPM control.&lt;/p&gt;&lt;p&gt;So summarizing aforementioned systems and their purposes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Flight controller&lt;/strong&gt; (FC): an SoC that contains a microcontroller and integrated circuits like a gyroscope and accelerometer. The FC interfaces to other devices such as GPS module, LEDs and antennas. Several manufacturers have similiar designs that are based on the SMT32 MCUS like F722 and F405 (see &lt;a href=&#34;https://www.ebay.com/sch/i.html?_nkw=flight+controller&amp;amp;_sacat=0&amp;amp;_from=R40&amp;amp;_trksid=p2334524.m570.l1313&amp;amp;_odkw=fc+esc&amp;amp;_osacat=0&#34;&gt;eBay search&lt;/a&gt;). The FC model name comes from the fact that they are based on STM32 MCUs of a corresponding microcontroller model type.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Electronic speed controller&lt;/strong&gt; (ESC): A controller for driving BLDC motors. It has an MCU that drives three MOSFETs. The FC sends one PWM signal to the ESC for one motor. A quadcopter thus needs four PWM signals.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Radio link&lt;/strong&gt;: (or &lt;em&gt;control link&lt;/em&gt;) a transceiver pair that passes control signals between end-points like a radio controller and a receiver module. A common open-source protocol used in is the ExpressLRS (ELRS).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Video trasmitter&lt;/strong&gt; (VTX): namely the transmitter on-board the drone. It receives and processes the video feed from the camera and transmits the signal to pilots video receiver.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Instead of a flight controller a more sophisticated system can carry a &lt;em&gt;flight computer&lt;/em&gt; which has more computational resources.&lt;/p&gt;&lt;p&gt;These are in my opinion the most relevant parts in an introduction. Let&amp;rsquo;s look at some example FCs, ESCs and connection diagrams.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;inspection-of-a-complete-fpv-build&#34;&gt;Inspection of a complete fpv build&lt;/h2&gt;&lt;p&gt;Drone frame sizes are noted by the size of the propellers used like 3″, 5″, 7″ and 10″. Racing and freestyle fpvs use the smaller 3″ and 5&amp;quot; frame sizes while long range fpvs use larger frames. Larger propellers are more efficient in creating thrust due to blade size. Consequently they can carry larger batteries which requires more thrust and so forth.&lt;/p&gt;&lt;p&gt;This Seeker 5 frame houses a complete fpv setup.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TBC&lt;/strong&gt;&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;flight-controller-firmware&#34;&gt;Flight controller firmware&lt;/h2&gt;&lt;p&gt;When looking at the FC software I want to point out two key characteristics: &lt;em&gt;autonomy&lt;/em&gt; and &lt;em&gt;control&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;When we have an autonomous drone, let&amp;rsquo;s say it is fully autonomous, then a pilot has no control over it during the drones operation time. Fully autonomous is a bit of a fuzzy term so let&amp;rsquo;s instead talk about partional autonomy with &lt;em&gt;waypoints&lt;/em&gt; and &lt;em&gt;missions&lt;/em&gt;. Here we give a drone a mission by defining a group of waypoints which the drone crosses in it&amp;rsquo;s flight path. A waypoint can be for example a GPS coordinate. When having this partial autonomy the control of motors is managed by the software alone.&lt;/p&gt;&lt;p&gt;Then on the other hand when a pilot wants full control of the drone the whole idea of autonomy looses it&amp;rsquo;s purpose. With full control other aspects become more relevant like minimal latency in radio transmission, control refresh rate and discharge rate.&lt;/p&gt;&lt;p&gt;Conveniently there exists three community driven FWs that have different levels of control and autonomy: &lt;em&gt;betaflight&lt;/em&gt;, &lt;em&gt;iNAV&lt;/em&gt; and &lt;em&gt;ardupilot&lt;/em&gt;.&lt;/p&gt;&lt;h3 id=&#34;betaflight&#34;&gt;Betaflight&lt;/h3&gt;&lt;p&gt;A fact that the Betaflight is open-source is particularily appealing. Because of the big community support betaflight supports a lot of different hardware. In theory a hobbyist could design their own hardware for this FW. The codebase for betaflight is rather large but very well structured. There is even a custom scheduler algorithm implemented on the system! A quick look shows that unit tests are in place as well. On GitHub author page a configuration software for new drone builds is provided. It supports all common drone configurations and goes through detailed calibration of all system components. A CLI mode is also available which is a big plus in my opinion.&lt;/p&gt;&lt;p&gt;If you are curios in learning an embedded system that is of semi-large scale then I suggest studying the betaflight repo in detail.&lt;/p&gt;&lt;h3 id=&#34;inav&#34;&gt;iNAV&lt;/h3&gt;&lt;p&gt;iNAV and betaflight are both forked from the same ancestor called &lt;a href=&#34;https://cleanflight.com/&#34;&gt;Cleanflight&lt;/a&gt; with betaflight focusing on FW for agility and iNAV more on navigation. iNAV supports very much the same hardware as betaflight and you can clearly see the common ancestor in the user interfaces. This means that it is relatively quick and easy to switch from betaflight to INAV and vice versa.&lt;/p&gt;&lt;p&gt;iNAV support waypoint based missions out of the box. During flgiht the pilot can activate a preloaded mission from their radio controller and the drone starts executing the mission. The implementation of the missions isn&amp;rsquo;t perfect but for the price tag it is excellent. It is very recommended that the drone has a GPS and compass for better navigation accuracy.&lt;/p&gt;&lt;h3 id=&#34;ardupilot&#34;&gt;ArduPilot&lt;/h3&gt;&lt;p&gt;Released in 2009 ArduPilot support a lot of different UAVs: multirotors, fixed-wings, even ROVs, boats and submarines. For longer distance operation there is an antenna tracker firmware included. Due to bigger complexity ArduPilot requires a flight computer and smaller FCs are no longer apt for the job. Common platforms like Raspberry Pi and BeagleBone are supported. 3D Robotics sells their own hardware called Pixhawk for custom ArduPilot builds. It is a bigger beast compared to small microcontrollers. Beginners can find the modular sensor mounts on Pixhawk helpful for first builds.&lt;/p&gt;&lt;p&gt;Being an ambitious project ArduPilot has it&amp;rsquo;s own Ground Control Station (GCS) software: &lt;a href=&#34;https://ardupilot.org/planner2/&#34;&gt;APM planner&lt;/a&gt; where user can plan missions and monitor a drone midflight.&lt;/p&gt;&lt;p&gt;While at the same time betaflight, iNAV and ArduPilot are all community supported there is little overlap between ArduPilot and Cleanflight derivatives. This means that the platforms have to be studied individually and thus you kinda need to pick you preference. Maybe one day there will be an even more unified open source tech pool to choose from.&lt;/p&gt;&lt;p&gt;While I have least experince on the ArduPilot their copter &lt;a href=&#34;https://ardupilot.org/copter/index.html&#34;&gt;wiki&lt;/a&gt; is something I browse regularily.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;other-components&#34;&gt;Other components&lt;/h2&gt;&lt;p&gt;To conclude let&amp;rsquo;s still skim through other relevant components.&lt;/p&gt;&lt;h3 id=&#34;battery&#34;&gt;Battery&lt;/h3&gt;&lt;p&gt;The controllers obviously need a power supply. Lithium-ions batteries are common because of their fast discharging capability. When looking at bifferent battery sizes you&amp;rsquo;ll see references to 18650 and 21700 li-ion battery types as well as cell counts like S1, S2, up to around S6. The number refers to the amount of li-ion cells in a single battery.&lt;/p&gt;&lt;p&gt;The battery is connected directly to the ESC. Commonly a XT60 connector with an AWG of 16 wire is used. ESCs can handle a current discharge rate of 20-65A. On larger ten inch drones ESCs of 80A current throughput are used.&lt;/p&gt;&lt;h3 id=&#34;bldc-motors-and-propellers&#34;&gt;BLDC motors and propellers&lt;/h3&gt;&lt;p&gt;A quadcopter uses four BLDC motors that spin in different directions to hold altitude. The BLDC motors have a KV rating (not kilovolts) that responds to the number of RPM per volt. For larger drones &amp;gt;7″ a set of lower KV motors are used (1000-1600). Likewise smaller drones &amp;lt;5″ use higher KV value motors (1800-2500). As the KV values increased the motors provide more RPM but less torque. Some very small drones &amp;lt;2.5″ can use KV value of &amp;gt;3200.&lt;/p&gt;&lt;p&gt;Unlike the KV value the size of the drone propellers is proportional to the drone frame size.&lt;/p&gt;&lt;h3 id=&#34;navigation&#34;&gt;Navigation&lt;/h3&gt;&lt;p&gt;By convention in aviation when talking about vehicle direction the three dimentions of movement are commonly used: &lt;em&gt;pitch&lt;/em&gt;, &lt;em&gt;roll&lt;/em&gt; and &lt;em&gt;yaw&lt;/em&gt; (&lt;a href=&#34;https://simple.wikipedia.org/wiki/Pitch,_yaw,_and_roll&#34;&gt;wiki&lt;/a&gt;). The joysticks on radio controllers are in fact used to adjust the pitch, roll and yaw directly.&lt;/p&gt;&lt;h3 id=&#34;video-link-formats&#34;&gt;Video link formats&lt;/h3&gt;&lt;p&gt;The analog format is either PAL or NTSC composite video format that is FM modulated and transmitter over 1.3GHz, 5.8GHz or some at 6.8-7.0GHz. The digital formats are often proprietary and thus only usable with manufacturers devices. The analog format provides lower latency but is subject to noise tearing. Digital formats implements a &lt;em&gt;guard band&lt;/em&gt; that protects the transmission from adjecent band interference.&lt;/p&gt;&lt;p&gt;Common manufacturers for digital VTXs are DJI, ZeroHD and Walksnail. Analog systems have a more diverse supply because of the use of standardized NTSC and PAL video formats.&lt;/p&gt;&lt;h3 id=&#34;fpv-simulations&#34;&gt;FPV Simulations&lt;/h3&gt;&lt;p&gt;Often first hours of flying are spent in fpv simulator. The two most known sims are Liftoff and VelociDrone. Liftoff is available on steam and it support many RC controllers that fly actual drones. This topic is covered in detail &lt;a href=&#34;https://oscarliang.com/fpv-simulator/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;&lt;h1 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h1&gt;&lt;p&gt;While the primary focus here was on fpvs the software mentioned here has been used on many other types of UAVs as well like rovers and RC planes. It goes to show how the ecosystem of both open source resources and community support have created a diverse playground for UAV experiments.&lt;/p&gt;&lt;p&gt;You should now be a bit more familiar on the state of the modern fpv drone systems. The system components discussed here are big topics on their own and would deserve their own posts. Luckily there is a &lt;a href=&#34;https://oscarliang.com/&#34;&gt;blog&lt;/a&gt; by Oscar Liang with earliest DIY fpv posts from 2013.&lt;/p&gt;&lt;h1 id=&#34;further-reading&#34;&gt;Further reading&lt;/h1&gt;&lt;ul&gt;&lt;li&gt;betaflight &lt;a href=&#34;https://www.betaflight.com/docs/wiki/guides/archive/wiki-welcome-old#introduction&#34;&gt;history&lt;/a&gt;&lt;/li&gt;&lt;li&gt;betaflight source on &lt;a href=&#34;https://github.com/betaflight/betaflight&#34;&gt;github&lt;/a&gt;&lt;/li&gt;&lt;li&gt;ArduPilot &lt;a href=&#34;https://ardupilot.org/&#34;&gt;home page&lt;/a&gt;&lt;/li&gt;&lt;li&gt;ArduPilot Copter &lt;a href=&#34;https://ardupilot.org/copter/index.html&#34;&gt;wiki&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Oscar Liang&amp;rsquo;s &lt;a href=&#34;https://oscarliang.com/&#34;&gt;blog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>On Linux init managers</title>
       <link>https://etinkerer.net/posts/0018-on-init-systems/</link>
       <pubDate>Sun, 28 Sep 2025 12:00:00 +0200</pubDate>
       
       <guid>https://etinkerer.net/posts/0018-on-init-systems/</guid>
       <description>&lt;p&gt;In this post let&amp;rsquo;s look into the first process that is run in the linux userland: the &lt;em&gt;init process&lt;/em&gt;. We&amp;rsquo;ll look briefly at the kernel-userland interface and existing init managers. This post is per se not a tutorial but an overview of concepts relating to init managers.&lt;/p&gt;&lt;h1 id=&#34;a-brief-overview-of-init-managers&#34;&gt;A brief overview of init managers&lt;/h1&gt;&lt;p&gt;A reminder of the kernel boot process: the kernel initializes the system, loads in-tree kernel modules, detects and initializes hardware. After this the kernel is ready to start running userland.&lt;/p&gt;&lt;p&gt;The init process is the first process that is run when the linux kernel enter the userland. For brevity let&amp;rsquo;s call the init process just &lt;em&gt;init&lt;/em&gt;. At this stage the kernel is looking for executable files. A &lt;a href=&#34;https://github.com/torvalds/linux/blob/ba36dd5ee6fd4643ebbf6ee6eefcecf0b07e35c7/init/main.c#L1539&#34;&gt;snippet&lt;/a&gt; of kernel source shows the exact paths the kernel looks for:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;try_to_run_init_process&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/sbin/init&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;try_to_run_init_process&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/etc/init&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;try_to_run_init_process&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/bin/init&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;try_to_run_init_process&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/bin/sh&amp;#34;&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;panic&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No working init found.  Try passing init= option to kernel. &amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;See Linux Documentation/admin-guide/init.rst for guidance.&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If no executable is found the kernel panics and displays a message that no working init is found. You can see that the last fallback is the &lt;code&gt;/bin/sh&lt;/code&gt;. The message also denotes that a custom path for init can be given with an option &lt;code&gt;init=/path/to/my/init&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Technically the init process can be any executable script, a schell script or an executable binary. If the init is a shell script, the script must start with a shebang &lt;code&gt;#!/bin/sh&lt;/code&gt; for the kernel to know to interpret the file with a &lt;code&gt;shell&lt;/code&gt;. If no shebang is found the init is assumed to be an executable binary and in case of a schell script this results in a panic.&lt;/p&gt;&lt;p&gt;If a executalve binary file found it needs to have the &lt;code&gt;int main()&lt;/code&gt; function implemented. By default the first process on the system becomes the process with PID number 1.&lt;/p&gt;&lt;p&gt;If all goes well, the kernel finds a proper executable and calls the &lt;code&gt;try_to_run_init_process()&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;try_to_run_init_process&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;init_filename)&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; ret;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run_init_process&lt;/span&gt;(init_filename);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (ret &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ret &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;ENOENT) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pr_err&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting init: %s exists but couldn&amp;#39;t execute it (error %d)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       init_filename, ret);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; ret;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;run_init_process()&lt;/code&gt; then calls &lt;code&gt;kernel_execve()&lt;/code&gt; with the executable path. The first process in now succesfully running!&lt;/p&gt;&lt;p&gt;As a side thought, if the system had a Python interpreter included in &lt;code&gt;PATH&lt;/code&gt; then technically you could run a &lt;code&gt;.py&lt;/code&gt; script as the first process in the system with &lt;code&gt;#!/bin/python&lt;/code&gt;. That might not be much of use but a fun experiment to try!&lt;/p&gt;&lt;p&gt;The first running process could in theory contain all the application logic. This might make sense if the application logic is brief not that complex. However crashing the PID 1 results in a panic and thus a system crash. In a more complex system separate layers and stacks (network, usb etc.) are needed for better decoupling, maintainability and testability.&lt;/p&gt;&lt;p&gt;Given all this there is a lot of responsibility on the init. In a running system the init should not return nor crash. Modern linux systems have evolved to use more sophisticated init managers such as &lt;code&gt;systemd&lt;/code&gt; that runs on Ubuntu, Red Hat and Fedora based systems.&lt;/p&gt;&lt;p&gt;A more seasoned systems developer knows the &lt;code&gt;sysvinit&lt;/code&gt; and the tacky syntax init script it uses. On Gentoo there is the &lt;code&gt;OpenRC&lt;/code&gt;, a kind of middle-ground between &lt;code&gt;systemd&lt;/code&gt; and &lt;code&gt;sysvinit&lt;/code&gt;, and some other smaller systems like &lt;code&gt;runit&lt;/code&gt; (docs on &lt;a href=&#34;https://www.smarden.org/runit/&#34;&gt;runit&lt;/a&gt;) and &lt;code&gt;minit&lt;/code&gt; (post about minit on &lt;a href=&#34;https://arunprasad86.medium.com/minit-an-experimental-and-tiny-init-system-for-containers-and-microvms-b3d3f9a4b718&#34;&gt;medium.com&lt;/a&gt;). Some init managers like &lt;code&gt;OpenRC&lt;/code&gt; have added modularity. Besides using &lt;code&gt;start-stop-daemon&lt;/code&gt; it supports separate daemon monitoring processes such like &lt;code&gt;s6&lt;/code&gt; (more info on OpenRC and s6 &lt;a href=&#34;https://github.com/OpenRC/openrc/blob/master/s6-guide.md&#34;&gt;here&lt;/a&gt;).&lt;/p&gt;&lt;h2 id=&#34;etcinittab-init-scripts-and-daemons&#34;&gt;/etc/inittab, init scripts and daemons&lt;/h2&gt;&lt;p&gt;On a linux system running full desktop environment there are a lot of daemons. The system manages internal clock with &lt;code&gt;ntpd&lt;/code&gt;, hot-plugged devices with &lt;code&gt;udev&lt;/code&gt;, an ssh server with &lt;code&gt;sshd&lt;/code&gt; etc. The user on a desktop systems needs not to burden themself of running these manually. &lt;code&gt;systemd&lt;/code&gt; in itself is a lot more than an init system that starts and stop services. For more details on &lt;code&gt;systemd&lt;/code&gt; features, see &lt;a href=&#34;https://medium.com/geekculture/the-rise-of-linux-systemd-a-beginners-guide-8ca1e226103a&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;As an example let&amp;rsquo;s look at BusyBox. BusyBox provides an init that supports the &lt;code&gt;/etc/inittab&lt;/code&gt; file from System V Release 2 times.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# inittab for linuxid:1:initdefault:rc::bootwait:/etc/rc1:1:respawn:/etc/getty 9600 tty12:1:respawn:/etc/getty 9600 tty23:1:respawn:/etc/getty 9600 tty34:1:respawn:/etc/getty 9600 tty4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;inittab&lt;/code&gt; is written per line with the format: &lt;em&gt;id:runlevels:action:process&lt;/em&gt;. In other words every line specifies a guide how to spawn a single process at system start up. The script above tells that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;id 1 is the default runlevel&lt;/li&gt;&lt;li&gt;run &lt;code&gt;/etc/rc&lt;/code&gt; at boot and wait until it returns&lt;/li&gt;&lt;li&gt;start four terminal instances on different &lt;code&gt;ttyX&lt;/code&gt; devices. If the getty instances return they are respawned&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This format proves handy in smaller systems. More lines and runlevels can be appended to the file. You can read more about inittab in &lt;a href=&#34;https://manpages.debian.org/unstable/sysvinit-core/inittab.5.en.html&#34;&gt;docs&lt;/a&gt; and how it is parsed by &lt;a href=&#34;https://git.busybox.net/busybox/tree/init/init.c?h=1_36_stable#n670&#34;&gt;BusyBox&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;While the &lt;code&gt;inittab&lt;/code&gt; has some process management rules, like respawning mechanism a larger systems with tens or hundreds or processes needs more sophisticated controls. An init manager, like systemd, treats daemons and user processes as &lt;em&gt;services&lt;/em&gt;. A service is a just a process that is forked from the PID 1. Systemd initiates services using the same syscalls as the &lt;code&gt;/bin/shell&lt;/code&gt;, that is &lt;code&gt;fork()&lt;/code&gt; follwoed by &lt;code&gt;execve()&lt;/code&gt;. An init manager provides commands like &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt; and &lt;code&gt;restart&lt;/code&gt; are provided to manage the running services.&lt;/p&gt;&lt;p&gt;A minimal init manager is an iterator that reads in a list of guide lines, called &lt;em&gt;init scripts&lt;/em&gt;, found in &lt;code&gt;/etc/init.d&lt;/code&gt;, that describe how to configure and start a service. The init script can define start up and tear down functions. The benefit of separate init scripts is easy to understand. Starting services as a part of a loop already allows a single init script startup to crash. A crashed init script is then cleaned and the init system continues to the next script in the list. Once the init scripts are all run the init system goes to sleep. At this stage the user can give commands to the init manager with system tools like &lt;code&gt;systemctl&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;openrc-systemd-sysvinit&#34;&gt;OpenRC, systemd, sysvinit&lt;/h2&gt;&lt;p&gt;Now let&amp;rsquo;s look at some init managers.&lt;/p&gt;&lt;p&gt;For simplicity let&amp;rsquo;s look at a init script that &lt;code&gt;OpenRC&lt;/code&gt; uses. I find the &lt;code&gt;OpenRC&lt;/code&gt; style init scripts easiest to read.&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/sbin/openrc-rundescription=&amp;#34;Example daemon service&amp;#34;command=&amp;#34;/usr/bin/mydaemon&amp;#34;command_args=&amp;#34;--config /etc/mydaemon.conf&amp;#34;pidfile=&amp;#34;/run/mydaemon.pid&amp;#34;command_user=&amp;#34;mydaemon:mydaemon&amp;#34;depend() {    need net    after firewall}start_pre() {    checkpath --directory --owner mydaemon:mydaemon --mode 0755 /run/mydaemon}start() {    ebegin &amp;#34;Starting mydaemon&amp;#34;    start-stop-daemon --start \        --quiet \        --pidfile &amp;#34;${pidfile}&amp;#34; \        --make-pidfile \        --background \        --user &amp;#34;${command_user}&amp;#34; \        --exec &amp;#34;${command}&amp;#34; -- ${command_args}    eend $?}stop() {    ebegin &amp;#34;Stopping mydaemon&amp;#34;    start-stop-daemon --stop \        --quiet \        --pidfile &amp;#34;${pidfile}&amp;#34; \        --exec &amp;#34;${command}&amp;#34;    eend $?}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is clear that the script runs &lt;code&gt;start_pre()&lt;/code&gt; after which &lt;code&gt;start()&lt;/code&gt; is run. When either the user or the init manager stops the service then the &lt;code&gt;stop()&lt;/code&gt; function is called. &lt;code&gt;OpenRC&lt;/code&gt; support custom functions to be implemented in init scripts. The &lt;code&gt;depend()&lt;/code&gt; function defines how the service relates to other services. This mainly has to do with the services start sequence order.&lt;/p&gt;&lt;p&gt;Next let&amp;rsquo;s look at a systemd example. A systemd unit might look like the following:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Unit]Description=Example background serviceAfter=network.target[Service]Type=simpleExecStart=/usr/local/bin/example-app --runRestart=on-failureUser=exampleGroup=exampleWorkingDirectory=/usr/local/bin# LoggingStandardOutput=journalStandardError=journal[Install]WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The syntax is less verbose compared to OpenRC but at the same time the function of some lines is not that obvious, like for example the &amp;ldquo;Type&amp;rdquo; and &amp;ldquo;WantedBy&amp;rdquo; tags.&lt;/p&gt;&lt;p&gt;And finally let&amp;rsquo;s look at a SysV example:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/bin/shDAEMON=/usr/local/bin/example-appDAEMON_OPTS=&amp;#34;--run&amp;#34;NAME=examplePIDFILE=/var/run/$NAME.pidUSER=example. /lib/lsb/init-functionsstart() {    echo &amp;#34;Starting $NAME...&amp;#34;    start-stop-daemon --start --quiet --background --pidfile $PIDFILE --make-pidfile \        --chuid $USER --exec $DAEMON -- $DAEMON_OPTS    status=$?    [ $status -eq 0 ] &amp;amp;&amp;amp; log_end_msg 0 || log_end_msg 1}stop() {    echo &amp;#34;Stopping $NAME...&amp;#34;    start-stop-daemon --stop --quiet --pidfile $PIDFILE --retry=TERM/30/KILL/5    status=$?    [ $status -eq 0 ] &amp;amp;&amp;amp; rm -f $PIDFILE &amp;amp;&amp;amp; log_end_msg 0 || log_end_msg 1}status() {    status_of_proc -p $PIDFILE $DAEMON $NAME &amp;amp;&amp;amp; exit 0 || exit $?}case &amp;#34;$1&amp;#34; in    start)        start        ;;    stop)        stop        ;;    restart)        stop        sleep 1        start        ;;    status)        status        ;;    *)        echo &amp;#34;Usage: $0 {start|stop|restart|status}&amp;#34;        exit 1esacexit 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see the SysV init script is just a shell script. This has two clear benefits: the script can be called directly, has a dependency for a shell, and it could be copied onto the system over &lt;code&gt;scp&lt;/code&gt; and be ready to use.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;start-stop-daemon&lt;/code&gt; has been seen used in several places here. We won&amp;rsquo;t look into how this shared binary works but feel free to explore the basics of &lt;code&gt;start-stop-daemon&lt;/code&gt; &lt;a href=&#34;https://chris-lamb.co.uk/posts/start-stop-daemon-exec-vs-startas&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;&lt;p&gt;In this overview we have briefly looked at init managers and how they handle system process management. We have seen the interface between the linux kernel and userland, and examples of inittab format and OpenRC, systemd and SysV init scripts.&lt;/p&gt;&lt;p&gt;While these topics are often not visible if you&amp;rsquo;re using Ubuntu and package management on a smaller system you might want to concider which init manager suits the system requirements the best.&lt;/p&gt;&lt;p&gt;Hope you learned something new here on init managers and linux. Have a good day!&lt;/p&gt;&lt;p&gt;&amp;ndash;&lt;/p&gt;&lt;h2 id=&#34;further-reading&#34;&gt;Further reading&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;A Survey of Unix Init Schemes (&lt;a href=&#34;https://arxiv.org/abs/0706.2748&#34;&gt;arvix.org&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Computing Fibonacci, but using registers, and assembly</title>
       <link>https://etinkerer.net/posts/0017-blazing-fast-fibonacci/</link>
       <pubDate>Sat, 30 Aug 2025 12:00:00 +0200</pubDate>
       
       <guid>https://etinkerer.net/posts/0017-blazing-fast-fibonacci/</guid>
       <description>&lt;p&gt;In this post we&amp;rsquo;ll go a bit deeper into computation methods using variables in different volatile and non-volatile memory storages. As a foreword, while I&amp;rsquo;ve had this experimentation idea for some time now the fact that there is only mild academic interest in the whole experimentation setup and possible results I&amp;rsquo;ve skipped the whole effort of actually take an iniative.&lt;/p&gt;&lt;p&gt;Still I&amp;rsquo;ve been curious on what the results of this experimentation would be. Thanks to AI tools I could finally look into this niche topic.&lt;/p&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;&lt;p&gt;A common approach to computing Fibonacci sequence is to use stack recursion. While recursion is a powerful method it repeatedly allocates new stack frames. Using this technique uncautiously can result in a system stack overflow. This is actually a very old vunerability in linux, called &lt;em&gt;fork bomb&lt;/em&gt;, that has been decisively addressed. In UNIX systems there is a stack limit policy for individual processes. Because of this the infamous Bash fork bomb REF is terminated by the kernel before crashing the whole system.&lt;/p&gt;&lt;p&gt;The Fibonacci sequence is computed by adding the two consequtive members together to produce the next member in the sequence. In &lt;code&gt;x86&lt;/code&gt; architecture CPU the bus width is 64 bits. In the general register we can store a &lt;code&gt;uint64_t number&lt;/code&gt; an integer up to size $1.84×10^{19}$. This means that $F(93) =  1.2200160415122×10^{19}$ still fits in the register but $F(94) = 1.9740274219868×10^{19}$ no longer fits the variable. Larger values &lt;code&gt;uint128_t&lt;/code&gt; and &lt;code&gt;uint256_t&lt;/code&gt; are rightly stored in multiple registers.&lt;/p&gt;&lt;h2 id=&#34;experimentation-setup&#34;&gt;Experimentation setup&lt;/h2&gt;&lt;p&gt;For our experiment we compute the 93rd Fibonacci number using &lt;code&gt;uint64_t&lt;/code&gt; variables using three different methods. First we use a textbook reference Fibonacci recursion written in C. Then we write the same algorithm but store the variables in registers using &lt;code&gt;register&lt;/code&gt; keyword. Finally we write the algorithm using assembly. We won&amp;rsquo;t stop here. Let&amp;rsquo;s also compare the algorithms when they return all the Fibonacci sequence member up to member $n$ and then only member $n$. As the 93 member won&amp;rsquo;t fit to general register we&amp;rsquo;ll need to store them in RAM.&lt;/p&gt;&lt;p&gt;These three algorithms (tehcnically the first two) are compiled using two different compiler options &lt;code&gt;-O0&lt;/code&gt; (zero optimization) and &lt;code&gt;-O2&lt;/code&gt; (use optimization).&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s look at the algorithms. The source code is in Appendix A. The A1 implements a simple for loop. The A2 is identical but all the variables have the &lt;code&gt;register&lt;/code&gt; keyword prepended. The A3 is a bit more lenghty.&lt;/p&gt;&lt;h2 id=&#34;results-comparison&#34;&gt;Results comparison&lt;/h2&gt;&lt;p&gt;TBC&lt;/p&gt;&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;&lt;p&gt;TBC&lt;/p&gt;&lt;hr&gt;&lt;h1 id=&#34;appendix-a-algorithms&#34;&gt;Appendix A: Algorithms&lt;/h1&gt;&lt;h2 id=&#34;1-textbook-fibonacci&#34;&gt;1#: Textbook Fibonacci&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fib_nonly&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; n;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; c;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; c;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fib_reg_nonly&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; fib_array &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;malloc&lt;/span&gt;((n &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) fib_array[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) fib_array[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fib_array[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fib_array[i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; fib_array[i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; fib_array;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;2-register-fibonacci&#34;&gt;2#: Register Fibonacci&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fib_register_compute_only&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; n;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; c;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; c;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; b;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fib_reg_alln&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; fib_array &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;malloc&lt;/span&gt;((n &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) fib_array[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) fib_array[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;register&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; prev1, prev2, curr;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prev1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fib_array[i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prev2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fib_array[i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        curr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; prev1 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; prev2;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fib_array[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; curr;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; fib_array;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;3-assembly-fibonacci&#34;&gt;3#: Assembly Fibonacci&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    .text    .globl fib_asm_compute_only    .globl fib_asm_compute_and_store# ============================================================================# fib_asm_compute_only# Compute nth Fibonacci number using only registers## Parameters:#   edi = n (int, first argument in x86-64 calling convention)# Returns:#   rax = nth Fibonacci number## Registers used:#   rax = a (previous Fibonacci number)#   rbx = b (current Fibonacci number)#   rcx = loop counter (i)#   rdx = temporary for addition#   r8d = n (saved, 32-bit to avoid sign issues)# ============================================================================    .p2align 4fib_asm_compute_only:    # Handle base cases n &amp;lt;= 1    cmp     $1, %edi    jg      .L_compute_loop_init    movsx   %edi, %rax              # return n if n &amp;lt;= 1    ret    .p2align 4.L_compute_loop_init:    xor     %eax, %eax              # a = 0    mov     $1, %edx                # b = 1 (use edx instead of rbx)    mov     $2, %ecx                # i = 2    .p2align 4.L_compute_loop:    cmp     %edi, %ecx              # compare i with n    jg      .L_compute_done         # if i &amp;gt; n, exit loop        # Compute next Fibonacci: c = a + b, a = b, b = c    lea     (%rax, %rdx), %rsi      # rsi = a + b (use lea for addition)    mov     %rdx, %rax              # a = b    mov     %rsi, %rdx              # b = temp        inc     %ecx                    # i++    jmp     .L_compute_loop         # loop back    .p2align 4.L_compute_done:    mov     %rdx, %rax              # return value = b    ret# ============================================================================# fib_asm_compute_and_store# Compute and store all Fibonacci numbers from 0 to n## Parameters:#   edi = n (int, first argument)# Returns:#   rax = pointer to allocated array (or NULL on error)## Registers used:#   rbx = saved n (callee-saved)#   r12 = array pointer (callee-saved)#   rcx = loop counter#   rax, rdx = for computation# ============================================================================fib_asm_compute_and_store:    push    %rbx                    # save callee-saved registers    push    %r12        movsx   %edi, %rbx              # save n in rbx (sign-extend to 64-bit)        # Allocate memory: (n+1) * 8 bytes    lea     1(%rbx), %rdi           # rdi = n + 1    shl     $3, %rdi                # multiply by 8 (size of uint64_t)    call    malloc                  # call malloc        test    %rax, %rax              # check if malloc returned NULL    jz      .L_store_error          # if NULL, return error        mov     %rax, %r12              # save array pointer in r12        # Initialize base cases    movq    $0, (%r12)              # fib[0] = 0        cmp     $0, %rbx                # if n == 0    je      .L_store_done           # we&amp;#39;re done        movq    $1, 8(%r12)             # fib[1] = 1        cmp     $1, %rbx                # if n == 1    je      .L_store_done           # we&amp;#39;re done        # Loop to compute remaining values    mov     $2, %rcx                # i = 2.L_store_loop:    cmp     %rbx, %rcx              # compare i with n    jg      .L_store_done           # if i &amp;gt; n, exit loop        # Load fib[i-1] and fib[i-2]    mov     %rcx, %rax              # rax = i    dec     %rax                    # rax = i - 1    mov     (%r12, %rax, 8), %rdx   # rdx = fib[i-1]    dec     %rax                    # rax = i - 2    mov     (%r12, %rax, 8), %rax   # rax = fib[i-2]        # Compute fib[i] = fib[i-1] + fib[i-2]    add     %rdx, %rax              # rax = fib[i-1] + fib[i-2]        # Store fib[i]    mov     %rax, (%r12, %rcx, 8)   # fib[i] = rax        inc     %rcx                    # i++    jmp     .L_store_loop           # loop back.L_store_done:    mov     %r12, %rax              # return array pointer    pop     %r12                    # restore callee-saved registers    pop     %rbx    ret.L_store_error:    xor     %rax, %rax              # return NULL    pop     %r12                    # restore callee-saved registers    pop     %rbx    ret    .section .note.GNU-stack,&amp;#34;&amp;#34;,@progbits&lt;/code&gt;&lt;/pre&gt;</description>
     </item>
   
     <item>
       <title>Fundamental data structures: Linked List</title>
       <link>https://etinkerer.net/posts/0015-linked-list/</link>
       <pubDate>Thu, 29 May 2025 12:00:00 +0200</pubDate>
       
       <guid>https://etinkerer.net/posts/0015-linked-list/</guid>
       <description>&lt;p&gt;This post covers an intermediate system/algorithm designer to a high-throughput data system using linked lists. For the same of brevity code examples are kept brief.&lt;/p&gt;&lt;h1 id=&#34;motivation&#34;&gt;Motivation&lt;/h1&gt;&lt;p&gt;The theory behind one of the most fundamental data structures linked list is by itself fascinating but to give a motivation let&amp;rsquo;s start with a real-life example. Recently I&amp;rsquo;ve had to deal with a lot of sensor data. By lot I mean 40Msps of 32bit data which is around 160MBps. Using &lt;code&gt;memcpy&lt;/code&gt; on all of it is simply not a viable option because memory oprations are known to be slow and the system is already busy with the data stream and we haven&amp;rsquo;t yet even talked about the application layer. Since we don&amp;rsquo;t want to waste cpu time copying this data stream and we still want to do something useful with the data what options do we have?&lt;/p&gt;&lt;p&gt;The first list implementation I used was the &lt;code&gt;ArrayList&lt;/code&gt; in Java. In Python a simple list is created with &lt;code&gt;[]&lt;/code&gt;. New variables can be added to the list with &lt;code&gt;.append()&lt;/code&gt;. These are good implementations to work with when learning basic data structure interfaces. They are good abstractions since they can be used without any knowledge of underlying implementation and they have readily available connections to algorithms like sorting and shuffling.&lt;/p&gt;&lt;p&gt;A benefitial standpoint for an argument over design is to be able to analyze the implementations and argue why it is best suitable for a given task. One might settle for a working implementation in this case a list structure because it &amp;ldquo;works out of the box&amp;rdquo; and &amp;ldquo;gets the job done&amp;rdquo;. But to be able to argue over design choises is crucial in algorithm analysis and solution evaluation. We&amp;rsquo;ll come back to this analysis at the end of this post.&lt;/p&gt;&lt;p&gt;Let&amp;rsquo;s dive into a little C code. We&amp;rsquo;ll look at different design choises in low-level data structure implementations and possible design patterns that emerge.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;data-structures&#34;&gt;Data structures&lt;/h2&gt;&lt;p&gt;To understand data structures deeply I would state the best way to learn is to look into low level programming concepts like instruction set architectures, CPU core architecture, memory layout, data types and pointers since they all common factors in all computing. At the core linked list is a collections of &lt;strong&gt;nodes&lt;/strong&gt; that point to the next node. This enables the list to be stored in scattered memory locations. This is in contrast to array where data is stored in a single block of continuous memory.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; node {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; payload;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    node&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; next;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} node;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; linked_list_h {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    node&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; first_element;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} linked_list_h;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In a &lt;code&gt;struct&lt;/code&gt; all elements are memory aligned by the compiler. The &lt;code&gt;node&lt;/code&gt; has a pointer to the next node in the list in a nested structure. This is a completely fine definition in C. The last element is null terminated so we know we have reached the end of the list. To access the &lt;strong&gt;nth&lt;/strong&gt; element in the list we simply count pointer accesses until we are at the &lt;strong&gt;nth&lt;/strong&gt; entry of the list. Note that we could eliminate out-of-bound index access by creating a &lt;code&gt;size&lt;/code&gt; variable that is incremented and decremented when adding or removing elements to the linked list.&lt;/p&gt;&lt;p&gt;One thing to note is that C does not enforce coding paradigmas like object oriented programming languages. This means the programmer is responsible for using different design patterns. Remember nothing is taken for granted in C.&lt;/p&gt;&lt;p&gt;For a system to access and use the linked list we need to initiate a handle with a fixed pointer to the first element of the list. This type of linekd list is called an intrusive linked list. The word intrusive means that the node pointers and the payload are embedded to the same data strcuture. To finish this implementation we would then design an API for the user. While this implementation is valid approach we may want to include the same payloads in multiple lists. Using this implementation would mean having multiple copies of a single payload in the memory.&lt;/p&gt;&lt;p&gt;If we know we have multiple lists that include the same items we can use another approach.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; node {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    node&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; next;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; payload;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;void*&lt;/code&gt; pointer is used to note that the data can be anything. This is not couraged in practise but it notes the payload can be in theory any data type.&lt;/p&gt;&lt;p&gt;This way the payload exists somewhere in the memory and it can be included to several lists. As a trade-off it is now the programmers resposibility to make sure the payload is freed correctly.&lt;/p&gt;&lt;p&gt;We can place these nodes in a flat memory map region. The region is managed separately and has it&amp;rsquo;s size limits etc. It can be placed in a shared memory location. These are just some ideas to prove what they can be used for. Isolating the nodes like this to a single &lt;em&gt;control block&lt;/em&gt; makes it likely the whole region fits to a cpu cache register. This again makes it possible to iterate the list in blazing fast speed! Note that the payload still need to be fetched from another region is not affected by cache hits.&lt;/p&gt;&lt;p&gt;Now if we think back didn&amp;rsquo;t we just state a while back that linked lists are good for the fact that they can be resized? Doesn&amp;rsquo;t the control block approach eliminate this feature? In one way yes.&lt;/p&gt;&lt;h2 id=&#34;optimization-and-linked-lists&#34;&gt;Optimization and linked lists&lt;/h2&gt;&lt;p&gt;To give a common example we can &lt;code&gt;malloc&lt;/code&gt; memory regions from the heap but the system memory is still finite. A proper approach to control block logic would be to initiate a bigger memory region chunk on the stack and divide the region to node sized slots using a &lt;em&gt;free list&lt;/em&gt;. This is a list of nodes that can be used to store a single node. When a new node is created it is removed from the free list and placed to an &lt;em&gt;occupied list&lt;/em&gt;. Adding and removing elements updates the free and occupied lists accordingly. Since the linked list is allocated in the stack no syscalls are made which improves performance.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;in-practise-a-high-troughput-data-system&#34;&gt;In practise: A high-troughput data system&lt;/h2&gt;&lt;p&gt;I want to discuss the design I settled with in the high throughput data system I talked about at the start of this post. Since we don&amp;rsquo;t want to do any unnecessary copies of the data with &lt;code&gt;memcpy&lt;/code&gt; a bigger ring buffer seemed like the best choise. Note that a ring buffer can be implemented with either an array or a linked list. An array was chosen because of the uniformly sampled data and ease of access for all indices (ref. base pointer).&lt;/p&gt;&lt;p&gt;A 10GB buffer was chosen. This ensures the buffer holds data history of 62.5 seconds which is plenty of time for readers to operate on the data. The buffer wraps around and overwrites the old data thus old data does not need to be manually erased. In the buffer we have a &lt;code&gt;_write_index&lt;/code&gt; and a &lt;code&gt;_read_index&lt;/code&gt; that point indices in the buffer where &lt;code&gt;_write_index&lt;/code&gt; is the next writable address and &lt;code&gt;_read_index&lt;/code&gt; points to the last unread address.&lt;/p&gt;&lt;p&gt;Data is accessed using pointers. This means we have a &lt;code&gt;get_next_write_buffer()&lt;/code&gt; and &lt;code&gt;get_next_read_buffer()&lt;/code&gt; that return a pointer to the indices. By assumption only one writer operates on the buffer. By design the buffer data should no be modified and is thus read only. Since I know that not all of the data is of interest as it might be just noise a seekable reader was chosen. Seekable means that the reader can traverse the ring buffer. Only every nth entry is read for noise check. If there happens to be something else besides noise in the data then the reader looks in the adjecent buffers as well.&lt;/p&gt;&lt;p&gt;You might have noticed, yes, the reader can be configured to skip buffers. After all processing the whole data stream is very expensive.&lt;/p&gt;&lt;p&gt;This results in sliced time series data where noise regions are discarded. Given a timestamp for recording start time and the data as payload the data is pushed to a fifo (first-in-first-out) queue linked list for later processing.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;final-notes&#34;&gt;Final notes&lt;/h2&gt;&lt;p&gt;In the before mentioned data system throughput is the most important requirement. Only a single linked list was used for the later fifo queue. For the first implementation of the system I did use a linked list that used a heap allocated memory region but it resulted in a lot of data overflows in the recording device. Operating solely on pointers makes the data writing and reading very fast thus nearly zero overflows. The system can be scaled vertically by using several reader/worker threads that copy data from the raw data buffer.&lt;/p&gt;&lt;p&gt;In the end hard real-time performance is not that critical in this system. This solution is itself pleasingly fast and provides flexibility to the upper stacks but it not as fast as possible. A yet more real-time system can be thought of as an exercise.&lt;/p&gt;&lt;p&gt;System design like this encourages to think deeply about data structures and see the whole system as a collection of different components. I also concidered double or n-buffering. Having a single writer and reader had the possibility to set a roughly constant time delay between the reader and writer.&lt;/p&gt;&lt;p&gt;Hope you learned something new here. Have a happy day!&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>On filters and AHRS systems</title>
       <link>https://etinkerer.net/posts/0009-ahrs-maths/</link>
       <pubDate>Sun, 04 May 2025 09:00:52 +1000</pubDate>
       
       <guid>https://etinkerer.net/posts/0009-ahrs-maths/</guid>
       <description>&lt;p&gt;In this post we go thorough basics of attitude and heading reference systems (AHRS) and dicuss in detail the Madgwick&amp;rsquo;s algorithm (MA) which is an optimized solution for pose estimation. Note that the use of terms &lt;em&gt;filter&lt;/em&gt; and &lt;em&gt;algorithm&lt;/em&gt; become intertwined and their differences are left for the reader to decipher.&lt;/p&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;&lt;p&gt;In a recent project I was introduced to inertial measurement units (IMU) that eventually led to inspect AHRS in greater detail. An IMU integrated circuit often cosist of &lt;em&gt;accelerometer&lt;/em&gt; and a &lt;em&gt;gyroscope&lt;/em&gt;. Some ICs include a &lt;em&gt;magnetometer&lt;/em&gt;. I will not go into the details of these sensor types here. For the curios I recommend a fantastic python library&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; that describes the bigger picture very well.&lt;/p&gt;&lt;p&gt;If you are like me you might like to try to see common factors accross seemingly different topics. I think it adds a layer of challenge and makes learning new areas both fun and rewarding. Hence I like the reader to think of traditional digital filters in mind when getting to know AHRS. In AHRS there are several ways to accomplish pose estimation with different sensor types. For quick reference: a rigid body&amp;rsquo;s pose can be estimated in a local or in global frame. Earth and compass points are one example of a global frame. We can denote earth frame as $g_e$. For a newcomer a suitable first pose estimation filter (PEF) to inspect is the &lt;em&gt;complementary filter&lt;/em&gt;. In a sense it can be thought of as a special digital filter.&lt;/p&gt;&lt;p&gt;The name for complementary filter (CF) is assumed to come from the fact that the CF uses two or three different sensor to complement one another for better overall pose estimation performance. The equation for CF is expressed as follows:&lt;/p&gt;&lt;p&gt;$$\theta_c = \alpha \cdot \theta_{\omega} + (1-\alpha) \cdot \theta_a$$&lt;/p&gt;&lt;p&gt;Often times for clarity we define the true pose as $\theta$ and the estimated pose as $\hat{\theta}$. Here we assume all values to be estimations of the true values.&lt;/p&gt;&lt;p&gt;In the equation $\theta_c$ is the filter output, $\alpha$ is the filter weighting coefficient, $\theta_a$ accelerometer pose estimation and $\theta_{\omega}$ gyroscope pose estimation. If $\alpha = \frac{1}{2}$ then the filter takes the average of the respected sensor values. Note that a filter with a magnetometer the $\theta_a$ is replaced by $\theta_{am}$.&lt;/p&gt;&lt;p&gt;$$\begin{split}{\theta}_{a} =\begin{bmatrix}\theta_x \ \theta_y \ \theta_z\end{bmatrix} =\begin{bmatrix}\mathrm{arctan2}(a_y, a_z), \\mathrm{arctan2}\big(-a_x, \sqrt{a_y^2+a_z^2}\big), \ 0\end{bmatrix}\end{split}$$&lt;/p&gt;&lt;p&gt;Both accelerometers and gyroscopes suffer from their own &amp;ldquo;flaws&amp;rdquo; and when combined with the equation before we have a more accurate and reliable estimation of pose. The alpha term should look familiar to those familiar with digital filters.&lt;/p&gt;&lt;p&gt;$$y_n = \alpha \cdot x_n + (1-\alpha) \cdot y_{n-1}$$&lt;/p&gt;&lt;p&gt;I want to point out that looking at the equations they don&amp;rsquo;t seem that different from one another. In the low-pass filter equation above $x_n$ is the most recent sensor value, $y_n$ is the updated filter value and $y_{n-1}$ is the previous filter value. Adjusting the $\alpha$ value makes the system favor either the long-term filter value of the recent sensor values. One way to think about this is that the filter has a longer history.&lt;/p&gt;&lt;p&gt;In the complementary filter there is no implication of system memory. We can think that the complementary filter favors either one pose estimation source over another based on the alpha term. Now just as an exercise think of the benefits of using a CF with a memory of past states. What could be gained from it?&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;more-sophisticated-filters&#34;&gt;More sophisticated filters&lt;/h2&gt;&lt;p&gt;It may come as a no surprise but Kalman filters are also used for PEF. Since it combines three different sensors (&lt;em&gt;magnetometer&lt;/em&gt; as a new one) the equations get quickly menacing &lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. The ability to add model noise and process noise to the filter has it&amp;rsquo;s benefits when using different grade sensors. As respectable as the solution is instead let&amp;rsquo;s focus on a different approach made by Sebastian O.H. Madgwick.&lt;/p&gt;&lt;p&gt;In his thesis Madgwick uses gradient descent as a PEF. To put briefly the pose estimation is constructed as a loss function optimization problem that uses complex values called quaternions. A quaternion is defined:&lt;/p&gt;&lt;p&gt;$$ q = a + b\hat{i} + c\hat{j} + d\hat{k} $$&lt;/p&gt;&lt;p&gt;and&lt;/p&gt;&lt;p&gt;$$ i^2 = j^2 = k^2 = ijk = -1 $$&lt;/p&gt;&lt;p&gt;A three-dimentional rotation is described as a quaternion multiplication $f(p) = q \cdot p \cdot q^{-1}$. In MA by loss function definition we try to find a quaternion that rotates the pose estimation based on sensor values to point to the direction of earth frame $g_e$. The pose estimation is described by function $f(q, g_s, g_e)$. Since hugo sites have a problem with rendering matrices and equations spanning mutile lines I won&amp;rsquo;t include all equations here. For full equations see &lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;TBC&lt;/p&gt;&lt;hr&gt;&lt;p&gt;A concept might have come to your mind when reading this post: &lt;em&gt;sensor fusion&lt;/em&gt;. In essence this is what AHRS systems are about.&lt;/p&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&lt;hr&gt;&lt;ol&gt;&lt;li id=&#34;fn:1&#34;&gt;&lt;p&gt;&lt;a href=&#34;https://ahrs.readthedocs.io/en/latest/&#34;&gt;https://ahrs.readthedocs.io/en/latest/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:2&#34;&gt;&lt;p&gt;&lt;a href=&#34;https://ahrs.readthedocs.io/en/latest/filters/ekf.html&#34;&gt;https://ahrs.readthedocs.io/en/latest/filters/ekf.html&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&#34;fn:3&#34;&gt;&lt;p&gt;&lt;a href=&#34;https://ahrs.readthedocs.io/en/latest/filters/madgwick.html&#34;&gt;https://ahrs.readthedocs.io/en/latest/filters/madgwick.html&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>A perspective on test-driven development</title>
       <link>https://etinkerer.net/posts/0007-test-driven-development/</link>
       <pubDate>Wed, 22 Jan 2025 15:00:00 +0200</pubDate>
       
       <guid>https://etinkerer.net/posts/0007-test-driven-development/</guid>
       <description>&lt;p&gt;In this post we&amp;rsquo;ll take a look at a development guideline called test-driven development. To give the discussion a pratical perspective we&amp;rsquo;ll look at an example in embedded C. Embedded applications are notoriosly challening to test and debug. Here we use TDD framework for a implementing a simple data buffer structure.&lt;/p&gt;&lt;hr&gt;&lt;h1 id=&#34;in-theory&#34;&gt;In Theory&lt;/h1&gt;&lt;h2 id=&#34;the-paradigm&#34;&gt;The paradigm&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Add a test&lt;/li&gt;&lt;li&gt;Run all the tests&lt;/li&gt;&lt;li&gt;Write some code&lt;/li&gt;&lt;li&gt;Run tests&lt;/li&gt;&lt;li&gt;Refactor code&lt;/li&gt;&lt;li&gt;Repeat&lt;/li&gt;&lt;/ol&gt;&lt;hr&gt;&lt;h3 id=&#34;motivation&#34;&gt;Motivation&lt;/h3&gt;&lt;p&gt;At first impression test-driven development (TDD) seemed like an excessive display of discipline in programming. Following the &amp;ldquo;the paradigm&amp;rdquo; would only to slow down the development process with a promise for &amp;ldquo;code that behaves the way it was designed to behave&amp;rdquo;. Every developer is optimistic about their own work so creating &amp;ldquo;code that works&amp;rdquo; seems like a silly motivation. Of course our code works!&lt;/p&gt;&lt;p&gt;Having debugged an embedded system with application logic and web API, and where no tests were defined the situation became a nightmare when strange bugs appeared. Our team of 3-4 specialists ended up spending two weeks on a bug that had to do with microSD card I/O operations. In the end the reason was a use of outdated software drivers. Using a new type of sd card caused compatibility issues because of the cards larger memory capasity. Now that some time has passed since then I realize this was a big rookie mistake. The whole situtation could have been avoided if there were a tailored test suite for the sd card I/O operations&amp;hellip;&lt;/p&gt;&lt;h3 id=&#34;tdd-in-context&#34;&gt;TDD in context&lt;/h3&gt;&lt;p&gt;Software testing can strike a beginning developer as tedious, confusing, even redundant process. Using test suites with unit and integration testing for a smaller codebase is often times an oversized decision. But as codebase grows in size a systematic approach to testing becomes relevant. Test suites are invaluable when a big codebase is under stress test to avoid the nightmare of bug tracking with zero context of the systems state.&lt;/p&gt;&lt;p&gt;Software testing is quite different in practise dependeing on the used programming language and testing framework. With a interpreted language and a framework (take Python and pytest for example) situations that require use of debugger are mainly unhandled exceptions and contradictions in design by contract. In a bare-metal environment with no OS the possible issues become more convoluted. A single failed pointer operation may lead to undefined system behaviour and following debug process time can be hard to predict. This doesn&amp;rsquo;t mean that we should only use TDD in a single context an nowhere else (there is a book on Test-driven Development &lt;a href=&#34;https://www.obeythetestinggoat.com/pages/book.html&#34;&gt;in Python&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;When a developer first enters their first career there are deadlines and waiting customers who want to see everything working as they want. There is a feeling of rush and getting things done on time. The rush rarely contributes to software quality neither in design or implementation. In worst case taking shortcuts increments technical debt and makes codebase maintanence challenging. Well defined software development process, including testing routines, code acceptance, possible CI/CD pipeline and a good team culture a codebase remaings under control.&lt;/p&gt;&lt;p&gt;TDD introduces realiability in existing codebase. When I saw the paradigm of TDD, I pictured a Venn diagram - of course. Put in one way the test suites set an outer bound for the codebase. Put yet another way the codebase has no more functionalities than what is determined by the test suites. Picture what this implicates. All of the codebase is tied to a set of test suites that &lt;strong&gt;scream&lt;/strong&gt; if something breaks down (yes, tests are said to scream when they break). Now if one team member would be eager to write production code before creating tests the codebase would have more fuctionality than what the tests cases determine. If, and when, things go south the tests suites are there to point where the possible bugs originate from.&lt;/p&gt;&lt;hr&gt;&lt;h1 id=&#34;in-practise&#34;&gt;In practise&lt;/h1&gt;&lt;h2 id=&#34;tdd-framework-in-embedded-c-ceedling&#34;&gt;TDD framework in embedded C: ceedling&lt;/h2&gt;&lt;p&gt;In &lt;em&gt;ceedling&lt;/em&gt; we create test suites that confirm the behaviour of our production code. The framework is specially meant to test application level code. The tests are compiled and run on the host, not on the target device. For this reason peripherals should be mocked so that they can be integrated to tests. Ceedling has support for &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=numaru.vscode-ceedling-test-adapter&#34;&gt;VSCode environment&lt;/a&gt;. If we were not thinking in terms of TDD we would start by defining a data structure like such:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;enum&lt;/span&gt; buffer_operations {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BUFFER_OK,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BUFFER_ERR&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; my_buffer {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;head;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; buffer_array[BUFFER_LENGTH];&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; BUFFER_LENGTH;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; my_buffer buff;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then proceed to create the function API&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;buff &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_create_buffer&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; buffer_lenth);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;buff &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_init_buffer&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; buffer_lenth);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int32_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_push&lt;/span&gt;(buff&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; item);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int32_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_pop&lt;/span&gt;(buff&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;dest);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_is_full&lt;/span&gt;(buff&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;buffer_is_empty&lt;/span&gt;(buff&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the sake of readability the &lt;code&gt;buffer_&lt;/code&gt; prefix is added to all functions to tell what source file the function call is defined in. This looks good yes. We then construct the implementations and create a test to vefiry our data structure works.&lt;/p&gt;&lt;p&gt;What we have done is actually part of step 3 in TDD: &lt;em&gt;&amp;ldquo;Write some code&amp;rdquo;&lt;/em&gt; and we have completely skipped the steps 1 and 2.&lt;/p&gt;&lt;h3 id=&#34;step-1-write-a-test&#34;&gt;Step 1: &amp;ldquo;Write a test&amp;rdquo;&lt;/h3&gt;&lt;p&gt;Instead we should think of the API design before writing any code. The API design can be expressed with a collection of tests. For out buffer example it would look something like this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;testIsBufferCreated()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testIsBufferEmpty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testIsBufferFull()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testWasHeadIncremented()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testWasHeadDecremented()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testAddingToUnitializedBufferFails()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testAddingToFullBufferFails()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I want to pause to think what we did. At this moment if there were any missing features from the data structure definition it would be very easy to add them in. Our buffer here is quite simple. Now what if we wanted to have a buffer that could take in multiple items at once? What if we try to add \(n\) items when there is not enough space in the buffer. Do we want to keep the first \(m\) items, when \(m\) is the remaining space and \(n&amp;gt;m\).&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;testAddMultipleWasHeadIncremented()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;testAddMultipleBufferOverflowFails()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Again we might want to be able to pop multiple items at once. Then we follow te same steps as before: &lt;em&gt;&amp;ldquo;Write a test&amp;rdquo;&lt;/em&gt;. You get the idea. It is preferable to write down all the tests you can come up with.&lt;/p&gt;&lt;p&gt;We start to see the API taking shape. At this point we can start to think how other system components interact with this data structure. We could also delegate this list of tests to a team of coders who can give a full focus on the implementation.&lt;/p&gt;&lt;h3 id=&#34;step-2-run-all-the-tests&#34;&gt;Step 2: &lt;em&gt;&amp;ldquo;Run all the tests&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;&lt;p&gt;You should first make all test return an error. I know this may sound confusing but this way we see that all our tests fail and we focus on making them pass one by one. In VSCode the test have a indicator in the menubar that show passing and failing tests.&lt;/p&gt;&lt;p&gt;Our buffer can be implemented in less than an hour so doing it this way is a clear overkill. Now imagine a larger database with tens of components. Try not to think of tests as a sign that something is not working but instead as a sign of progress. Once a given test suite passes the component is done! When a superior asks on the state of some project you can now say &amp;ldquo;67% of all tests pass&amp;rdquo; instead of &amp;ldquo;we&amp;rsquo;re getting there&amp;rdquo;.&lt;/p&gt;&lt;h3 id=&#34;step-3-write-some-code&#34;&gt;Step 3: &lt;em&gt;&amp;ldquo;Write some code&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;&lt;p&gt;We would then proceed to write production code until&amp;hellip;&lt;/p&gt;&lt;h3 id=&#34;step-4-run-test&#34;&gt;Step 4: &lt;em&gt;&amp;ldquo;Run test&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;&lt;p&gt;our tests pass. After this some might say that we have finished and should move on to something else.&lt;/p&gt;&lt;h3 id=&#34;step-5-refactor-code&#34;&gt;Step 5: &lt;em&gt;&amp;ldquo;Refactor code&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;&lt;p&gt;At this step our code should run and other people should be able to use it. In this step we focus on refactoring the code with a specific focus in mind: readability, performance, maintainability. We basically rewrite existing code to make it better. Once this is done we rerun our tests and if all tests pass our code still works as expected.&lt;/p&gt;&lt;h3 id=&#34;step-6-repeat&#34;&gt;Step 6: &lt;em&gt;&amp;ldquo;Repeat&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;&lt;p&gt;We follow this pattern for all of our codebase.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;The implemented tests become a rudimentary process in software development. Since nothing should be taken for granted in embedded design all hardware should go through test suites.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Cases of feature selection</title>
       <link>https://etinkerer.net/posts/0001_feature_selection/</link>
       <pubDate>Wed, 10 Jul 2024 09:00:52 +1000</pubDate>
       
       <guid>https://etinkerer.net/posts/0001_feature_selection/</guid>
       <description>&lt;p&gt;In this post I share a solution to one school submission on feature selection. Have a good day!&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&#34;submission&#34;&gt;Submission&lt;/h2&gt;&lt;p&gt;When we train our models with a determined number of features we increase the required computational resources needed to make a prediction. In some cases the features do not provide any meaningful data or the information is already given by another feature. We should discard these features whenever possible. There are multiple methods with which this can be done.&lt;/p&gt;&lt;p&gt;There are three categories of feature selection: 1) filter methods 2) wrapper methods 3) embedded methods. In this example I will go through some examples of each category.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Filter methods use statisical analysis to evaluate features. These methods are computationally less demanding than cross-validation based methods.&lt;/p&gt;&lt;h3 id=&#34;information-gain-sourcehttpsmachinelearningmasterycominformation-gain-and-mutual-information&#34;&gt;Information gain &lt;a href=&#34;https://machinelearningmastery.com/information-gain-and-mutual-information/&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Decision trees use this method to find suitable features for the hypothesis space. Features are ranked by the amount of entropy loss when a feature is split two to groups according to a given label. This can be used to find correlation between input data and the labels. A split resulting in a little entropy loss in ranked low and vice versa.&lt;/p&gt;&lt;h3 id=&#34;correlation-coefficient-sourcehttpsenwikipediaorgwikipearson_correlation_coefficient&#34;&gt;Correlation Coefficient &lt;a href=&#34;https://en.wikipedia.org/wiki/Pearson_correlation_coefficient&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;We can use Pearson&amp;rsquo;s correlation to determine whether features are linearly correlated with one another. Together linearly correlated features do not provide any additional information for classifying. Calculating these coefficients as a correlation matrix is useful information to discover if some of features are codependent. Selected features should still be correlated with the label classes.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Wrapper methods use a classifier and some metric to determine the best features to use. These often yield better results than filter methods but are computationally more demaning.&lt;/p&gt;&lt;h3 id=&#34;leave-out-one-feature-lofo-sourcehttpsmediumcommlearning-aileave-one-feature-out-lofo-for-feature-importance-3ed04a60ae40&#34;&gt;Leave Out One Feature (LOFO) &lt;a href=&#34;https://medium.com/mlearning-ai/leave-one-feature-out-lofo-for-feature-importance-3ed04a60ae40&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;LOFO tests the model accuracy loss by leaving out one feature at a time in every training iteration. The features causing the most accuracy lost are ranked as the most important features.&lt;/p&gt;&lt;h3 id=&#34;forward-feature-selection-sourcehttpswwwanalyticsvidhyacomblog202104forward-feature-selection-and-its-implementation&#34;&gt;Forward Feature Selection &lt;a href=&#34;https://www.analyticsvidhya.com/blog/2021/04/forward-feature-selection-and-its-implementation/&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In forward feature selection we go trough all possible features to predict a given label. The one that provides best accuracy is selected. We then continue to use combinations of the selected features together with the remaining ones ans see which improves the model accuracy best. We keep doing this until a sufficient accuray or a limit of features is reached.&lt;/p&gt;&lt;h3 id=&#34;exhaustive-feature-selection-sourcehttpsrasbtgithubiomlxtenduser_guidefeature_selectionexhaustivefeatureselector&#34;&gt;Exhaustive Feature Selection &lt;a href=&#34;https://rasbt.github.io/mlxtend/user_guide/feature_selection/ExhaustiveFeatureSelector/&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Exhaustive feature selection is a brute-force method to select a group of features based on a scoring method for example AUC of ROC. It takes the minimum and maximum number of features as parameters and goes through all possible combinations and returns the group of features with the best score.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Embedded methods combine both filter methods and wrapper methods to find the best feature combinations while having reasonable computationla cost.&lt;/p&gt;&lt;h3 id=&#34;random-forest-importance-sourcehttpsmljarcomblogfeature-importance-in-random-forest&#34;&gt;Random Forest Importance &lt;a href=&#34;https://mljar.com/blog/feature-importance-in-random-forest/&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Random Forest Classifier chooses features based on Gini impurity. With a large number of decision trees we can examine all the trees and their nodes to find which features have ended up in the nodes near decision tree roots. The closer the features are to the root the more important they are.&lt;/p&gt;&lt;h3 id=&#34;lasso-regression-sourcehttpswwwmygreatlearningcomblogunderstanding-of-lasso-regression&#34;&gt;Lasso Regression &lt;a href=&#34;https://www.mygreatlearning.com/blog/understanding-of-lasso-regression/&#34;&gt;source&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Lasso uses L1 regularization to determine which features to use for prediction and which to discard.&lt;/p&gt;&lt;p&gt;&amp;ldquo;L1 regularization adds a penalty that is equal to the absolute value of the magnitude of the coefficient. This regularization type can result in sparse models with few coefficients. Some coefficients might become zero and get eliminated from the model. Larger penalties result in coefficient values that are closer to zero (ideal for producing simpler models).&amp;rdquo;&lt;/p&gt;&lt;hr&gt;&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;&lt;p&gt;There are a few of possible feature selection methods to use. The coice of method depends on the amount of data dimensions and computational recources.&lt;/p&gt;&lt;h4 id=&#34;more-sources&#34;&gt;More sources&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Feature_learning&#34;&gt;Wikipedia: Feature Learning&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.neuraldesigner.com/blog/genetic_algorithms_for_feature_selection&#34;&gt;Genetic algorithms for feature selection&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Genetic_algorithm&#34;&gt;Wikipedia: Genetic algoritms&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
 </channel>
</rss>
