<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Arch Linux Archives - ClickedyClick</title>
	<atom:link href="https://gergely.imreh.net/blog/tag/arch-linux/feed/" rel="self" type="application/rss+xml" />
	<link>https://gergely.imreh.net/blog/tag/arch-linux/</link>
	<description>Life in real, complex and digital.</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:16:35 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>The curious case of binfmt for x86 emulation for ARM Docker</title>
		<link>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/</link>
					<comments>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Fri, 04 Apr 2025 05:15:48 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[ArchiveTeam]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[QEMU]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2967</guid>

					<description><![CDATA[<p>Seemingly identical configurations, different results. When two methods for setting up x86 emulation on ARM showed the exact same system configuration but behaved completely differently in Docker, I began questioning my system administration knowledge and my sanity &#8211; and briefly contemplated a new career as a blacksmith. This is a debugging tale for those working [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/">The curious case of binfmt for x86 emulation for ARM Docker</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Seemingly identical configurations, different results. When two methods for setting up x86 emulation on ARM showed the exact same system configuration but behaved completely differently in Docker, I began questioning my system administration knowledge and my sanity &#8211; and briefly contemplated a new career as a blacksmith.</p>



<p class="wp-block-paragraph">This is a debugging tale for those working with containers, and a reminder that things aren&#8217;t always what they seem in Linux, all with a big pinch reminder to Read the Fine Manual, Always!</p>



<span id="more-2967"></span>



<h2 class="wp-block-heading">ARM with Achiveteam v2</h2>



<p class="wp-block-paragraph">Recently I&#8217;ve got an email from a reader of the <a href="https://gergely.imreh.net/blog/2021/04/arm-images-to-help-the-archive-team/">ARM images to help the Archive Team</a> blogpost from years ago, asking me about refreshing that project to use again. There I was recompiling the ArchiveTeam&#8217;s Docker images to support ARM, and thus I was looking how things changed in the intervening time. I also got more <s>lazy</s> pragmatic since then, I was was wondering if the <a href="https://wiki.archiveteam.org/">Archiveteam</a> just made some ARM or multi-arch images as I believe(d) they should. That lead me to <a href="https://wiki.archiveteam.org/index.php/ArchiveTeam_Warrior#Can_I_run_the_Warrior_on_ARM_or_some_other_unusual_architecture?">their FAQ entry about ARM images</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Can I run the Warrior on ARM or some other unusual architecture?</strong></p>



<p class="wp-block-paragraph">Not directly. We currently do not allow ARM (used on Raspberry Pi and M1 Macs) or other non-x86 architectures. This is because we have previously discovered questionable practices in the Wget archive-creating components and are not confident that they run correctly under (among other things) different endiannesses. [&#8230;]</p>



<p class="wp-block-paragraph">Set up <a href="https://docs.docker.com/build/building/multi-platform/#qemu">QEMU with your Docker install</a> and add &#8211;platform linux/amd64 to your docker run command.</p>
</blockquote>



<p class="wp-block-paragraph">This actually seems like a sensible thing &#8211; if they dug that deep that they&#8217;ve seen issues in <a href="https://www.gnu.org/software/wget/">wget</a>, I&#8217;ve definitely been doing things naively before.</p>



<p class="wp-block-paragraph">The guidance of installing QEMU seems sensible as well (we were doing a lot of those at <a href="https://www.balena.io/">balena</a>), and it goes roughly like.</p>



<ol class="wp-block-list">
<li>install <code>binfmt</code></li>



<li>install QEMU with statically compiled binaries</li>



<li>load those binaries to emulate the platforms you want with the <code>F</code> / <code>fix_binary</code> flag</li>
</ol>



<p class="wp-block-paragraph">For those unfamiliar, <code>binfmt_misc</code> is a <a href="https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html">Linux kernel feature</a> that allows non-native binary formats to be recognized and passed to user space applications. It&#8217;s what makes it possible to run ARM binaries on x86 systems and vice versa through emulation. The various flags are how the actual behaviour of <code>binfmt</code> is adjusted (F, P, C, O)</p>



<p class="wp-block-paragraph">Docker advised to use a image to set things up, that is, for example for the <code>x86_64</code>/<code>amd64</code> platform like this:</p>



<pre class="wp-block-code"><code class="">docker run --privileged --rm tonistiigi/binfmt --install amd4</code></pre>



<p class="wp-block-paragraph">My Raspberry Pi is running <a href="https://archlinuxarm.org/">ArchLinuxARM</a> which installs <a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-binfmt.service.html">systemd-binfmt</a> to load the relevant emulation settings at boot time, which seemed handy: with the docker method I had to run that every time before I could run an emulated container, with systemd I would have thing ready by every time the time Docker is ready to run (ie. keeping the Archiveteam containers always on and restarting after reboot.) So I have a strong incentive to use the systemd-based approach instead of the <code>docker run</code> based one.</p>



<p class="wp-block-paragraph">Now comes the kicker <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92f.png" alt="🤯" class="wp-smiley" style="height: 1em; max-height: 1em;" />:</p>



<ul class="wp-block-list">
<li>the <code>docker</code> installed <code>binfmt</code> setup worked and allowed to run <code>linux/amd64</code> containers</li>



<li><code>systemd-binfmt</code> initiated <code>binfmt</code> setup worked for the <code>x86_64</code> binaries in the file system, but not in Docker where the binaries just failed to run</li>



<li>both setups had identical output when looking at the config in <code>/proc/sys/fs/binfmt_misc</code></li>
</ul>



<h2 class="wp-block-heading">When Same&#8217;s Not the Same</h2>



<p class="wp-block-paragraph">To see whether emulation works, the <code>tonistiigi/binfmt</code> container can be invoked without any arguments and it shows the status. For example setting things up with <code>docker</code> would show:</p>



<pre class="wp-block-code"><code class="">$ docker run --privileged --rm tonistiigi/binfmt
{
  "supported": [
    "linux/arm64",
    "linux/amd64",
    "linux/amd64/v2",
    "linux/arm/v7",
    "linux/arm/v6"
  ],
  "emulators": [
    "qemu-x86_64"
  ]
}</code></pre>



<p class="wp-block-paragraph">Here the <code>supported</code> section shows <code>amd64</code> as it should, and their test of running an amd64 image to check if the binaries are run has the expected output:</p>



<pre class="wp-block-code"><code class="">$ docker run --rm --platform linux/amd64 -t alpine uname -m
x86_64</code></pre>



<p class="wp-block-paragraph">Going back to the alternative, after uninstalling that emulatior I start up <code>systemd-binfmt</code>I can test the status again:</p>



<pre class="wp-block-code"><code class="">$ docker run --privileged --rm tonistiigi/binfmt
{
  "supported": [
    "linux/arm64",
    "linux/arm/v7",
    "linux/arm/v6"
  ],
  "emulators": [
[...snip...]
    "qemu-x86_64",
[...snip...]
  ]
}</code></pre>



<p class="wp-block-paragraph">This shows that while the emulator is installed, Docker doesn&#8217;t find that the <code>linux/amd64</code> platform is supported, and this checks out with running the <code>alpine</code> image again as above:</p>



<pre class="wp-block-code"><code class="">$ docker run --rm --platform linux/amd64 -t alpine uname -m
exec /bin/uname: exec format error</code></pre>



<p class="wp-block-paragraph">Well, this doesn&#8217;t work.</p>



<p class="wp-block-paragraph">The <a href="https://docs.kernel.org/admin-guide/binfmt-misc.html">binfmt_misc docs in the Linux Kernel wiki</a> do have plenty of info on the setup and use of the that emulation function. For example to check the configuration of the emulation setup, we can look at the contents of a file in <code>/proc</code> filesystem:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64
enabled
interpreter /usr/bin/qemu-x86_64
flags: POCF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p class="wp-block-paragraph">This was the almost the same whether I the <code>docker</code> based setup or used <code>systemd-binfmt</code> with a slight difference: the <code>flags</code> bit is only <code>PF</code> when run with <code>systemd-binfmt</code>, and <code>POCF</code> when set things up with <code>docker run</code>. Even if the Docker docs are asking for the <code>F</code> flag, I wanted to make sure we are on equal footing, so I&#8217;ve tried to modify  the QEMU setup to be the same. This means overriding the <code>qemu-x86_64.conf</code> that is shipped by default:</p>



<ul class="wp-block-list">
<li>Copy the config from <code>/usr/lib/binfmt.d/qemu-x86_64.conf</code> to <code>/etc/binfmt.d/qemu-x86_64.conf</code> (make sure the file has the same name to ensure this new file overrides the one from the lib folder)</li>



<li>Edit the end of the line from <code>:FP</code> to <code>:FPOC</code> </li>



<li>restart <code>systemd-binfmt</code></li>
</ul>



<p class="wp-block-paragraph">After this the output of the the runtime info in <code>/proc/sys/fs/binfmt_misc/qemu-x86_64</code> was completely the same. Why&#8217;s the difference?</p>



<p class="wp-block-paragraph">More debugging steps ensued:</p>



<h3 class="wp-block-heading">More Debugging Ensued</h3>



<p class="wp-block-paragraph">I&#8217;ve <strong>read through the source code</strong> of <code>tonistiigi/binfmt</code> on <a href="https://github.com/tonistiigi/binfmt/tree/master/cmd/binfmt">GitHub</a> and seen that it doesn&#8217;t do anything fancy, it&#8217;s quite clear implementation of the `binfmt_misc` usage docs and the same magic values as QEMU shipped on my system. Good that no surprise, but no hints of any difference</p>



<p class="wp-block-paragraph">I&#8217;ve tried to <strong>replicate that process</strong> of setting up QEMU through translating it into Python and running, still the same</p>



<p class="wp-block-paragraph">I&#8217;ve <strong>recompiled the binary</strong> on my system, and run it outside of docker: it worked the same way as the <code>systemd-binfmt</code> setup: <code>x86_64</code> static binaries<sup data-fn="edde2512-e0d8-4c40-8fc7-32ce3232f660" class="fn"><a id="edde2512-e0d8-4c40-8fc7-32ce3232f660-link" href="#edde2512-e0d8-4c40-8fc7-32ce3232f660">1</a></sup> worked outside of Docker but not inside of it</p>



<p class="wp-block-paragraph">A sort-of <strong>breakthrough</strong> came when I&#8217;ve tried out <a href="https://github.com/dbhi/qus">dbhi/qus</a> Docker images, that promises &#8220;qemu-user-static (qus) and containers, non-invasive minimal working setups&#8221;, and can do the similar emulator &amp; platform support setup with:</p>



<pre class="wp-block-code"><code class="">docker run --rm --privileged aptman/qus -s -- -p x86_64</code></pre>



<p class="wp-block-paragraph">It was a lot slower to run (coming back to this later), but worked like the charm, just like Docker&#8217;s own recommendation. However there was a difference in the outcome when I checked the runtime config info:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64
enabled
interpreter /qus/bin/qemu-x86_64-static
flags: F
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p class="wp-block-paragraph">It has just the apparently required <code>F</code> flag, but the interpreter points to <code>/qus/bin/qemu-x86_64-static</code> &#8230; which is not in the regular file system. Nevertheless <code>alpine</code> happily runs, just as my local static binaries.</p>



<p class="wp-block-paragraph">How does this actually work, then?</p>



<h2 class="wp-block-heading">Everything&#8217;s Illuminated</h2>



<p class="wp-block-paragraph">With this above, and with a better understanding what the docs say, we have everything in place to understand the all the behaviours above, things we had pointers throughout, but not enough experience to put them together:</p>



<p class="wp-block-paragraph">So, the <code>F</code> flag was required by the Docker docs, what does that <em>actually</em> do?</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><code>F</code> &#8211; fix binary</p>



<p class="wp-block-paragraph">The usual behaviour of binfmt_misc is to spawn the binary lazily when the misc format file is invoked. However, this doesn’t work very well in the face of mount namespaces and changeroots, so the <code>F</code> mode opens the binary as soon as the emulation is installed and uses the opened image to spawn the emulator, meaning it is always available once installed, regardless of how the environment changes.</p>
</blockquote>



<p class="wp-block-paragraph">Because of this, if <code>F</code> is set, the <code>interpreter</code> entry in the runtime settings doesn&#8217;t mean the path of the interpreter it <em>will</em> be called, but where it <em>was</em> called at the time &#8211; ie. it&#8217;s irrelevant for the actual runtime.</p>



<p class="wp-block-paragraph">The <code>tonistiigi/binfmt</code> image ships its own static-compiled <code>qemu-*</code> binarlies, as well as  <code>aptman/qus</code> container gets the right ones at runtime (hence the slowness), and the <code>interpreter</code> path is the binary <em>inside the container when the command is run</em>. The binary is then kept in memory, and the container can go away, the <code>interpreter</code> path&#8217;s not refering anything that exists any longer.</p>



<p class="wp-block-paragraph">Why does <code>systemd-binfmt</code> fail then? Well of course because it&#8217;s a <code>dynamically</code> linked binary:</p>



<pre class="wp-block-code"><code class="">$ file /usr/bin/qemu-x86_64
/usr/bin/qemu-x86_64: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=a4b8a93a4361be61dfa34a0eab40083325853839, for GNU/Linux 3.7.0, stripped</code></pre>



<p class="wp-block-paragraph">&#8230; and because it&#8217;s dynamically linked, even if the <code>F</code> flag makes it stay in memory, its lib dependencies aren&#8217;t, so when in run in Docker (which uses namespaces) it doesn&#8217;t have everything to run&#8230;</p>



<p class="wp-block-paragraph">And of course, ArchLinux <a href="https://wiki.archlinux.org/title/QEMU">spells</a> this out:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Note:</strong> At present, Arch does not offer a full-system mode and statically linked variant (neither officially nor via AUR), as this is usually not needed.</p>
</blockquote>



<p class="wp-block-paragraph">Yes, &#8220;as this is usually not needed&#8221;. :)</p>



<h2 class="wp-block-heading">Updated Setup and Looking Forward</h2>



<p class="wp-block-paragraph">Sort of lobbying ArchLinux to have static QEMU<sup data-fn="f5c8a1a6-6844-4956-adf5-488644168f92" class="fn"><a id="f5c8a1a6-6844-4956-adf5-488644168f92-link" href="#f5c8a1a6-6844-4956-adf5-488644168f92">2</a></sup> what options do I have?</p>



<ul class="wp-block-list">
<li>set up a <code>systemd</code> service to run the <code>tonistiigi/binfmt</code> container on startup (which is <a href="https://blog.container-solutions.com/running-docker-containers-with-systemd">possible</a>)</li>



<li>get some static QEMU binaries and override the settings that <code>systemd-binfmt</code> uses</li>



<li>switch to anothe Linux Distro that supports the Pi, the software I run, but also ships static QEMU builds</li>
</ul>



<p class="wp-block-paragraph">All three are suboptimal, potentially fragile, and the third is way too much work. Still the second one was kinda fine:</p>



<pre class="wp-block-code"><code class="">cd $(mktemp -d)
docker create --name="tmp_$$"  tonistiigi/binfmt
docker export tmp_$$ -o tonistiigi.tar.gz
docker rm tmp_$$
tar -xf tonistiigi.tar.gz --wildcards "*/qemu-x86_64"
# Copy along the binaries folder:
sudo cp usr/bin/qemu-x86_64 /usr/bin/qemu-x86_64-static</code></pre>



<p class="wp-block-paragraph">Then just like we&#8217;ve overridden the upstream <code>qemu-x86_64.conf</code> we do it again:</p>



<ul class="wp-block-list">
<li>Copy the config from <code>/usr/lib/binfmt.d/qemu-x86_64.conf</code> to <code>/etc/binfmt.d/qemu-x86_64.conf</code> (make sure the file has the same name to ensure this new file overrides the one from the lib folder)</li>



<li>Edit the end of the line from <code>:/usr/bin/qemu-x86_64:FP</code> to <code>:/usr/bin/qemu-x86_64-static:FPOC</code> (that is updating the binary it points at and the flags for good measure too</li>



<li>As a bonus, can update the <code>:qemu-x86_64:</code> in the front too, say to <code>:qemu-x86_64-static:</code>, to change the display name of the emulator without affecting any of the functionality, it will just rename the entrin in <code>/proc/sys/fs/binfmt_misc</code> </li>



<li>restart <code>systemd-binfmt</code></li>
</ul>



<p class="wp-block-paragraph">Then the check again:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64-static
enabled
interpreter /usr/bin/qemu-x86_64-static
flags: POCF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p class="wp-block-paragraph">And the <code>alpine</code>-based checks work once more.</p>



<h2 class="wp-block-heading">Lessons Learned</h2>



<p class="wp-block-paragraph">The details were all in plain sight, but not enough experience to piece these things together. The Docker-recommended image ships its own QEMU? What does that <code>F</code> flag actually do? Can you run binaries while you don&#8217;t have them anymore? Dynamic and static linking and the signs of their misbehaviours to provide hints&#8230; However this is coupled with confusion when expectations are broken (say the <code>interpreter</code> doesn&#8217;t have to refer to an actual file path that exists <em>now</em>), until I started to question my expectations. Also, just being a heavy user of Docker doesn&#8217;t mean I&#8217;m knowledgeable of the relevant kernel functionality, and probably I should be more…</p>



<p class="wp-block-paragraph">This whole process underlined my previous thoughts on <a href="https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/">Software Engineering when AI seems Everywhere</a>, as I did try to debug things by rubber ducking with <a href="https://claude.ai">Claude</a>: this time the hallucinations were through the roof (a metric tonne of non-existent systemd funcionality, non-existent command line flags), definitely got me on a wild goose chase in a few cases. So even more care&#8217;s needed, maybe a version of <a href="https://en.wikipedia.org/wiki/Hofstadter%27s_law">Hofstadter&#8217;s Law</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Imreh&#8217;s Law<sup data-fn="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc" class="fn"><a id="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc-link" href="#f632dbc3-dd26-4b73-b6cc-0b53e878e3dc">3</a></sup>: LLMs are always more wrong than you expect, even when you take into account Imreh&#8217;s Law.</p>
</blockquote>



<p class="wp-block-paragraph">In the end, Don&#8217;t Panic, make theories and try to prove them, and talk with anyone who listens, even when they are wrong, and you are more likely to get there<sup data-fn="692118e9-bc19-43d4-8ac0-1603d91facdf" class="fn"><a id="692118e9-bc19-43d4-8ac0-1603d91facdf-link" href="#692118e9-bc19-43d4-8ac0-1603d91facdf">4</a></sup>.</p>


<ol class="wp-block-footnotes"><li id="edde2512-e0d8-4c40-8fc7-32ce3232f660">I&#8217;ve download static binaries from <a href="https://github.com/andrew-d/static-binaries">andrew-d/static-binaries</a>, recommend <code>strings</code> as something that&#8217;s quick and simple to use <code>./strings /bin/sh | head</code> for example, allowing fast iteration. <a href="#edde2512-e0d8-4c40-8fc7-32ce3232f660-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="f5c8a1a6-6844-4956-adf5-488644168f92">ArchLinux is x86 by default, for them it would be to emulate <code>linux/arm64</code>, <code>linux/arm/v7</code>, <code>linux/arm/v6</code> images. For ArchLinux ARM it would be a different work the other direction. If only the main Arch would support ARM, it would be a happier world (even if even more complex). <a href="#f5c8a1a6-6844-4956-adf5-488644168f92-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc">Tongue-in-cheek, of course. <a href="#f632dbc3-dd26-4b73-b6cc-0b53e878e3dc-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="692118e9-bc19-43d4-8ac0-1603d91facdf">And with this we just rediscovered the <a href="https://wiki.c2.com/?FeynmanAlgorithm">Feynman Algorithm</a>, I guess. <a href="#692118e9-bc19-43d4-8ac0-1603d91facdf-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/">The curious case of binfmt for x86 emulation for ARM Docker</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ZFS on a Raspberry Pi</title>
		<link>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/</link>
					<comments>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/#comments</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Wed, 28 Feb 2024 11:48:50 +0000</pubDate>
				<category><![CDATA[Admin]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[ZFS]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2862</guid>

					<description><![CDATA[<p>Using ZFS to make the most an assorted bunch of SD cards and USB sticks for storage, and hitting Linux kernel licensing issues along the way.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/">ZFS on a Raspberry Pi</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I have a little home server, just like mike many other geeks / nerds / programmers / technical people&#8230; It can be both <em>useful</em>, a learning experience, as well as <em>a real chore</em>; most of the time the balance is shifting between these two ends. Today I&#8217;m taking notes here on one aspect of that home server that is widely swing between those two use cases.</p>



<p class="wp-block-paragraph">When I say I have a home server, that might be too generous description of the status quo: I have a pretty banged up <a href="https://www.raspberrypi.com/products/raspberry-pi-3-model-b/">Raspberry Pi 3B</a>. It&#8217;s running <a href="https://archlinuxarm.org/">ArchLinux ARM</a>, the 64-bit, AAarch64 version, looking a bit more retro on the hardware front while pushing for more modernity on the software side &#8211; a mix that I find fun.</p>



<p class="wp-block-paragraph">There are a handful of services running on the device &#8212; not <em>that</em> many, mostly limited by it&#8217;s <em>*gulp*</em> 1GB memory; plenty of things I&#8217;d love to run, doesn&#8217;t well co-locate in such a tiny compartment. Besides the memory, it&#8217;s also limited by storage: the Raspberry Pi runs off an SD card, and those are both fragile, and limited in size. If one wants to run a home file server, say using a handful of other SD cards lying around, to expand the available storage, that will be awkward very soon. To make that task less awkward (or replace one kind of awkward with a more interesting one), I&#8217;ve set out to set up a ZFS storage pool, using <a href="https://openzfs.readthedocs.io/en/latest/introduction.html">OpenZFS</a>. </p>



<h2 class="wp-block-heading">The idea</h2>



<p class="wp-block-paragraph">Why ZFS? In big part, to be able to credibly answer that question. </p>



<p class="wp-block-paragraph">But with a single, more concrete reason: being able to build a more solid and expandable storage unit. ZFS cancombine different storage units</p>



<ul class="wp-block-list">
<li>in a way that combats data errors, e.g. mirroring: this addresses SD cards fragility</li>



<li>in a way that data can expand across all of them in a single file system: this addresses the SD cards size limitations</li>
</ul>



<p class="wp-block-paragraph">This sounds great in theory and after a bit of trial-and-error, I&#8217;ve made the following setup, relying on dynamic kernel modules for support for flexibility, and a hodgepodge of drives at hand for the storage</p>



<p class="wp-block-paragraph">The file system supports needs is provided by the <a href="https://aur.archlinux.org/packages/zfs-dkms">zfs-dkms package</a> dynamic kernel module (<a href="https://wiki.archlinux.org/title/Dynamic_Kernel_Module_Support">DKMS</a>), which means the kernel module required for being able to manage that file system is recompiled for each new Linux kernel version as it is updated. This is handy in theory, as I can use the main kernel packages provided by the ArchLinux ARM team.</p>



<p class="wp-block-paragraph">For storage, I&#8217;ve started off with two SD cards in mirror mode (going for data integrity first). Later I&#8217;ve found &#8212; and invested in &#8212; some large capacity USB sticks that bumped the storage size quite a bit. With these, the currentl ZFS pool looks like this:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="365" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1024x365.png" alt="Terminal screenshot of the 'zpool status' command." class="wp-image-2863" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1024x365.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-500x178.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-768x274.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1200x428.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage.png 1454w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">It already saved me &#8212; or rather my data &#8212; once where an SD card was acting up, though that&#8217;s par for the course. One very large benefit is that the main system card is being used less, so hopefully will last longer.</p>



<h2 class="wp-block-heading">The complications</h2>



<p class="wp-block-paragraph">Of course, it&#8217;s never this easy&#8230; With non-mainline kernel modules and with DKMS, every update is a bit of a gamble, that can suddenly not pay off. That&#8217;s exactly what happened last year, when suddenly the module didn&#8217;t compile anymore on a new kernel version, and thus all that storage was sitting dump and inaccessible. After <a href="https://github.com/openzfs/zfs/issues/14555">digging into</a> the issue, it down to:</p>



<ol class="wp-block-list">
<li>the OpenZFS project being under <a href="https://opensource.org/license/cddl-1-0">Common Development and Distribution License</a> (CDDL)</li>



<li>the Linux kernel <a href="https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3">deliberately breaking non-GPL licensed code</a> by starting to withold certain floating point capabilities, because &#8220;this is not expected to be disruptive to existing users&#8221;.</li>
</ol>



<p class="wp-block-paragraph">This wasn&#8217;t great, as user being between pretty much a rock &amp; a hard place, even if this is a hobby and not strictly speaking a production use case on my side. </p>



<p class="wp-block-paragraph">Nonetheless, it worked by <a href="https://wiki.archlinux.org/title/Downgrading_packages">downgrading</a> to a working version and <a href="https://wiki.archlinux.org/title/Pacman#Skip_package_from_being_upgraded">skipping updates</a> to the kernel packages.</p>



<p class="wp-block-paragraph">Then based on a <a href="https://github.com/openzfs/zfs/issues/14555#issuecomment-1475119774">suggestion</a>, patching the zfs-dkms package (rewriting the license entry in the META file) to make it look like it&#8217;s a GPL-licensed module &#8212; which is fair game for one doing on their own machine. This is hacky, or let&#8217;s call it pragmatic.</p>



<pre class="wp-block-code"><code class="">--- META.prev   2024-02-28 08:42:21.526641154 +0800
+++ META        2024-02-28 08:42:36.435569959 +0800
@@ -4,7 +4,7 @@
 Version:       2.2.3
 Release:       1
 Release-Tags:  relext
-License:       CDDL
+License:       GPL
 Author:        OpenZFS
 Linux-Maximum: 6.7
 Linux-Minimum: 3.10</code></pre>



<p class="wp-block-paragraph">Now, with the current 2.2.3 version, it seems like there&#8217;s an official f<a href="https://github.com/openzfs/zfs/pull/15711">ix-slash-workaround</a> for being able to get the module to compile, even if it&#8217;s not a full fix. From the linked merge request message I&#8217;m not fully convinced that this is not a fragile status quo, but it&#8217;s at least front of mind &#8211; good going for wider ARM hardware usage that brings out people&#8217;s willingness to fix things! </p>



<h2 class="wp-block-heading">Future development</h2>



<p class="wp-block-paragraph">Some while back, while working at <a href="https://www.balena.io/">an IoT software deploument &amp; management company</a>, I had a lot of interesting hardware at hand, naturally, to build things with (or wrestle with&#8230;). Nowadays I have things I best describe as <em>spare parts</em>, and thus loads of thingss are more fragile than they need to be, as well as gosh-it-takes-a-long-time to compile things on a Raspberry Pi 3 &#8211; making every kernel update some half-an-hour longer!</p>



<p class="wp-block-paragraph">Likely the best move would be to upgrade to a (much more powerful) <a href="https://www.raspberrypi.com/products/raspberry-pi-5/">Raspberry Pi 5</a> and use an <a href="https://learn.pimoroni.com/article/getting-started-with-nvme-base">external NVMe drive</a>, where I&#8217;d have much less need for ZFS, at least for the original reasons. It would likely be still useful for other aspects (such as <a href="https://wiki.archlinux.org/title/ZFS#Automatic_snapshots">snapshotting</a>, or sending/receiving the drive data, compression, deduplication, etc&#8230;), changing the learning path away from multi-device support to the file system features.</p>



<p class="wp-block-paragraph">If I wanted to use more storage in the existing system, I could also get rid of the mirrored SD cards and just just 4 large USB sticks (maybe in a <a href="https://openzfs.github.io/openzfs-docs/Basic%20Concepts/RAIDZ.html">RAIDZ</a> setup), a poor-man&#8217;s NAS, I guess. Though there I&#8217;d worry a bit about using the sticks with the same sizes for this to work (unlike pooling, which has no same-size requirements), given the differences in the supposedly same sized products from different companies (likely locking me into a having the same brand and model across the board).</p>



<p class="wp-block-paragraph">I also feel like I&#8217;m not using ZFS to its full potential. If I know enough just to be dangerous&#8230; maybe that&#8217;s the generalists natural habitat?</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/">ZFS on a Raspberry Pi</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Continuous integration testing of Arch User Repository packages</title>
		<link>https://gergely.imreh.net/blog/2018/04/circleci-aur/</link>
					<comments>https://gergely.imreh.net/blog/2018/04/circleci-aur/#comments</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Thu, 12 Apr 2018 11:56:40 +0000</pubDate>
				<category><![CDATA[Admin]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[CircleCI]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[YAML]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2425</guid>

					<description><![CDATA[<p>Using git submodules and CircleCI workflows to build and test the AUR packages I maintain.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2018/04/circleci-aur/">Continuous integration testing of Arch User Repository packages</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I maintain <a href="https://aur.archlinux.org/packages/?K=imrehg&amp;SeB=m">a couple of ArchLinux user-contributed packages</a> on the <a href="https://wiki.archlinux.org/index.php/Arch_User_Repository">Arch User Repository (AUR)</a>, and over time I&#8217;ve built out a bit of infrastructure around that to make that maintenance easier (and hopefully the results better). The core of it is automated building of packages in Continuous Integration, which catches a number of issues which otherwise would be more difficult.</p>
<p>This write-up will go through the entire packaging process to make it easily reproducible.<span id="more-2425"></span></p>
<h2>Contributing a package</h2>
<p>AUR is a great resource for Arch Linux users, and  it is pretty easy to <a href="https://wiki.archlinux.org/index.php/Arch_User_Repository#Sharing_and_maintaining_packages">create and contribute new packages</a>.</p>
<p>Packages are created by cloning an empty <a href="https://git-scm.com/">git</a> repository with the desired package name. I do it in a slightly different setup compared to the wiki that&#8217;s linked just above, as:</p>
<pre class="lang:sh decode:true" title="Clone an empty repo to create a new AUR package">git clone ssh+git://aur@aur.archlinux.org/&lt;PACKAGENAME&gt;.git</pre>
<p>Add your PKGBUILD and any other required files, run mksrcinfo, and git commit, and push&#8230; If everything went well, your package is now visible in the <a href="https://aur.archlinux.org/packages/?O=0&amp;K=">AUR search</a>.</p>
<p>Next time that repository is cloned, it will contain the code, and changes (i.e. package updates) can be pushed just as well too.</p>
<h2>Keeping track of packages</h2>
<p>As more packages are contributed, it is increasingly hard to keep track of them as separate repositories. One way to improve on this, is creating a &#8220;meta&#8221; repository (or repo), where all the contributed packages are linked as <a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules">git submodules</a>.</p>
<p>This organization is achieved by creating your meta-repo, and add your package as a submodule:</p>
<pre class="lang:sh decode:true " title="Adding AUR package as a submodule to a git repo">git submodule add ssh+git://aur@aur.archlinux.org/&lt;PACKAGENAME&gt;.git</pre>
<p>Then you&#8217;d make package updates in that submodule, and the meta repo would contain all your packages as a collection.</p>
<p>My packages&#8217; meta repo that show this arrangement is on Github at <a href="https://github.com/imrehg/aur">imrehg/aur</a>.</p>
<h2>Continuous integration testing</h2>
<p>What we can do with this setup now, is to automatically check out, build, analyze, and test (including installation) of the all the packages.  I&#8217;ve set that up as <a href="https://circleci.com/">CircleCI</a> build jobs for each of the packages: each of them built and installed in a clean Arch Linux environment.</p>
<p>The clean Arch Linux environment is provided by a <a href="https://www.docker.com/">Docker</a> image, that I&#8217;ve created for this purpose, <a href="https://github.com/imrehg/archlinux-makepkg-docker">archlinux-makepkg-docker</a>. That image builds on an upstream Arch Linux image, and sets a few things up:</p>
<ul>
<li>updates the image with the latest base build system</li>
<li>creates a &#8220;builder&#8221; user that can run sudo</li>
<li>installs two packages from scratch that are sometimes needed for working with AUR packages: &#8220;package-query&#8221; and &#8220;yaourt&#8221;</li>
<li>installs &#8220;<a href="https://wiki.archlinux.org/index.php/Namcap">namcap</a>&#8221; to analyze the package</li>
</ul>
<p>Each AUR package is set up with its own CircleCI build job as part of a <a href="https://circleci.com/docs/2.0/workflows/">workflow</a>.</p>
<p>Since most of the work for each package is pretty much the same, we can simplify things with templates, such as this:</p>
<pre class="lang:yaml decode:true" title="A CircleCI config to build my-package"># Common sections
defaults: &amp;defaults
  working_directory: ~/aur
  docker:
    - image: imrehg/archlinux-makepkg

updatepackage: &amp;updatepackage
  name: Update packages
  command: sudo pacman -Syu --noconfirm

gitupdate: &amp;gitupdate
  name: Git repo updates
  command: |
    sed -i "s#ssh+git://aur@aur.archlinux.org#https://aur.archlinux.org#" .gitmodules
    git submodule update --init
pkgbuildtest: &amp;pkgbuildtest
  name: Testing PKGBUILD
  command: |
    cd ~/aur/${CIRCLE_JOB}
    namcap PKGBUILD
buildtest: &amp;buildtest
  name: Building package
  command: |
    cd ~/aur/${CIRCLE_JOB}
    makepkg -sci --noconfirm

# Main
version: 2
jobs:
  my-package:
    &lt;&lt;: *defaults
    steps:
      - run:
          &lt;&lt;: *updatepackage
      - checkout
      - run:
          &lt;&lt;: *gitupdate
      - run:
          &lt;&lt;: *pkgbuildtest
      - run:
          &lt;&lt;: *buildtest

workflows:
  version: 2
  build:
    jobs:
      - my-package
</pre>
<p>The sample CircleCI &#8220;config.yml&#8221; here is set up to build an AUR package called &#8220;my-package&#8221;:</p>
<ul>
<li>it pulls the Arch Linux Docker image mentioned earlier</li>
<li>updates any outdated OS package</li>
<li>checks out meta repo that we are working from</li>
<li>updates the submodule configuration to be able to pull the required submodule without authentication. the &#8220;ssh+git://&#8221; setup requires the maintainer&#8217;s SSH credentials, while switching to &#8220;https://&#8221; the CI environment is allowed to check the package&#8217;s code out (and won&#8217;t be able to push back upstream, which is safer)</li>
<li>runs &#8220;namcap&#8221; on the PKGBUILD to catch any obvious issues</li>
<li>builds and installs the package (including dependencies)</li>
</ul>
<p>As &#8220;my-package&#8221; is set up above, it does not have any line specific in to that package in the build steps. The specifics are set up using CircleCI variables (CIRCLE_JOB) and <a href="http://yaml.org/spec/1.2/spec.html">YAML</a> <a href="http://yaml.org/type/merge.html">Merge Key Language-Independent Types</a> (the &#8220;foo: &amp;foo&#8221; and &#8220;&lt;&lt; : *foo&#8221; section). Thus if there&#8217;s &#8220;another-package&#8221;, it&#8217;s easy to clone the &#8220;my-package&#8221; section as it is, naming that &#8220;another-package&#8221;, and adding a new build job to the end of the file called &#8220;another-package&#8221;. With this &#8220;templating&#8221; when the build steps need to be modified, they can be updated in the header, and all the packages will pick that up.</p>
<p>Workflows are also useful, as  jobs can be made dependent on each other, if they are related, such as my &#8220;gnushogi&#8221; and &#8220;xshogi&#8221; packages, or likely any AUR package that requires other AUR packages that need to be built.</p>
<pre class="lang:yaml decode:true" title="Workflow with dependency">...
workflows:
  version: 2
  build:
    jobs:
      &lt;other jobs&gt;
      - gnushogi
      - xshogi:
          requires:
            - gnushogi
</pre>
<p>This would result in a dependency in the jobs as:</p>
<p><figure id="attachment_2443" aria-describedby="caption-attachment-2443" style="width: 724px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_jobs.png"><img decoding="async" class="size-full wp-image-2443" src="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_jobs.png" alt="" width="724" height="973" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_jobs.png 724w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_jobs-372x500.png 372w" sizes="(max-width: 724px) 100vw, 724px" /></a><figcaption id="caption-attachment-2443" class="wp-caption-text">Jobs in the CircleCI workflow</figcaption></figure></p>
<p>The workflows also allow for jobs to give files to each other. E.g. as above &#8220;xshogi&#8221; depends on &#8220;gnushogi&#8221; to be installed, I could build all the required dependencies again in &#8220;xshogi&#8221;, but it was already built, I could just pass on the created package from the earlier job to the next, using <a href="https://circleci.com/blog/deep-diving-into-circleci-workspaces/">CircleCI workspaces</a>.</p>
<pre class="lang:yaml decode:true" title="Sample file passing between jobs with workspaces">  gnushogi:
    &lt;&lt;: *defaults
    steps:
      - run:
          &lt;&lt;: *updatepackage
      - checkout
      - run:
          &lt;&lt;: *gitupdate
      - run:
          &lt;&lt;: *pkgbuildtest
      - run:
          &lt;&lt;: *buildtest
      - persist_to_workspace:
          root: gnushogi
          paths: gnushogi-*.pkg.tar.xz

  xshogi:
    &lt;&lt;: *defaults
    steps:
    &lt;&lt;: *defaults
    steps:
      - run:
          &lt;&lt;: *updatepackage
      - checkout
      - run:
          &lt;&lt;: *gitupdate
      - run:
          &lt;&lt;: *pkgbuildtest
      - attach_workspace:
          at: /tmp/workspace
      - run:
          name: Installing gnushogi
          command: sudo pacman -U --noconfirm /tmp/workspace/gnushogi*.pkg.*
      - run:
          &lt;&lt;: *buildtest
</pre>
<p>The meta repo is now ready to go with such &#8220;.circleci/config.yml&#8221;, and on each push, it will build all the packages defined in the job list. You can check how the results look for my AUR packages in CircleCI&#8217;s <a href="https://circleci.com/gh/imrehg/aur">build job view</a> (one entry by build job, ie. package-per-push) or <a href="https://circleci.com/gh/imrehg/workflows/aur">workflow view</a> (one entry per push, aggregating all jobs).</p>
<p><figure id="attachment_2453" aria-describedby="caption-attachment-2453" style="width: 1020px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_workflow_small.png"><img decoding="async" class="size-full wp-image-2453" src="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_workflow_small.png" alt="" width="1020" height="845" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_workflow_small.png 1020w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_workflow_small-500x414.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/circleci_aur_workflow_small-768x636.png 768w" sizes="(max-width: 1020px) 100vw, 1020px" /></a><figcaption id="caption-attachment-2453" class="wp-caption-text">Last build workflows</figcaption></figure></p>
<p>One of the advantages of this setup, is that if a build fails on any of the package (e.g. a source file is no longer available) it&#8217;s easy to see, and I can catch a number of out-of-date packages sooner than someone reports it on AUR.</p>
<h2>Keeping the build image up to data</h2>
<p>The  Arch Linux Docker image is <a href="https://docs.docker.com/docker-hub/builds/">automatically built</a> on Docker Hub (and can be found at <a href="https://hub.docker.com/r/imrehg/archlinux-makepkg/">imrehg/archlinux-makepkg</a>. It is kept fresh by an <a href="https://ifttt.com/">If This Than That</a> applet, which triggers the build every morning.</p>
<p><figure id="attachment_2441" aria-describedby="caption-attachment-2441" style="width: 691px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/aur_build_image_ifttt.png"><img loading="lazy" decoding="async" class="wp-image-2441 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/aur_build_image_ifttt.png" alt="" width="691" height="549" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/aur_build_image_ifttt.png 691w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/aur_build_image_ifttt-500x397.png 500w" sizes="auto, (max-width: 691px) 100vw, 691px" /></a><figcaption id="caption-attachment-2441" class="wp-caption-text">IFTTT Applet to trigger Docker Hub automated builds</figcaption></figure></p>
<p>That applet just uses the <a href="https://ifttt.com/date_and_time">Date &amp; Time</a> and <a href="https://ifttt.com/maker_webhooks">Webhooks</a> recipes. The webhook points to the Trigger URL provided by the &#8220;Build Settings / Build Triggers&#8221; section on Docker Hub for the image, and it&#8217;s a POST request with payload of:</p>
<pre class="lang:js decode:true" title="Docker Hub build trigger payload">{"docker_tag": "latest"}</pre>
<p><figure id="attachment_2442" aria-describedby="caption-attachment-2442" style="width: 525px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers.png"><img loading="lazy" decoding="async" class="wp-image-2442 size-large" src="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers-1024x563.png" alt="" width="525" height="289" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers-1024x563.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers-500x275.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers-768x422.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2018/04/docker_hub_triggers.png 1909w" sizes="auto, (max-width: 525px) 100vw, 525px" /></a><figcaption id="caption-attachment-2442" class="wp-caption-text">Docker HUB Build Settings / Build Triggers settings</figcaption></figure></p>
<p>Keeping the image fresh like this shortens the build time when running the jobs on CircleCI (fewer packages need to be updated), which especially important as free users have limited CPU time available each month.</p>
<p>Not many packages which use other AUR packages, which likely need more setup here.</p>
<h2>Update workflow</h2>
<p>As an aside, the process to update any given package with this setup as follows:</p>
<ul>
<li>Update the &#8220;PKGBUILD&#8221; for the package, quite often it&#8217;s just the version number</li>
<li>Update the checksums easily with &#8220;updpkgsums&#8221; (part of &#8220;pacman&#8221; so it should be always available)</li>
<li>Build the package</li>
<li>If everything goes well, update the required &#8220;.SRCINFO&#8221; with &#8220;mksrcinfo&#8221; (part of &#8220;pkgbuild-introspection&#8221;)</li>
<li>git add, commit (signed if you can:), and push to AUR</li>
<li>Clean up the package directory (&#8220;git clean -d -f &amp;&amp; rm -rf src&#8221;)</li>
<li>Going back up in the folder hierarchy to the meta repo git add and commit the changes to the submodules</li>
<li>Push to github, and enjoy the build!</li>
</ul>
<h2>Future</h2>
<p>Many things can be improved on this setup (one day), here are some ideas</p>
<p>It should be possible to <strong>publish the build artifacts</strong> to somewhere (say S3) and set it up as a custom Arch Linux package repository, thus can be reused without everyone needing to build from scratch every time.</p>
<p>If that publishing would happen, I&#8217;m guessing it would be good to also <strong>sign the built packages</strong>, which might be a bit trickier to set up safely, but would make package distribution nicer and more robust.</p>
<p>In my list of packages there are not that many that depend on other AUR packages. Other <strong>packages with more AUR dependencies</strong> might need even more custom setup than shown above, besides the templated sections, to make them speedy and logical.</p>
<p>In the package testing steps, probably should <strong>run &#8220;namcap&#8221; on the finished package</strong> too, to catch other issues (e.g. dependencies required but not included).</p>
<hr />
<p>What&#8217;s your experience with maintaining AUR packages, or with CircleCI? Have any feedback on how to make this above even more useful?</p>
<p>The post <a href="https://gergely.imreh.net/blog/2018/04/circleci-aur/">Continuous integration testing of Arch User Repository packages</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2018/04/circleci-aur/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Dual Satellite NTP server with Navspark</title>
		<link>https://gergely.imreh.net/blog/2015/11/navspark-beidou-ntp/</link>
					<comments>https://gergely.imreh.net/blog/2015/11/navspark-beidou-ntp/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Thu, 26 Nov 2015 10:46:14 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Lab]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[beidou]]></category>
		<category><![CDATA[gps]]></category>
		<category><![CDATA[navspark]]></category>
		<category><![CDATA[NTP]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2144</guid>

					<description><![CDATA[<p>A friend from NIST recently told me about a Raspberry Pi Stratum-1 NTP server project, and that reminded me of the experiments I did with the Navspark dual GPS+Beidou receiver module. Navspark is a small, Arduino-compatible module that besides GPS can also receive data from China&#8217;s Beidou 北斗 satellite navigation system , that is currently being built. [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2015/11/navspark-beidou-ntp/">Dual Satellite NTP server with Navspark</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A friend from NIST recently told me about a <a href="http://www.satsignal.eu/ntp/Raspberry-Pi-NTP.html">Raspberry Pi Stratum-1 NTP server</a> project, and that reminded me of the <a href="https://gergely.imreh.net/blog/2014/05/navspark-beidou/">experiments I did</a> with the Navspark dual GPS+Beidou receiver module. Navspark is a small, Arduino-compatible module that besides GPS can also receive data from China&#8217;s <a href="https://en.wikipedia.org/wiki/BeiDou_Navigation_Satellite_System">Beidou 北斗 satellite navigation system</a> , that is currently being built. I thought it would be fun to build a Beidou-powered Stratum-1 NTP server to see how does it compare to GPS.</p>
<h2>Hardware</h2>
<p>To have a really good really good, satellite-powered reference clock, I have to have access to a 1-pulse-per-second (1PPS) signal from the receiver. The pure USB-connected receivers don&#8217;t really seem to do that yet (looks like plenty of opportunities there!), instead I have to use separate hardware for it.</p>
<p>The Navspark module has a 1PPS pin (GPO3 below), and the only other pin I&#8217;ll really need is a serial pin to receive the NMEA stream of the satellite lock data (TXD1 below).</p>
<p><figure id="attachment_1768" aria-describedby="caption-attachment-1768" style="width: 500px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2014/05/pinout1.jpg"><img loading="lazy" decoding="async" class="wp-image-1768 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2014/05/pinout1.jpg" alt="Detailed Navspark pinout with pin functions" width="500" height="530" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2014/05/pinout1.jpg 500w, https://gergely.imreh.net/blog/wp-content/uploads/2014/05/pinout1-472x500.jpg 472w" sizes="auto, (max-width: 500px) 100vw, 500px" /></a><figcaption id="caption-attachment-1768" class="wp-caption-text">Navspark pinout from the User Manual</figcaption></figure></p>
<p><span id="more-2144"></span>These pins have to be connected to a Raspberry Pi 2&#8217;s GPIO pins.</p>
<p><figure id="attachment_2155" aria-describedby="caption-attachment-2155" style="width: 882px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/raspberry_pi_circuit_note_fig2a-min.jpg"><img loading="lazy" decoding="async" class="wp-image-2155 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/raspberry_pi_circuit_note_fig2a-min.jpg" alt="Raspberry Pi 2 pinout diagram" width="882" height="770" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/raspberry_pi_circuit_note_fig2a-min.jpg 882w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/raspberry_pi_circuit_note_fig2a-min-500x437.jpg 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/raspberry_pi_circuit_note_fig2a-min-768x670.jpg 768w" sizes="auto, (max-width: 882px) 100vw, 882px" /></a><figcaption id="caption-attachment-2155" class="wp-caption-text">Raspberry Pi 2 pinout diagram</figcaption></figure></p>
<p>Based on the pinout above, and some digging around, the connections I needed to make between the Pi (left) and Navspark (right) were:</p>
<ul>
<li>5V <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> BATTERY</li>
<li>Ground <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> GND</li>
<li>GPIO15 (UART0_RX) <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> TXD1</li>
<li>GPIO8 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> P1PPS (GPO3)</li>
</ul>
<p>Some things to remember here, that on serial connections one side&#8217;s RX line is connected to the other&#8217;s TX  and vice versa. Also, as much as I gather any other GPIO pins on the Pi could be used to receive the 1PPS signal, I used GPIO8 because it&#8217;s been used by other projects and playing it safe&#8230;</p>
<p>The adapter board was easy enough to put together (though single-side pads are a pain when the components are on both sides, as it is the case here).</p>
<p><figure id="attachment_2147" aria-describedby="caption-attachment-2147" style="width: 604px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/RPi_Navspark_testrun_home.jpg"><img loading="lazy" decoding="async" class="wp-image-2147 size-large" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/RPi_Navspark_testrun_home-1024x715.jpg" alt="Raspberry Pi 2 with Navspark adapter board" width="604" height="422" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/RPi_Navspark_testrun_home-1024x715.jpg 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/RPi_Navspark_testrun_home-500x349.jpg 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/RPi_Navspark_testrun_home-768x536.jpg 768w" sizes="auto, (max-width: 604px) 100vw, 604px" /></a><figcaption id="caption-attachment-2147" class="wp-caption-text">Raspberry Pi 2 with Navspark adapter board</figcaption></figure></p>
<p>The adapter works well enough, though I&#8217;m sure that it could be done nicer.</p>
<h2>Software</h2>
<p>I found a lot of examples and help from other people&#8217;s similar projects but there were still some differences to be figured along the way. The overall setup was to be as follows:</p>
<ul>
<li><a href="http://archlinuxarm.org/platforms/armv7/broadcom/raspberry-pi-2">ArchLinux ARM</a> for base platform</li>
<li><a href="http://www.catb.org/gpsd/">gpsd</a> for interpreting the Navspark NMEA messages</li>
<li>LinuxPPS for 1PPS signal passing from GPIO to the userspace</li>
<li><a href="http://chrony.tuxfamily.org/">chrony</a> as time server (as opposed to the usual ntpd)</li>
</ul>
<p>Installing the system was straightforward enough (well, because I&#8217;ve done it so many times, but I digress), and added a few required basic packages: gpsd, chrony, git, the base-devel group, <a href="https://wiki.archlinux.org/index.php/Avahi">avahi</a> (for easier network discovery).</p>
<h3>GPSD</h3>
<p>UART0 serial communication is done through the /dev/ttyAMA0 device in Pi, though the default settings needed some change for gpsd to be able to receive the data from Navspark.</p>
<p>Most of the changes our <a href="http://logicalgenetics.com/serial-on-raspberry-pi-arch-linux/">outline in a blogpost</a> where I have learned from as well. First remove all config settings from /boot/cmdline.txt, basically these parts:</p>
<pre class="lang:default decode:true">console=ttyAMA0,115200 kgdboc=ttyAMA0,115200</pre>
<p>Next disable the automatic serial console on that device with removing relevant systemd unit with</p>
<pre class="lang:default decode:true">sudo systemctl disable serial-getty@ttyAMA0.service</pre>
<p>Then have to set up the serial channel&#8217;s baud rate to enable communication, and here I&#8217;ve done that with udev: create a new rule file at /etc/udev/rules.d/AMA0_serial.rules with the content of</p>
<pre class="lang:default decode:true ">KERNEL=="ttyAMA0", RUN+="/bin/stty -F /dev/ttyAMA0 115200 raw -echo"</pre>
<p>which will set the 115200 baud rate used by Navspark when the ttyAMA0 device comes up.</p>
<p>Finally have to add gpsd&#8217;s default settings in the /etc/gpsd file:</p>
<pre class="lang:default decode:true "># Default settings for gpsd.
START_DAEMON="true"
GPSD_OPTIONS="-b -n"
DEVICES="/dev/ttyAMA0"
USBAUTO="true"</pre>
<p>Now it&#8217;s ready, and should be able to run gpsd and see some communication.</p>
<pre class="lang:default decode:true">sudo systemctl start gpsd</pre>
<p>and if want to start it on boot, then run the same command with &#8220;enable&#8221; instead &#8220;start&#8221;. Use gpsmon to see what NMEA data Navspark sends, and hopefully it will lock on soon too (mind the sky visibility!). Can also use cgps to monitor which satellites the system is locked onto.</p>
<h3>1PPS</h3>
<p>For 1PPS I had to add an item to /boot/config.txt:</p>
<pre class="lang:default decode:true">dttoverlay=pps-gpio,gpiopin=8</pre>
<p>This adds the relevant device tree overlay to enable the pps-gpio driver on GPIO8. Upon boot there was a /dev/pps0 device that can be poked at e.g. with ppstest from <a href="https://github.com/ago/pps-tools">pps-tools</a>. Since pps-tools did not have a package on ArchLinux, I made one, and the <a href="https://gist.github.com/imrehg/5548370432737a82f58c">PKGBUILD is available on Github</a>. That package also adds the timepps.h header file which is required for the chrony PPS driver to work.</p>
<p>The pps signal can be checked by running ppstest:</p>
<pre class="lang:sh decode:true ">$ sudo ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1448436177.999366423, sequence: 199 - clear  0.000000000, sequence: 0
source 0 - assert 1448436178.999373610, sequence: 200 - clear  0.000000000, sequence: 0
...</pre>
<p>but it needs a 3D fix for the signal to show up.</p>
<h3>Chrony</h3>
<p>Next up is the time server itself. The satellite time will be pulled in using two &#8220;reference clock&#8221;: the GPS timestamps provided by gpsd through a memory interface, and the PPS data that locks those timestamps. Unfortunately the PPS driver is not built in by default because it needs the timepps.h header. This means after installing pps-tools chrony needs to be recompiled.</p>
<p>Update the chrony configuration by editing the /etc/chrony.conf file. I usually leave the defaults alone, and add my modifications to the end of the file for clarity. Here&#8217;s my current setup:</p>
<pre class="lang:default decode:true">lock_all

logdir /var/log/chrony
log measurements statistics tracking refclocks

# being a server
allow 192.168/16

server 2.tw.pool.ntp.org iburst
server 3.asia.pool.ntp.org iburst
server 2.asia.pool.ntp.org iburst
server 1.europe.pool.ntp.org iburst

# on-boot time setup, from https://news.ycombinator.com/item?id=9796885
makestep 0.1 1

# from https://www.raspberrypi.org/forums/viewtopic.php?f=41&amp;t=1970&amp;start=275
refclock PPS /dev/pps0 lock GPS prefer refid PPS0
# If Beidou, there is a day offset somehow
refclock SHM 0 offset -86400 delay 0.1 refid GPS noselect

leapsectz right/Asia/Taipei</pre>
<p>It uses <a href="http://www.pool.ntp.org/en/">NTP pool servers</a> for initial time setup and a quick on-boot step (as we don&#8217;t want to be stuck at 1970, do we?). The time service related settings are the &#8220;<a href="http://chrony.tuxfamily.org/manual.html#refclock">refclock</a>&#8221; parts. The first line sets the 1PPS lock-on and I tell chrony to prefer that over other sources so we&#8217;ll switch over to that when there&#8217;s good-enough data. The second refclock section uses gpsd&#8217;s NMEA data. The large offset there is due to some issues where the reference time shows up with 24h offset. That I still need to investigate. The delay time set needs some fine-tuning too (I think), though from comments 0.1-0.3 (that is seconds) seems to be suitable? I also tell chrony not to use that for reference.</p>
<p>I also set leap seconds timezones (with the &#8220;right/&#8230;&#8221;  TAI time zone, see e.g. the <a href="http://support.ntp.org/bin/view/Support/TimeScales">NTP support page</a> and a <a href="https://lists.debian.org/debian-user/2005/12/msg03343.html">discussion on the Debian mailing list</a>). And I enable the server on the local network so other can connect to it and get the time.</p>
<p>I&#8217;ve enabled logging of the tracking data (so there&#8217;s offset and other information available) and refclocks (tailing /var/log/chrony/refclocks.log continuously is a handy way to see when the lock is lost).</p>
<p>Start chrony (or enable if you want to start it on boot)</p>
<pre class="lang:default decode:true">sudo systemctl start chrony</pre>
<p>and the results can be monitored with chronyc.</p>
<h3>All together now</h3>
<p>If everything worked well, gpsmon should show a good locked signal after not too much time, something like this:</p>
<p><figure id="attachment_2149" aria-describedby="caption-attachment-2149" style="width: 823px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_gpsmon.png"><img loading="lazy" decoding="async" class="wp-image-2149 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_gpsmon.png" alt="Navspark lock in gpsmon" width="823" height="513" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_gpsmon.png 823w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_gpsmon-500x312.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_gpsmon-768x479.png 768w" sizes="auto, (max-width: 823px) 100vw, 823px" /></a><figcaption id="caption-attachment-2149" class="wp-caption-text">Navspark lock in gpsmon</figcaption></figure></p>
<p>From the interface above I can also see that Navspark has a 3D lock (Mode A3) to satellites number 209, 207, and 206, which are Beidou birds. And that the time offset is 86399.96 seconds (~24h), that is what the chrony configuration compensates for.</p>
<p>Checking the results in chrony I the system time it&#8217;s locked to PPS0 where the last sample had 410ns offset (pretty good).  Also printed the sourcestats.</p>
<p><figure id="attachment_2150" aria-describedby="caption-attachment-2150" style="width: 823px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_chronyc.png"><img loading="lazy" decoding="async" class="wp-image-2150 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_chronyc.png" alt="Navspark-powered chrony time lock (PPS0 and GPS signal is relevant): sources and sourcestats" width="823" height="513" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_chronyc.png 823w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_chronyc-500x312.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_chronyc-768x479.png 768w" sizes="auto, (max-width: 823px) 100vw, 823px" /></a><figcaption id="caption-attachment-2150" class="wp-caption-text">Navspark-powered chrony time lock (PPS0 and GPS signal is relevant): sources and sourcestats</figcaption></figure></p>
<p>And this is pretty much all that is needed in general to have a Stratum-1 (atomic clock-locked) NTP server in general.</p>
<p>Some extra references:</p>
<ul>
<li><a href="http://brandon.penglase.net/index.php?title=NavSpark_GPS%2BGLONASS_Stratum-1_NTP_Time_Source_with_a_Raspberry_Pi">NavSpark GPS+GLONASS Stratum-1 NTP Time Source with a Raspberry Pi</a></li>
<li><a href="http://wiki.hackspherelabs.com/index.php?title=NTP_GPS_Time_Server">Hackspherelab: NTP GPS Time Server</a></li>
<li><a href="http://www.catb.org/gpsd/gpsd-time-service-howto.html">GPSD Time Service HOWTO</a></li>
</ul>
<h2>Experience</h2>
<p>Navspark has a GNSS Viewer program (available for download <a href="http://www.navspark.com.tw/downloads/">from their website</a>) which can also set the module&#8217;s parameters, for example forcing it to only listen to the GPS signal or only Beidou signal. There are a lot of settings, and I think most don&#8217;t need any change, but there&#8217;s still a bit to explore, and it&#8217;s definitely a handy tool.</p>
<p><figure id="attachment_2152" aria-describedby="caption-attachment-2152" style="width: 1012px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/GNSS_Viewer_GPS_BD.png"><img loading="lazy" decoding="async" class="wp-image-2152 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/GNSS_Viewer_GPS_BD.png" alt="GNSS Viewer (and configuration) in Windows" width="1012" height="740" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/GNSS_Viewer_GPS_BD.png 1012w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/GNSS_Viewer_GPS_BD-500x366.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/GNSS_Viewer_GPS_BD-768x562.png 768w" sizes="auto, (max-width: 1012px) 100vw, 1012px" /></a><figcaption id="caption-attachment-2152" class="wp-caption-text">GNSS Viewer (and configuration) in Windows</figcaption></figure></p>
<p>Running the NTP lock and looking at the tracking lock there are immediately a few lessons in the ~1 day I&#8217;ve been playing with the system:</p>
<p><strong>Environment matters a lot</strong>: having wind blowing over the system and being exposed to temperature changes (e.g. outdoor setting) is quite bad for frequency/time stability. One could say duh! but it&#8217;s good to see. Even a curtain and placing it indoors helps too, though having it in a well air conditioned space is even better. Too bad that in that case the sky visibility and thus the signal strength suffers a lot, but as long as there&#8217;s enough signal to have a lock it is worth it.</p>
<p><strong>PPS is lost when 3D fix is lost</strong>: this took a while to figure out why did I lose lock every now and then, especially because once chrony loses the PPS signal it won&#8217;t pick it up again.</p>
<p><strong>Indoor can work</strong>: indoor lock with the <a href="http://navspark.mybigcommerce.com/internal-gps-beidou-active-antenna/">default antenna</a> can work, but the building structure matters a lot.</p>
<h3>GPS vs Beidou</h3>
<p>Of course the most interesting part is comparing GPS and Beidou. What follows here is not very well qualified yet, just first impressions and need more work.</p>
<p>One of the biggest advantage of GPS is having a lot more satellites than Beidou. I wouldn&#8217;t be surprised if they are transmitting at a higher power too, or some other ways easier to receive. When locked onto GPS I could have rock-solid indoors time lock with 4-9 satellites visible at any one time. The time precision is judged by the offset frequency distribution that chrony reports (and would love to hear if there&#8217;s a better way to do that!), and that had a standard deviation of about 1µs by experience (in an air-conditioned office).</p>
<p>When I&#8217;ve enabled both GPS and Beidou then Navspark seemed to have generally locked onto the Beidou signal. When i was lucky I got 5-6 satellites, but generally it&#8217;s around 3 &#8211; just on the threshold of having a 3D fix, and the source of the PPS-unlock mentioned above. The offset stability on the other hand can be really good, I had runs that had about 5x smaller time offset standard deviation than GPS had (~0.2µs), and that&#8217;s quite a bit of improvement!</p>
<p>The issue is, that when I set the Navspark module GPS+Beidou then when there was a low Beidou satellite count (2-3), even though it still kept a (fragile) 3D fix, the time offset stability was pretty much the same as GPS on its own, though with some intervals of improved readings&#8230;.</p>
<p><figure id="attachment_2154" aria-describedby="caption-attachment-2154" style="width: 823px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_cgps.png"><img loading="lazy" decoding="async" class="wp-image-2154 size-full" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_cgps.png" alt="Locked onto a bunch of Beidou satellites (PRN larger than 200)" width="823" height="513" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_cgps.png 823w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_cgps-500x312.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Navspark_cgps-768x479.png 768w" sizes="auto, (max-width: 823px) 100vw, 823px" /></a><figcaption id="caption-attachment-2154" class="wp-caption-text">Locked onto a bunch of Beidou satellites (PRN larger than 200)</figcaption></figure></p>
<p>This below is a ~35minutes run of GPS+Beidou setup in an air-conditioned office with limited sky-view and 3 more stories above, using the default 12dB antenna. Using cpgs I could see that in general the lock was provided by 2-3 Beidou satellites, and in some intervals switched over to GPS without losing PPS signal (until it did). Thus it&#8217;s not the best performance that I&#8217;ve seen but still pretty good. Offset is plotted with the reported standard deviation as error bars. The central line is the mean value of the offset readings, and the two bars are the standard deviation calculated from the offsets.</p>
<p><figure id="attachment_2158" aria-describedby="caption-attachment-2158" style="width: 604px" class="wp-caption aligncenter"><a href="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log.png"><img loading="lazy" decoding="async" class="wp-image-2158 size-large" src="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log-1024x745.png" alt="Beidou time-lock offset over time (cutting the transients in the beginning, and finishing with signal being lost)" width="604" height="439" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log-1024x745.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log-500x364.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log-768x559.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2015/11/Beidou_offset_log.png 1100w" sizes="auto, (max-width: 604px) 100vw, 604px" /></a><figcaption id="caption-attachment-2158" class="wp-caption-text">Beidou time-lock offset over time (cutting the transients in the beginning, and finishing with signal being lost)</figcaption></figure></p>
<h2>Future</h2>
<p>The ideal setup would be a well air-conditioned roof-top location (I think it would be possible to reliably see 6-7 satellites that way), and switching to Beidou only measurements to clearly see the difference between the two satellite network.</p>
<p>Improved reception could probably be achieved by <a href="http://navspark.mybigcommerce.com/tri-band-gps-galileo-glonass-bds-active-antenna/">a different Navspark antenna</a>, which provides 25±3dB gain (that&#8217;s an extra ~13dB above the current antenna), and should help indoor reception a lot. It&#8217;s currently on pre-sale and expected by the end of the year, so will keep an eye on it!</p>
<p>For more advanced improvements, the adapter board would be easy enough to redo on PCB (instead of surface mount soldering), add maybe some more voltage stabilization for the module to improve the noise figure, and probably have cleaner 1PPS signal too (compared to flying wires). Will definitely give it a try as it is so easy to get the boards printed these days, e.g. with <a href="http://www.seeedstudio.com/propagate/">Seeed Studio</a>.</p>
<p>Finally some more setting adjustments could improve the final result, such as tweaking the 1PPS pulse length, using the surveying profile for higher precision (though have to figure out what does it mean first, but looks promision), and other little tweaks&#8230;.</p>
<p>It looks to me that if the satellite reception issues are cleaned up, the Navspark + Beidou 1PPS signal should be interesting to try with laboratory frequency references for long term stability (such as a <a href="http://www.thinksrs.com/products/FS725.htm">Rubidium Clock</a>). And of course to run the Pi+Navspark as a local time reference and provide a NTP service, e.g. from the <a href="https://taipeihack.org">Tapei Hackerspace</a>&#8230;. Plenty to do. :)</p>
<p>The post <a href="https://gergely.imreh.net/blog/2015/11/navspark-beidou-ntp/">Dual Satellite NTP server with Navspark</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2015/11/navspark-beidou-ntp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Object Caching 14/88 objects using APC
Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: gergely.imreh.net @ 2026-07-26 10:14:33 by W3 Total Cache
-->