<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mklimenko.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mklimenko.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2024-12-19T08:37:29+00:00</updated><id>https://mklimenko.github.io/feed.xml</id><title type="html">GNSS C++ solutions</title><subtitle>A blog about digital signal processing and GNSS. My personal thoughts about interesting work stuff.
</subtitle><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><entry xml:lang="en"><title type="html">Global Navigation Satellite Systems Software-Defined Receivers explained. Part 0: Hardware and system design</title><link href="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design/" rel="alternate" type="text/html" title="Global Navigation Satellite Systems Software-Defined Receivers explained. Part 0: Hardware and system design" /><published>2022-06-20T00:00:00+00:00</published><updated>2022-06-20T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design</id><content type="html" xml:base="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design/"><![CDATA[<p>In this series of articles I’d like to share my experience in GNSS receiver design over the last 10+ years. Writing down the knowledge on various topics is an excellent way to share the information, as well as structuring and preparing this kind of educational material allows one to find the missing pieces in one’s knowledge.</p>

<p>I’d like to start from a system design perspective since most of the decisions made at this stage are directly influencing the type of processing that will be used later.</p>

<ul>
  <li><a href="/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design/">Part 0: Hardware and system design</a>
    <ul>
      <li><a href="#overview">Overview</a></li>
      <li><a href="#hardware">Hardware</a></li>
      <li><a href="#frequency-plan">Frequency plan</a></li>
      <li><a href="#summary">Summary</a></li>
    </ul>
  </li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-1-digital-frontend/">Part 1: Digital frontend</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation/">Part 2: Jamming mitigation</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-3-acquisition/">Part 3: Acquisition</a></li>
  <li>Part 4: Correlation and tracking</li>
  <li>Part 5: Standalone positioning</li>
  <li>Part 6: Advanced positioning methods</li>
</ul>

<h2 id="overview">Overview</h2>

<p>A general schematic of the GNSS receiver is presented below with the high-level blocks. Generally, the plurality of the satellite signals is received in an antenna (or multiple antennas), filtered, amplified and then fed into the (multiband) radio-frequency (RF) frontend to convert the signal into the baseband.</p>

<p>The baseband signal is quantized and sampled in the ADC and then processed by the digital frontend, acquisition engine, correlation engine, tracking loops etc. The tracking loop results are used to generate the observables and with (optional) external data the receiver performs the positioning routines. On top of that, there is a user interaction like position, velocity and time (PVT) and observables data output, generation of the synchronized timescale pulses, various external events registration and so on.</p>

<p><img src="/assets/img/gnss-sdr/mermaid_0.svg" alt="High-level overview" /></p>

<p>To be honest, with all the receivers I’ve made over the last decade, the system design was by far my favourite stage of development. It involves a lot of research and communication with the potential customers, handling of both software and hardware aspects, like PCB, mechanical, environmental, not to say the RF and electrical.</p>

<h2 id="hardware">Hardware</h2>

<p>The system design starts with collecting all the requirements and limitations for the future receiver, which will result in some related decisions. For example:</p>

<ol>
  <li>We need to build an accurate heading solution → we’ll have to build a receiver with two antenna inputs → we need a multi-channel RF-frontend and a baseband processor with enough processing power</li>
  <li>We are limited to a special ASIC or FPGA family (this happens, when you need to use the in-house solution) → depending on the supposed use cases we should choose the appropriate RF frontend, inspect it with an evaluation kit and propose a frequency plan and a functional diagram of the receiver</li>
  <li>We have to comply with the certain form-factor either by legacy reasons or if we want to propose a hot-swap for some competitor → we should carefully inspect all the tolerances and limits (mechanical, electrical and thermal) and check for the undocumented behaviour of the reserved pins.</li>
</ol>

<p>One of the interesting topics is the selection of an appropriate antenna. There are three kinds of antennas:</p>

<ol>
  <li>Passive antennas, are often used in trackers, smartphones and other cost-effective solutions. There’s a great application note (<a href="https://content.u-blox.com/sites/default/files/products/documents/GNSS-Antennas_AppNote_%28UBX-15030289%29.pdf">GNSS antennas. RF design considerations for u-blox GNSS receivers. Application note</a>) by u-blox.</li>
  <li>High-precision active antennas, with built-in filters and amplifiers and, most importantly guarantee a stable phase centre position. As you may know, all the positioning and signal processing is performed for the signals, received at the phase centre of an antenna. When the phase centre is stable, signals from the satellites at various elevation angles are perceived at the same virtual position. An excellent overview of the active antennas is provided in a whitepaper (<a href="http://www.topcon.com.sg/images/GNSS_Topcon_Cavity_Filters.pdf">Topcon GNSS Reference Station with Cavity Filters TPS CR-G5-C &amp; TPS PN-A5-C</a>) by Topcon Positioning Systems with a major focus on the cavity filters.</li>
  <li>Antenna arrays with null- and beam-formers. This is a deep and interesting topic, check out the NovAtel <a href="https://novatel.com/products/anti-jam-antenna-systems-gajt/gajt-710ml-anti-jam-antenna">GAJT-710</a> antenna, I’ll briefly explain the antenna array algorithms in the antijamming section.</li>
</ol>

<p>Active antennas, due to the presence of an amplifier, require additional power to operate properly. This is solved (quite elegantly) by providing the DC via the same signal coaxial cable. The DC flows from the receiver (or the bias tee) to the antenna and the RF signal comes the other way around.</p>

<p>The signal from the antenna in the receiver is amplified with an onboard LNA to compensate for the signal losses in the cable. Then, after additional signal splitting and band-dependent filtering, the signal is processed in the RF frontend. The goals of the RF frontend are the following:</p>

<ol>
  <li>Additional signal amplification with automatic gain control (AGC)</li>
  <li>Signal spectrum shift to the baseband frequency</li>
  <li>Additional signal filtering</li>
  <li>(Optional) Signal sampling with onboard ADCs</li>
</ol>

<p>The first gain-controlled amplifier, which is often omitted in the low-end frontends is required when the interference is present. As you may know, the mixer is a non-linear device that multiplies the input signal with a reference signal to produce a signal with a set of sum and difference of the original frequencies. However, to operate correctly, the powers of both signals should be similar. If this condition is not met the major signal distortion will be observed.</p>

<blockquote>
  <p>There’s a way to overcome this issue with an approach, usually found in RADARs, called the bandpass sampling. It’s based on the properties of the digital signal and allows to use of the ADC as a perfect mixer. There’s an <a href="https://doi.org/10.22184/1993-8578.2021.14.7s.64.65">article</a> I’m happy to share or discuss where I demonstrate that with sufficient amplification this approach is equivalent and even surpasses the traditional RF frontends in terms of positioning precision.</p>
</blockquote>

<p>Nowadays it’s common to have all the functions of the frontend in a single RF integrated circuit. This approach allows to reduce the footprint of the receiver, simplify the overall design and reduce the cost both of the receiver itself and the R&amp;D expenses. There are two major players in COTS (commercially available off-the-shelf) RF frontends: Maxim Integrated (now part of Analog Devices) with MAX2769/MAX2771 chips and NTLab with NT1065/NT1066/NT1062 device family. Let’s illustrate a hardware part of the L1/L2 receiver:</p>

<p><img src="/assets/img/gnss-sdr/mermaid_1.svg" alt="Hardware part" /></p>

<h2 id="frequency-plan">Frequency plan</h2>

<p>When the hardware system design is more or less determined, it’s time to estimate the frequency plan, which defines the set of parameters responsible for the satellite signal processing. It includes:</p>

<ol>
  <li>Reference oscillator frequency $f_{ref}$</li>
  <li>Sampling rate $f_s$</li>
  <li>Local oscillator (LO) frequencies $f_{LO_i}$</li>
  <li>Resampling factors in the digital frontend</li>
</ol>

<p>The key point to the frequency plan design is to distribute and allocate the signal spectrum in a manner to avoid spectrum overlapping, which is often referred to as aliasing. This should take into account all the frequency shifts in RF and digital frontends, as well as resampling. The tricky part here is that when it comes down to the real hardware there are a lot of limitations like integer PLLs (hence, a finite set of local oscillator frequencies), sampling rate and bandwidth limits, presence or lack of the I/Q processing in the baseband processor and so on. For example, here’s an illustration of the frequency plan for the one of my first receivers:</p>

<p><img src="/assets/img/easter/frequency_plan.png" alt="Frequency plan" /></p>

<p>It was built with two NT1065 RF frontends to cover the L1/L2/L3/L5 GNSS signal bands and had local oscillators at 1590, 1237.5 and 1190 MHz. The fourth local oscillator was set to 1330 MHz and was a source of the 66.5 MHz ADC clock.</p>

<p>One more important thing is that the frequency plan should not be treated as something engraved in stone: it may (and should) change to reflect the situation. One of the best things about the modern IC-based RF frontends is their reconfigurability: if the selected ADC clock or the LO frequency doesn’t fit you for some reason (unexpected spectrum harmonics from the self-interference is one of the possible problems) it’s easy to use another configuration with a different set of register values.</p>

<h2 id="summary">Summary</h2>

<p>This introductory part is a very high-level overview of the system design process for the GNSS receivers, but this is the kind of knowledge you get from experience and have to acquire piece by piece from multiple sources. As I’ve mentioned, it always was my favourite design stage since it always represented pure research and, in a matter of speaking, art.</p>

<p>There’s also a big part of the system design related to the mechanical specifications, external data and user interaction, I hope to add it once I revisit this text.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="GNSS" /><summary type="html"><![CDATA[In this series of articles I’d like to share my experience in GNSS receiver design over the last 10+ years. Writing down the knowledge on various topics is an excellent way to share the information, as well as structuring and preparing this kind of educational material allows one to find the missing pieces in one’s knowledge.]]></summary></entry><entry xml:lang="en"><title type="html">Global Navigation Satellite Systems Software-Defined Receivers explained. Part 1: Digital frontend</title><link href="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-1-digital-frontend/" rel="alternate" type="text/html" title="Global Navigation Satellite Systems Software-Defined Receivers explained. Part 1: Digital frontend" /><published>2022-06-20T00:00:00+00:00</published><updated>2022-06-20T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-1-digital-frontend</id><content type="html" xml:base="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-1-digital-frontend/"><![CDATA[<p><img src="/assets/img/gnss-sdr/mermaid_2.svg" alt="Digital frontend" /></p>

<ul>
  <li><a href="/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design">Part 0: Hardware and system design</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-1-digital-frontend">Part 1: Digital frontend</a>
    <ul>
      <li><a href="#overview">Overview</a></li>
      <li><a href="#mixer">Mixer</a>
        <ul>
          <li><a href="#verification-of-operations">Verification of operations</a></li>
          <li><a href="#hardware-friendly-optimizations">Hardware-friendly optimizations</a></li>
        </ul>
      </li>
      <li><a href="#resampler">Resampler</a>
        <ul>
          <li><a href="#hardware-friendly-optimizations-1">Hardware-friendly optimizations</a></li>
        </ul>
      </li>
      <li><a href="#signal-packer">Signal packer</a></li>
      <li><a href="#summary">Summary</a></li>
    </ul>
  </li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation">Part 2: Jamming mitigation</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-3-acquisition">Part 3: Acquisition</a></li>
  <li>Part 4: Correlation and tracking</li>
  <li>Part 5: Standalone positioning</li>
  <li>Part 6: Advanced positioning methods</li>
</ul>

<h2 id="overview">Overview</h2>

<p>In software-defined and software-oriented GNSS receivers, systems engineers can decouple the satellite signal processing into two separate stages: group and individual.</p>

<ol>
  <li>Group signal processing is performed on the whole subband of the signal spectrum without distinguishing the signals of the individual satellite vehicles (SV). This is implemented by the digital frontend block, where the number of channels is roughly the number of processed signals (GPS L1 C/A, GLONASS L1OF etc.).</li>
  <li>Individual signal processing is the traditional digital channel explained in-depth by many authors [Kaplan, Springer]. This will be explained in the <a href="#part-4-correlation">Part 4: Correlation</a> article.</li>
</ol>

<p>The digital frontend may be perceived as a signal conditioner, the purpose of it is to prepare the input signal for the subsequent processing: translate the signal spectrum, reduce the data rate, remove interference and pack the signal in a format, suitable for the correlator.</p>

<p>There are two major beneficial aspects of using the digital frontend:</p>

<ol>
  <li>As I’ve mentioned earlier (<a href="#frequency-plan">Frequency plan</a>), ADCs usually operate on a fixed sampling rate, which often is not optimal for the specific signals. The resampling part of the digital frontend allows us to reduce the sampling rate individually for every group signal, hence reducing the computational load on the correlators.
    <blockquote>
      <p>For example, if the ADC is providing samples with 79.5 MHz and we want to develop a narrowband GPS L1 C/A timing receiver we can downsample the incoming signal by the factor of 20 (down to 3.975 MHz). In that case, we’ll perform one spectrum translation on the high frequency and resampling, but the following N (by the number of satellites) translations and correlations would take 20 times fewer operations for both mixer and correlator.</p>
    </blockquote>
  </li>
  <li>By separating the group and individual SV processing it is possible to separate the timing approach as well. For real-time receivers (implemented with FPGAs or ASICs), the group processing is often implemented synchronously with the ADC clock (~ tens of MHz), but the resulting signal can be saved to the internal memory and then processed in a co-processor manner, running as fast as it can be synthesized/clocked in case of FPGA or ASIC accordingly. The benefit here is that the same hardware can be reused for the benefit of silicon area optimization.</li>
</ol>

<p>The second aspect is not so relevant for the PC-based receivers, since it’s virtually impossible to organize the sample-based streamed data, and those kinds of receivers are asynchronous by design.</p>

<h2 id="mixer">Mixer</h2>

<p>Mixers are the devices used to translate the signal, which results in the shift in the frequency domain. Most digital mixers are complex and perform the multiplication of the input signal with the complex exponent:</p>

\[result(t) = input(t) * e^{-j(2\pi ft + \varphi)}\]

<p>With the transition to the digital signal processing and samples, this can be re-written as:</p>

\[result[i] = input[i] * e^{-j(2\pi i\frac{f}{f_s} + \varphi)}\]

<p>For the demonstration, I’ve used a simple Jupyter Notebook so you can try it yourself.</p>

<p>First of all, we download and read the data buffer. For the sake of simplicity of the demonstration I’ll convert the data to the floating-point:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">file_link</span> <span class="o">=</span> <span class="s">'https://www.dropbox.com/s/b2il4hnu30fup7c/GPSdata-DiscreteComponents-fs38_192-if9_55_partial.bin?dl=1'</span>
<span class="n">filename</span> <span class="o">=</span> <span class="s">'GPSdata-DiscreteComponents-fs38_192-if9_55.bin'</span>
<span class="n">fs</span> <span class="o">=</span> <span class="mf">38.192e6</span>
<span class="n">intermediate_frequency</span> <span class="o">=</span> <span class="mf">9.55e6</span>
<span class="n">request</span><span class="p">.</span><span class="n">urlretrieve</span><span class="p">(</span><span class="n">file_link</span><span class="p">,</span> <span class="n">filename</span><span class="p">);</span>

<span class="n">signal_data</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fromfile</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">int8</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="s">'float'</span><span class="p">)</span>
</code></pre></div></div>

<p>To illustrate the data properties, we’ll plot the power spectrum via Welch’s method, the first 100 microseconds and the signal histogram:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_7_0.png" alt="Input signal" /></p>

<p>As it was mentioned earlier, to translate the signal we’d have to multiply it by the complex sine:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">time</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">signal_data</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">/</span> <span class="n">fs</span>
<span class="n">sine</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="mf">1j</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">time</span>  <span class="o">*</span> <span class="n">intermediate_frequency</span><span class="p">)</span>
<span class="n">translated_signal</span> <span class="o">=</span> <span class="n">signal_data</span> <span class="o">*</span> <span class="n">sine</span>
</code></pre></div></div>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_8_0.png" alt="Output signal" /></p>

<p>Here are the key points that can be observed in the results of the multiplication:</p>

<ol>
  <li>Since we’re multiplying the real signal with the complex exponent we can observe the spectrum repetition in the frequency domain plot. This is present because the real signal has a spectrum that’s symmetrical relative to the $\frac{f_s}{2}$. This won’t affect the following processing as long as we have a proper anti-aliasing filter in the downsampling block.</li>
  <li>The peak at -9.55MHz is the DC part of the spectrum being translated to the negative intermediate frequency.</li>
  <li>The multiplication operation is linear and doesn’t change the nature of the underlying signal.</li>
</ol>

<h3 id="verification-of-operations">Verification of operations</h3>

<p>It’s always good to check and test your algorithms and operations throughout the development process. In those articles, I’ll omit most of the tests for demonstration purposes but I intend to keep the most visual and high-level test: the matched filter test.</p>

<p>The matched filter is an optimal filter, designed to maximize the signal-to-noise ratio. It has a complex frequency response conjugated to the complex frequency response of the signal it is designed to detect. According to the convolution theorem, we can substitute the circular convolution with the multiplication in the frequency domain. Therefore, the Python code for the matched filter is straightforward:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">MatchedFilter</span><span class="p">(</span><span class="n">signal_data</span><span class="p">,</span> <span class="n">code_data</span><span class="p">):</span>
  <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">ifft</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fft</span><span class="p">(</span><span class="n">signal_data</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">conj</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fft</span><span class="p">(</span><span class="n">code_data</span><span class="p">)))</span>
</code></pre></div></div>

<p>Since we’re working on a well-known GNSS dataset, we can use external information about the signals present. I’ll use a GPS SV21, but any satellite can be used. With known Doppler offset this test performs an additional spectrum translation, upsamples the PRN code and calculates the matched filter output, which is then being normalized and plotted:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_8_1.png" alt="Matched filter output" /></p>

<h3 id="hardware-friendly-optimizations">Hardware-friendly optimizations</h3>

<p>It is worth pointing out that digital signal processing devices and GNSS receivers in particular rarely operate with floating-point values and usually work with integers.</p>

<blockquote>
  <p>:information_source: Interestingly, with my research on PC-based software-defined receivers, <code class="language-plaintext highlighter-rouge">fp32</code> digital signal processing routines significantly outperformed the integer-based ones, while both approaches were implemented with the Intel® Integrated Performance Primitives library. I’m looking forward to evaluating the performance and accuracy of the lower-resolution floating-point values (like <code class="language-plaintext highlighter-rouge">fp16</code>, <code class="language-plaintext highlighter-rouge">bfloat16</code> or even <code class="language-plaintext highlighter-rouge">fp8</code>) but without the optimized libraries and hardware support, this kind of research isn’t available yet.</p>
</blockquote>

<p>The most hardware-friendly approach for the integer-sine is a numerically controlled oscillator (NCO) with the corresponding phase-to-value sine table.</p>

<p>NCO may be viewed as an arbitrary precision unsigned integer with the full-scale treated as a period of the target function. NCOs have two main input parameters: phase accumulator value (phase) and an adder (relative frequency). The bit depth of the NCO is directly related to the possible frequency precision it’s able to achieve. NCOs are widely used in various digital signal processing systems for both receivers and transceivers.</p>

<p>To create a sine with an NCO it’s required to precalculate the lookup table that will convert the NCO phase into the amplitude of the target function. Usually, the number of these lookup table entries is much smaller than the resolution of the NCO. To address this issue and keep the frequency resolution of the NCO the index in the lookup table can be achieved as the higher $M$ bits of the $N$-bit phase accumulator.</p>

<p>Another thing worth pointing out: if your lookup table is filled with integers (scaled sine values) you may find that the bus width is not enough for several consecutive operations. For that matter, you might use a so-called normalizer block, which is a scale-down operation. To illustrate this: your input is an 8-bit and the lookup table is also using 8 bits to represent the sine. When you multiply the two numbers the worst-case scenario is that you’ll require 17 bits to store the result.</p>

<blockquote>
  <p>The rule of thumb: when you’re adding two integer numbers, the required bit depth will be increased by one from the max: <em>result_bit_depth = 1 + max(bit_depth_first, bit_depth_second)</em>.</p>

  <p>If you’re multiplying two integer numbers you’re facing the sum of the bit depths increased by one: result_bit_depth = <em>1 + bit_depth_first} * bit_depth_second</em>.</p>
</blockquote>

<p>This is an interesting and very well-reviewed topic, mainly targeted at the microelectronics and FPGA engineers, but if you think this chapter would benefit from a more detailed review please do let me know.</p>

<h2 id="resampler">Resampler</h2>

<p>Changing the data rate of the digital signal is a complex task with numerous approaches, each with its set of pros and cons. The preferred type may vary based on the subsequent processing stages, a priori information about the signal and limitations of the hardware platform. The upsampling routine may be described as a two step-process: inserting $N-1$ zeroes between the source samples with the (optional) follow-up low-pass filtering to suppress spectrum copies.
Filtering type is the main customization point of the upsampling methods. To name a few:</p>

<ol>
  <li>Lack of low-pass filtering. This will induce minimal signal distortion, but is only applicable if the subsequent stages are tolerant to the spectrum copies;</li>
  <li>Digital low-pass filtering with a cutoff frequency of $\frac{f_{sOld}}{2}$, to suppress the spectrum copies. Filter design is always a trade-off between suppression level, passband ripple and hardware complexity. However, this method provides one of the best results and is used internally in the resample function in MATLAB;</li>
  <li>Low-pass filtering in the frequency domain will achieve the best results in exchange for increased computational complexity. However, this would be a good fit for post-processing applications;</li>
  <li>CIC-based filters. This design was used quite a lot due to the very hardware-friendly architecture with no multiplications involved. However, a very specific roll-off of the frequency response should be noted, which is commonly being compensated with the following FIR-filter, which negates the hardware complexity profit.</li>
</ol>

<p>The easiest and most vectorized-friendly way to do insert zeros in Python and MATLAB environments is to resize the input vector:</p>

\[\begin{pmatrix} 
s_0 \\ s_1 \\ \vdots \\ s_{M-1} 
\end{pmatrix}\]

<p>into the matrix with zero-filled columns</p>

\[\begin{pmatrix} 
s_0 &amp; 0 &amp; \cdots &amp; 0 \\ s_1 &amp; 0 &amp; \cdots &amp; 0 \\ \vdots  &amp; \vdots &amp; \ddots &amp; \vdots \\ s_{M-1} &amp; 0 &amp; \cdots &amp; 0 
\end{pmatrix}\]

<p>For example, we’ll upsample by the factor of four. After that, to get the vector we need, we’ll reshape the matrix into the flattened vector</p>

\[\begin{pmatrix} 
s_0 &amp; 0 &amp; \cdots &amp; 0 &amp; s_1 &amp; 0 &amp; \cdots &amp; 0 &amp; \cdots &amp; &amp; s_{M-1} &amp; 0 &amp; \cdots &amp; 0 
\end{pmatrix}\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">upsampled_signal</span> <span class="o">=</span> <span class="n">translated_signal</span><span class="p">;</span>
<span class="n">upsampled_signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">c_</span><span class="p">[</span><span class="n">upsampled_signal</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">upsampled_signal</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">upsampled_signal</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">upsampled_signal</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">])]</span>
<span class="n">upsampled_signal</span> <span class="o">=</span> <span class="n">upsampled_signal</span><span class="p">.</span><span class="n">ravel</span><span class="p">()</span>
<span class="n">PlotSignal</span><span class="p">(</span><span class="n">upsampled_signal</span><span class="p">,</span> <span class="n">fs</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="s">'Upsampled signal'</span><span class="p">)</span>
</code></pre></div></div>

<p>It will produce a signal with the following spectral and temporal characteristics:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_10_0.png" alt="Upsampled signal" /></p>

<p>There are two main points in this signal:</p>

<ol>
  <li>The spectrum is periodical and repeated. This is due to the properties of the digital signal</li>
  <li>The histogram shows a lot of zeros because for every normally distributed sample we’ve inserted $N - 1$ zeros.</li>
</ol>

<p>Integer resampling is an expansion of the previous task with the downsampling follow-up. Downsampling may be viewed as an inverted upsampling: low-pass filtering with the following signal decimation (taking every $M$th sample). Downsampling filter types are the same with the only difference in cutoff frequency: $M\frac{f_{sOld}}{2}$.</p>

<h3 id="hardware-friendly-optimizations-1">Hardware-friendly optimizations</h3>

<p>One of the hardware-efficient downsampling methods I’ve been using a lot in GNSS signal processing is accumulation, in which every output sample is the (normalized) sum of $M$ samples of the input samples.</p>

<p>For the sake of vectorization, interpreter-based languages can perform accumulation as a pair of reshaping and column-wise sums. Similar to the upsampling, the input vector</p>

\[\begin{pmatrix} 
s_0 \\ s_1 \\ \vdots \\ s_{M-1} 
\end{pmatrix}\]

<p>is reshaped into the $(M / N, N)$ matrix</p>

\[\begin{pmatrix} 
s_0 &amp; s_1 &amp; \cdots &amp; s_{N-1} \\ s_N &amp; \vdots &amp; \ddots &amp; \vdots \\ \cdots &amp; \cdots &amp; \cdots &amp; s_{M-1} 
\end{pmatrix}\]

<p>and then being row-wise summed</p>

\[\begin{pmatrix} 
\sum_{i=0}^{N-1} s_i \\ 
\sum_{i=N}^{2N-1} s_i \\
\vdots \\
\end{pmatrix}\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fs_down</span> <span class="o">=</span> <span class="n">fs</span> <span class="o">/</span> <span class="mi">4</span>
<span class="n">time_down</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">translated_signal</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">/</span> <span class="mi">4</span><span class="p">)</span> <span class="o">/</span> <span class="n">fs_down</span>
<span class="n">reshaped</span> <span class="o">=</span> <span class="n">translated_signal</span><span class="p">.</span><span class="n">reshape</span><span class="p">((</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="n">accumulated</span> <span class="o">=</span> <span class="n">reshaped</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span>
<span class="n">PlotSignal</span><span class="p">(</span><span class="n">accumulated</span><span class="p">,</span> <span class="n">fs_down</span><span class="p">,</span> <span class="s">'Downsampled signal'</span><span class="p">)</span>
</code></pre></div></div>

<p>The point of accumulating is the implicit low-pass filtering with a $\frac{sin(x)}{x}$ frequency response, which is relatively inefficient from the high-frequency suppression point of view, but it’s one of the most easy-to-implement digital filters I’ve seen.</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_11_0.png" alt="Downsampled signal" /></p>

<h2 id="signal-packer">Signal packer</h2>

<p>The final operation in the digital frontend is to save the final group signal into the local memory in the format, suitable for the used correlator implementation. This is a hardware-dependent block and all the possibilities should be thoroughly benchmarked.</p>

<p>One of the things worth pointing out here is that since the digital frontend can be treated as a continuation of the traditional analog RF frontend, it’s possible to interpret the signal packer as some kind of the ADC. For example, if there’s no interference present (or mitigated, more on that in <a href="/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation">Part 2: Jamming mitigation</a>), the resulting signal can be stored with low precision: 1 or 2 bits, like the ADCs, used widely in GNSS frontends.</p>

<p>Another benefit of such low-precision signal storage is the possibility to implement the correlator block to operate on such packed data. It will allow to increase the throughput of the block and reduce the required silicon area.</p>

<p>Curiously, during my experiments with PC-based digital signal processing, the most performant versions turned out to be the <code class="language-plaintext highlighter-rouge">float</code>-based ones. This can be explained simply by the fact that with <code class="language-plaintext highlighter-rouge">float</code>s we’re only required to perform the operations we intend to: multiply, add, accumulate etc. On the other hand, if we’re dealing with integers (or even packed integers) the number of operations and memory accesses is increased because now it’s necessary to unpack the data, scale the result and pack it back.</p>

<h2 id="summary">Summary</h2>

<p>This chapter has provided a brief overview and explanation of the digital frontend block for the software-defined GNSS receivers. It, by no means, is a necessary part of the receiver, but it provides enough flexibility for the system engineers to design the receiver tailored for the specific market and/or applications.</p>

<p>Using the digital frontend in the GNSS receiver design allows decoupling of the synchronous digital processing, performed at the ADC sampling rate, from the asynchronous correlation processing. This, in turn, helps to design a more flexible signal data flow with additional filtering and resampling.</p>

<p>Another essential part of the digital frontend is the jamming detection and mitigation block, but I’ve decided to dedicate a standalone chapter to it since the topic is very deep.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="GNSS" /><summary type="html"><![CDATA[]]></summary></entry><entry xml:lang="en"><title type="html">Global Navigation Satellite Systems Software-Defined Receivers explained. Part 2: Jamming mitigation</title><link href="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation/" rel="alternate" type="text/html" title="Global Navigation Satellite Systems Software-Defined Receivers explained. Part 2: Jamming mitigation" /><published>2022-06-20T00:00:00+00:00</published><updated>2022-06-20T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation</id><content type="html" xml:base="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation/"><![CDATA[<p>GNSS jamming is something I’m very familiar with and with what I’ve been involved in for most of my career. My specialist’s thesis (6-year degree, Russian alternative to bachelor and master combined) was about real-time narrowband interference mitigation for GNSS receivers with FIR-filters, I wrote several papers about that and, eventually, I became a scientific supervisor of the big antijamming and antispoofing project. In that project, I and my team have investigated various approaches for spatial (digital CRPA) and non-spatial interference and spoofing mitigation. Then we implemented them with MATLAB models and built some test benches with real hardware to test the models and algorithms with real-world data. And we’ve even designed and implemented antispoofing and CRPA antijamming receivers as working prototypes, operating in real-time! As you already guessed, this is the topic I’m very comfortable with and could talk about for hours, but I’ll try to keep it brief and concentrated.</p>

<ul>
  <li><a href="/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design">Part 0: Hardware and system design</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-1-digital-frontend">Part 1: Digital frontend</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation">Part 2: Jamming mitigation</a>
    <ul>
      <li><a href="#overview-and-effects-of-the-interference">Overview and effects of the interference</a></li>
      <li><a href="#narrowband-interference">Narrowband interference</a>
        <ul>
          <li><a href="#digital-filters-approach">Digital filters approach</a></li>
          <li><a href="#frequency-domain-approach">Frequency domain approach</a></li>
        </ul>
      </li>
      <li><a href="#wideband-interference">Wideband interference</a>
        <ul>
          <li><a href="#digital-filters-approach-1">Digital filters approach</a></li>
          <li><a href="#frequency-domain-approach-1">Frequency domain approach</a></li>
        </ul>
      </li>
      <li><a href="#space-based-approaches">Space-based approaches</a></li>
      <li><a href="#a-note-about-spoofing">A note about spoofing</a></li>
      <li><a href="#summary">Summary</a></li>
    </ul>
  </li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-3-acquisition">Part 3: Acquisition</a></li>
  <li>Part 4: Correlation and tracking</li>
  <li>Part 5: Standalone positioning</li>
  <li>Part 6: Advanced positioning methods</li>
</ul>

<h2 id="overview-and-effects-of-the-interference">Overview and effects of the interference</h2>

<p>Jamming is the term used for a radio-frequency interference of the GNSS signal, and it can be unintentional (like the DME landing systems for the L3/L5 subband) or harmful or intentional (jammers and other “personal privacy devices”). The goal of jamming is pretty simple: to disable the GNSS receivers in some areas. We’ll omit the reasons for this and just get to the effects and countermeasures.</p>

<p>The receiver, affected by jamming, will provide a deteriorated solution or even no solution at all. The presence of jamming leads to a decrease in signal-to-noise ratio (SNR) up to the point where the signal acquisition and, then, the tracking are impossible. For simplicity, we’ll assume that before the jamming mitigation the signal path is linear and the jammer signal is not distorted. For real-world applications, you’d need to look at the 1 dB compression point parameter (P1dB) of the RF frontend and the number of bits of the ADC. For example, if the narrowband tone interference is being distorted (in the frontend or by using the 2-bit ADC) it’ll turn into the multitone. It’s still possible to mitigate, but at the cost of an additional SNR.</p>

<p>To illustrate this let’s add a tone interference to the signal we’re working on:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">interference_offset</span> <span class="o">=</span> <span class="mf">1.023e6</span>
<span class="n">interference_level</span> <span class="o">=</span> <span class="mi">25</span>
<span class="n">signal_with_interference</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">ceil</span><span class="p">(</span><span class="n">signal_data</span> <span class="o">+</span> <span class="n">interference_level</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">cos</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">time</span>  <span class="o">*</span> <span class="p">(</span><span class="n">intermediate_frequency</span> <span class="o">+</span> <span class="n">interference_offset</span><span class="p">)))</span>

<span class="n">PlotSignal</span><span class="p">(</span><span class="n">signal_with_interference</span><span class="p">.</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">int8</span><span class="p">),</span> <span class="n">fs</span><span class="p">,</span> <span class="s">'Signal with narrowband interference'</span><span class="p">)</span>
</code></pre></div></div>

<p>In the following figure, we can see the main interference tone and some harmonics due to the quantization error (<code class="language-plaintext highlighter-rouge">ceil</code> + cast to <code class="language-plaintext highlighter-rouge">int8</code> for the following clipping demonstration). Another interesting point here is in the histogram. Usually, in the lack of interference, the signal histogram is bell-shaped due to the normal distribution of the input signal, which is caused by the satellite signal being buried ~20 dB beneath the noise. However, if we add the narrowband interference, it’ll shift to the ramp shape, since the harmonic signals “spend” most of their time in their upper and lowest values.</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_13_0.png" alt="Ideal frequency response" /></p>

<p>When the GNSS receiver uses low-bit ADCs, they’re clipping the input signal. For example, let’s demonstrate the simplest case of the 1-bit ADC, which is effectively a comparator. When you clip the sine into two levels it becomes a square wave. As you may know, the spectrum of the square wave is full of harmonics with a $\frac{sin(f)}{f}$ envelope. This is exactly what we see in the figure below:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_15_0.png" alt="Clipped signal with interference" /></p>

<h2 id="narrowband-interference">Narrowband interference</h2>

<p>The narrowband (single-tone and multitone) interference is a common thing to spot in the GNSS receivers. Since the signals are weak, it’s easy to pick up some spurs from both external and internal equipment. There are two approaches to interference mitigation, based on the domain the filtering is performed: temporal and frequency. The former is more suitable for hardware-oriented approaches or systems with very limited resources, while the latter is more software-friendly and provides better results.</p>

<h3 id="digital-filters-approach">Digital filters approach</h3>

<p>There are two kinds of digital filters: finite and infinite impulse response (FIR and IIR correspondingly). The main difference between them is the latter being recursive with the possibility of being unstable. Another downside to the IIR-filters is that there’s no way to design a filter with a linear phase. However, there’s a significant advantage: it’s possible to design the IIR-filter with the same attenuation characteristics of a significant less order, therefore, less silicon area.</p>

<p>For GNSS jamming mitigation it’s reasonable to go with the FIR-filters because it’s possible to design a filter with a linear phase response. During my research, I’ve found a very elegant and computationally-efficient design method I’d like to share: the frequency response sampling method. To demonstrate this algorithm I’ll use two additional tones at -1.023 MHz and 2.046 MHz on top of the translated signal:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_16_0.png" alt="Complex signal with two tones" /></p>

<p>If we use such a signal as-is with the matched filter we won’t be able to distinguish the signal from the noise:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_16_1.png" alt="Matched filter output with interference" /></p>

<p>The idea and the algorithm are simple: create an ideal frequency response with zeros for frequencies to suppress and ones otherwise:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_17_0.png" alt="Ideal frequency response" /></p>

<p>The second step is to apply the phase multiplier to create a linear phase response. The preliminary impulse response is obtained by the inverse Fourier transform, but such a system is susceptible to the Gibbs phenomenon, resulting in a massive suppression performance degradation. As a final step, to mitigate this effect, a window function is applied.</p>

<p>All those steps are implemented in the <code class="language-plaintext highlighter-rouge">GetIr</code> function, which is in the composed Jupyther Notebook. The resulting impulse response with a linear phase and nulls at the frequency locations of the interference looks like this:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_17_1.png" alt="Complex impulse response" /></p>

<p>Bear in mind, that since the input signal (and interference) is complex, we’d need a complex frequency response to mitigate it and, therefore, a complex impulse response. After the complex FIR processing, we can observe the mitigated interference:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_18_0.png" alt="FIR result" /></p>

<p>The temporal processing will allow to suppress the narrowband interference and locate the satellite signals:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_18_1.png" alt="Matched filter output with interference mitigated" /></p>

<h3 id="frequency-domain-approach">Frequency domain approach</h3>

<p>The frequency-domain approach is more demanding in terms of memory but provides much better results. The main idea behind it is to detect the interference, zero out the corresponding frequency components of the input signals Fourier transform and then perform an inverse transform to get a signal for the following signal processing.</p>

<p>Another benefit of frequency-based processing is the lack of filter-induced group delay. This is just a constant for a traditional linear-phase FIR-filter, but it’s another thing to keep in mind and pass to the observable block and, therefore, a potential source of errors.</p>

<p>For the same signal and interference mix we’ve used in the temporal approach demonstration, we may observe the superiority of the frequency domain-based approach:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_19_0.png" alt="FFT results" /></p>

<p>The matched filter will again prove that we’re suppressing the interference well and the signal can be found:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_19_1.png" alt="Matched filter after the FFT" /></p>

<h2 id="wideband-interference">Wideband interference</h2>

<p>Unlike the narrowband interference, the wideband is covering the whole frequency band of the GNSS signals. One of the most common and easy-to-assemble devices produces a chirp signal. A chirp or sweep signal is a kind of signal in which the frequency is varying over time. The most common is the linear frequency sweep interference, but there’s an interesting ‘tick’ swept frequency signal worth investigating (more information in <a href="https://www.mdpi.com/1424-8220/19/6/1276/pdf">Impact Analysis of Standardized GNSS Receiver Testing against Real-World Interferences Detected at Live Monitoring Sites</a> by NSL). There are also some debates going on in academia regarding the pseudo-white noise interference, but I’ve never seen it implemented in the real world due to the heavy practical limitations like the crest factor.</p>

<p>Usually, wideband jammer signals would look like this with an additional (not displayed here) shape of the RF filter in the antenna:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_21_0.png" alt="FFT results" /></p>

<p>As you can see in the figure above, a regular Fourier transform will render all the efforts useless, since it’s impossible to separate the jammer signal from the satellite signal.</p>

<p>To get around this limitation we can use an algorithm called Short-Time Fourier Transform (<em>STFT</em>) and plot a time-frequency spectrum dependency called a periodogram. It splits the input signal into overlapping segments and calculates the Fourier transform for each of them independently. The length of the segments is selected to match the time resolution, or, simply put, the portion of the signal where the frequency of the interference is relatively constant.</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_22_0.png" alt="STFT results" /></p>

<p>This kind of jammer is very efficient, as you can see in the matched filter output:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_22_1.png" alt="Matched filter with chirp" /></p>

<h3 id="digital-filters-approach-1">Digital filters approach</h3>

<p>The approach I’ve tested numerous times is described in the <a href="https://www.iis.fraunhofer.de/content/dam/iis/de/doc/lv/los/lokalisierung/SatNAV/IONGNSS2017_ChirpMitigation.pdf">Chirp Mitigation for Wideband GNSS Signals with Filter Bank Pulse Blanking</a> paper. It splits the input signal into the $N$ non-overlapping subbands with digital filters followed by the blanking device. The blanker compares the magnitude of the filtered signal with some threshold and, if it’s exceeding the threshold, zeros the signal. Afterwards, the subbands are summed to reconstruct the whole signal.</p>

<p>For example, to synthesize the filter bank for this approach we can use the following Python code. This is by no means optimal and should be reviewed and investigated much further for the production-grade receiver, but it works for demonstration purposes.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bandwidth</span> <span class="o">=</span> <span class="mf">0.5e6</span>
<span class="n">frequency_start</span> <span class="o">=</span> <span class="mf">7.5e6</span>
<span class="n">frequency_stop</span> <span class="o">=</span> <span class="mf">11.5e6</span>

<span class="n">frequency_bands</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">current_start</span> <span class="o">=</span> <span class="n">frequency_start</span>
<span class="k">while</span> <span class="n">current_start</span> <span class="o">&lt;</span> <span class="n">frequency_stop</span><span class="p">:</span>
  <span class="n">frequency_bands</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="n">current_start</span><span class="p">,</span> <span class="n">current_start</span> <span class="o">+</span> <span class="n">bandwidth</span><span class="p">])</span>
  <span class="n">current_start</span> <span class="o">+=</span> <span class="n">bandwidth</span>

<span class="n">filter_bank</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">current_range</span> <span class="ow">in</span> <span class="n">frequency_bands</span><span class="p">:</span>
  <span class="n">filter_bank</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">signal</span><span class="p">.</span><span class="n">firwin</span><span class="p">(</span><span class="mi">512</span><span class="p">,</span> <span class="n">current_range</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">window</span><span class="o">=</span><span class="s">'hamming'</span><span class="p">,</span> <span class="n">pass_zero</span><span class="o">=</span><span class="s">'bandpass'</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">nyq</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">fs</span><span class="o">=</span><span class="n">fs</span><span class="p">))</span>
</code></pre></div></div>

<p>This code will generate the filters with the following frequency responses:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_24_0.png" alt="Filter bank" /></p>

<p>Modern chirp jammers have a sweep period of around 20 to 100μs and the described method effectively blanks each subband multiple times during each accumulation period.</p>

<p>Using this approach we can achieve this kind of mitigation:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_25_0.png" alt="Wideband mitigation" /></p>

<p>Even though the cleared spectrum doesn’t look very similar to the input one (before jamming), the satellite signal beneath the noise floor is still there, and can be acquired and tracked:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_25_1.png" alt="Wideband mitigation matched filter" /></p>

<h3 id="frequency-domain-approach-1">Frequency domain approach</h3>

<p>As I’ve mentioned earlier, it’s nearly impossible to distinguish between the jammer and satellite signals with the regular Fourier transform, which is why we’re using the STFT for the signals with wideband interference. The segments of the STFT are divided in a manner to keep the jammer frequency relatively constant. With that in mind, the interference is mitigated the same way as in the narrowband case.</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_27_0.png" alt="Wideband mitigation" /></p>

<p>Like the temporal pulse blanking approach, the signal is detectable after the jamming mitigation:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_27_1.png" alt="Wideband mitigation matched filter" /></p>

<h2 id="space-based-approaches">Space-based approaches</h2>

<p>The most advanced jamming mitigation algorithms use space-time processing via the special antennas called antenna arrays. It is possible to manipulate levels and phases from each individual antenna element to modify the equivalent antenna radiation shape to form nulls and focused beams in the combined signal.</p>

<p>Unfortunately, since this approach requires multiple antennas and multiple synchronized RF frontends, it drastically increases the device cost and is rarely used for the traditional (civil) GNSS receivers.</p>

<p>The main idea behind the space-time GNSS processing is to fix the signal from the reference antenna element and to manipulate the levels and phases of the other elements to minimize the output power. Since the GNSS signal is well below the noise floor, everything exceeding it is considered to be an interference and should be mitigated.</p>

<p>Since there are much fewer openly available datasets from antenna arrays, I’ll leave this chapter without examples, possibly to return and revise in the future.</p>

<h2 id="a-note-about-spoofing">A note about spoofing</h2>

<p>It is a quite controversial topic whether to treat spoofing as interference or not. In my personal opinion, since the spoofing detection and mitigation approaches are vastly different from the antijamming algorithms, they should be discussed separately.</p>

<p>There are two ways to counter the spoofing threat:</p>

<ol>
  <li>Antenna-based via space-time or space-polarization processing. The latter may require a special antenna and is being investigated by <a href="https://www.septentrio.com/en/company/technical-papers/authentication-polarization-powerful-anti-spoofing-method">Septentrio</a>.</li>
  <li>Individual satellite signal processing with multi-decision acquisition parallel tracking and decoding. With that approach, the receiver will track all the copies of the signal simultaneously followed by duplicate rejection and grouping or clustering. After that, the cluster with the higher confidence is selected as a PVT candidate.</li>
</ol>

<p>This is a deep and interesting topic like spatial processing, I intend to get back to it in the future.</p>

<h2 id="summary">Summary</h2>

<p>GNSS signal jamming is a major threat in the current world. Thankfully, some algorithms and countermeasures allow for preserving the resilient PNT even in case of major interference.</p>

<p>We’ve inspected temporal and frequency-based approaches to mitigate narrowband and wideband interference, well suited for both traditional and software-defined receivers.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="GNSS" /><summary type="html"><![CDATA[GNSS jamming is something I’m very familiar with and with what I’ve been involved in for most of my career. My specialist’s thesis (6-year degree, Russian alternative to bachelor and master combined) was about real-time narrowband interference mitigation for GNSS receivers with FIR-filters, I wrote several papers about that and, eventually, I became a scientific supervisor of the big antijamming and antispoofing project. In that project, I and my team have investigated various approaches for spatial (digital CRPA) and non-spatial interference and spoofing mitigation. Then we implemented them with MATLAB models and built some test benches with real hardware to test the models and algorithms with real-world data. And we’ve even designed and implemented antispoofing and CRPA antijamming receivers as working prototypes, operating in real-time! As you already guessed, this is the topic I’m very comfortable with and could talk about for hours, but I’ll try to keep it brief and concentrated.]]></summary></entry><entry xml:lang="en"><title type="html">Global Navigation Satellite Systems Software-Defined Receivers explained. Part 3: Acquisition</title><link href="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-3-acquisition/" rel="alternate" type="text/html" title="Global Navigation Satellite Systems Software-Defined Receivers explained. Part 3: Acquisition" /><published>2022-06-20T00:00:00+00:00</published><updated>2022-06-20T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-3-acquisition</id><content type="html" xml:base="https://mklimenko.github.io/english/2022/06/20/gnss-sdr-part-3-acquisition/"><![CDATA[<p>Signal acquisition is a process of a rough estimation of partial code delay and pseudo-Doppler frequency to provide a bootstrap for the tracking loops. In the modern software-defined receivers there’s a designated hardware/software block called the Fast Search Engine (<em>FSE</em>) that is used to speed up the initialization of the receiver in the case of a cold start.</p>

<ul>
  <li><a href="/english/2022/06/20/gnss-sdr-part-0-hardware-and-system-design">Part 0: Hardware and system design</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-1-digital-frontend">Part 1: Digital frontend</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-2-jamming-mitigation">Part 2: Jamming mitigation</a></li>
  <li><a href="/english/2022/06/20/gnss-sdr-part-3-acquisition">Part 3: Acquisition</a>
    <ul>
      <li><a href="#overview">Overview</a>
        <ul>
          <li><a href="#a-note-about-the-pseudo--in--the-doppler-frequency-offset">A note about the <em>pseudo-</em> in  the Doppler frequency offset</a></li>
        </ul>
      </li>
      <li><a href="#fast-search-engine">Fast Search Engine</a>
        <ul>
          <li><a href="#accumulation-in-the-acquisition">Accumulation in the acquisition</a></li>
          <li><a href="#matrix-oriented-acquisition">Matrix-oriented acquisition</a></li>
        </ul>
      </li>
      <li><a href="#fine-acquisition-and-bit-border-detection">Fine acquisition and bit border detection</a></li>
      <li><a href="#summary">Summary</a></li>
    </ul>
  </li>
  <li>Part 4: Correlation and tracking</li>
  <li>Part 5: Standalone positioning</li>
  <li>Part 6: Advanced positioning methods</li>
</ul>

<h2 id="overview">Overview</h2>

<p>Before we start with acquisition let’s revisit the model of the signal, simplified for one satellite in one frequency band without the multipath effect:</p>

\[s(t) = AC(t - \tau)D(t - \tau)e^{j2\pi (f + f_D) t + \phi_0} + n(t)\]

<p>In that model $A$ represents the signal power, $\tau$ is the total code delay, $C(t - \tau)$ is the delayed ranging code, $D(t - \tau)$ is the delayed navigation data message, $f$ is the carrier frequency of the satellite, $f_D$ is the pseudo-Doppler frequency offset (more on the <em>pseudo-</em> part later), $\phi_0$ is the phase delay and the $n(t)$ is the additive noise of the receiver.</p>

<p>The goals of the acquisition engine are:</p>

<ol>
  <li>Determine the partial code delay by estimating the ranging code $C(t - \tau)$ phase ($\tau\  \% {\ code\ duration}$)</li>
  <li>Evaluate the $f_D$ pseudo-Doppler frequency of the signal</li>
  <li>(Optional) Estimate the $D(t - \tau)$ navigation message partial phase (bit switch event)</li>
</ol>

<h3 id="a-note-about-the-pseudo--in--the-doppler-frequency-offset">A note about the <em>pseudo-</em> in  the Doppler frequency offset</h3>

<p>As you’re aware, the Doppler effect is the change of the signal frequency proportional to the radial velocity between the emitter and the observer. The radial velocity occurs when those two objects either approach or move away from each other.</p>

<blockquote>
  <p>I’m sorry for an offtopic, but this explanation is just hilarious:</p>

  <p><img src="/assets/img/gnss-sdr/tbbt.gif" alt="TBBT" /></p>
</blockquote>

<p>The pure (so to speak) Doppler effect is observed when the observer is producing the initial signal as well with the same reference oscillator, for example, a traditional passive radar.</p>

<p>On the other hand, if the reference oscillators of the emitter and the observer aren’t the same, an additional frequency offset may be observed. To illustrate this, let’s imagine that a GPS satellite is in the zenith (directly above) and a (simplified) stationary receiver with a reference oscillator with a nominal frequency $f_{ref}$ of 10 MHz. However, due to the temperature and ageing, the frequency is slightly off, let’s say 9.99 MHz.</p>

<p>A simple single-conversion IQ RF frontend would use some kind of PLL to multiply the reference frequency $f_{ref}$ to become $f_{LO}$ of 1575.42 MHz. For the sake of simplicity, let’s assume a fractional-N PLL with a coefficient of 1575.42. If we multiply the “real” $f_{ref}$ with that coefficient we’ll get roughly 1573.84 MHz.</p>

<p>So simply because of the single heterodyne, we’ll get more than 1.57 MHz of an additional frequency shift, and we haven’t even started with the sampling effects of the ADC with the mismatched clock.</p>

<p>Thankfully, modern GNSS receivers use Temperature Compensated Crystal Oscillators (<em>TCXO</em>) as a standard option with good stability and accuracy for a reasonable price and the <em>pseudo-</em> part of the Doppler offset is kept within the tens and hundreds of Hz, but that’s something worth remembering.</p>

<p>It’s also worth pointing out that there are devices called GNSSDOs (GNSS-disciplined oscillators), that estimate the offset of the reference oscillator by an additional step in the positioning phase called the speed equation: by using the same maths on pseudo-Doppler shifts instead of the pseudoranges it’s possible to estimate the speed of the receiver and the clock drift (with $\frac{seconds}{seconds}$ dimension) instead of the coordinates and a clock shift with the traditional navigation equation.</p>

<p>The whole timing receivers and time scales is a deep and interesting topic, I highly recommend the <a href="https://urss.ru/cgi-bin/db.pl?lang=Ru&amp;blang=ru&amp;page=Book&amp;id=243732">Global synchronization and near-Earth movement control satellite systems</a> (unfortunately, only in Russian) book by <a href="https://insidegnss.com/alexander-povalyaev/">Prof. Povalyaev</a>. It’s quite difficult to understand, but really worth it.</p>

<h2 id="fast-search-engine">Fast Search Engine</h2>

<p>As I’ve mentioned earlier, the acquisition engine is used to determine the partial delay and the pseudo-Doppler frequency offset. Therefore, the acquisition process may be treated as a 2-dimensional maximum search with a certain threshold. There are three customization points:</p>

<ol>
  <li>Search range: usually, the whole code period for the delay axis and ±5 kHz for the pseudo-Doppler offset. The frequency range should be selected for the assumed receiver dynamics and the accuracy of the onboard reference oscillator</li>
  <li>Search step: determines the granularity and is defined by the initialization bandwidth of the (following) tracking loops on one hand and timing constraints on the other.</li>
  <li>Accumulation quantity and type: how many milliseconds we accumulate and how exactly the accumulation is performed (coherent vs non-coherent)</li>
</ol>

<p>The acquisition process in GNSS receivers is operating in the so-called soft real-time mode.</p>

<blockquote>
  <p>A quick detour to explain the real-time operations and the difference between the modes:</p>

  <p>The real-time is all about constraints and, technically speaking, isn’t about fast software. Depending on what are the effects of missing the processing deadline, three types of real-time modes can be highlighted:</p>

  <ol>
    <li>Non-real-time: this is the most widespread type of operation when no timeframe is specified.</li>
    <li>Soft real-time provides a timeframe for a response, but if the system misses the deadline user will observe a temporary degradation, following restoration. One of the great examples is a video player: if for some reason, the video frame is missed, the player will produce output with some artefacts, but the movie will continue to play.</li>
    <li>Hard real-time has a more strict timeframe, if you miss it the whole system is rendered useless. Therefore, hard real-time systems preferably run either bare-metal or with a thinnest scheduler-type pseudo-OS.</li>
  </ol>
</blockquote>

<p>The software-friendly acquisition engine saves the signal samples synchronously upon receiving an acquisition request, followed by an asynchronous process of 2-dimensional search. The first step is to reduce the data rate to lower the number of required calculations. It is reasonable to reduce the sampling to the minimal feasible ($f_S= 2f_{code}$) due to the usage of the code tracking loops.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fs_acquisition</span> <span class="o">=</span> <span class="mf">2.046e6</span>
<span class="n">samples_per_ms</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">fs_acquisition</span> <span class="o">/</span> <span class="mf">1e3</span><span class="p">)</span>
<span class="n">ms_to_process</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">acquisition_signal</span> <span class="o">=</span> <span class="n">signal</span><span class="p">.</span><span class="n">resample</span><span class="p">(</span><span class="n">translated_signal</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nb">int</span><span class="p">(</span><span class="n">ms_to_process</span> <span class="o">*</span> <span class="n">fs</span> <span class="o">/</span> <span class="mf">1e3</span><span class="p">)],</span> <span class="nb">int</span><span class="p">(</span><span class="n">ms_to_process</span> <span class="o">*</span> <span class="n">samples_per_ms</span><span class="p">))</span>
</code></pre></div></div>

<p>The next step is to slice the 2-dimensional window of search into the consecutive iterations with fixed Doppler offset and run the matched filter calculations on the signal. The next step is the accumulation, finding the maximum (peak value and location) and comparing it with a threshold.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">doppler</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">7000</span><span class="p">,</span> <span class="mi">7000</span><span class="p">,</span> <span class="mi">50</span><span class="p">):</span>
  <span class="n">sine</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="mf">1j</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">doppler</span> <span class="o">*</span> <span class="n">time_acquisition</span><span class="p">)</span>
  <span class="n">translated</span> <span class="o">=</span> <span class="p">(</span><span class="n">acquisition_signal</span> <span class="o">*</span> <span class="n">sine</span><span class="p">)</span>
  <span class="n">matched_output</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">ifft</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fft</span><span class="p">(</span><span class="n">translated</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">conj</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fft</span><span class="p">(</span><span class="n">code</span><span class="p">)))</span>
  <span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">matched_output</span><span class="p">.</span><span class="n">reshape</span><span class="p">((</span><span class="n">ms_to_process</span><span class="p">,</span> <span class="n">samples_per_ms</span><span class="p">))</span>
  <span class="k">if</span> <span class="n">use_coherent_acquisition</span><span class="p">:</span>
    <span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">matched_output_ms</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">matched_output_ms</span><span class="p">).</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

  <span class="n">current_peak_value</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">matched_output_ms</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">current_peak_value</span> <span class="o">&gt;</span> <span class="n">max_peak_value</span><span class="p">:</span>
    <span class="n">max_peak_value</span> <span class="o">=</span> <span class="n">current_peak_value</span>
