<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
>
	<channel>
		<title>Tom Hodson&apos;s Blog</title>
		<description>Physics, Programming and Baking.</description>		
		<sy:updatePeriod>monthly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
		<link>https://thod.dev</link>
		<atom:link href="https://thod.dev/feed.xml" rel="self" type="application/rss+xml" />
		<lastBuildDate>Mon, 06 Jul 2026 12:41:15 +0200</lastBuildDate>
        <language>en-gb</language>
        
        
        
        
			<item>
				<title>Quick and dirty Linescan photography</title>
				<dc:creator>Tom Hodson</dc:creator>
				
					<description>
                    
				    <![CDATA[<img src="https://thod.dev/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/thumbnail.jpeg" alt=""/><br><br>]]>
                    
                    &lt;style&gt;
img.square {
    aspect-ratio: 1 / 1;
}

img.wide {
    width: unset;
    height: 400px;
}

figure {
    overflow: scroll;
}
&lt;/style&gt;

&lt;p&gt;&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_6.jpeg&quot; class=&quot;square&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When you look out of a train window, it just scrolls past right? When you think about it a bit more, no, because of parallax… but what if we just clamped our hands over our ears and pretended parallax didn’t exist?&lt;/p&gt;

&lt;p&gt;This script extracts vertical slices from the middle of each frame of a video and stitches them into one long image.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;skimage&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tqdm&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tqdm&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PIL&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# I used to work at a place where all the usernames 
# were of the form ma[your initials]
# so I got &quot;math&quot; and have stuck with it even after leaving.
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Users/math/Downloads/IMG_3612.MOV&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;VideoCapture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;samples&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findFileOrKeep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAP_PROP_FRAME_COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# frames = 100
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# channel_swizzle = (2,1,0) # Use this to &quot;fix&quot; the channels being wrong
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel_swizzle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vertical_slice&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Be a bit lazy and just grab a test frame to 
# compute the height after vertical_slice is applied
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n_channels&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertical_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Reopen the file so we don&apos;t lose that frame we just grabbed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;VideoCapture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;samples&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findFileOrKeep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n_channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tqdm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Got to end of video on frame &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# warped = transform.warp(frame, tform3, preserve_range = True).astype(np.uint8)
&lt;/span&gt;        
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice_width&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice_width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertical_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice_width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel_swizzle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PIL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fromarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;linescan.jpeg&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This scripts doesn’t account for a bunch of things, it reads the image data as bgr instead of rgb for whatever reason, it ignores perspective, the vertical bouncing of the train and the camera, the variation in speed of the train, the variation in speed of each object due to its varying distance from the camera…&lt;/p&gt;

&lt;p&gt;and yet, the images look really cool.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_4.jpeg&quot; class=&quot;square&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Depending on how you tune the parameters (really just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slice_width&lt;/code&gt;), some parts of the image will move at the right speed to appear normal, while anything closer will become squeezed horizontally and anything further away will be stretched out.&lt;/p&gt;

&lt;p&gt;This is why the clouds appear as long streaks in the sky, because they’re so far away we’re essentially seeing just single vertical slice of them smeared across the image.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_5.jpeg&quot; class=&quot;square&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In contrast, some the videos contain electricity poles that are so close that they flash past, only appearing in a single frame sometimes, if they happen to appear with the narrow band that I’m extracting then they will appear, otherwise you get electrical cables that appear to floating, supported by nothing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_7.jpeg&quot; class=&quot;square&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also, shock horror, move the camera around!&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_9.jpeg&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Width is meaningless here so I’ve constrained most of the images to be square but for longer videos you need a bit more room. These ones below should scroll left and right if I haven’t bungled the css on your device.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_8.jpeg&quot; class=&quot;wide&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;This one I scaled down a bit less than the others so it’s 5Mb.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_10.jpeg&quot; class=&quot;wide&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;All the previous ones have been done with an iPhone 13 mini set to 60fps / 4k using the normal camera. This next one is at 30fps / 1080p using the wide angle lense. Yes I am changing all the variables at once, you can’t stop me.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_11.jpeg&quot; class=&quot;wide&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Back to 4K / 60fps source, scroll to the end for some trucks nestled under a bridge.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/linescan_12.jpeg&quot; class=&quot;wide&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;extra-notes&quot;&gt;Extra Notes&lt;/h2&gt;