</code></pre></div></div>

<p>The result of the acquisition of all the GPS satellites can be illustrated like this:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_29_1.png" alt="Acquisition result" /></p>

<h3 id="accumulation-in-the-acquisition">Accumulation in the acquisition</h3>

<p>One of the important questions in the acquisition is accumulation. To increase the detection probability in case of the navigation message sign change or shadowing it’s common to operate on several consecutive milliseconds followed by accumulation. There are two approaches for accumulation: coherent and non-coherent.</p>

<p>In the acquisition routine we don’t care about the carrier phase the difference between the two types of accumulation is the order of the magnitude and sum operations:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">matched_output</span><span class="p">.</span><span class="n">reshape</span><span class="p">((</span><span class="n">ms_to_process</span><span class="p">,</span> <span class="n">samples_per_ms</span><span class="p">))</span>
<span class="k">if</span> <span class="n">use_coherent_acquisition</span><span class="p">:</span>
  <span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">matched_output_ms</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
  <span class="n">matched_output_ms</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">matched_output_ms</span><span class="p">).</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>The coherent accumulation usually results in a more “sharp” 2-dimensional ambiguity body but has a major downside being prone to the navigational message sign bit change. For example, if working with 4 milliseconds and the first two are positive and the last two are negative, resulting in a massively decreased signal-to-noise ratio.</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_31_0.png" alt="Acquisition result" /></p>