&lt;p&gt;I also tried doing a perspective correction on the image prior to extracting the slice but because the slices are so narrow this doesn’t really make much of a difference.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;skimage&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;VideoCapture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;samples&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;findFileOrKeep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Users/math/Downloads/IMG_3609.MOV&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;750&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;750&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;750&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;tform3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProjectiveTransform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_estimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tform3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preserve_range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subplots&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ncols&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.r&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/perspective_correction.png&quot; /&gt;
&lt;/figure&gt;

                    </description>
                
				<pubDate>Mon, 06 Jul 2026 00:00:00 +0200</pubDate>
				<link>https://thod.dev/2026/07/06/quick-and-dirty-linescan-photography</link>
				<guid isPermaLink="true">https://thod.dev/2026/07/06/quick-and-dirty-linescan-photography</guid>
            <!-- 
                <media:thumbnail width="250" height="250" url="https://thod.dev/assets/blog/2026-07-06-quick-and-dirty-linescan-photography/thumbnail.jpeg"/>
                 -->
            </item>
        
        
        
        
        
			<item>
				<title>Correlation does not imply Causation</title>
				<dc:creator>Tom Hodson</dc:creator>
				
					<description>
                    
				    <![CDATA[<img src="https://thod.dev/assets/blog/2026-06-26-correlation-does-not-imply-causation/thumbnail.png" alt=""/><br><br>]]>
                    
                    &lt;p&gt;The first time I heard the phrase “correlation does not imply causation”, I thought, well ok, but what’s a good real world example of this?&lt;/p&gt;

&lt;p&gt;I think the example given in the textbook I was reading was something to do with ice cream consumption and swimming pool use… fine but contrived.&lt;/p&gt;

&lt;p&gt;Here’s are more eye catching one that I saw in a post over at &lt;a href=&quot;https://dynomight.net/vitamin-d/&quot;&gt;dynomight&lt;/a&gt;. The following two statements are true:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;If you measure a bunch of people’s blood levels of Vitamin D and also check to see how many of them die in a multiyear period you find that low vitamin D levels are highly correlated with higher mortality. Specifically, people in the 25th percentile are 30% more likely to die than those in the 75% percentile.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Giving people vitamin D supplements has little to no effect on their health.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The 25th and 75th percentiles are not that far from the mean! As a point of comparison, those percentiles for UK men’s height are about 174cm and 181cm&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Not particularly high or low values!&lt;/p&gt;

&lt;p&gt;How can this be? As far as I can tell we don’t really know. My guess would be that there are one or more health conditions that cause both low vitamin D levels and elevated risk of death. Given how complicated the signaling systems in the human body are, this doesn’t seem all that far fetched.&lt;/p&gt;

&lt;p&gt;At the same time I find this fact quite upsetting. Why couldn’t we live in the universe where vitamin D was a magical health bullet? Actually we do live in a world replete with magical health bullets already: antibiotics, vaccines, basic nutrition, exercise, statins, maybe soon GLP-1 inhibitors. It’s just that vitamin D supplements aren’t one of them.&lt;/p&gt;

&lt;p&gt;Of course, there are also a lot of footnotes to the above claims. For example, the RCT studies, the ones where you actually give half of your participants the supplements, all seem to be done on people with a wide range of starting levels of vitamin D, they also don’t really measure the starting or new levels very often. I suspect this is because doing 30,000 blood tests is much more expensive than asking 30,000 people to take some cheap as chips supplements. As a result you could wonder if perhaps vitamin D supplements do help people with lower starting levels of vitamin D and that effect just gets washed out by the slightly higher chance of bladder stones everyone else gets.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;See adult height predictor in the bottom right of &lt;a href=&quot;https://www.rcpch.ac.uk/sites/default/files/Boys_2-18_years_growth_chart.pdf&quot;&gt;this&lt;/a&gt;. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

                    </description>
                
				<pubDate>Fri, 26 Jun 2026 00:00:00 +0200</pubDate>
				<link>https://thod.dev/2026/06/26/correlation-does-not-imply-causation</link>
				<guid isPermaLink="true">https://thod.dev/2026/06/26/correlation-does-not-imply-causation</guid>
            <!-- 
                <media:thumbnail width="250" height="250" url="https://thod.dev/assets/blog/2026-06-26-correlation-does-not-imply-causation/thumbnail.png"/>
                 -->
            </item>
        
        
        
        
        
			<item>
				<title>Buying my Mum an Electric Car</title>
				<dc:creator>Tom Hodson</dc:creator>
				
					<description>
                    
				    <![CDATA[<img src="https://thod.dev/assets/blog/2026-06-11-buying-my-mum-an-electric-car/thumbnail.jpeg" alt=""/><br><br>]]>
                    
                    &lt;p&gt;My mum needed a new car. He current one is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Daihatsu_Mira&quot;&gt;Daihatsu Cuore&lt;/a&gt; from about 1998, it’s a sweet little japanese car that looks bizarrely small and simple on UK roads. It served her well but it’s really starting to show its age. That coupled with increasingly large vehicles on the road and the arrival of her grandson she doesn’t feel safe enough in it anymore. Her one looks like &lt;a href=&quot;https://en.wikipedia.org/wiki/File:1999_Daihatsu_Mira_LS_(New_Zealand).jpg&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the plan was to get something slightly more modern, cheap and hopefully electric.&lt;/p&gt;

&lt;h2 id=&quot;which-car-to-get&quot;&gt;Which car to get?&lt;/h2&gt;

&lt;p&gt;Scrolling on Autotrader for a couple weeks we quickly realised that there are really only two models of second hand electric car that were going to make sense: either a &lt;a href=&quot;https://en.wikipedia.org/wiki/Nissan_Leaf&quot;&gt;Nissan Leaf&lt;/a&gt; or a &lt;a href=&quot;https://en.wikipedia.org/wiki/Renault_Zoe&quot;&gt;Renault Zoe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Nissan Leaf was the first commercially produced electric car, the early ones don’t use the standard CCS connector and we were seeing comments online about them having issues with battery thermal management of the battery that lead to faster than expected degradation.&lt;/p&gt;

&lt;p&gt;On the other hand, we were seeing a lot of 2014 Zoes going for cheap and reading reddits comments and reviews it seems that lots of people still own and love their 2014 Zoes.&lt;/p&gt;

&lt;p&gt;I’m not a car person but people were saying they found them very reliable, that they rarely had to have them repaired and that the battery degradation really wasn’t a big problem. So to simplify our lives we focussed on the Zoes in our search.&lt;/p&gt;

&lt;h2 id=&quot;battery-degradation&quot;&gt;Battery Degradation&lt;/h2&gt;

&lt;p&gt;On the subject of battery degradation, firstly, it doesn’t matter hugely to my mum because her range needs are incredibly modest. Secondly this &lt;a href=&quot;https://www.geotab.com/uk/blog/ev-battery-health/&quot;&gt;review of EV battery degradation&lt;/a&gt; over five years really makes it sound like a non-issue. Yes the batteries degrade but it seems to be about commensurate with the rate at which any vehicle degrades. ICE vehicles also have a limited lifespan too and it just looks… fine as far as I can tell?&lt;/p&gt;

&lt;p&gt;For a 2014 zoe, if we take the headline figure of 2.3% battery degradation per year from the 2020 study (the newer study averages at 1.8 but this is an old car) we’d expect a 2014 zoe to be somewhere about 100 - (2026 - 2014) * 2.3 = 72% battery health&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. The Zoe has a 22kWh battery and an estimated range between 100 km (62 mi) in cold weather and 150 km (93 mi) in warmer weather&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. So at 70% battery you might expect 70 - 105 km range.&lt;/p&gt;

&lt;p&gt;And I haven’t done the lifecycle analysis here but I’m gonna go ahead and assume that in order to offset the carbon emitted by the manufacture of this 2014 Zoe as much as physically possible, it makes sense to keep driving the thing until it’s really, completely dead.&lt;/p&gt;

&lt;h2 id=&quot;costs&quot;&gt;Costs&lt;/h2&gt;