<p>This is also the case for the relatively high-speed data transfer signals, like the SBAS L1 or the signals with overlay codes like the BeiDou B1I.</p>

<h3 id="matrix-oriented-acquisition">Matrix-oriented acquisition</h3>

<p>The sliced approach, described above, is often used in memory-limited devices like ASICs or FPGAs. However, for PC-based receivers (both CPU- and GPU-based) a matrix-based algorithm can be used. It benefits from the pre-existing highly optimized matrix multiplication libraries and massively parallel architectures at the cost of higher memory consumption.</p>

<p>To illustrate this with an example we’ll use the following data:</p>

<ol>
  <li>$N$ milliseconds of data at $f_S$ sampling rate, resulting in a $(\frac{N*f_S}{10^{-3}}, 1)$ vector, which we’ll denote as a $(n, 1)$ vector</li>
  <li>Vector of Doppler offset frequencies within the range of $\pm F_D$ Hz and a step of $\Delta f_D$ Hz, resulting in a $(1, \frac{2F_D + 1}{\Delta f_D})$ vector, which we’ll denote as a $(1, p)$ vector</li>
</ol>

<p>To match the dimensions we’ll need to prepare a translation matrix of size $(n, p)$ by calculating the complex exponent of frequencies over the $N$ milliseconds (see the translation chapter).</p>