&lt;p&gt;I chose a random relatively efficient ICE car as a point of comparison. A Toyota Yaris apparently does about 18 km per litre of fuel and at time of writing petrol costs about £1.6 per litre which works out to about 9p per kilometer driven (This fits with the current UK &lt;a href=&quot;https://www.gov.uk/guidance/advisory-fuel-rates&quot;&gt;advisory fuel rates&lt;/a&gt;). In comparison a 22kWh Zoe in cold weather apparently does about 100km, assuming the charger is about 80% efficient that works out to 0.28kWh drawn from the grid for every kilometer driven. My mum’s current electricity tariff is 20p per kWh so she’d pay about 4-6p per kilometer depending on the weather.&lt;/p&gt;

&lt;p&gt;With EV electricity tariffs that can be even lower, Octopus and other providers can give you very cheap energy at night, currently about 9.5p per kWh which would equate to 1.7 - 2.6 pence per kilometer driven. If you have a compatible car or charger you can also give control of the charger to octopus and in exchange they’ll give you electricity for 8p per kWh!&lt;/p&gt;

&lt;h2 id=&quot;charging&quot;&gt;Charging&lt;/h2&gt;

&lt;p&gt;This brings us to the subject of charging, my mum owns a ground floor flat in a block of about 10 flats. It’s a leasehold so while she can modify things in the interior, I’m not particularly expecting the management agency to let us have a dedicated charger installed.&lt;/p&gt;

&lt;p&gt;But luckily for us, her flat has an exterior plug, a dedicated parking space and the run between is about 20m down a unused side of the building. So we bought her a “granny charger” and a heavy duty extension cable. A granny charger is just a cable with a standard UK plug on one end and a CCS EV charging plug on the other. A bit like a USB-C cable, the cable has some smarts in it that talks to the car and says “Hello, you’re allowed to draw 10A from me today”. The granny charger has a little selector to switch between 6/8/10/13A charging. Thankfully it remembers your choice through a power cycle so for now I will just set it to 10A and forget about it.&lt;/p&gt;

&lt;p&gt;Interestingly, I learned that while we often refer to the standard UK plug as a 13A plug, it’s actually only rated for 13A for intermittent loads. For constant loads like charging an EV you have to watch out for heat build-up so most plugs are only rated to 10A in that case. From what I’ve read, if you get an &lt;a href=&quot;https://toughleads.co.uk/collections/ev-electric-vehicle-extension-leads/products/ev-socket&quot;&gt;EV rated socket&lt;/a&gt; and check the wiring you can probably do 13A safely but I have yet to try it.&lt;/p&gt;

&lt;p&gt;Anyway, 10A at 240V is 2.4kW. In practice this seems to translate to about 12 hours for a full charge (the numbers don’t add up on that I know) which seems to be plenty fast enough for our purposes.&lt;/p&gt;

&lt;figure class=&quot;multiple&quot;&gt;
&lt;img src=&quot;/assets/blog/2026-06-11-buying-my-mum-an-electric-car/extension.webp&quot; /&gt;
&lt;img src=&quot;/assets/blog/2026-06-11-buying-my-mum-an-electric-car/granny_charger.webp&quot; /&gt;
&lt;figcaption&gt;A granny charger and a heavy duty extension cable with inline DIN rail. The DIN rail hosts a wifi breaker and an RCD.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To take advantage of cheaper charging at night we got the extension able with an wifi breaker box that we can set up to turn on at a specific time.&lt;/p&gt;

&lt;h2 id=&quot;the-actual-car&quot;&gt;The actual car&lt;/h2&gt;

&lt;p&gt;So we ended up buying a 2014 Renault Zoe Dynamique Intens (Q210 motor I think) for £2500!&lt;/p&gt;

&lt;p&gt;I’m going to publish this now and add to it later as we get more experience with the car but so far it’s lovely to drive and the range is perfectly acceptable for a 12 year old car!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I wondered if that should maybe be applied exponentially like a compound interest i.e $(1 - 2.3/100)^{(2026 - 2014)}$ but the graphs look pretty linear and since the rate is low you get similar numbers anyway because the rate is small and $(1 - x)^y \approx 1 - yx$ when x is small. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;See &lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S0196890425002298&quot;&gt;here&lt;/a&gt; for a bit of an intro to this but the reason EVs are less efficient in cold temperatures seems to be a mix of the need to warm the batteries to keep them safe and also the fact that heating the cabin for the human occupant just uses a lot of energy. While in an ICE vehicle you have a convenient source of waste heat that can be repurposed for cabin heating. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

                    </description>
                
				<pubDate>Thu, 11 Jun 2026 00:00:00 +0200</pubDate>
				<link>https://thod.dev/2026/06/11/buying-my-mum-an-electric-car</link>
				<guid isPermaLink="true">https://thod.dev/2026/06/11/buying-my-mum-an-electric-car</guid>
            <!-- 
                <media:thumbnail width="250" height="250" url="https://thod.dev/assets/blog/2026-06-11-buying-my-mum-an-electric-car/thumbnail.jpeg"/>
                 -->
            </item>
        
        
        
        
        
        
        
        
        
        
        
			<item>
				<title>Sums of the choose function and the Binomial Formula</title>
				<dc:creator>Tom Hodson</dc:creator>
				
					<description>
                    
				    <![CDATA[<img src="https://thod.dev/assets/blog/2026-02-11-sums-of-the-choose-function-and-the-binomial-formula/thumbnail.png" alt="The expression (p + q)^m or in words p + q all raised to the power m"/><br><br>]]>
                    
                    &lt;p&gt;Here’s a little maths thing. I was thinking about this game called Kniffel, which is a German game played with 5 dice. The internet says it has similar rules to Yahtzee though both seem to have many variants.&lt;/p&gt;

&lt;p&gt;The aspect that is important here is that you roll your 5 dice and then you may choose to re-roll any subset of them (up to twice). You choose which subset to re-roll in order to maximise your chances of achieving certain goals, like getting 5 of the same value (and shouting Kniffel!) or a consecutive run etc.&lt;/p&gt;

&lt;p&gt;I was thinking about how one might compute the optimum choice of subset to re-roll given a starting point. Let’s say you were going to just brute force it. How hard would that be?&lt;/p&gt;

&lt;p&gt;Well you have 5 dice and you want to choose a good subset to roll, there are $2^5 = 32$ possible subsets. How many of each subset are there? That’s actually easy, it’s the choose function:&lt;/p&gt;

\[{n \choose r} = \frac{n!}{r! \; (n - r)!}\]

&lt;p&gt;which tells you how many ways there are to choose a subset of size r from a set of size n if you don’t care how it’s ordered.&lt;/p&gt;

&lt;p&gt;Let’s quickly look at how many choices there are if we choose to re-roll $r$ dice:&lt;/p&gt;

&lt;div class=&quot;code-cell&quot;&gt;
  &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ncr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, ncr(5,&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;) = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ncr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;r = 0, ncr(5,0) = 1
r = 1, ncr(5,1) = 5
r = 2, ncr(5,2) = 10
r = 3, ncr(5,3) = 10
r = 4, ncr(5,4) = 5
r = 5, ncr(5,5) = 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That checks out, there’s only one way to choose all or none of the dice, are there are more ways when you choose about half of them.
This is actually the origin of that nice identity about rows in Pascals triangle adding to powers of 2:&lt;/p&gt;

\[2^5 = 32 = {5 \choose 0} + {5 \choose 1} + {5 \choose 2} + {5 \choose 3} + {5 \choose 4} + {5 \choose 5} =\]

&lt;p&gt;\(= 1 + 5 + 10 + 10 + 5 + 1\)
What I want to know though, is how many outcomes do we need to consider? Because for each of those ways of choosing $r$ dice, we will have to evaluate every possible outcome of rolling those $r$ dice. Since a die has 6 values there are $6^r$ possible outcomes.
If we loop over every subset of size $r$ and perform $6^r$ computations, how big is that number? It’s:&lt;/p&gt;

\[N = \sum_{r = 0}^5 {5 \choose r} 6^r\]

&lt;p&gt;This is the bit that surprised me, it turns out we can evaluate that sum in closed form! I’ve hidden the answer below just in case you want to try yourself.&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;Hint&lt;/summary&gt;
  &lt;p&gt;Recall or look up the &lt;a href=&quot;https://en.wikipedia.org/wiki/Binomial_theorem&quot;&gt;binomial formula&lt;/a&gt;.&lt;/p&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;Answer&lt;/summary&gt;
  &lt;p&gt;By noticing that it looks a bit like the binomial formula:&lt;/p&gt;

\[(p + q)^m = \sum_{r = 0}^m {m \choose r} p^r q^{m-r}\]

  &lt;p&gt;By setting $m=5, p=6, q=1$ we get:&lt;/p&gt;

\[\sum_{r = 0}^5 {5 \choose r} 6^r  = (6 + 1)^5 = 16807\]

  &lt;p&gt;We can check this number quickly with some python, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itertools&lt;/code&gt; has a handy function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it.product((0,1), repeat = 5)&lt;/code&gt; that will give us all 32 possible subsets in the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1,1,0,0,0)&lt;/code&gt; where a 1 means ‘roll that die’.&lt;/p&gt;

  &lt;p&gt;With that representation, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum(subset)&lt;/code&gt; is how many dice we have to roll and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6 ** sum(subset)&lt;/code&gt; is how many outcomes we have to consider.&lt;/p&gt;

  &lt;div class=&quot;code-cell&quot;&gt;
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itertools&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subset&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;16807
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/div&gt;
  &lt;p&gt;Yay!&lt;/p&gt;

  &lt;div class=&quot;code-cell&quot;&gt;
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;16807
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/div&gt;
&lt;/details&gt;

                    </description>
                
				<pubDate>Wed, 11 Feb 2026 00:00:00 +0100</pubDate>
				<link>https://thod.dev/2026/02/11/sums-of-the-choose-function-and-the-binomial-formula</link>
				<guid isPermaLink="true">https://thod.dev/2026/02/11/sums-of-the-choose-function-and-the-binomial-formula</guid>
            <!-- 
                <media:thumbnail width="250" height="250" url="https://thod.dev/assets/blog/2026-02-11-sums-of-the-choose-function-and-the-binomial-formula/thumbnail.png"/>
                 -->
            </item>
        
        
        
        
        
        
        
			<item>
				<title>Complex Impedances are simpler than the alternative.</title>
				<dc:creator>Tom Hodson</dc:creator>
				
					<description>
                    
				    <![CDATA[<img src="https://thod.dev/assets/blog/complex-impedances-are-simpler-than-the-alternative/thumbnail.png" alt="An image of the equations I = 1 / L integral V dt and V = 1/C integral I dt"/><br><br>]]>
                    
                    &lt;p&gt;I’ve been thinking a bit about inductors and capacitors lately and wanted to share some nice facts about them. But to start with, what exactly is a capacitor? Well, it’s a thing that stores charge, usually by having two metal plates close together but not electrically connected. How do we know it stores charge? At a microscopic level, electrons congregate on one plate and are depleted on the other, in addition, a strong electric field builds up between the two plates.&lt;/p&gt;

&lt;p&gt;Ok but you can’t really see or measure either of those effects at home. What you can measure are voltage and current, so for the rest of this post we’ll mostly worry about what the voltage and current are doing.&lt;/p&gt;

&lt;p&gt;For a two terminal device like a resistor, capacitor or inductor there are only really two external variables relating to those two terminals:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What is the (signed) current entering one terminal and exiting the other? Let’s just call this $I$.&lt;/li&gt;
  &lt;li&gt;What is the (signed) voltage difference between the terminals? Call this $V$.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Aside: This made me wonder about the possibility of current going into one terminal but not leaving by the other which would be a violation of Kirchhoff’s current law. I think the reason we don’t worry about this is because the timescale over which is electrons equilibrate is so fast that we wouldn’t notice these violations in most conditions, see the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lumped-element_model&quot;&gt;lumped element model&lt;/a&gt; on wikipedia for more detail on these kinds of assumptions.&lt;/p&gt;

&lt;p&gt;The other thing worth noting here is that the hydraulic analogy for a capacitor is not that of a bucket filling with electrons. If you see it like that then you’ll have noticed that that view also violates Kirchhoff’s current law. They’re more like a pipe with a flexible membrane.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/assets/blog/complex-impedances-are-simpler-than-the-alternative/capacitor_hydraulic_analogy.gif&quot; alt=&quot;In the hydraulic analogy, a capacitor is analogous to a rubber membrane sealed inside a pipe. It is possible to push water towards the membrane, but as the membrane stretches it will push back against the flow more and more. This animation illustrates a membrane being repeatedly stretched and un-stretched by the flow of water, which is analogous to a capacitor being repeatedly charged and discharged by the flow of current. This alt text was taken from the original Wikipedia image.&quot; /&gt;
&lt;figcaption&gt;
    &lt;p&gt;Image credit: &lt;a href=&quot;https://en.wikipedia.org/wiki/Hydraulic_analogy#/media/File:CapacitorHydraulicAnalogyAnimation.gif&quot;&gt;Sbyrnes321&lt;/a&gt; Wikipedia&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Nitpick: Also the membrane follows Hooke’s law so the restoring force (voltage) scales linearly with the volume displaced (charge) and the energy stored scales quadratically with the same. But that’s not so important for a mental model.&lt;/p&gt;