<p>To translate the input signal we’ll perform an elementwise multiplication of the input signal and the translation matrix, resulting in a matrix of the same size $(n, p)$.</p>

<p>The next step is to acquire the Fourier transform of those matrices, but keep in mind, that this would be the traditional 1-dimensional DFT and not the 2-dimensional DFT. This means that we need only the column-wise transform without the follow-up row-wise calculations. Ths upside here is that it can be paralleled to utilize the multicore architecture.</p>

<p>According to the convolution theorem, to get the result we’ll elementwise multiply the input matrix and the complex conjugated input code, followed by the inverse Fourier transform. Accumulation and maximum search is performed as in the usual acquisition engine, but with fewer temporary variables and better memory locality.</p>

<h2 id="fine-acquisition-and-bit-border-detection">Fine acquisition and bit border detection</h2>

<p>One of the nice little acquisition tricks up my sleeve I’ve learned during my university years is the combined fine acquisition and bit border detection. Usually, when there’s an acquisition candidate, the first tracking step is the frequency locked loop (FLL) for some time followed by a phase-locked loop (PLL). However, this approach may take some time to obtain stable tracking, up to several seconds.</p>

<p>The idea is that for each successful acquisition we run the correlator as-is, without any tracking attempts, for 32 milliseconds to see something like this:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_35_0.png" alt="Fine acquisition start" /></p>

<p>In the lower-left, you can see the correlator outputs and in the lower right the FFT output. As you can see, there are two low peaks in the spectrum of the correlator outputs. The fact that there are a finite number of bit shift combinations (I’ll leave it up to you to write them all down if you’d decide to implement this yourself) allows to test multiple hypotheses about the bit shift location and correct the Doppler frequency ambiguity to no more than a 32.5 Hz ($\frac{1000Hz}{32}$).</p>

<p>For example, with one of the combinations we can turn the sign-changing sine to the smooth one, fixing the 200 Hz Doppler offset and finding the sign change at the 8th millisecond:</p>

<p><img src="/assets/img/gnss-sdr/gnss_sdr_illustrations_36_0.png" alt="Fine acquisition result" /></p>

<h2 id="summary">Summary</h2>

<p>Signal acquisition is an essential part of every GNSS receiver. The basic algorithm is the same, but the details are system- and signal-dependent. The traditional approach for multi-band receivers is to acquire whichever signal’s easier for you and use this data to bootstrap the tracking in the other frequency bands.</p>