&lt;p&gt;For a capacitor in this idealised mathematical model of electrical circuits, it turns out it’s enough to simply ask what the relationship between $V$ and $I$ is. And you can kinda guess what it’s gonna be, the restoring force, voltage, is proportional to the stored charge. Current is the movement of charge over time so we know we probably have to integrate current over time and lastly we know that a bigger capacitor should charge up more slowly than a smaller one for a given current. Putting those together gives us:&lt;/p&gt;

\[V = \frac{1}{C} \int I \,dt\]

&lt;p&gt;Where we know it has to be $1/C$ rather than $C$ because bigger capacitors charge more slowly. We can also write this in derivative form:&lt;/p&gt;

\[V = \frac{1}{C} \int I \,dt \iff I = C\dot{V}\]

&lt;p&gt;Capacitors store charge and that stored charge resists changes in voltage across the terminals. Inductors are the dual, they’re like a flywheel that spins up with current and they resist changes in that current based on their inductance. This happens physically because a current creates a magnetic field in which the energy is stored and an inductor is basically a wire wound to have a higher than normal self inductance.&lt;/p&gt;

&lt;p&gt;We can ‘spin up’ an inductor by putting a voltage a across it which will cause the current going through it to rise steadily, as with capacitors, bigger inductors take long to spin up.&lt;/p&gt;

\[I = \frac{1}{L} \int V \,dt \iff V = L\dot{I}\]

&lt;p&gt;See how the equations for capacitors and inductors basically take the same form? The only difference is we swap $V$ and $I$ and rename the symbol for how big they are.&lt;/p&gt;

&lt;p&gt;When I try to remember webs of knowledge like this, I find it helpful to have a kernel that I know I can memorise correctly, in this case it’s&lt;/p&gt;

\[V = \frac{1}{C} \int I \,dt\]

&lt;p&gt;because I have a intuitive feeling for a capacitor charging up over time and I know bigger capacitors charge slower. From there I know I will remember that inductors are dual and I can differentiate to get the other forms.&lt;/p&gt;

&lt;p&gt;However, it turns out that most of the time we don’t really use differential equations like these to think about capacitance and inductance. That’s the next bit I want to talk about.&lt;/p&gt;

&lt;p&gt;Aside: These differential equations can be useful, you can solve them exactly for charging a capacitor from a voltage source and you find that capacitors charge and discharge exponentially. Same deal for inductors supplied from a constant current source but who does that? You can also use these differential equations if you want to do a numerical simulation of a circuit that has non linear elements in it like diodes, transistors… all of modern electronics really.&lt;/p&gt;

&lt;h2 id=&quot;complex-impedance&quot;&gt;Complex Impedance&lt;/h2&gt;