<p>The main challenge in the acquisition algorithms, in my opinion, is overcoming the memory and computational limits and implementing everything accurately because acquisition sensitivity is usually much lower than the tracking, therefore errors and mismatches here and there may lead to even further degradation.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="GNSS" /><summary type="html"><![CDATA[Signal acquisition is a process of a rough estimation of partial code delay and pseudo-Doppler frequency to provide a bootstrap for the tracking loops. In the modern software-defined receivers there’s a designated hardware/software block called the Fast Search Engine (FSE) that is used to speed up the initialization of the receiver in the case of a cold start.]]></summary></entry><entry xml:lang="en"><title type="html">FIR-based electric guitar cabinet simulation explained</title><link href="https://mklimenko.github.io/english/2022/05/10/ir-guitar-cabinet/" rel="alternate" type="text/html" title="FIR-based electric guitar cabinet simulation explained" /><published>2022-05-10T00:00:00+00:00</published><updated>2022-05-10T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2022/05/10/ir-guitar-cabinet</id><content type="html" xml:base="https://mklimenko.github.io/english/2022/05/10/ir-guitar-cabinet/"><![CDATA[<p>It is extremely useful to have a little vacation every once in a while, to distract yourself from your day-to-day basis. You may find yourself thinking about various tasks you were too busy to give some serious time to. One of such tasks I was intrigued about is the guitar cabinet simulation via the impulse responses of the real cabinet-microphone pair.</p>

<h2 id="introduction">Introduction</h2>

<p>Let’s analyze the traditional electric guitar signal chain. We have a nice looking strat, some guitar cable, effects (missing here, as well as the effects loop), amplifier and the cabinet. Every part of this chain is important. The guitar (with your help, of course) produces the music, which is then transferred by the guitar cable to the amplifier.</p>

<p>Electric guitar amplifiers are different species than the HiFi ones. This is because the main purpose of the amplifier is not to make the signal louder, but to distort it in a way, that’s pleasant to the human ear. HiFi ones, on the contrary, are designed to be as transparent as possible. At the end of the signal chain, there is a cabinet, which is, mainly, a speaker in a box. This is what we’ll discuss further in this article.</p>

<blockquote>
  <p>One part of the signal chain that is being overlooked more often, than it should be, is the guitar cable. An important characteristic of the cable is its capacitance. If the cable is long enough, it acts as a low-pass filter (RC-circuit) and cuts some of the high frequencies. A friend of mine once told me, that he almost sold his guitar due to the “muddy” sound. What a relief it was for him when he swapped the cable for a good one. Bear in mind, that for active pickups the influence of the cable is almost neglectable.</p>
</blockquote>

<p><img src="/assets/img/fir-guitar-cabinet/schematic.png" alt="Traditional signal chain" /></p>

<h2 id="guitar-cabinets">Guitar cabinets</h2>

<p>First of all, let’s grey out the parts of the scheme, that we’re not currently interested in, and only leave the cabinet highlighted. The cabinet is used for the:</p>

<ol>
  <li>Conversion of the electric signal to the sound waves (sound);</li>
  <li>Low-pass filtering of the signal.</li>
</ol>

<p><img src="/assets/img/fir-guitar-cabinet/cabinet_greyed.png" alt="Part of interest" /></p>

<p>Amplified (and distorted) guitar signal has a lot of harmonics, especially in the high frequencies, which is harsh and doesn’t sound good. To solve this, most guitar speakers have a steep cutoff at around 6-8 kHz. Here’s an example of the frequency response of the cabinet (some Mesa Boogie if I recall correctly).</p>

<p><img src="/assets/img/fir-guitar-cabinet/frequency_response_db.png" alt="Frequency response" /></p>

<p>The task is to emulate this behaviour with digital signal processing techniques.</p>

<h2 id="guitar-cabinet-simulation">Guitar cabinet simulation</h2>

<p>A guitar cabinet is a linear device and may be modelled as a digital FIR filter. <strong>FIR</strong> stands for finite impulse response and it’s the kind of digital filter where you don’t have any internal feedback.</p>

<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/FIR_Filter.svg/1200px-FIR_Filter.svg.png" alt="Fir schematic" /></p>

<p>The math behind FIR-filters is quite simple: for each output sample you multiply the last N input samples with the corresponding coefficients (often called weights) and accumulate them:</p>

\[y[n] = b_0x[n] + b_1x[n-1] + ... + b_Nx[n-N] = \sum_{i=0}^Nb_ix[n-i]\]

<p>This process is also known as convolution, becoming more famous with the rising popularity of the artificial convolutional neural networks.</p>

<h2 id="influence-of-the-guitar-cabinet">Influence of the guitar cabinet</h2>

<p>Let’s assume we have a nice little guitar part which has been directly recorded with a sound card or some DI-box:</p>

<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1265562760&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&amp;visual=true"></iframe>
<div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/michael-klimenko-331527785" title="Michael Klimenko" target="_blank" style="color: #cccccc; text-decoration: none;">Michael Klimenko</a> · <a href="https://soundcloud.com/michael-klimenko-331527785/dry-1" title="Dry" target="_blank" style="color: #cccccc; text-decoration: none;">Dry</a></div>

<p>It sounds a bit dull, but that’s a start. According to our diagram above here, we have a guitar and a cable. The next step would be the amplifier (with some optional effects). For the sake of demonstration let’s assume that we want a gainy amplifier. High-gain amplifiers are used to get some nonlinear distortion to the input signal, which results in an enrichment of the original signal with harmonics. Be careful, the following sound isn’t what you’d call pleasant.</p>

<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1265562754&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&amp;visual=true"></iframe>
<div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/michael-klimenko-331527785" title="Michael Klimenko" target="_blank" style="color: #cccccc; text-decoration: none;">Michael Klimenko</a> · <a href="https://soundcloud.com/michael-klimenko-331527785/without-cabinet-1" title="Without cabinet" target="_blank" style="color: #cccccc; text-decoration: none;">Without cabinet</a></div>

<p>It has a lot of so-called “sand” and this isn’t something you want to hear on your recording or while jamming on your couch. To mitigate that the cabinet is used to get something like this:</p>

<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1265562757&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&amp;visual=true"></iframe>
<div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/michael-klimenko-331527785" title="Michael Klimenko" target="_blank" style="color: #cccccc; text-decoration: none;">Michael Klimenko</a> · <a href="https://soundcloud.com/michael-klimenko-331527785/full-1" title="Full" target="_blank" style="color: #cccccc; text-decoration: none;">Full</a></div>

<p>The simplicity of this approach has resulted in a big number of products related to the cabinet simulation, which is great for musicians who gig a lot to get a reproducible tone as well as the recording guitarists to reamp and get the sound they’re looking for.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><summary type="html"><![CDATA[It is extremely useful to have a little vacation every once in a while, to distract yourself from your day-to-day basis. You may find yourself thinking about various tasks you were too busy to give some serious time to. One of such tasks I was intrigued about is the guitar cabinet simulation via the impulse responses of the real cabinet-microphone pair.]]></summary></entry><entry xml:lang="en"><title type="html">Automate your C library type-based overload resolutions with C++17</title><link href="https://mklimenko.github.io/english/2021/08/17/c-library-plusifier/" rel="alternate" type="text/html" title="Automate your C library type-based overload resolutions with C++17" /><published>2021-08-17T00:00:00+00:00</published><updated>2021-08-17T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2021/08/17/c-library-plusifier</id><content type="html" xml:base="https://mklimenko.github.io/english/2021/08/17/c-library-plusifier/"><![CDATA[<p>Every time I work with a C library, I miss the power and capability of the type system C++ provides. That’s why I developed a simple C++17 header-only helper library to pack the multiple type-dependent C-style functions into single overload deduced at compile-time. No external libraries are required. Repo link: https://github.com/MKlimenko/plusifier. Currently, it’s just the header and a compile-time test file, CMake integration coming soon.</p>

<blockquote>

  <p><strong>UPD:</strong> Some of the comments (somewhy I can’t see them now) suggested <a href="https://www.youtube.com/watch?v=n-W56XbXHHM">this</a> lightning talk by Niel Waldren. It is indeed a slightly less bulky solution, but, in my opinion, it won’t trigger a warning with a type conversion mismatch (<code class="language-plaintext highlighter-rouge">std::size_t</code> vs plain <code class="language-plaintext highlighter-rouge">int</code>) and, due to the usage of <code class="language-plaintext highlighter-rouge">std::function</code>, it’s heavier to compile. On my local machine results with the clang-10 via WSL2 it took twice as long to compile: 359 vs 183 ms.</p>

</blockquote>

<ul>
  <li><a href="#motivation">Motivation</a></li>
  <li><a href="#usage-and-examples">Usage and examples</a>
    <ul>
      <li><a href="#function-overloading">Function overloading</a></li>
      <li><a href="#pointer-automation">Pointer automation</a></li>
    </ul>
  </li>
  <li><a href="#under-the-hood">Under the hood</a>
    <ul>
      <li><a href="#internals-of-the-class">Internals of the class</a></li>
      <li><a href="#operator"><code class="language-plaintext highlighter-rouge">operator()</code></a></li>
      <li><a href="#function-verification">Function verification</a></li>
    </ul>
  </li>
</ul>

<h2 id="motivation">Motivation</h2>

<p>Many programming languages can call libraries with the pure C interface. Libraries themselves may be written in various languages, however, it is a de-facto standard for them to have a C interface.</p>

<p>Due to the lack of function overloading in pure C, library maintainers are required to explicitly specify all of the available types for the function. For example, I’d like to list one of my favourite libraries out there, the Intel Integrated Performance Primitives, <a href="https://software.intel.com/content/www/us/en/develop/tools/oneapi/components/ipp.html">IPP</a>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IppStatus</span>   <span class="nf">ippsMulC_16s_I</span><span class="p">(</span><span class="n">Ipp16s</span> <span class="n">val</span><span class="p">,</span> <span class="n">Ipp16s</span><span class="o">*</span> <span class="n">pSrcDst</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">IppStatus</span>   <span class="nf">ippsMulC_32f_I</span><span class="p">(</span><span class="n">Ipp32f</span> <span class="n">val</span><span class="p">,</span> <span class="n">Ipp32f</span><span class="o">*</span> <span class="n">pSrcDst</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">IppStatus</span>   <span class="nf">ippsMulC_64f_I</span><span class="p">(</span><span class="n">Ipp64f</span> <span class="n">val</span><span class="p">,</span> <span class="n">Ipp64f</span><span class="o">*</span> <span class="n">pSrcDst</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">IppStatus</span>   <span class="nf">ippsMulC_32fc_I</span><span class="p">(</span><span class="n">Ipp32fc</span> <span class="n">val</span><span class="p">,</span> <span class="n">Ipp32fc</span><span class="o">*</span> <span class="n">pSrcDst</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">IppStatus</span>   <span class="nf">ippsMulC_64fc_I</span><span class="p">(</span><span class="n">Ipp64fc</span> <span class="n">val</span><span class="p">,</span> <span class="n">Ipp64fc</span><span class="o">*</span> <span class="n">pSrcDst</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="c1">// ... and so on</span>
</code></pre></div></div>

<p>If you’re a C++ developer like myself, you may find this mildly irritating to look up and change the function every single time you decide to change the type. And it works poorly with generic (templated) code as well.</p>

<h2 id="usage-and-examples">Usage and examples</h2>

<p>Wrapper object is created in the constructor and then the correct overload is selected in the <code class="language-plaintext highlighter-rouge">operator()</code> call:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">fn</span> <span class="o">=</span> <span class="n">plusifier</span><span class="o">::</span><span class="n">FunctionWrapper</span><span class="p">(</span><span class="cm">/*function overloads*/</span><span class="p">);</span>

<span class="k">auto</span> <span class="n">dst</span> <span class="o">=</span> <span class="n">fn</span><span class="p">(</span><span class="cm">/* function arguments... */</span><span class="p">);</span>
</code></pre></div></div>

<p>Pointer wrapper object is used similarally:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">ptr</span> <span class="o">=</span> <span class="n">plusifier</span><span class="o">::</span><span class="n">PointerWrapper</span><span class="o">&lt;</span><span class="n">PointerType</span><span class="p">,</span> <span class="n">DeleterFunction</span><span class="o">&gt;</span><span class="p">(</span><span class="n">allocator_function</span><span class="p">,</span> <span class="cm">/* allocator function arguments... */</span><span class="p">);</span>
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">allocator_function</code> may be both the callable (function pointer, lambda, <code class="language-plaintext highlighter-rouge">std::function</code>) as well as the <code class="language-plaintext highlighter-rouge">plusifier::FunctionWrapper</code>.</p>

<h3 id="function-overloading">Function overloading</h3>

<p>For a more simplified example, suppose we have three functions with a slightly different signature:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">square_s8</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="kt">int8_t</span><span class="o">*</span> <span class="n">val</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sz</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">square_s32</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="kt">int32_t</span><span class="o">*</span> <span class="n">val</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sz</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">4</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">square_fp32</span><span class="p">(</span><span class="k">const</span> <span class="kt">float</span><span class="o">*</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">8</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this library, they may be packed into single object:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">square</span> <span class="o">=</span> <span class="n">plusifier</span><span class="o">::</span><span class="n">FunctionWrapper</span><span class="p">(</span><span class="n">square_s8</span><span class="p">,</span> <span class="n">square_s32</span><span class="p">,</span> <span class="n">square_fp32</span><span class="p">);</span>

<span class="k">auto</span> <span class="n">dst_ch</span> <span class="o">=</span> <span class="n">square</span><span class="p">(</span><span class="n">arr_ch</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span> <span class="mi">0</span><span class="p">);</span>     <span class="c1">// &lt;-- calls square_s8</span>
<span class="k">auto</span> <span class="n">dst_int</span> <span class="o">=</span> <span class="n">square</span><span class="p">(</span><span class="n">arr_int</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span> <span class="mi">0</span><span class="p">);</span>   <span class="c1">// &lt;-- calls square_s32</span>
<span class="k">auto</span> <span class="n">dst_fp32</span> <span class="o">=</span> <span class="n">square</span><span class="p">(</span><span class="n">arr_fp32</span><span class="p">.</span><span class="n">data</span><span class="p">());</span>    <span class="c1">// &lt;-- calls square_fp32</span>
</code></pre></div></div>

<p>It will check if the passed arguments are viable to be used as the arguments for the functions at the compile-time and select the most appropriate overload.</p>

<h3 id="pointer-automation">Pointer automation</h3>

<p>RAII is the lifesaver in modern C++. However, it’s a bit tedious to mix it with the C-style allocations. One of the approaches would be to use the <code class="language-plaintext highlighter-rouge">std::unique_ptr</code> with a custom deleter, but it’s quite excess, so I decided to expand this library a little bit more.</p>

<p>For example, we might have a specified allocation functions for various types:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Ipp8u</span><span class="o">*</span>      <span class="nf">ippsMalloc_8u</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp16u</span><span class="o">*</span>     <span class="nf">ippsMalloc_16u</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp32u</span><span class="o">*</span>     <span class="nf">ippsMalloc_32u</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp8s</span><span class="o">*</span>      <span class="nf">ippsMalloc_8s</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp16s</span><span class="o">*</span>     <span class="nf">ippsMalloc_16s</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp32s</span><span class="o">*</span>     <span class="nf">ippsMalloc_32s</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp64s</span><span class="o">*</span>     <span class="nf">ippsMalloc_64s</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp32f</span><span class="o">*</span>     <span class="nf">ippsMalloc_32f</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="n">Ipp64f</span><span class="o">*</span>     <span class="nf">ippsMalloc_64f</span><span class="p">(</span><span class="kt">int</span> <span class="n">len</span><span class="p">);</span>
<span class="c1">// and so on...</span>
</code></pre></div></div>

<p>We’ll wrap all of them into single <code class="language-plaintext highlighter-rouge">FunctionWrapper</code> and pass it to the <code class="language-plaintext highlighter-rouge">PointerWrapper</code>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">ippsMalloc</span> <span class="o">=</span> <span class="n">plusifier</span><span class="o">::</span><span class="n">FunctionWrapper</span><span class="p">(</span><span class="n">ippsMalloc_8u</span><span class="p">,</span> <span class="n">ippsMalloc_16u</span><span class="p">,</span> <span class="n">ippsMalloc_32u</span><span class="p">,</span> <span class="cm">/* etc */</span><span class="p">);</span>

<span class="k">auto</span> <span class="n">ptr</span> <span class="o">=</span> <span class="n">plusifier</span><span class="o">::</span><span class="n">PointerWrapper</span><span class="o">&lt;</span><span class="n">Ipp8u</span><span class="p">,</span> <span class="n">ippsFree</span><span class="o">&gt;</span><span class="p">(</span><span class="n">ippsMalloc</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="under-the-hood">Under the hood</h2>
<h3 id="internals-of-the-class">Internals of the class</h3>

<p><code class="language-plaintext highlighter-rouge">FunctionWrapper</code> is a variadic template class with the types being the function pointers:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="o">...</span> <span class="nc">F</span><span class="p">&gt;</span>
<span class="k">class</span> <span class="nc">FunctionWrapper</span>  <span class="k">final</span> <span class="p">{</span>
        <span class="k">static_assert</span><span class="p">(</span><span class="k">sizeof</span><span class="p">...(</span><span class="n">F</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"FunctionWrapper should be not empty"</span><span class="p">);</span>
        <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">F</span><span class="p">...</span><span class="o">&gt;</span> <span class="n">var</span><span class="p">;</span>
        <span class="k">constexpr</span> <span class="k">static</span> <span class="kr">inline</span> <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span> <span class="n">pack_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">...(</span><span class="n">F</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p>First <code class="language-plaintext highlighter-rouge">static_assert</code> is used to create a legit compile-time error when there are no functions passed. <code class="language-plaintext highlighter-rouge">std::tuple</code> is a heterogeneous container to store those function pointers, and a <code class="language-plaintext highlighter-rouge">pack_size</code> is a simple helper constant.</p>

<p>Due to the fact, that there are no references and move semantics in pure C, I’ve decided to omit the <a href="https://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c">perfect forwarding</a> and pass the parameter pack in the constructor as-is, so the constructor is extremely trivial:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">FunctionWrapper</span><span class="p">(</span><span class="n">F</span> <span class="p">...</span> <span class="n">functions</span><span class="p">)</span> <span class="o">:</span> <span class="n">var</span><span class="p">(</span><span class="n">functions</span><span class="p">...)</span> <span class="p">{}</span>
</code></pre></div></div>

<p>Then there is a function call operator (<code class="language-plaintext highlighter-rouge">operator()</code>), overload search and verification routines and small helper functions and classes.</p>

<h3 id="operator"><code class="language-plaintext highlighter-rouge">operator()</code></h3>

<p>Function call operator may be split into two parts: compile-time and run-time. First is used to select the correct overload or to indicate the lack of one, while the runtime calls the selected function.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="o">...</span> <span class="nc">Args</span><span class="p">&gt;</span>
<span class="k">auto</span> <span class="nf">operator</span><span class="p">()(</span><span class="n">Args</span> <span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
    <span class="c1">// compile-time</span>
    <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">verification_result</span> <span class="o">=</span> <span class="n">VerifyOverload</span><span class="o">&lt;</span><span class="mi">0</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;</span><span class="p">();</span>
    <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="o">!</span><span class="n">verification_result</span><span class="p">)</span>
        <span class="k">static_assert</span><span class="p">(</span><span class="n">NoOverloadFound</span><span class="o">&lt;</span><span class="n">F</span><span class="p">...</span><span class="o">&gt;</span><span class="p">(),</span> <span class="s">"No suitable overload is found"</span><span class="p">);</span>

    <span class="c1">// run-time</span>
    <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="n">verification_result</span><span class="o">&gt;</span><span class="p">(</span><span class="n">var</span><span class="p">)(</span><span class="n">args</span><span class="p">...);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here the <code class="language-plaintext highlighter-rouge">verification_result</code> variable is an object of a simple helper struct with two fields and conversion operators. In the first place, I wanted to use a structured binding, but the compiler told me I’m not supposed to. This struct contains an index of the function inside the tuple and the fact that the correct overload has been found. This flag ended up there due to the recursive nature of the used template metaprogramming approach.</p>

<p>Verification starts at index 0 and iterates up to the end of the tuple.</p>

<h3 id="function-verification">Function verification</h3>
<p>Every iteration, I get the function pointer signature from the tuple, as well as the <code class="language-plaintext highlighter-rouge">std::function</code> signature to ease the following metaprogramming. Then there’s an excellent function <code class="language-plaintext highlighter-rouge">std::is_invocable_v</code> in the standard library, that allows me to check if the function pointer in the tuple may be called with the type pack passed to the <code class="language-plaintext highlighter-rouge">operator()</code>. If we’re good, we would prematurely quit the function, otherwise, we’ll continue iterating, until the very end of the tuple.</p>

<p>If there’s no suitable overload, a function with a failing <code class="language-plaintext highlighter-rouge">static_assert</code> is called for better error diagnostics.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><summary type="html"><![CDATA[Every time I work with a C library, I miss the power and capability of the type system C++ provides. That’s why I developed a simple C++17 header-only helper library to pack the multiple type-dependent C-style functions into single overload deduced at compile-time. No external libraries are required. Repo link: https://github.com/MKlimenko/plusifier. Currently, it’s just the header and a compile-time test file, CMake integration coming soon.]]></summary></entry><entry xml:lang="en"><title type="html">Estimating the penalty of including Boost libraries</title><link href="https://mklimenko.github.io/english/2021/06/29/boost-inclusion-times/" rel="alternate" type="text/html" title="Estimating the penalty of including Boost libraries" /><published>2021-06-29T00:00:00+00:00</published><updated>2021-06-29T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2021/06/29/boost-inclusion-times</id><content type="html" xml:base="https://mklimenko.github.io/english/2021/06/29/boost-inclusion-times/"><![CDATA[<p><strong>TL;DR;</strong> I’ve made a (yet another) simple <a href="https://github.com/MKlimenko/check_compile_times">repo</a> and a table on its’ <a href="https://github.com/MKlimenko/check_compile_times/wiki">wiki</a> to estimate the build penalty when including boost headers.</p>

<p>Boost <a href="https://www.boost.org/">libraries</a> have a mixed reputation in the C++ community. There are a lot of exceptional quality libraries with algorithms and data structures missing in the standard library. One might even say, that the boost is kind of a playground to test something before it can get into the standard (smart pointers are one of the many examples). On the other hand, boost gets heavily criticized for overcomplication, custom build environment and a lot of cross-connections. Don’t forget the NIH syndrome, which once forced me to re-implement the <code class="language-plaintext highlighter-rouge">static_vector</code> class.</p>

<p>C++ is (in)famous for it’s compilation times, especially for the template-heavy code. That got me thinking, how bad is the penalty for including the boost headers? So I’ve came up with a simple CMake script, which creates a trivial source file with a single <code class="language-plaintext highlighter-rouge">#include</code> directive, repeated N times for all the main boost headers I could reach:</p>

<pre><code class="language-CMake">foreach(header ${HEADERS_TO_PROCESS})
  string(REPLACE "." "-" filename_preliminary ${header})
  string(REPLACE "/" "-" header_name ${filename_preliminary})
  set(filename "${CMAKE_CURRENT_LIST_DIR}/${header_name}_main.cpp")
  file(WRITE ${filename} "#include &lt;${header}&gt;\n  int main() { return 0; }\n")
      
  set(executable_name "Check${header_name}")
  add_executable(${executable_name} ${filename})
  target_compile_options(${executable_name} PUBLIC -ftime-trace)
  target_link_libraries(${executable_name} pthread stdc++ stdc++fs)
endforeach()
</code></pre>

<p>Then I used the <code class="language-plaintext highlighter-rouge">-ftime-trace</code> clang (9.0+, IIRC) switch to generate JSON report on the compilation times. I decided to settle for the whole <code class="language-plaintext highlighter-rouge">.cpp</code> compilation time since it’s easier to drag it from the report.</p>

<p>Due to the fact, that neither Linux nor Windows are real-time operating systems, compilation times wouldn’t be constant and will have some distribution. To account for that, I ran the compilation process several times (10 to 20 looks fine to me) and averaged the results.</p>

<p>I wrote a simple program to read the clang reports, average the values and print them in a markdown-friendly way. I also decided that it would be interesting to estimate the relative slowdown to the plain <code class="language-plaintext highlighter-rouge">int main()</code> source file. The resulting table looks something like this:</p>

<table>
  <thead>
    <tr>
      <th>Header</th>
      <th>Time, ms</th>
      <th>Relative slowdown</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>boost/accumulators/accumulators.hpp</td>
      <td>3000.4</td>
      <td>357.19</td>
    </tr>
    <tr>
      <td>boost/algorithm/algorithm.hpp</td>
      <td>693.667</td>
      <td>82.5794</td>
    </tr>
    <tr>
      <td>boost/align.hpp</td>
      <td>495.733</td>
      <td>59.0159</td>
    </tr>
    <tr>
      <td>…</td>
      <td>…</td>
      <td>…</td>
    </tr>
  </tbody>
</table>

<p>To make it more reproducible and trustworthy, I’ve added the Travis CI script to build, measure the time and auto-generate and upload to the <a href="https://github.com/MKlimenko/check_compile_times/wiki">wiki</a>. As a rant, I’d like to say that I much prefer the <a href="https://mklimenko.github.io/english/2020/02/02/gitlab-ci-cpp/">GitLab</a> way of CI, which is much more intuitive to me.</p>

<p>An interesting fact: I’ve conducted my first runs at my local PC (Ryzen 3700X, 32GB, WSL Ubuntu) and the bare <code class="language-plaintext highlighter-rouge">int main()</code> took relatively the same time to compile (7.2 vs 8 ms), the time tripled for the heaviest boost files (2100 vs 6600 ms for the boost/geometry.hpp).</p>

<p>There’s a simple <a href="https://github.com/MKlimenko/check_compile_times">repo</a> you may check out, the build dependencies are relatively simple (clang 9+ and boost, however, I encourage you to use ninja for speedup).</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><summary type="html"><![CDATA[TL;DR; I’ve made a (yet another) simple repo and a table on its’ wiki to estimate the build penalty when including boost headers.]]></summary></entry><entry xml:lang="en"><title type="html">TEX-CUP GNSS dataset</title><link href="https://mklimenko.github.io/english/2020/05/23/texcup_gnss_dataset/" rel="alternate" type="text/html" title="TEX-CUP GNSS dataset" /><published>2020-05-23T00:00:00+00:00</published><updated>2020-05-23T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2020/05/23/texcup_gnss_dataset</id><content type="html" xml:base="https://mklimenko.github.io/english/2020/05/23/texcup_gnss_dataset/"><![CDATA[<p><strong>TL;DR</strong>: There’s an excellent new GNSS dataset which would allow you to become a better engineer: <a href="http://rnl.ae.utexas.edu/texcup">link</a>. Beware, it would take some time to download it.</p>

<ul>
  <li><a href="#tex-cup">TEX-CUP</a></li>
  <li><a href="#gnss-data-overview">GNSS data overview</a>
    <ul>
      <li><a href="#signal-parameters">Signal parameters</a></li>
      <li><a href="#signal-acquisition">Signal acquisition</a></li>
      <li><a href="#signal-tracking">Signal tracking</a></li>
      <li><a href="#positioning">Positioning</a></li>
    </ul>
  </li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<p>There is an opinion, that an engineer nowadays should know (at least to some degree) MATLAB, a data scientist should get his hands on the <a href="http://yann.lecun.com/exdb/mnist/">MNIST</a> handwritten digit database, every FPGA developer should blink a LED and so on. In my opinion, any engineer, related to the GNSS and signal processing should try to perform a signal acquisition to get all the details about the signals. It’s a relatively simple task and it’s one of the first we give to the interns in our team.</p>

<p>However, there’s a problem: due to the extremely high rate of the IF data (ADC samples at the intermediate frequency, which is about 60 MHz for modern high-precision receivers), any file worth investigating would be enormous, mind the fact that it’s extremely difficult to collect such data in the first place.</p>

<p>There are several known existing signal records, but most of them are for basic use only: GPS-only/L1-only, narrowband low-pass filters in the RF front-end etc. They are good to get your hands on the GNSS, but it won’t get you far in terms of high-precision or modern receiver design, where multi-band multi-constellation signal processing is important, as well as the sensor fusion with inertial sensors, cameras, radars and so on.</p>

<p>For example, there’s a well-known dataset with GPS L1-only, which you may find useful and it’s a (relatively) lightweight signal record, only 1.8G. It’s called <a href="http://agamenon.tsc.uah.es/Asignaturas/it/rd/apuntes/">GPSdata-DiscreteComponents-fs38_192-if9_55</a>, it is static (no movement of the receiver), narrowband (as you can see from the power spectrum plot below), about 50 seconds long:</p>

<p><img src="/assets/img/texcup_gnss_dataset/GPSdata.png" alt="GPSdata" /></p>

<h2 id="tex-cup">TEX-CUP</h2>

<p>Let’s get to the point. About a month ago I’ve stumbled upon an extremely well-conducted experiment by a team from the University of Texas at Austin Radionavigation Laboratory, which is described in details in the following <a href="http://rnl.ae.utexas.edu/images/stories/files/papers/texcup.pdf">article</a>.</p>

<p>This team developed a platform called <em>Sensorium</em>, which consists of multiple sensors, which are sampled simultaneously and recorded independently. The dataset consists of two identical sub-datasets, taken at two days. Each of the sub-datasets contains the following data:</p>

<ol>
  <li>Binary protocol log and the derivative RINEX files from the <a href="https://www.septentrio.com/en/products/gnss-receivers/rover-base-receivers/oem-receiver-boards/asterx4-oem">Septentrio AsteRx4</a> high-precision receiver;</li>
  <li>Accelerometer and gyroscope data from the <a href="https://www.bosch-sensortec.com/products/motion-sensors/absolute-orientation-sensors/absolute-orientation-sensors-bmx055.html">Bosch BMX055</a> IMU;</li>
  <li>Stereo images from two <a href="https://www.baslerweb.com/en/products/cameras/area-scan-cameras/ace/aca2040-35gm/">Basler acA2040-35gm</a> cameras with the Sony IMX265 CMOS sensor;</li>
  <li>Accelerometer and gyroscope data from the <a href="https://www.microstrain.com/inertial/3dm-gx5-25">LORD MicroStrain 3DM-GX5-25</a> AHRS;</li>
  <li>Binary IF-data form the <a href="http://ntlab.com/section/sec:v:36729.htm">NTLab NT1065-based</a> USB3-grabber (dual/triple band);</li>
  <li>Binary IF-data from the RadioLynx GNSS RF front end;</li>
  <li>uBlox EVK-M8T NMEA data.</li>
</ol>

<p>According to the figure of the <em>Sensorium</em>, there are RADAR sensors as well, but I’ve been told that the lab wants to conduct some of the test themselves before making this data public.</p>

<p>As you can see, this is an excellent opportunity for GNSS-related research. One may develop an SDR of various complexity (GPS-only, multi-constellation, multi-frequency etc), sensor fusion with IMU, sensor fusion with cameras and so on. There’s even a RINEX data from a separate receiver if you’d like to develop a custom RTK engine!</p>

<p>Due to the fact of the data synchronization, you may develop inertial sensor fusion algorithms on different levels: loose, tight and ultra-tight coupling. As far as I know, this is a first dataset, consisting of both IMU and IF GNSS data, which allow you to develop your ultra-tight coupling algorithms.</p>

<h2 id="gnss-data-overview">GNSS data overview</h2>

<p>Let’s take a look at the GNSS data. For this, I’ll be using my own WIP software-defined receiver, which I’ve been working on in my spare time for the last couple of months. It’s currently L1-only, GPS+GLONASS (combined).</p>

<h3 id="signal-parameters">Signal parameters</h3>

<p>First, let’s take a look at the signal in the L1-band. NT1065 frontend separates the signals to the different sidebands (therefore, GPS L1 and GLONASS L1 are technically independent IF signals), but for the sake of simplicity, I’ve combined them using the Hilbert transform (Hilbert transform of the real signal provides you with a complex signal with an imaginary part being the real one, shifted 90 degrees).</p>

<p>One more shortcut I’ve taken is that, because currently, I’m not processing BeiDou signals, I can downsample the signal by the factor of two:</p>

<p><img src="/assets/img/texcup_gnss_dataset/signal.png" alt="signal" /></p>

<p>In the upper subplot, you may see the spectrum with the distinctive hump of the NT1065 lowpass filter frequency response at around 1 MHz, as well as the slight elevation in the -14.58 MHz (GPS L1). The central frequency of the mixer is the 1590 MHz, which is typical for most NT1065-based designs.</p>

<p>In the bottom left plot, I’ve plotted the signal itself, blue being the real and orange — the imaginary parts. To the right, there’s a histogram, which looks like a Gaussian distribution, which is what we’d expect from a GNSS signal record. One thing I’d like to point out is the bit depth increment due to the Hilbert transform (the original data was 2-bit).</p>

<h3 id="signal-acquisition">Signal acquisition</h3>

<p>The next step is the acquisition process. There are several approaches for acquisition, which aren’t related to this subject, let me provide the acquisition result plots.</p>

<p><img src="/assets/img/texcup_gnss_dataset/gps_acquisition.png" alt="GPS acquisition" /></p>

<p>Bear in mind, that the acquisition is a statistical process and heavily depends on the detection threshold you set. A good acquisition engine also provides additional steps to get the more precise frequency and delay estimation of the signals, as well as the bit and/or overlay code synchronization.</p>

<p><img src="/assets/img/texcup_gnss_dataset/glonass_acquisition.png" alt="GLONASS acquisition" /></p>

<h3 id="signal-tracking">Signal tracking</h3>

<p>After the signals are acquired, the tracker kicks in. In my opinion, it is one of the most scientifically intensive parts of the receiver. Currently, I have a primitive delay- and phase-locked loops, but they do their job, which is fine for me right now.</p>

<p><img src="/assets/img/texcup_gnss_dataset/gps_tracking.png" alt="GPS tracking" /></p>

<p>Tracking results look pretty similar with GLONASS (except for an additional square-wave modulation), therefore let’s not provide it here.</p>

<h3 id="positioning">Positioning</h3>

<p>As a final step, the receiver decodes the ephemeris data from the tracker output and estimates its position. Currently, due to the time consumption of the receiver, I’m only processing 1 minute of the data (of the about 90 minutes in total). For the first 10 minutes or so the car with the <em>Sensorium</em> on top of it just stands still, and then it begins it’s movement.</p>

<p><img src="/assets/img/texcup_gnss_dataset/positioning.png" alt="Positioning" /></p>

<h2 id="conclusion">Conclusion</h2>

<p>Honestly, this dataset is extremely useful for every GNSS-related engineer out there. It’s kind of like Large Hadron Collider, where physicists conduct some experiments, record all the data they can, and research it for decades afterwards. I’m really glad I’ve found it and would like to thank the authors for sharing it with the community. It was a bit of a pain to download it (more than 500GB for the single sub-dataset), but it’s worth it. The other thing I’m glad I did just before I’ve found it is a PC upgrade to 8c/16t CPU, which allowed me to process this data much faster than it would on my old PC.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><category term="GNSS" /><summary type="html"><![CDATA[TL;DR: There’s an excellent new GNSS dataset which would allow you to become a better engineer: link. Beware, it would take some time to download it.]]></summary></entry><entry xml:lang="en"><title type="html">CPU instruction set dispatcher</title><link href="https://mklimenko.github.io/english/2020/03/09/auto-instruction-set/" rel="alternate" type="text/html" title="CPU instruction set dispatcher" /><published>2020-03-09T00:00:00+00:00</published><updated>2020-03-09T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2020/03/09/auto-instruction-set</id><content type="html" xml:base="https://mklimenko.github.io/english/2020/03/09/auto-instruction-set/"><![CDATA[<ul>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#benchmarking-first">Benchmarking first</a></li>
  <li><a href="#creating-a-library">Creating a library</a></li>
  <li><a href="#delayed-library-loading">Delayed library loading</a></li>
  <li><a href="#generating-multiple-libraries">Generating multiple libraries</a></li>
  <li><a href="#detecting-the-processor-architecture-at-runtime">Detecting the processor architecture at runtime</a></li>
  <li><a href="#using-the-library">Using the library</a></li>
</ul>

<blockquote>
  <p>TL;DR: In this blog post we’ll generate multiple libraries from the same source code with the various architecture flags. Later on, at the runtime, an application selects the most appropriate library based on the instruction set and will gain a 3x performance gain on the simple function I’ve decided to implement.</p>
</blockquote>

<h2 id="introduction">Introduction</h2>

<p>Modern processors are often much more capable than we think because the CPU vendors care about us, fellow programmers. There’s an amazing <a href="https://www.youtube.com/watch?v=bSkpMdDe4g4">talk</a> by Matt Godbolt, go check it out if you haven’t already. The <code class="language-plaintext highlighter-rouge">popcnt</code> example blows my mind to this day. Briefly: modern (Haswell and forth) processors have a special instruction which counts the number of set bits.</p>

<p><img src="https://i.ytimg.com/vi/bSkpMdDe4g4/maxresdefault.jpg" alt="cppcon" /></p>

<p>As well as the additional operations, SIMD (single instruction, multiple data) is a thing to be reckoned with. Primarily, it was used to perform simple operations (such as an addition) on “vectors” of data. In this case, “vectors” meant loading the data from the memory to the wide registers of the CPU, processing it with a single instruction and then repeating. Nowadays, AVX-512 allows both long registers (512-bit wide) as well as the <a href="https://en.wikichip.org/wiki/x86/avx512vnni">sophisticated operations</a>, which are useful for neural network tasks for both inference and <a href="https://www.intel.ai/nervana/wp-content/uploads/sites/53/2018/05/Lower-Numerical-Precision-Deep-Learning-Inference-Training.pdf">training</a>.</p>

<p><img src="https://www.hardwareluxx.ru/images/cdn01/407E174325AB4E6A8532AB504B13B6D3/img/97B691A9BD1A443AA1A7CCCE780D1C3F/Intel-HotChips30-3_97B691A9BD1A443AA1A7CCCE780D1C3F.jpg" alt="convolution" /></p>

<p>If you’re running some scientific/research code on your local powerful computer, this is a place to stop reading, just build your code with <code class="language-plaintext highlighter-rouge">-march=native</code> (<code class="language-plaintext highlighter-rouge">/arch</code> on MSVC) switch and enjoy all the benefits your hardware can provide. However, if you’re planning on distributing your software, there might be a little problem. By default, modern compilers don’t utilize any of the vector extensions to make the resulting program as portable as possible. This is a good approach, but owners of the modern hardware won’t be as happy, as they could’ve been. Today I’d like to discuss and implement a dispatcher pattern, which is used in the <a href="https://software.intel.com/en-us/ipp-dev-guide-dispatching">Intel IPP</a> library.</p>

<p>The main idea is to put the most performance-critical code into a separate library, build several variants of it and dispatch the calls at the runtime. In this post, I’ll only consider shared (dynamic) libraries, since it’s easier to implement, but with several tweaks, you may get a static executable with the same functionality</p>

<h2 id="benchmarking-first">Benchmarking first</h2>

<p>As in any optimization related article, first of all, one must benchmark various aspects and parts of an application and decide, which functions should be extracted for the library. My toy example happened to have such a function:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extern</span> <span class="s">"C"</span> <span class="kt">void</span> <span class="nf">Add</span><span class="p">(</span><span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">double</span><span class="o">*</span> <span class="n">dst</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="n">dst</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Yup, I know, raw pointers, but you don’t want to pass C++ objects in and out from the dynamic library, trust me.</p>

<p>So I’ve measured this code on my machine with a Google Benchmark and got these result for 256 elements:</p>

<table>
  <thead>
    <tr>
      <th>Instruction set</th>
      <th>Time, ns</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Common</td>
      <td>95</td>
    </tr>
    <tr>
      <td>AVX</td>
      <td>40</td>
    </tr>
    <tr>
      <td>AVX2</td>
      <td>30</td>
    </tr>
  </tbody>
</table>

<p>I’d like to point out that I haven’t hand-optimized any of the code, haven’t used any intrinsic functions or whatever, just recompiled with the change of one flag: <code class="language-plaintext highlighter-rouge">/arch</code>. Triple performance is a target worth pursuing, so this is a perfect candidate for such an optimization, so let’s proceed to the next step.</p>

<h2 id="creating-a-library">Creating a library</h2>

<p>As we’ve decided with the functions we’ll be extracting, the next step is to create a library. This is as straightforward as it gets: a pair of header and a source file:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma once
</span>
<span class="cp">#include</span> <span class="cpf">&lt;cstddef&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;cstdint&gt;</span><span class="cp">
</span>
<span class="k">namespace</span> <span class="n">lib</span> <span class="p">{</span>
    <span class="k">extern</span> <span class="s">"C"</span> <span class="kt">void</span> <span class="n">Add</span><span class="p">(</span><span class="k">const</span> <span class="kt">double</span> <span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">double</span> <span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">double</span> <span class="o">*</span> <span class="n">dst</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"lib.hpp"</span><span class="cp">
</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="kt">void</span> <span class="n">lib</span><span class="o">::</span><span class="n">Add</span><span class="p">(</span><span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">double</span><span class="o">*</span> <span class="n">dst</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="n">dst</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And we’ll use the basic CMake script to generate a library:</p>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.10<span class="p">)</span>
<span class="nb">project</span><span class="p">(</span>best_instruction_set<span class="p">)</span>

<span class="nb">set</span><span class="p">(</span>SOURCES src/lib.cpp<span class="p">)</span> 
<span class="nb">set</span><span class="p">(</span>HEADERS 
            src/lib.hpp 
            src/lib.def
<span class="p">)</span>
            
<span class="nb">add_library</span><span class="p">(</span>best_instruction_set SHARED <span class="si">${</span><span class="nv">SOURCES</span><span class="si">}</span> <span class="si">${</span><span class="nv">HEADERS</span><span class="si">}</span><span class="p">)</span>
<span class="nb">set_property</span><span class="p">(</span>TARGET best_instruction_set PROPERTY CXX_STANDARD 17<span class="p">)</span>
<span class="nb">set_property</span><span class="p">(</span>TARGET best_instruction_set PROPERTY CXX_STANDARD_REQUIRED ON<span class="p">)</span> 
</code></pre></div></div>

<h2 id="delayed-library-loading">Delayed library loading</h2>

<p>The key to the dispatcher is the delayed loading of the library. This is a concept when the library is being loaded in some moment at runtime and a developer assigns function pointers to the exported functions of a library.</p>

<p>For this purpose I’ve written a simple cross-platform wrapper, which is used as-is for multiple projects:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma once
#include</span> <span class="cpf">&lt;stdexcept&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#ifdef _WIN32
#include</span> <span class="cpf">&lt;Windows.h&gt;</span><span class="cp">
#else
#include</span> <span class="cpf">&lt;dlfcn.h&gt;</span><span class="cp">
#endif
</span>
<span class="k">namespace</span> <span class="n">DllWrapper</span> <span class="p">{</span>
<span class="cp">#ifdef _WIN32
</span>    <span class="k">using</span> <span class="n">InstanceType</span> <span class="o">=</span> <span class="n">HMODULE</span><span class="p">;</span>
<span class="cp">#else
</span>    <span class="k">using</span> <span class="n">InstanceType</span> <span class="o">=</span> <span class="kt">void</span><span class="o">*</span><span class="p">;</span>
<span class="cp">#endif
</span>
    <span class="kr">inline</span> <span class="k">auto</span> <span class="n">GetInstance</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">path</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#ifdef _WIN32
</span>        <span class="k">return</span> <span class="n">LoadLibraryExA</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="cp">#else
</span>        <span class="k">return</span> <span class="n">dlopen</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">RTLD_LAZY</span><span class="p">);</span>
<span class="cp">#endif
</span>    <span class="p">}</span>

    <span class="kr">inline</span> <span class="kt">void</span> <span class="n">FreeInstance</span><span class="p">(</span><span class="n">InstanceType</span> <span class="n">instance</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">instance</span><span class="p">)</span>
            <span class="k">return</span><span class="p">;</span>
<span class="cp">#ifdef _WIN32
</span>        <span class="n">FreeLibrary</span><span class="p">(</span><span class="n">instance</span><span class="p">);</span>
<span class="cp">#else
</span>        <span class="n">dlclose</span><span class="p">(</span><span class="n">instance</span><span class="p">);</span>
<span class="cp">#endif
</span>    <span class="p">}</span>

    <span class="kr">inline</span> <span class="k">auto</span> <span class="n">GetAddress</span><span class="p">(</span><span class="n">InstanceType</span> <span class="n">instance</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">symbol_name</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#ifdef _WIN32
</span>        <span class="k">return</span> <span class="n">GetProcAddress</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">symbol_name</span><span class="p">);</span>
<span class="cp">#else
</span>        <span class="k">return</span> <span class="n">dlsym</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">symbol_name</span><span class="p">);</span>
<span class="cp">#endif
</span>    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Based on this common wrapper, every library has to get a custom wrapper, such as the following:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma once
</span>
<span class="cp">#include</span> <span class="cpf">"lib.hpp"</span><span class="cp">
#include</span> <span class="cpf">"wrapper_common.hpp"</span><span class="cp">
#include</span> <span class="cpf">"cpuinfo_x86.h"</span><span class="cp">
</span>
<span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string_view&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="nc">LibWrapper</span> <span class="p">{</span>
    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">Add</span><span class="p">)(</span><span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">double</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">double</span><span class="o">*</span> <span class="n">dst</span><span class="p">)</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>

    <span class="n">LibWrapper</span><span class="p">()</span> <span class="p">{</span>      
        <span class="k">auto</span> <span class="n">path</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="s">"best_instruction_set"</span><span class="p">);</span>

<span class="cp">#ifdef _WIN32
</span>        <span class="n">path</span> <span class="o">+=</span> <span class="s">".dll"</span><span class="p">;</span>
<span class="cp">#elif __linux__
</span>        <span class="n">path</span> <span class="o">=</span> <span class="s">"lib"</span> <span class="o">+</span> <span class="n">path</span> <span class="o">+</span> <span class="s">".so"</span><span class="p">;</span>
<span class="cp">#else
</span>        <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"Unexpected system"</span><span class="p">);</span>
<span class="cp">#endif
</span>        <span class="n">instance</span> <span class="o">=</span> <span class="n">DllWrapper</span><span class="o">::</span><span class="n">GetInstance</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">instance</span><span class="p">)</span>
            <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"Unable to load library "</span> <span class="o">+</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">path</span><span class="p">));</span>

        <span class="n">Assign</span><span class="p">(</span><span class="s">"Add"</span><span class="p">,</span> <span class="n">Add</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="o">~</span><span class="n">LibWrapper</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">DllWrapper</span><span class="o">::</span><span class="n">FreeInstance</span><span class="p">(</span><span class="n">instance</span><span class="p">);</span>
    <span class="p">}</span>

<span class="nl">private:</span>
    <span class="n">DllWrapper</span><span class="o">::</span><span class="n">InstanceType</span> <span class="n">instance</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
    
    <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="nc">T</span><span class="p">&gt;</span>
    <span class="kt">void</span> <span class="n">Assign</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">symbol_name</span><span class="p">,</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">dst_pointer</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">auto</span> <span class="n">address</span> <span class="o">=</span> <span class="n">DllWrapper</span><span class="o">::</span><span class="n">GetAddress</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">symbol_name</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">address</span><span class="p">)</span>
            <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"Unable to find symbol: "</span> <span class="o">+</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">symbol_name</span><span class="p">));</span>

        <span class="n">dst_pointer</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">address</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>This wrapper is a class with a function pointer and a bare-bones logic. In the constructor, the library is loaded and the pointer is assigned via the helper function. In the destructor, the binary resources are released.</p>

<h2 id="generating-multiple-libraries">Generating multiple libraries</h2>

<p>There’s a simple extension to the provided CMake script, which will allow us to generate multiple libraries from the same source code at the same time:</p>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">set</span><span class="p">(</span>ARCHITECTURE_OPTIONS <span class="s2">"avx;avx2;avx512"</span><span class="p">)</span>
            
<span class="nb">foreach</span> <span class="p">(</span>INSTRUCTION_SET <span class="si">${</span><span class="nv">ARCHITECTURE_OPTIONS</span><span class="si">}</span><span class="p">)</span>
    <span class="nb">message</span><span class="p">(</span>STATUS <span class="s2">"Generating </span><span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span><span class="s2"> library"</span><span class="p">)</span>
    <span class="nb">add_library</span><span class="p">(</span>best_instruction_set_<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> SHARED <span class="si">${</span><span class="nv">SOURCES</span><span class="si">}</span> <span class="si">${</span><span class="nv">HEADERS</span><span class="si">}</span><span class="p">)</span>
    <span class="nb">if</span> <span class="p">(</span>WIN32<span class="p">)</span>
        <span class="nb">string</span><span class="p">(</span>TOUPPER <span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> UPPERCASE_INSTRUCTION_SET<span class="p">)</span>
        <span class="nb">set</span><span class="p">(</span>COMPILER_OPTION /arch:<span class="si">${</span><span class="nv">UPPERCASE_INSTRUCTION_SET</span><span class="si">}</span><span class="p">)</span>
    <span class="nb">elseif</span> <span class="p">(</span>UNIX<span class="p">)</span>
        <span class="nb">set</span><span class="p">(</span>COMPILER_OPTION -m<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span><span class="p">)</span>
        <span class="nb">if</span> <span class="p">(</span><span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> STREQUAL <span class="s2">"avx512"</span><span class="p">)</span>
            <span class="nb">set</span><span class="p">(</span>COMPILER_OPTION -m<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span>f<span class="p">)</span>
        <span class="nb">endif</span> <span class="p">(</span><span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> STREQUAL <span class="s2">"avx512"</span><span class="p">)</span>
    <span class="nb">endif</span><span class="p">(</span>WIN32<span class="p">)</span>

    <span class="nb">target_compile_options</span><span class="p">(</span>best_instruction_set_<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span>
          PRIVATE <span class="si">${</span><span class="nv">COMPILER_OPTION</span><span class="si">}</span>
    <span class="p">)</span>
    <span class="nb">set_property</span><span class="p">(</span>TARGET best_instruction_set_<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> PROPERTY CXX_STANDARD 17<span class="p">)</span>
    <span class="nb">set_property</span><span class="p">(</span>TARGET best_instruction_set_<span class="si">${</span><span class="nv">INSTRUCTION_SET</span><span class="si">}</span> PROPERTY CXX_STANDARD_REQUIRED ON<span class="p">)</span>  
<span class="nb">endforeach</span><span class="p">(</span>INSTRUCTION_SET<span class="p">)</span>
</code></pre></div></div>

<h2 id="detecting-the-processor-architecture-at-runtime">Detecting the processor architecture at runtime</h2>

<p>For this, we’ll be using one of the Google side-projects, <a href="https://github.com/google/cpu_features">cpu_features</a>. It will extend the library wrapper class in such a manner:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">auto</span> <span class="n">GetSuffix</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="p">{</span>
    <span class="k">const</span> <span class="k">auto</span> <span class="n">features</span> <span class="o">=</span> <span class="n">cpu_features</span><span class="o">::</span><span class="n">GetX86Info</span><span class="p">().</span><span class="n">features</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">features</span><span class="p">.</span><span class="n">avx512f</span><span class="p">)</span>
        <span class="k">return</span> <span class="s">"avx512"</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">features</span><span class="p">.</span><span class="n">avx2</span><span class="p">)</span>
        <span class="k">return</span> <span class="s">"avx2"</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">features</span><span class="p">.</span><span class="n">avx</span><span class="p">)</span>
        <span class="k">return</span> <span class="s">"avx"</span><span class="p">;</span>

    <span class="k">return</span> <span class="s">""</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we’re using suffixes to distinguish our libraries, this is good enough. So the constructor of the wrapper will be extended as well:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">LibWrapper</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">SwitchImplementation</span><span class="p">(</span><span class="n">GetSuffix</span><span class="p">());</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">SwitchImplementation</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">suffix</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">DllWrapper</span><span class="o">::</span><span class="n">FreeInstance</span><span class="p">(</span><span class="n">instance</span><span class="p">);</span>

    <span class="k">auto</span> <span class="n">path</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="s">"best_instruction_set"</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">suffix</span><span class="p">.</span><span class="n">empty</span><span class="p">()</span> <span class="o">?</span> <span class="s">""</span> <span class="o">:</span> <span class="p">(</span><span class="s">"_"</span> <span class="o">+</span> <span class="n">suffix</span><span class="p">));</span>

<span class="cp">#ifdef _WIN32
</span>    <span class="n">path</span> <span class="o">+=</span> <span class="s">".dll"</span><span class="p">;</span>
<span class="cp">#elif __linux__
</span>    <span class="n">path</span> <span class="o">=</span> <span class="s">"lib"</span> <span class="o">+</span> <span class="n">path</span> <span class="o">+</span> <span class="s">".so"</span><span class="p">;</span>
<span class="cp">#else
</span>    <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"Unexpected system"</span><span class="p">);</span>
<span class="cp">#endif
</span>    <span class="n">instance</span> <span class="o">=</span> <span class="n">DllWrapper</span><span class="o">::</span><span class="n">GetInstance</span><span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">instance</span><span class="p">)</span>
        <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"Unable to load library "</span> <span class="o">+</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">path</span><span class="p">));</span>

    <span class="n">Assign</span><span class="p">(</span><span class="s">"Add"</span><span class="p">,</span> <span class="n">Add</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="using-the-library">Using the library</h2>

<p>And that’s pretty much it. One last this is to use the library, which we’ll be doing through the wrapper we’ve just created:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"lib_wrapper.hpp"</span><span class="cp">
</span>
<span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">try</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">a</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">b</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
        <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">dst</span><span class="p">(</span><span class="mi">64</span><span class="p">);</span>

        <span class="k">auto</span> <span class="n">wrapper</span> <span class="o">=</span> <span class="n">LibWrapper</span><span class="p">();</span>
        <span class="n">wrapper</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="o">&amp;</span><span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">b</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">a</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="o">&amp;</span><span class="n">dst</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        
<span class="c1">//      wrapper.SwitchImplementation("avx512");</span>
<span class="c1">//      wrapper.Add(&amp;a[0], &amp;b[0], a.size(), &amp;dst[0]);</span>
    <span class="p">}</span>
    <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is a basic example, but you’re free to play with it in the <a href="https://gitlab.com/mklimenko29/bestinstructionset">repository</a>. Looking forward to all the feedback and discussions about this approach and have a nice day.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><summary type="html"><![CDATA[Introduction Benchmarking first Creating a library Delayed library loading Generating multiple libraries Detecting the processor architecture at runtime Using the library]]></summary></entry><entry xml:lang="en"><title type="html">GitLab CI for C++ projects</title><link href="https://mklimenko.github.io/english/2020/02/02/gitlab-ci-cpp/" rel="alternate" type="text/html" title="GitLab CI for C++ projects" /><published>2020-02-02T00:00:00+00:00</published><updated>2020-02-02T00:00:00+00:00</updated><id>https://mklimenko.github.io/english/2020/02/02/gitlab-ci-cpp</id><content type="html" xml:base="https://mklimenko.github.io/english/2020/02/02/gitlab-ci-cpp/"><![CDATA[<p>In 2016, before moving to GitHub pages as a hosting platform for this blog, I wrote a little <a href="https://mklimenko.github.io/english/2016/07/26/automated-builds-en/">post</a> about CI and automated builds for C++ projects as a synopsis for the week I spent at work with this task. Currently, we’re modernizing the technological stack for one of our paramount product (neural network middleware for the NeuroMatrix processors called <strong>NMDL</strong>) and one of the tasks is to configure and maintain the continuous integration system. The modernizing process also involved integration and renovation of tools, projects, architecture VCS and various “best practices”, I hope I’ll compile it as a talk at some C++ conference.</p>

<blockquote>
  <p>TL;DR: GitLab CI is a great instrument to build, test and deploy C++ projects and it takes less than an hour to setup. There’s a <a href="https://gitlab.com/mklimenko29/ci_example">repo</a> with some minimal build and test code you may start with.</p>
</blockquote>

<h2 id="project-structure">Project structure</h2>

<p>Throughout this whole post I’ll be referring to this <a href="https://gitlab.com/mklimenko29/ci_example">repo</a>, which is a simplified representation of the project structure we use in our projects:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">src</code> folder, which contains source files of the project. We tend to keep the header files and various <code class="language-plaintext highlighter-rouge">.ui</code> stuff here as well. For our purposes it contains:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">lib.hpp</code> — a header-only library we intend to use and test. It contains a simple <code class="language-plaintext highlighter-rouge">Add</code> function template which, according to its’ name, summarizes two numbers and returns the result</li>
      <li><code class="language-plaintext highlighter-rouge">main.cpp</code> with simple usage of the library</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">test</code> folder is introduced to separate the project code from the tests. In this folder we have:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">test.cpp</code> — the test itself, written to use the <a href="https://github.com/google/googletest">googletest</a> library. I prefer it to others (such as Catch2, or the Boost.Test, but it’s up to you to choose which one you like)</li>
      <li><code class="language-plaintext highlighter-rouge">CMakeLists.txt</code> — simple CMake script, which is responsible for building the tests</li>
      <li><code class="language-plaintext highlighter-rouge">CMakeLists.txt.in</code> — a little CMake helper, designed to download the latest <code class="language-plaintext highlighter-rouge">googletest</code> library version and build it. It provides tight integration with the build process of the tests themselves and leads to more isolated (in a good way) builds.</li>
    </ul>
  </li>
  <li>Global <code class="language-plaintext highlighter-rouge">CMakeLists.txt</code> designed to build and test the library</li>
  <li>Default <code class="language-plaintext highlighter-rouge">.gitignore</code> as a sign of good manners</li>
  <li><code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> — the most important file for this post, this is the script that defines the structure and order of pipelines.</li>
</ul>

<p>When the project is ready, it is time to set up the CI.</p>

<h2 id="step-1-set-up-the-runner">Step 1: set up the runner</h2>

<p>To familiarize you with CI, I’ll provide a brief simplified explanation of what we’re about to achieve. Our ultimate goal is to constantly check whether the current version of our product (library, project etc) is good enough to be shipped. This can be verified by running the set of predefined tests for every pushed commit of the repository. The program used for it is called the runner. Every time there’s a new commit, GitLab (both the original one and the self-hosted, whichever you prefer) will notify the runner, so it could fetch the latest changes and perform the actions you’ve listed int the <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> file.</p>

<p>Installing the runner for Windows is extremely easy, just follow the <a href="https://docs.gitlab.com/runner/install/">instructions</a> from GitLab. During the installation, the runner will ask you to provide some information and will register itself as a service.</p>

<p>I’ve had a couple of difficulties with the runner, which I’ll list to save you some time:</p>

<ol>
  <li>Make sure you have <code class="language-plaintext highlighter-rouge">git.exe</code> added to your <code class="language-plaintext highlighter-rouge">PATH</code> environment variable;</li>
  <li>The runner registers itself as a system service, which may not be something you want. If you’re getting some weird access errors, find the <code class="language-plaintext highlighter-rouge">gitlab-runner</code> in the services (Start -&gt; <code class="language-plaintext highlighter-rouge">services</code>) and change the login type to one of the administrator accounts you have on that server.</li>
  <li>I’d like to state this as a separate point if you want to use WSL (Windows Subsystem for Linux, lightweight virtual machine in Windows 10, highly recommend!) be aware, that the WSL currently is installed per-user, therefore, to make it available for the runner, the runners’ service must be logged-in via that account.</li>
</ol>

<h2 id="step-2-come-up-with-the-ci-scenario">Step 2: come up with the CI scenario</h2>

<p>After the runner is set, you’re almost good to go. You have a server to run your tasks, optionally, some specific hardware and a repository. It’s obvious, but be sure to install all the required developer environment at the server you’ll build your project on.</p>

<p>This is the where CMake shines. If you’ve done everything correctly, all you have to do is just these simple commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir build &amp;&amp; cd build
cmake ..
cmake --build .
ctest .
</code></pre></div></div>

<p>The thing I love the most about that approach is that it’s entirely cross-platform. CMake will generate Visual Studio solutions for Windows and Makefiles for Linux. You don’t have to write specific build scripts, just one common <code class="language-plaintext highlighter-rouge">CMakeLists</code> will do.</p>

<p>Of course, it is always better to separate the CI script into several stages, so if something fails, you won’t have to read all the listing to reveal the part where everything went wrong.</p>

<p>There are numerous additional topics one may cover. Here I’ve provided the very basics for you to start integrating CI into your project and making it a little better every time.</p>]]></content><author><name>Michael Klimenko</name><email>mailto:mklimenko29@gmail.com</email></author><category term="english" /><category term="C++" /><summary type="html"><![CDATA[In 2016, before moving to GitHub pages as a hosting platform for this blog, I wrote a little post about CI and automated builds for C++ projects as a synopsis for the week I spent at work with this task. Currently, we’re modernizing the technological stack for one of our paramount product (neural network middleware for the NeuroMatrix processors called NMDL) and one of the tasks is to configure and maintain the continuous integration system. The modernizing process also involved integration and renovation of tools, projects, architecture VCS and various “best practices”, I hope I’ll compile it as a talk at some C++ conference.]]></summary></entry></feed>