&lt;p&gt;Ok this is the bit I really like. We can write down three governing equations for capacitors, resistors and inductors in the form $V = …$&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Inductor&lt;/td&gt;
      &lt;td&gt;\(L \dot{I}\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Resistor&lt;/td&gt;
      &lt;td&gt;\(RI\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Capacitor&lt;/td&gt;
      &lt;td&gt;\(C^{-1} \int I dt\)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This form is cute because each time we go down the table we integrate current once with respect to time. This nicely situates resistors in the middle between inductors and capacitors.&lt;/p&gt;

&lt;p&gt;Ok the cool idea here is this, we know these equations are all linear differential equations and that has a very useful consequence &lt;strong&gt;sinusoidal waveforms will stay sinusoidal when modulated by these components&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now the undisputed was to represent sinusoidal waveforms is with complex numbers:&lt;/p&gt;

\[I(t) = Re \left( \; I_0 \; e^{i \omega t} \right)\]

\[V(t) = Re \left( V_0 \; e^{i \omega t} \right)\]

&lt;p&gt;$I_0$ and $V_0$ are both complex numbers that encode the magnitude and phase of the current and voltage waveforms that we’re considering. We’re also only considering one frequency $\omega$ at a time but we can vary it to understand how the components behave at different frequencies.&lt;/p&gt;

&lt;p&gt;So the trick with complex impedances is this: if we temporarily ignore those $Re()$ operators in the above equations for a minute we can come up with a complex version of Ohm’s law that works for resistors, capacitors and inductors all in one:&lt;/p&gt;

\[V = \mathbb{Z} I\]

&lt;p&gt;But what is the value of the complex impedance $\mathbb{Z}$? For resistors, it’s easy, it’s just $R$. For capacitors we can rearrange and use the fact that $\int e^{i \omega t} dt = \omega e^{i \omega t} $ to get:&lt;/p&gt;

\[\mathbb{Z} = V / I = \frac{1}{i \omega C}\]

&lt;p&gt;and similarly for inductors:&lt;/p&gt;

\[\mathbb{Z} = V / I = i \omega L\]

&lt;p&gt;These expressions tells us two things, for capacitors the voltage lags the current by 90 degrees or a quarter of a cycle and for inductors the opposite. It also tells use that capacitors look like an open circuit at DC and become a dead short at high frequency while for inductors the opposite is true.&lt;/p&gt;

&lt;p&gt;This can all be summed up in this table:&lt;/p&gt;

&lt;meta class=&quot;full-width-table&quot; /&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Inductors&lt;/th&gt;
      &lt;th&gt;Resistors&lt;/th&gt;
      &lt;th&gt;Capacitors&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Integral Eqn.&lt;/td&gt;
      &lt;td&gt;\(I = \frac{1}{L} \int V \,dt\)&lt;/td&gt;
      &lt;td&gt;\(V = R I\)&lt;/td&gt;
      &lt;td&gt;\(V = \frac{1}{C} \int I \,dt\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Differential Eqn&lt;/td&gt;
      &lt;td&gt;\(V = L\dot{I}\)&lt;/td&gt;
      &lt;td&gt;\(V = R I\)&lt;/td&gt;
      &lt;td&gt;\(I = C \dot{V}\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;\(V = \;...\)&lt;/td&gt;
      &lt;td&gt;\(V = L\dot{I}\)&lt;/td&gt;
      &lt;td&gt;\(V = R I\)&lt;/td&gt;
      &lt;td&gt;\(V = \frac{1}{C} \int I \,dt\)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;\(\mathbb{Z} =\)&lt;/td&gt;
      &lt;td&gt;\(i \omega L\)&lt;/td&gt;
      &lt;td&gt;\(R\)&lt;/td&gt;
      &lt;td&gt;\((i \omega C)^{-1}\)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I won’t go into it here but the next thing that makes complex impedances really useful is that the same equations that govern resistors in series and in parallel also carry over to complex impedances. This allows you to easily derive and expression for the frequency dependent behaviour of a complicated network of resistors, capacitors and inductors.&lt;/p&gt;

                    </description>
                
				<pubDate>Sat, 16 Aug 2025 00:00:00 +0200</pubDate>
				<link>https://thod.dev/2025/08/16/complex-impedances-are-simpler-than-the-alternative</link>
				<guid isPermaLink="true">https://thod.dev/2025/08/16/complex-impedances-are-simpler-than-the-alternative</guid>
            <!-- 
                <media:thumbnail width="250" height="250" url="https://thod.dev/assets/blog/complex-impedances-are-simpler-than-the-alternative/thumbnail.png"/>
                 -->
            </item>
        
        
        
        
        
	</channel>
</rss>