<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Henrik's Blog</title><link href="https://hforsten.com/" rel="alternate"></link><link href="https://hforsten.com/feeds/all.atom.xml" rel="self"></link><id>https://hforsten.com/</id><updated>2026-01-27T00:00:00+02:00</updated><entry><title>Online 2D transmission line field solver</title><link href="https://hforsten.com/online-2d-transmission-line-field-solver.html" rel="alternate"></link><published>2026-01-27T00:00:00+02:00</published><updated>2026-01-27T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2026-01-27:/online-2d-transmission-line-field-solver.html</id><summary type="html">&lt;p&gt;2D field solver that can calculate transmission line loss, characteristic impedance, and more, working in your browser&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/field_solver.html" style="text-decoration: none; color: inherit;"&gt;&lt;/p&gt;
&lt;div id="app-launcher"&gt;
&lt;div class="image-wrapper"&gt;
&lt;img src="https://hforsten.com/img/field-solver/header.png" alt="2D transmission line field
solver" width="1493" height="540"&gt;
&lt;div class="open-app-btn"&gt;Open application
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I often use online impedance calculator for quick microstrip, stripline and grounded CPW line dimension calculation. However, they often are not quite good enough. For example when microstrip is covered with solder mask its impedance drops due to dielectric of the mask and the mask increases effective permittivity. Most calculators can't give impedance of the line in that case and it can be significant for thin substrates. Another common issue is unusual transmission lines such as asymmetric stripline, embedded microstrip or lines over ground plane cuts (for example cutout under SMD capacitor or resistor pads). Many calculators are also unable to estimate line losses.&lt;/p&gt;
&lt;p&gt;At work I usually use 2D transmission line field solver to simulate cross-sections (E.g., Ansys 2D extractor, &lt;a href="https://openparem.org/"&gt;OpenParEM&lt;/a&gt;, and many others), but it's quite lot of effort to open the program, draw the geometry, setup simulation parameters, and then simulate it. User interface of those programs also often leaves a lot to be desired.&lt;/p&gt;
&lt;p&gt;I decided to make my own online 2D transmission line field simulator that works entirely in local browser. It's fast to open and transmission lines can be added just by inputting parameters just like with simple impedance calculators. It supports microstrip, stripline, and grounded CPW lines, both single-ended and differential. There's also support for adding solder mask, top-dielectric for embedded microstrip, cutout in ground plane, and metal enclosure around the line. It calculates both dielectric and conductor loss and can export S-parameters. Nice field visualizations are also included.&lt;/p&gt;
&lt;p&gt;For surface roughness &lt;a href="https://ieeexplore.ieee.org/document/7932148"&gt;Gradient model&lt;/a&gt; is implemented. It's relatively new surface roughness model that matches very well with measurements. Loss increase from roughness and increase of inductance due to roughness are calculated. The latter can be very important for simulating filters, and I highly recommend reading the gradient model paper for more information. It's very easy to get or estimate the required RMS roughness parameter for PCB copper foils. Djordjevic-Sarkar (Wideband Debye) causal material model can also be enabled to enforce causality for time-domain simulations.&lt;/p&gt;
&lt;h1 id="accuracy"&gt;Accuracy&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/field-solver/gold_s21.png" width="70%"/&gt;
    &lt;p style="font-size:13px"&gt;Measured and modeled stripline loss from gradient
    model paper (left), and same simulation in my simulator (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I tested it against few simulations and measurements with good accuracy. Above
is example stripline measurement using the surface roughness model compared
against the measurement in Gradient model paper (Figure 23 in the paper). The
loss of the stripline calculated with my simulator matches very well. Effective
permittivity rise due to increased inductance from the roughness is also
simulated well. &lt;a href="/field_solver.html?params=JTdCJTIydGxfdHlwZSUyMiUzQSUyMnN0cmlwbGluZSUyMiUyQyUyMnclMjIlM0EwLjE1OTk5OTk5OTk5OTk5OTk4JTJDJTIyaCUyMiUzQTAuMTc3JTJDJTIydCUyMiUzQS0xNC45OTk5OTk5OTk5OTk5OTglMkMlMjJlciUyMiUzQTMuNDglMkMlMjJ0YW5kJTIyJTNBMC4wMDQlMkMlMjJzaWdtYSUyMiUzQTU4MDAwMDAwJTJDJTIyZnJlcV9zdGFydCUyMiUzQTAuMSUyQyUyMmZyZXFfc3RvcCUyMiUzQTY3JTJDJTIyZnJlcV9wb2ludHMlMjIlM0E1MCUyQyUyMnRyYWNlX3NwYWNpbmclMjIlM0EwLjIlMkMlMjJnYXAlMjIlM0EwLjElMkMlMjJ0b3BfZ25kX3clMjIlM0EwLjIlMkMlMjJ2aWFfZ2FwJTIyJTNBMC4xJTJDJTIyc3RyaXBsaW5lX3RvcF9oJTIyJTNBMC4xNzclMkMlMjJlcl90b3AlMjIlM0EzLjMlMkMlMjJ0YW5kX3RvcCUyMiUzQTAuMDA0JTJDJTIydXNlX3NtJTIyJTNBMCUyQyUyMnNtX3Rfc3ViJTIyJTNBMjAlMkMlMjJzbV90X3RyYWNlJTIyJTNBMjAlMkMlMjJzbV90X3NpZGUlMjIlM0EyMCUyQyUyMnNtX2VyJTIyJTNBMy41JTJDJTIyc21fdGFuZCUyMiUzQTAuMDIlMkMlMjJ1c2VfdG9wX2RpZWwlMjIlM0EwJTJDJTIydG9wX2RpZWxfaCUyMiUzQTAuMiUyQyUyMnRvcF9kaWVsX2VyJTIyJTNBNC41JTJDJTIydG9wX2RpZWxfdGFuZCUyMiUzQTAuMDIlMkMlMjJ1c2VfZ25kX2N1dCUyMiUzQTAlMkMlMjJnbmRfY3V0X3clMjIlM0EwLjUlMkMlMjJnbmRfY3V0X2glMjIlM0EwLjUlMkMlMjJ1c2VfZW5jbG9zdXJlJTIyJTNBMCUyQyUyMnVzZV9zaWRlX2duZCUyMiUzQTAlMkMlMjJ1c2VfdG9wX2duZCUyMiUzQTAlMkMlMjJlbmNsb3N1cmVfd2lkdGglMjIlM0FudWxsJTJDJTIyZW5jbG9zdXJlX2hlaWdodCUyMiUzQW51bGwlMkMlMjJtYXhfaXRlcnMlMjIlM0ExMCUyQyUyMnRvbGVyYW5jZSUyMiUzQTAuMDElMkMlMjJtYXhfbm9kZXMlMjIlM0EyMCUyQyUyMnJxJTIyJTNBMC42JTJDJTIyc3BhcmFtX2xlbmd0aCUyMiUzQTEwMDAlMkMlMjJzcGFyYW1fel9yZWYlMjIlM0E1MCUyQyUyMnVzZV9jYXVzYWxfbWF0ZXJpYWxzJTIyJTNBMCU3RA=="&gt;Click here to open these parameters in the simulator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have more tests in &lt;a href="https://github.com/Ttl/js_2d_fields"&gt;source code tests
folders&lt;/a&gt; against various single-ended and
differential transmission lines that are tested against other simulators or
measurements. Solved characteristic impedances, RLGC parameters, and losses all
currently agree well with the reference simulation or measurements.&lt;/p&gt;
&lt;p&gt;More detailed computation method description and limitations of the method are
in "About" tab of the program.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Click the image above to open the simulator. The source code is available on
&lt;a href="https://github.com/Ttl/js_2d_fields"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Online radar image formation simulator</title><link href="https://hforsten.com/online-radar-image-formation-simulator.html" rel="alternate"></link><published>2026-01-05T00:00:00+02:00</published><updated>2026-01-05T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2026-01-05:/online-radar-image-formation-simulator.html</id><summary type="html">&lt;p&gt;Synthetic aperture radar backprojection image formation simulator working in your browser.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/sar-bp-sim.html" style="text-decoration: none; color: inherit;"&gt;&lt;/p&gt;
&lt;div id="app-launcher"&gt;
&lt;div class="image-wrapper"&gt;
&lt;img src="https://hforsten.com/img/sar-bp-sim/program.png" alt="SAR Backprojection Simulator" width="866" height="874"&gt;
&lt;div class="open-app-btn"&gt;Open application
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was making some radar image formation visualizations using Python and thought
that it would be nice if it was instead written in Javascript and ran on my
browser. That way I could put some real-time visualizations on my blog posts.
I don't have much web development experience, so I gave my Python
program to Claude AI and asked it to convert it to Javascript and it did okay
job. After one day of manual work I think it's working pretty well and I decided
to put it up as a separate web application. Click the above image to open it. It
was quite fun to be able to focus on content and leave the web frontend part
mostly to the AI, I don't think I would have had enough motivation to make this
without it.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Synthetic aperture radar autofocus and calibration</title><link href="https://hforsten.com/synthetic-aperture-radar-autofocus-and-calibration.html" rel="alternate"></link><published>2025-10-07T00:00:00+03:00</published><updated>2025-10-07T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2025-10-07:/synthetic-aperture-radar-autofocus-and-calibration.html</id><summary type="html">&lt;p&gt;3D trajectory position error estimation autofocus, antenna pattern normalization, and polarimetric calibration for drone mounted SAR radar.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/sar_fmcw.jpg" width="2062" height="1200" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar drone. Since the first post I have added
    a Gopro camera and a second GPS for redundancy.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Earlier this year I made a &lt;a href="https://hforsten.com/homemade-polarimetric-synthetic-aperture-radar-drone.html"&gt;polarimetric synthetic aperture radar (SAR) mounted
on a drone&lt;/a&gt;. I have since worked a lot on the software
side to improve the image quality and now the same hardware can generate much
better quality images. The main contribution of this article is description of
a new SAR autofocus algorithm that combines some of the existing SAR autofocus
algorithms to make an algorithm that is well-suited for drone mounted SAR. Also
antenna pattern normalization and polarimetric calibration suitable for drone
mounted SAR with non-linear track is described.&lt;/p&gt;
&lt;h1 id="radar-signal"&gt;Radar signal&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/coords.svg" width="537" height="450" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image formation geometry.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While the drone flies along a track, the radar measures the distances and phases
of the targets in the antenna beam. Neither azimuth (&lt;span class="math"&gt;\(\varphi\)&lt;/span&gt;) nor elevation
(&lt;span class="math"&gt;\(\theta\)&lt;/span&gt;) 
angle of a target can be determined from a single measurement, and all targets at
the same range will overlap in the measurement. However, by recording many
radar measurements from multiple locations, a radar image can be formed.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/meas_range.png" width="711" height="473" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Magnitude of a single recorded range compressed radar signal.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Depending on the radar parameters and radiated waveform, the signal processing to
obtain a range-compressed signal from the raw recorded data might vary. After 
range compression, however, the image formation process is essentially the
same.&lt;/p&gt;
&lt;p&gt;Range resolution is the ability to separate two closely spaced targets. Ideally
it's calculated as &lt;span class="math"&gt;\(\Delta r = c/2B\)&lt;/span&gt;, where &lt;span class="math"&gt;\(c\)&lt;/span&gt; is speed of light, and &lt;span class="math"&gt;\(B\)&lt;/span&gt; is
the bandwidth of the radiated waveform. For example, with a 150 MHz bandwidth the range
resolution is 1 m. For a linear flight track this is also the image resolution
in range direction.&lt;/p&gt;
&lt;p&gt;The phase of the received signal depends on the distance to the target. Because
the signal travels to the target and back, a distance change equal to half the
wavelength causes a full wavelength (360 degrees) shift in the received signal
phase. As a function of target distance, the phase can be
written as &lt;span class="math"&gt;\(\varphi = 4\pi d/\lambda\)&lt;/span&gt;, where &lt;span class="math"&gt;\(d\)&lt;/span&gt; is distance, and &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is
wavelength. For instance, with 6 GHz RF frequency, a distance change of 25 mm (1
inch) causes 360 degree phase shift.&lt;/p&gt;
&lt;h1 id="sar-image-formation"&gt;SAR image formation&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/sar_example.png" width="1788" height="473" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image example with one point target. Radar
    track and target position (left), magnitude of the recorded radar data at each position (middle), and processed SAR image (right).
    Platform height is 30 m.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Due to the wide antenna beam there are multiple targets in the beam at the same
time, but with signal processing it's possible to separate them and generate
much higher resolution image.&lt;/p&gt;
&lt;p&gt;To convert a set of radar measurements into a radar image, matched filtering can
be used. For each pixel in the image grid, generate a reference signal
corresponding to what a target at that position would reflect. For each radar
measurement, multiply the measured signal with complex conjugate of the
reference signal, then sum these products over all measurements. When the
measured signal closely matches the reference signal, their product becomes
large because the phases align and target is generated at that location in the
image. If the phases don't match, the result of the multiplication is a complex number
with a random phase, and summing random complex numbers will average out
generating a low response and that pixel in the image will have low amplitude.&lt;/p&gt;
&lt;p&gt;Assuming the signal is already range compressed and ignoring the amplitude
(antenna pattern and loss due to distance can be compensated after image
formation), then the reference signal just needs to compensate for the phase.&lt;/p&gt;
&lt;p&gt;The image formation can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$I = \sum_{p \in \mathcal{P}} \sum_{n=1}^N S_n(d(\mathbf{p},\mathbf{x_n}))
\exp \left(j \frac{4\pi}{\lambda} d(\mathbf{p},\mathbf{x_n})\right)$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\mathcal{P}\)&lt;/span&gt; is the set of pixels in the image, &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the number of
radar measurements, &lt;span class="math"&gt;\(S_n\)&lt;/span&gt; is range compressed single channel radar measurement,
and &lt;span class="math"&gt;\(d(\mathbf{p},\mathbf{x_n})\)&lt;/span&gt; is the distance to location of pixel
&lt;span class="math"&gt;\(\mathbf{p}\)&lt;/span&gt; from radar position at measurement &lt;span class="math"&gt;\(\mathbf{x_n}\)&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id="position-error"&gt;Position error&lt;/h2&gt;
&lt;p&gt;To get a well focused radar image, the phase of the reference signal used in the
matched filtering needs to match with the recorded signal. If there is any error
in the actual position of the radar during measurement to the position used in
image formation, it will cause errors in the image.&lt;/p&gt;
&lt;p&gt;Assuming the target is just a single point and ignoring the amplitude, with
&lt;span class="math"&gt;\(\Delta \mathbf{x_n}\)&lt;/span&gt; position error on the &lt;span class="math"&gt;\(n\)&lt;/span&gt;th measurement, the error in
image at the location of the target can be calculated as:&lt;/p&gt;
&lt;div class="math"&gt;$$I_p = \sum_{n=1}^N S_n(d(\mathbf{p},\mathbf{x_n})) \exp \left(j \frac{4\pi}{\lambda} d(\mathbf{p},\mathbf{x_n} + \Delta\mathbf{x_n})\right)
= \sum_{n=1}^N \exp \left(-j \frac{4\pi}{\lambda} d(\mathbf{p},\mathbf{x_n}) + j \varphi_p\right) \exp \left(j \frac{4\pi}{\lambda} (d(\mathbf{p},\mathbf{x_n} + \Delta\mathbf{x_n})\right)\\
= \sum_{n=1}^N \exp \left(-j \frac{4\pi}{\lambda}\left(d(\mathbf{p},\mathbf{x_n}) - d(\mathbf{p},\mathbf{x_n} + \Delta\mathbf{x_n})\right) + j\varphi_p \right)
= \sum_{n=1}^N \exp \left(-j \frac{4\pi}{\lambda}\Delta r_{n,p} + j\varphi_p\right) $$&lt;/div&gt;
&lt;p&gt;Instead of all phases lining up at the location of the target, there is some
phase offset left that depends on how the position error changes the distance.
The remaining error &lt;span class="math"&gt;\(\Delta r_{n,p}\)&lt;/span&gt; is the distance error from &lt;span class="math"&gt;\(n\)&lt;/span&gt;th radar position to
the target location at pixel &lt;span class="math"&gt;\(p\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\varphi_p\)&lt;/span&gt; is a constant phase offset from the target. It can be caused by
phase change in reflection, small position offset or just phase offset in the
radar electronics.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;img src="https://hforsten.com/img/autofocus/sar_example2.png" width="1788" height="473" style="width: 90%; height: auto;"/&gt;
&lt;p style="font-size:13px"&gt;SAR image example with one point target, with and
without linear error in radar position. Radar
track and target position (left), image processed without position error
(middle), and image processed with linear position error (right) causing
shift in the image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;SAR track can have any shape, but often it's a straight line. With this
simple track it's possible to calculate how some position errors affect the
image.&lt;/p&gt;
&lt;p&gt;If the distance error &lt;span class="math"&gt;\(\Delta r_{n,p}\)&lt;/span&gt; is constant, then the error is just
a constant phase offset which doesn't affect the magnitude of the image at all.&lt;/p&gt;
&lt;p&gt;Linear error affects the image by shifting the azimuth positions of the targets
in the image. Analysis for it is in many different papers and textbooks, but a good detailed overview can be found in the paper: &lt;a href="https://ieeexplore.ieee.org/document/10868585"&gt;"Performance
Limit of Phase Gradient Autofocus Class Method for Synthetic Aperture Radar" by C. Tu, Z. Dong, A. Yu, Y. Ji, X. Chen and Z. Zhang&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;img src="https://hforsten.com/img/autofocus/sar_example3.png" width="1076" height="473" style="width: 60%; height: auto;"/&gt;
&lt;p style="font-size:13px"&gt;The same scene, but with more complicated position
error. Radar
track and position error (left), and image processed with position error (right) causing
blurring of the target.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;More complicated errors can be thought as piecewise linear error, each piece in
the position error causes the resulting image to be formed at different location
resulting in a blurred image.&lt;/p&gt;
&lt;p&gt;Note the scale of the position error that causes the blurring in the simulate
image. The maximum error is only 1 cm, but even this small error causes
significant loss in the image quality. Since the image formation relies on the
phase, position error must be much smaller than wavelength to not cause phase
errors during image formation. In this case the simulation uses 6 GHz RF
frequency which corresponds to 5 cm wavelength.&lt;/p&gt;
&lt;h2 id="generalized-phase-gradient-autofocus"&gt;Generalized phase gradient autofocus&lt;/h2&gt;
&lt;p&gt;The idea behind autofocus algorithms is to estimate the error in radar position
from the SAR image and radar data. There are many different autofocus algorithms
for different applications, but the most widely used SAR autofocus algorithm is
&lt;a href="https://ieeexplore.ieee.org/document/303752"&gt;phase gradient autofocus (PGA)&lt;/a&gt;.
The &lt;a href="https://ieeexplore.ieee.org/document/8642429"&gt;generalized version&lt;/a&gt; isn't as
well known and was published much later, but in my opinion it's easier to
understand with backprojection image formation background.&lt;/p&gt;
&lt;p&gt;To simplify the notation let's call the terms used for calculating a single
pixel &lt;span class="math"&gt;\(I_p\)&lt;/span&gt; in backprojection &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt;:&lt;/p&gt;
&lt;div class="math"&gt;$$\xi_{n,p} = S_n(d(\mathbf{p},\mathbf{x_n})) \exp \left(j \frac{4\pi}{\lambda} d(\mathbf{p},\mathbf{x_n})\right)\\
I_p = \sum_{n=1}^N \xi_{n,p}$$&lt;/div&gt;
&lt;p&gt;The idea behind phase gradient autofocus is that if we know that a target in the
SAR image is a point target, then phase of &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; should be constant. If this
is not the case, the position error can be solved from the &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; by finding
a phase offset that makes it constant. The phase error can then be converted to
distance to find the position error.&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; is calculated exactly like a pixel in the backprojection image
formation but just without summing up the terms.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;img src="https://hforsten.com/img/autofocus/sar4_labels.png" width="1170" height="454" style="width: 70%; height: auto;"/&gt;
&lt;p style="font-size:13px"&gt;SAR image with position error (left) and phase of ξ calculated at the target location compared to position error.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Calculating the demodulated target phase (&lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt;) for the previous example
scene gives a phase that matches very well with the X-position error that was
added to the radar position. Shifting the phase of the input data by negative of
the &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; would result in a constant phase, which results in a focused
target in the image.&lt;/p&gt;
&lt;p&gt;In this case since the target is on X-axis the distance error is very close to
the X-axis error, but this is not the case generally if the target is at some
other angle. A more general method for solving for the 3D position error from
the phase error will be presented later.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/sar_example6.png" width="1306" height="473" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Low-pass filtering will improve the phase error
    estimation when phase error is slowly varying. SAR image with noise and position error (left) and demodulated phase of the target with and without low-pass filtering (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Low-pass filtering is used to filter out noise
and clutter at the same distance in measurements as the target. Since the target
response is a constant it will be preserved as the signal is low-pass filtered,
but any noise and other objects that were momentarily at the same distance as
the target being considered are filtered out. Low-pass filtering also filters
out high frequency position errors if it's too small, so the low-pass window
size should be set carefully.&lt;/p&gt;
&lt;p&gt;Low-pass filtering operation is often implemented as Fourier transforming the
signal, zeroing the high frequency bins, and then inverse Fourier transforming
to get the filtered signal.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;img src="https://hforsten.com/img/autofocus/gpga.svg" width="1420" height="1304" style="width: 40%; height: auto;"/&gt;
&lt;p style="font-size:13px"&gt;GPGA algorithm flow-chart. Algorithm is iterated
until either phase error is below a threshold or minimum low-pass filter
window size is reached.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The complete GPGA algorithm needs few more operations. In general there are many
point-like targets in the same image and for the best position error estimation
multiple different targets should be used.&lt;/p&gt;
&lt;p&gt;We know that for a point target with position error: &lt;span class="math"&gt;\(\xi_{n,p} = \exp(-j\frac{4\pi}{\lambda}\Delta r_{n,p}
+ j\varphi_p)\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The constant phase offset, &lt;span class="math"&gt;\(\varphi_p\)&lt;/span&gt;, that is different for each
target makes it impossible to just sum different &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; for each target
together. There are few different phase estimators for estimating the phase
error from set of &lt;span class="math"&gt;\(\xi_{n,p}\)&lt;/span&gt; from many targets (pixels &lt;span class="math"&gt;\(p\)&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;One solution is to calculate:&lt;/p&gt;
&lt;div class="math"&gt;$$g_{n,p} = \xi_{n-1,p}^* \xi_{n,p} = \exp\left(j\frac{4\pi}{\lambda}\Delta r_{n-1,p}
- j\varphi_p\right) \exp\left(-j\frac{4\pi}{\lambda}\Delta r_{n,p}
  + j\varphi_p\right)
  = \exp\left(-j\frac{4\pi}{\lambda}(\Delta r_{n,p} - \Delta r_{n-1,p})\right)$$&lt;/div&gt;
&lt;p&gt;Multiplying complex conjugate of n+1th term with nth term causes the constant
phase to cancel out and the result is a difference in phase errors between
adjacent positions (phase gradient in the algorithm name). In this forms the
&lt;span class="math"&gt;\(g_{n,p}\)&lt;/span&gt; from different targets can be summed, taking the argument of the sum
gives the gradient of the phase error, and the phase error can be recovered up
to a constant phase offset by calculating a cumulative sum.&lt;/p&gt;
&lt;div class="math"&gt;$$g_n = \text{Arg} \left(\sum_{p} g_{n,p}\right)$$&lt;/div&gt;
&lt;p&gt;In practice the sum should be weighted according to signal-to-clutter ratio in
each target response for the optimal estimate and this weighting will be
analyzed later. Finally taking cumulative sum of the phase gradient &lt;span class="math"&gt;\(g_n\)&lt;/span&gt; gives
estimate for the phase error.&lt;/p&gt;
&lt;p&gt;After correcting the image with the solved phase error, the phase error is
reduced but might not be completely gone. Decreasing the low pass window size
and repeating the process improves the estimate.&lt;/p&gt;
&lt;h2 id="3d-trajectory-estimation"&gt;3D trajectory estimation&lt;/h2&gt;
&lt;p&gt;So far only the phase error has been solved. It can be used to correct the SAR
image by multiplying the input data with the negative of the phase error.
However, the phase error is proportional to distance error from the radar
position to the target, and depending on a target's location, the phase error
caused by the position error varies. If several targets at
different azimuth and elevation angles are visible at the same time, it might not be
possible to focus them all at the same time with a single phase-correction term.
This is the case when antenna beam is wide, which is precisely the case with my
drone mounted SAR.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/local_images2.svg" width="236" height="147" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Example scenario with two targets and 2D position
    error. Distance errors to the two targets are not equal due to their
    different azimuth angle. r1' is about equal to r1 due to direction of the position error
    being mostly perpendicular to target 1, but this is not the case for target 2. There isn't a single phase correction that will focus them both at the
    same time.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To get all the targets in focus at the same time it's necessary to solve the 3D
position error and use the corrected position of the radar during the image
formation.&lt;/p&gt;
&lt;p&gt;The 3D trajectory estimation is based on paper &lt;a href="https://ieeexplore.ieee.org/document/9380507"&gt;"An Autofocus Approach for
UAV-Based Ultrawideband Ultrawidebeam SAR Data With Frequency-Dependent and 2-D
Space-Variant Motion Errors" by Z. Ding et
al&lt;/a&gt;. The first step is to divide
the SAR image into &lt;span class="math"&gt;\(K\)&lt;/span&gt; subimages and then calculate the phase error in each
image using PGA or GPGA.  The phase error can be turned into a distance error by
unwrapping the phase and multiplying by &lt;span class="math"&gt;\(\lambda/4\pi\)&lt;/span&gt;. The result is
distance error from each radar position (&lt;span class="math"&gt;\(N\)&lt;/span&gt; positions) to each subimage (&lt;span class="math"&gt;\(K\)&lt;/span&gt;
subimages).&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/local_images.svg" width="308" height="276" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;GPGA can solve for distance error to each local
    image and the 3D position error can be solved from multiple distance errors to
    different subimages.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If the subimages are small, we can assume that azimuth and elevation angle to
all the targets in the subimage are similar and use the weighted target
positions as the subimage center position. At this point we have a distance
error from each radar position to each subimage. The number of subimages
is large and we only have three unknowns (x, y, and z position errors), and we
can set up an overdetermined system of equations to solve for the 3D position error from
the distance errors and known subimage center positions.&lt;/p&gt;
&lt;p&gt;Calculating the 3D position error from distance errors to each subimage would
require solving a non-linear system of equations due to distance calculation
not being linear, but assuming that 3D position error is small it can be
linearized which makes solving it much easier and faster.&lt;/p&gt;
&lt;p&gt;Knowing the subimage center azimuth and elevation angles, the distance error
&lt;span class="math"&gt;\(\Delta r_{n,k}\)&lt;/span&gt; from &lt;span class="math"&gt;\(n\)&lt;/span&gt;th radar position to &lt;span class="math"&gt;\(k\)&lt;/span&gt;th subimage center can be
written as a 3D Cartesian vector: &lt;span class="math"&gt;\([\Delta r_{n,k} \cos \theta_{n,k} \cos \varphi_{n,k},
\Delta r_{n,k} \cos \theta_{n,k} \sin \varphi_{n,k}, \Delta r_{n,k} \sin \theta_{n,k}]^T\)&lt;/span&gt;, where
&lt;span class="math"&gt;\(\varphi_{n,k}\)&lt;/span&gt; is azimuth angle from &lt;span class="math"&gt;\(n\)&lt;/span&gt;th radar position to &lt;span class="math"&gt;\(k\)&lt;/span&gt;th subimage
center (&lt;span class="math"&gt;\(0\)&lt;/span&gt; to &lt;span class="math"&gt;\(2\pi\)&lt;/span&gt;) and &lt;span class="math"&gt;\(\theta_{n,k}\)&lt;/span&gt; is elevation angle (&lt;span class="math"&gt;\(-\pi/2\)&lt;/span&gt; to
&lt;span class="math"&gt;\(+\pi/2\)&lt;/span&gt;), note that this is different from the usual spherical coordinate
definition that uses inclination. Assuming that &lt;span class="math"&gt;\(\Delta r_{n,k}\)&lt;/span&gt; is small then the
target azimuth and elevation angles are almost the same before and after the
shift and the linear system for solving the 3D position error at &lt;span class="math"&gt;\(n\)&lt;/span&gt;th position
can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$\mathbf{R_n} = \mathbf{M_n} \Delta \mathbf{x_n}$$&lt;/div&gt;
&lt;div class="math"&gt;$$\mathbf{M_n} = \begin{bmatrix}\cos \theta_{n,0} \cos \varphi_{n,0} &amp;amp; \cos \theta_{n,0} \sin \varphi_{n,0}
&amp;amp; \sin \theta_{n,0} \\ \cos \theta_{n,1} \cos \varphi_{n,1} &amp;amp; \cos \theta_{n,1} \sin \varphi_{n,1}
&amp;amp; \sin \theta_{n,1}\\ ... &amp;amp; ... &amp;amp; ...\\ \cos \theta_{n,k} \cos \varphi_{n,k} &amp;amp; \cos \theta_{n,k} \sin \varphi_{n,k}
&amp;amp; \sin \theta_{n,k} \end{bmatrix}, \, \mathbf{R_n} = \begin{bmatrix}\Delta r_{n,0} &amp;amp; \Delta
r_{n,1} &amp;amp; ... &amp;amp; \Delta r_{n,k}\end{bmatrix}^T, \, \Delta \mathbf{x_n} = \begin{bmatrix}
\Delta x_n &amp;amp; \Delta y_n &amp;amp; \Delta z_n \end{bmatrix}^T$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\mathbf{M_n}\)&lt;/span&gt; is a Kx3 matrix of subimage center angles, &lt;span class="math"&gt;\(\mathbf{R_n}\)&lt;/span&gt; is Kx1 matrix
of distance errors to each subimage, and &lt;span class="math"&gt;\(\Delta \mathbf{x_n}\)&lt;/span&gt; is 3x1 matrix of 3D position errors.&lt;/p&gt;
&lt;p&gt;Solving the linear system for &lt;span class="math"&gt;\(\Delta \mathbf{x_n}\)&lt;/span&gt; at each radar position gives
the desired 3D position error.&lt;/p&gt;
&lt;h3 id="weighted-least-squares"&gt;Weighted Least Squares&lt;/h3&gt;
&lt;p&gt;In a real SAR image signal-to-clutter ratio can vary a lot between different
targets. To get the most accurate estimate measurements should be weighted by
the signal-to-clutter ratio. The calculation for approximate of the
signal-to-clutter ratio from the target demodulated phase is quite involved and
the full details can be found in paper: &lt;a href="https://ieeexplore.ieee.org/document/789644"&gt;"Weighted least-squares estimation of
phase errors for SAR/ISAR autofocus" by Wei Ye, Tat Soon Yeo and Zheng
Bao&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The final result is that weighting factor &lt;span class="math"&gt;\(w_p\)&lt;/span&gt; for each target in GPGA can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$w_p = \frac{1}{\sigma_p^2} = \frac{d}{4 c^2 - 2d - 2 c \sqrt{4 c^2 - 3 d}}$$&lt;/div&gt;
&lt;div class="math"&gt;$$d = E(|g_{n,p}|^2),\, c = E(|g_{n,p}|)$$&lt;/div&gt;
&lt;p&gt;Weighting factor equals &lt;span class="math"&gt;\(1/\sigma_p^2\)&lt;/span&gt; which is the inverse of variance of the
phase error in the measurement from target &lt;span class="math"&gt;\(p\)&lt;/span&gt;. This weight should be used to
weight each target in GPGA phase gradient estimation.&lt;/p&gt;
&lt;p&gt;In 3D trajectory estimation the weighting factor is the combined weight of all
targets in the subimage. It can be calculated as:&lt;/p&gt;
&lt;div class="math"&gt;$$w_k = 1 / \sum_{p} 1 / w_p$$&lt;/div&gt;
&lt;h2 id="gpga-3d-trajectory-deviation-estimation"&gt;GPGA 3D trajectory deviation estimation&lt;/h2&gt;
&lt;div id="centered" &gt;
&lt;img src="https://hforsten.com/img/autofocus/gpga_tde.svg" width="1406" height="1225" style="width: 40%; height: auto;"/&gt;
&lt;p style="font-size:13px"&gt;GPGA algorithm with 3D trajectory deviation estimation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Putting it all together the algorithm is quite computationally demanding
due to multiple image formations required. They could be skipped in some cases,
but the best performance is obtained when a new image is formed after every
iteration to get a better estimate of true target locations. However, when using
&lt;a href="https://ieeexplore.ieee.org/document/1238734"&gt;fast factorized backprojection&lt;/a&gt;
it's quite fast when implemented on GPU even with a large amount of data.&lt;/p&gt;
&lt;p&gt;Compared to other autofocus algorithms I believe this is one of the most general
ones. It computes the 3D position error, it doesn't assume linear track and can
work with any shaped flight track, it can correct for position error that
exceeds range resolution, it works with digital elevation model, and it can be
extended to work with 3D imaging SAR or bistatic SAR systems.&lt;/p&gt;
&lt;p&gt;I implemented the algorithm on GPU using custom CUDA kernels and despite it's
heavy computation requirement it runs very quickly. In practice even just few
iterations result in a large improvement in image quality and it's much more
efficient than the &lt;a href="https://hforsten.com/homemade-polarimetric-synthetic-aperture-radar-drone.html"&gt;Minimum entropy autofocus&lt;/a&gt; that
I previously used.&lt;/p&gt;
&lt;p&gt;The code is open source and available as &lt;a href="https://github.com/Ttl/torchbp/blob/d756704656ce8c18cd77ed40516bc8da597ca448/torchbp/autofocus.py#L325"&gt;"gpga_bp_polar_tde" function in torchbp package&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="synthetic-example"&gt;Synthetic example&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/synthetic0.png" width="1192" height="473" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Ideal SAR image of the point targets without any position error (left). Position error added to the image (right), Y-axis linear trend is removed.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I made a small synthetic example with nine point targets in a grid. The height
of the radar was set to 20 m and it moves linearly along Y-axis sampling every
quarter wave length (12.5 mm / 0.5 inch). I added random low frequency 3D
position error to the radar position and used the corrupted position to form the
initial SAR image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/synthetic1.png" width="1192" height="473" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Image with position error (left) and the same image after
    applying the autofocus algorithm (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The maximum position error is 0.1 meters and it causes the image to be extremely
defocused. Position error causes sidelobes to appear around each target.&lt;/p&gt;
&lt;p&gt;The image was divided into nine subimages for autofocus so that each point
target is in its own image. After applying six iterations of the autofocus
algorithm the solved image looks almost exactly like the error-free ideal image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/synthetic2.png" width="1307" height="473" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solved 3D position error (left) and error in
    solved position in wavelengths (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The solved 3D position error matches well to the error that was added. The
maximum error is 0.1 wavelengths in Z-direction and RMS error is 0.0007
wavelengths. The image quality is the most sensitive to X-direction error that
changes the measured distance the most, Z-axis error causes the smallest error
in this case with low look angle as Z-axis is mostly perpendicular to the
distance to the target.&lt;/p&gt;
&lt;h2 id="real-data-example"&gt;Real data example&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_gopro.jpg" width="2160" height="2424" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Camera image of the scene. This is from a different
    day and little farther away to capture more of the scene in the same image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For a real data example I flew the radar drone in a straight line at 5 m/s
velocity at 110 m altitude. The radar recorded full quad (HH, HV, VH, VV)
polarizations with all four polarizations sampled at 1.8 ms repetition interval.
The total number of samples is 20,000 for a total track length of 180 meters and
36 seconds of data capture time.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_no_autofocus.png" width="681" height="866" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image without autofocus using only the IMU and GPS
    for the radar position.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Without any autofocus algorithm and just relying on the GPS and IMU data fusion
results in very poor quality image. The reported GPS position &lt;a href="https://en.wikipedia.org/wiki/Dilution_of_precision"&gt;horizontal
dilution of precision
(HDOP)&lt;/a&gt; is on average 0.5
which is very good, but it's still not good enough for SAR accuracy requirement.
&lt;a href="https://en.wikipedia.org/wiki/Real-time_kinematic_positioning"&gt;RTK GPS&lt;/a&gt; would
be much more accurate, but it requires a ground station and there isn't a small
enough RTK GPS module that would fit well on the small FPV drone I'm using.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_autofocus.png" width="681" height="866" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;The same image after autofocus with GPGA 3D
    trajectory estimation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After GPGA 3D trajectory deviation autofocus algorithm the image quality is much
better. The image isn't still perfectly focused and it shouldn't be expected to,
because no digital elevation model (DEM) is used. Instead every target is
assumed to be on a flat plane at zero height. This assumption does not hold for
a real scene as buildings and trees are at higher elevation than the ground and
there is some elevation difference of the ground at different locations in the
image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_trajectory.png" width="711" height="473" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solved position error.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The Z-axis solved position error is very large and it's unlikely that the real
Z-axis error in the drone's flight elevation is this big and the error is likely
due to the flat-plane assumption. If I disable Z-axis error estimation and force
it to be zero &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_no_z.png"&gt;the image quality is
worse&lt;/a&gt;. For a better
comparison here's a link to image &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus.png"&gt;with Z-axis correction for
reference&lt;/a&gt; and also the &lt;a href="https://hforsten.com/img/autofocus/07_19_1_no_autofocus.png"&gt;image
without autofocus&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The raw image dimensions are 6663 range samples by 13935 azimuth samples,
requiring a total of &lt;span class="math"&gt;\(6663\times13935\times20000 = 1.9\times10^{12}\)&lt;/span&gt;
backprojections to form the image when using ordinary backprojection. Using fast
factorized backprojection image formation takes just 1.3 seconds on RTX 3090 Ti
GPU, which is about 10 times faster than non-factorized backprojection. The
final fully polarized image takes four times longer as it requires generating image for
each of the four polarizations.&lt;/p&gt;
&lt;p&gt;Autofocus uses a smaller image size of 2333 x 12541 pixels and divides it into
10x10 grid for a total of 100 subimages. Autofocus is only calculated for one
polarization and the same solved position is used also for the other
polarizations. The starting lowpass window size is 2000 samples and it's reduced
by a factor of 0.7 every iteration until it's below 10 samples. The total number
of iterations is 15 and the calculation takes a total of 17.5 seconds. It's a significant
increase compared to the time it takes to form a single image due to requiring
multiple image formations, but since the image formation is so fast when
implemented on GPU it doesn't matter that much. Less iterations could work in
this case, but in general these settings work well.&lt;/p&gt;
&lt;h1 id="antenna-pattern-normalization"&gt;Antenna pattern normalization&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/ground_patch.svg" width="285" height="161" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Patch of ground illuminated by the radar. Three
    different areas for normalizing the reflectivity are colored.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The received power from a target depends on the target's distance to the radar, 
location in the antenna beam, and the radar reflectivity of the target. Radar
reflectivity is what we want to determine and amplitude variations caused by the
distance and antenna pattern is something that should be removed from the radar
image.&lt;/p&gt;
&lt;p&gt;Received power of a single radar pulse from a target can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$P_r = C \frac{\sigma G_{rx}(\theta, \phi) G_{tx}(\theta, \phi)}{r^4}$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(C\)&lt;/span&gt; is a constant that includes transmit power, receiver gain and other
radar constant factors affecting the received power, &lt;span class="math"&gt;\(G_{rx}\)&lt;/span&gt; is the receiver
antenna gain, &lt;span class="math"&gt;\(G_{tx}\)&lt;/span&gt; is the transmitting antenna gain, &lt;span class="math"&gt;\(r\)&lt;/span&gt; is the distance
from radar to the target, and &lt;span class="math"&gt;\(\sigma\)&lt;/span&gt; is the radar reflectivity of the target.&lt;/p&gt;
&lt;p&gt;Amplitude of the received signal is proportional to square root of &lt;span class="math"&gt;\(P_r\)&lt;/span&gt;. It
would be possible to add normalization in the backprojection calculation, but
it's more efficient to calculate backprojection of the power received from
a reference reflectivity separately:&lt;/p&gt;
&lt;div class="math"&gt;$$P_{ref} = C \sum_{p \in \mathcal{P}} \sum_{n=1}^N \frac{\sigma_{ref} G_{rx}(\theta_n, \phi_n) G_{tx}(\theta_n, \phi_n)}{r^4}$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\mathcal{P}\)&lt;/span&gt; is the set of pixels in the image, &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the
number of radar measurements, &lt;span class="math"&gt;\(\theta_n\)&lt;/span&gt; and &lt;span class="math"&gt;\(\phi_n\)&lt;/span&gt; are elevation and azimuth
angles to the pixel at &lt;span class="math"&gt;\(n\)&lt;/span&gt;th position, and &lt;span class="math"&gt;\(\sigma_{ref}\)&lt;/span&gt; is the reference reflectivity
where we want to normalize. &lt;/p&gt;
&lt;p&gt;Removing the antenna pattern and distance variation from SAR image &lt;span class="math"&gt;\(I\)&lt;/span&gt; using
&lt;span class="math"&gt;\(P_{ref}\)&lt;/span&gt; can be done by simply dividing the squared image by the reference power:&lt;/p&gt;
&lt;div class="math"&gt;$$\sigma_0 = \frac{|I|^2}{P_{ref}}$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(P_{ref}\)&lt;/span&gt; can be calculated on a coarser grid and number of sweeps can be
decimated since antenna pattern doesn't change very fast making it faster to
calculate than image backprojection.&lt;/p&gt;
&lt;p&gt;We could simply set &lt;span class="math"&gt;\(\sigma_{ref} = 1\)&lt;/span&gt; and this is valid option if we want to
normalize to point targets, but depending on how exactly the ground reflectivity
is defined some other options are also available. Reflectivity of the ground can
be defined in multiple ways. The most common one is to define &lt;span class="math"&gt;\(\sigma = \sigma_0
A_{\sigma}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\sigma_0\)&lt;/span&gt; is the radar reflectivity of ground per unit area
and &lt;span class="math"&gt;\(A_{\sigma} = \delta x \delta y\)&lt;/span&gt; is the area of the patch on the ground. &lt;/p&gt;
&lt;p&gt;The size of the patch that needs to be considered is the pixel resolution in the
SAR image. Radar measures radials distance, which means that &lt;span class="math"&gt;\(\delta_r\)&lt;/span&gt; is
a constant that depends on the radar parameters. The angular resolution of the radar
&lt;span class="math"&gt;\(\delta_\varphi\)&lt;/span&gt; is constant, so &lt;span class="math"&gt;\(\delta y = 2r\sin(\delta_\varphi/2)\)&lt;/span&gt;. For SAR
image with non-isotropic antenna the resolution isn't as good, but
antenna weighting is already included in the calculation and we can use the
resolution with isotropic antenna.&lt;/p&gt;
&lt;p&gt;This means that to normalize to ground reflectivity &lt;span class="math"&gt;\(\sigma_0\)&lt;/span&gt; we should set:&lt;/p&gt;
&lt;div class="math"&gt;$$\sigma_{ref} = A_{\sigma} = \delta_x \delta_y = \frac{\delta_r 2 r \sin(\delta_\varphi/2)}{\sin(\theta)} = C \frac{r}{\sin(\theta)}$$&lt;/div&gt;
&lt;p&gt;Constant factor &lt;span class="math"&gt;\(C\)&lt;/span&gt; can be merged with the constant factor in the &lt;span class="math"&gt;\(P_{ref}\)&lt;/span&gt;
calculation. To avoid dividing by zero at zero ground range we can limit the &lt;span class="math"&gt;\(\theta\)&lt;/span&gt; used in
calculation to angular size of the first resolution cell: &lt;span class="math"&gt;\(\theta_{\text{max}} \approx \arcsin(z/(z+\delta r)) \approx \pi/2 - \sqrt{2\delta_r/z}\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\beta_0\)&lt;/span&gt; or &lt;span class="math"&gt;\(\gamma_0\)&lt;/span&gt; can be calculated the same way by changing the
&lt;span class="math"&gt;\(\sigma_{ref}\)&lt;/span&gt; definition. &lt;span class="math"&gt;\(\beta_0\)&lt;/span&gt; doesn't include any correction for the
grazing angle and &lt;span class="math"&gt;\(\sigma_0 = \beta_0 \sin\theta\)&lt;/span&gt;. The third definition is to
calculate &lt;span class="math"&gt;\(\gamma_0 = \sigma/A_\gamma = \beta_0 \tan \theta\)&lt;/span&gt;. This normalizes
for the area of the patch that the radar sees. If the ground reflectivity would
be completely diffuse so that it reflects equally well in all directions, then
the reflected power only depends on the area of the target that the radar sees
and only this definition would result in no incidence angle variation in the
image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_autofocus_gamma0.png" width="681" height="866" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;The previous measurement after removal of range
    and antenna pattern caused amplitude variation. The reflectivity is
    normalized to sigma zero.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After normalization the resulting image is much flatter in amplitude and at the
edges where the signal-to-noise ratio is lower the noise floor is raised to
visible level. Absolute radiometric calibration is not done since the constant
&lt;span class="math"&gt;\(C\)&lt;/span&gt; has not been determined. It would require measuring a known reflectivity
target and based on that measurement the &lt;span class="math"&gt;\(C\)&lt;/span&gt; constant can be determined.
Incidence angle was calculates assuming a flat ground.&lt;/p&gt;
&lt;p&gt;For comparison &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_gamma0.png"&gt;the same image with gamma
zero&lt;/a&gt; is slightly brighter
at farther distances where the grazing angle is low. &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_beta0.png"&gt;Beta0
image&lt;/a&gt; looks very similar to
the sigma0 image, there's only difference at extremely close ranges.  &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_sigma0.png"&gt;Here's
also a clickable link to sigma0
image&lt;/a&gt;.  I recommend
opening them in separate tabs for easier comparison.&lt;/p&gt;
&lt;p&gt;The first option of just setting &lt;span class="math"&gt;\(\sigma_{ref}=1\)&lt;/span&gt; results in &lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_point.png"&gt;very
similar image&lt;/a&gt; to gamma zero
normalization, only differing at very close distances. It turns out that at low
grazing angle they are the same up to a constant.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_tx_power.png" width="822" height="849" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Visualized &lt;span class="math"&gt;\(P_{ref}\)&lt;/span&gt; for the previous image for VV polarization.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Plotting the image of &lt;span class="math"&gt;\(P_{ref}\)&lt;/span&gt; shows how the antenna illuminates the ground.
The flight height was 110 m and the antenna was angled at 10 degrees below the
horizon and the antenna boresight should hit the ground at 600 m distance.
However, the best signal to noise ratio is obtained at 
100 m since it's closer to the radar and antenna gain is still quite good at
that angle.&lt;/p&gt;
&lt;p&gt;Simulated antenna pattern was used in normalization and especially at far away
angles from the boresight the actual radiation pattern can differ significantly.
The uncertainty in antenna pattern causes amplitude difference at different
angles, and in the polarimetric case the antenna pattern for H and
V polarizations is slightly different which can cause errors in the relative
channel amplitudes.&lt;/p&gt;
&lt;h1 id="polarimetric-calibration"&gt;Polarimetric calibration&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/sar_fmcw_block_pol.svg" width="706" height="339" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Block diagram of the radar with error sources
    affecting the measured polarization labeled.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A fully polarized radar measures four polarizations: HH, HV, VH, and VV. The
first letter is the polarization of the transmitter, the second one is the
polarization of the receiver, and V and H stand for vertical and horizontal.
This is enough information to fully characterize the polarization response of
a target and allows calculating received power for an arbitrarily polarized receiver
antenna when the target is illuminated with an arbitrary polarization.&lt;/p&gt;
&lt;p&gt;The transmission and reception of the different polarizations are implemented by
a switch that selects which polarization is transmitted or received. The same
electronics are used for all polarizations which results in good amplitude and
phase match across all frequencies. However, due to
polarization switches, slightly different lengths of SMA cables between switches and
antennas, and small differences between the antennas, there are amplitude and
phase differences between the polarizations. Additionally, the limited
isolation of the switches and antennas causes some crosstalk between
different polarizations.&lt;/p&gt;
&lt;p&gt;To make accurate polarimetric measurements, channel amplitude and phase
differences as well as crosstalk need to be measured and calibrated out. The effect of
receiver and transmitter errors can be modeled as (&lt;a href="https://ieeexplore.ieee.org/document/1610834"&gt;Ainsworth, et.
al&lt;/a&gt;):&lt;/p&gt;
&lt;div class="math"&gt;$$\begin{bmatrix} O_{HH} &amp;amp; O_{HV}\\ O_{VH} &amp;amp; O_{VV}\end{bmatrix} = \begin{bmatrix} r_{HH} &amp;amp; r_{HV}\\ r_{VH} &amp;amp; r_{VV}\end{bmatrix} \begin{bmatrix} S_{HH} &amp;amp; S_{HV}\\ S_{VH} &amp;amp; S_{VV}\end{bmatrix} \begin{bmatrix} t_{HH} &amp;amp; t_{HV}\\ t_{VH} &amp;amp; t_{VV}\end{bmatrix}$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\([O]\)&lt;/span&gt; is the observed polarization, &lt;span class="math"&gt;\([S]\)&lt;/span&gt; is the true target scattering
matrix, and &lt;span class="math"&gt;\([t]\)&lt;/span&gt; and &lt;span class="math"&gt;\([r]\)&lt;/span&gt; are matrices that model how the scattering matrix is
transformed during the measurement by transmitter and receiver.&lt;/p&gt;
&lt;p&gt;The previous equation can be written also in form:&lt;/p&gt;
&lt;div class="math"&gt;$$ \begin{bmatrix}O_{HH}\\O_{HV}\\O_{VH}\\O_{VV}\end{bmatrix} = \mathbf{M} \begin{bmatrix}S_{HH}\\S_{HV}\\S_{VH}\\S_{VV}\end{bmatrix} $$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\mathbf{M}\)&lt;/span&gt; is a 4x4 complex valued matrix that contains all the error terms
that affect the target measurement.&lt;/p&gt;
&lt;p&gt;A simple error correction method is to assume that there is no crosstalk and
that absolute radiometric correction is already done. In
which case &lt;span class="math"&gt;\(\mathbf{M}\)&lt;/span&gt; can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$\mathbf{M} = \begin{bmatrix}k\alpha&amp;amp;0&amp;amp;0&amp;amp;0\\0&amp;amp;1/\alpha&amp;amp;0&amp;amp;0\\0&amp;amp;0&amp;amp;\alpha&amp;amp;0\\0&amp;amp;0&amp;amp;0&amp;amp;k^{-1}\alpha^{-1}\end{bmatrix}$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\alpha\)&lt;/span&gt; corrects for the imbalance of &lt;span class="math"&gt;\(HV\)&lt;/span&gt; and &lt;span class="math"&gt;\(VH\)&lt;/span&gt; measurements and &lt;span class="math"&gt;\(k\alpha\)&lt;/span&gt;
corrects for &lt;span class="math"&gt;\(HH\)&lt;/span&gt; and &lt;span class="math"&gt;\(VV\)&lt;/span&gt; imbalance. Due to reciprocity, we should have &lt;span class="math"&gt;\(HV
= VH\)&lt;/span&gt; and we can solve for &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt; from the measured data by calculating the
amplitude and phase difference between the measured &lt;span class="math"&gt;\(HV\)&lt;/span&gt; and &lt;span class="math"&gt;\(VH\)&lt;/span&gt; polarizations.
Calculating the correlation matrix &lt;span class="math"&gt;\(C = E(O O^\dagger)\)&lt;/span&gt;, where &lt;span class="math"&gt;\(E(\cdot)\)&lt;/span&gt;
calculates the mean over all pixels in the SAR image and &lt;span class="math"&gt;\(\dagger\)&lt;/span&gt; is
conjugate transpose, we can solve for &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt;:&lt;/p&gt;
&lt;div class="math"&gt;$$\alpha = \left|\frac{C_{VHVH}}{C_{HVHV}}\right|^{1/4} \exp(j\text{Arg}(C_{VHHV})/2)$$&lt;/div&gt;
&lt;p&gt;For example: &lt;span class="math"&gt;\(C_{VHHV} = E(O_{VH} O_{HV}^*)\)&lt;/span&gt;. This calibration was already
performed in the previous images so that the HV and VH channels could be summed for
visualization (with &lt;span class="math"&gt;\(k=1\)&lt;/span&gt;). Determining the &lt;span class="math"&gt;\(k\)&lt;/span&gt; factor is harder and requires measuring
a target with a known scattering matrix, usually a corner reflector.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/dihedral.svg" width="924" height="395" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Scattering matrices of trihedral and dihedral
    corner reflectors at various angles. By measuring a known target, the
    polarimetric calibration matrix can be solved.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This simple measurement is enough to calibrate the biggest error sources, but
for more accurate result the crosstalk terms should also be solved. There are
several different polarimetric calibration methods and the one I decided to
implement is &lt;a href="https://ieeexplore.ieee.org/document/1610834"&gt;"Orientation angle preserving a posteriori polarimetric SAR calibration" by T. L. Ainsworth, L. Ferro-Famil and Jong-Sen Lee&lt;/a&gt;. This method works by
calculating correlation matrix from a SAR image of a scene, and only assuming
scattering reciprocity (HV=VH) calculates most of the error terms based on the expected
form of the correlation matrix. However, even with this method, at least one
measurement of a known target is required to determine the &lt;span class="math"&gt;\(k\)&lt;/span&gt; calibration
factor.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_autofocus_sigma0_pol_cal.png" width="681" height="866" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;The previous image with polarimetric calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the polarimetric calibration, the largest difference is that the HH and VV
channel amplitude balance was corrected increasing the VV channel power making
the image redder compared to 
&lt;a href="https://hforsten.com/img/autofocus/07_19_1_autofocus_sigma0.png"&gt;the uncorrected image&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_autofocus_sigma0_pol_cal_pauli.png" width="681" height="865" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Pauli decomposition of the calibrated image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After calibration, polarimetric decompositions, such as Pauli
decomposition, can be applied. This is often used presentation for SAR data since it can be
interpreted physically. The three channels are: HH+VV, which corresponds to
targets where H and V polarization return at the same phase, for example
a sphere or trihedral corner reflector. HH-VV corresponds to a dihedral
reflector at 0 degrees, where one of the polarizations is flipped in phase. The last
channel VH+HV corresponds to targets that return orthogonal polarization, such
as 45 degree oriented dihedral reflector.&lt;/p&gt;
&lt;p&gt;For surface targets, HH+VV corresponds to single or odd number of bounces, and
HH-VV double or even number of bounces. Targets that depolarize the
reflection, such as tree canopy are visible on the HV+VH channel.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/07_19_1_autofocus_full_pauli.png" width="920" height="865" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Full SAR image with Pauli decomposition and
    without antenna pattern normalization.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the fully zoomed-out image, quite lot of details can be seen even at long
distance, but the grazing angle at far away is very low, so only tree tops and
buildings are visible and shadowing is severe. This is without antenna
pattern normalization, since it doesn't make sense to normalize to flat ground
that isn't visible at long range, but the polarimetric calibration is still applied.&lt;/p&gt;
&lt;p&gt;Buildings at (+250 X, -1000 Y) with lot of sharp corners look very different
from the surrounding trees and grass, which reflect a lot of cross-polarization.
Using polarimetric image, it's possible to classify the target response, such as vegetation
or artificial targets, which is one of the main uses of polarimetric SAR imaging.&lt;/p&gt;
&lt;h1 id="videosar"&gt;VideoSAR&lt;/h1&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/autofocus/9_27_map.webm" type="video/webm"&gt;
&lt;source src="https://hforsten.com/video/autofocus/9_27_map.mp4" type="video/mp4"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;When a long radar measurement is divided into multiple short images, the result
can be turned into a video. The drone flew autonomously a circular pattern over a field
while the radar recorded continuously. Afterwards I autofocused and generated SAR
images of short sections of the data, then synced them with the GoPro recording. The
video is played back at 2x speed.&lt;/p&gt;
&lt;p&gt;Each image has 2048 radar measurements, with 75% overlap between consecutive images.
Having more data in each frame improves the image resolution and decrease the
amount of noise, but lowers the frame rate in the video.
The high overlap increases the frame rate as a new frame can be created when
there is 25% of new data instead of waiting for a full window size and allows
for good quality image with reasonable frame rate.&lt;/p&gt;
&lt;p&gt;Each frame is autofocused individually and the solved position is used for the
future frames. Apart from this, no inter-frame stabilization is performed. There
are some small geometric errors, but overall the stability between frames
is quite good. Antenna pattern is normalized to gamma0 and image edges with
low SNR are cut out. Radial lines appear on some frames caused by RF interference.&lt;/p&gt;
&lt;h1 id="urban-scene"&gt;Urban scene&lt;/h1&gt;
&lt;p&gt;I captured another scene with more buildings. The drone flew in a straight line
at height of 110 m and a speed of 5 m/s for 72 seconds, covering a total track
length of 360 m. During this time the radar captured 40,000 sweeps.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/08_23_2_gopro.jpg" width="2160" height="2196" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Camera image of the scene.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/08_23_2_autofocus.png" style="width: 80%;
    height: auto;"/&gt; &lt;p style="font-size:13px"&gt;Image of urban scene after
    autofocus, antenna pattern normalization, polarimetric calibration,
    gamma zero normalization, and Pauli decomposition visualization. The camera image
    is taken from approximately X=100 m.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Autofocus required 41 seconds for this scene, and image formation took 2.3
seconds per polarization. The resulting image is 6663 by 27863 pixels.
Autofocus improves the image quality significantly. &lt;a href="https://hforsten.com/img/autofocus/08_23_2_no_autofocus.png"&gt;Without
autofocus&lt;/a&gt; the image is badly
unfocused.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;A complete software pipeline for autofocusing and calibrating polarimetric SAR
image was presented. Autofocus algorithm that uses generalized phase gradient
autofocus (GPGA) and 3D trajectory deviation estimation to solve for the 3D
position error was introduced. Using GPU accelerated fast factorized
backprojection even very large SAR images can be focused very quickly.&lt;/p&gt;
&lt;p&gt;The resulting image quality is very good especially considering that this might
be one of the cheapest drone mounted SAR systems.&lt;/p&gt;
&lt;p&gt;The software is open sourced and available at:
&lt;a href="https://github.com/Ttl/torchbp"&gt;https://github.com/Ttl/torchbp&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Designing a low-cost high-performance 10 MHz - 15 GHz vector network analyzer</title><link href="https://hforsten.com/designing-a-low-cost-high-performance-10-mhz-15-ghz-vector-network-analyzer.html" rel="alternate"></link><published>2025-04-15T00:00:00+03:00</published><updated>2025-04-15T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2025-04-15:/designing-a-low-cost-high-performance-10-mhz-15-ghz-vector-network-analyzer.html</id><summary type="html">&lt;p&gt;Designing a cheap two-port vector network analyzer with good measurement accuracy.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna3_on.jpg" width="1600" height="1186" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;VNA PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Vector network analyzer is a device used to measure &lt;a href="https://en.wikipedia.org/wiki/Scattering_parameters"&gt;scattering
parameters&lt;/a&gt; of electrical
circuits operating at high frequencies. S-parameters tell how much a circuit
reflects power back to the source and how much it attenuates or amplifies it to
the other ports. Working with S-parameters is an essential part of designing any
electronics operating at GHz frequencies.&lt;/p&gt;
&lt;p&gt;These devices aren't cheap, especially as the operating frequency increases.
High end VNAs that operate at millimeter-wave frequencies can cost several
hundred thousand dollars, and even affordable lower-frequency devices are
expensive.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/old_vnas.jpg" width="1814" height="1600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;My two previous homemade VNAs.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In 2016, I made my first &lt;a href="https://hforsten.com/cheap-homemade-30-mhz-6-ghz-vector-network-analyzer.html"&gt;cheap 10 MHz to 6 GHz two-port vector network
analyzer&lt;/a&gt;. At the time, there were no cheap VNAs
available, and I had to design one myself. It was
functional but had high leakage between the two ports, causing big measurement
inaccuracies, which resulted in unusable accuracy for many measurements. The next
year, I designed &lt;a href="https://hforsten.com/improved-homemade-vna.html"&gt;improved version&lt;/a&gt; that had better
performance and was actually useful. I have used it since for my own projects,
but lately, it has started to feel limiting, and I wanted to either buy or make
a better VNA. I would want to have higher maximum frequency, hopefully &amp;gt;10 GHz,
better measurement accuracy, and good port-to-port isolation.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/nano_and_librevna.jpg" width="1691" height="800" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;nanoVNA (left) and LibreVNA (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Nowadays, there are many cheap commercial VNAs. The most popular is probably
&lt;a href="https://nanovna.com/"&gt;nanoVNA&lt;/a&gt;. There are several different versions of it, but
they are mostly limited to around 1 - 4 GHz, which is not enough for me. It's
also a so-called 1.5-receiver design that can only measure S11 and S21,
requiring manually flipping the device being measured to fully measure the
two-port S-parameters. It isn't any better than what I already have. There are
also several other 1.5-receiver VNAs, some better and some worse, but the
1.5-receiver architecture is too limiting and results in inaccurate
measurements to be worth considering.&lt;/p&gt;
&lt;p&gt;A step up on that is &lt;a href="https://github.com/jankae/LibreVNA"&gt;LibreVNA&lt;/a&gt;. It's a 100
kHz to 6 GHz two-port VNA that was &lt;a href="https://github.com/jankae/VNA?tab=readme-ov-file#motivation"&gt;partly based on my previous VNA
design&lt;/a&gt;. It has better performance than my previous 6 GHz
VNA, but the performance isn't as good as I would want. There is quite high
leakage above 3 GHz that limits the measurement accuracy. Unlike
the nanoVNA, it's a proper two-port VNA that can measure two-port S-parameters
without manually requiring flipping of the device. However, it's
a three-receiver design instead of a better-performing four-receiver design.
There is a shared reference receiver before the port switch that is used for
measuring the output signal instead of two separate reference receivers for each
port. Using advanced calibrations such as
&lt;a href="https://scikit-rf.readthedocs.io/en/latest/examples/metrology/Multiline%20TRL.html"&gt;TRL&lt;/a&gt;,
and &lt;a href="https://scikit-rf.readthedocs.io/en/latest/api/calibration/generated/skrf.calibration.calibration.UnknownThru.html"&gt;unknown
thru&lt;/a&gt;
requires a two-port VNA with four receivers. These are very useful as they relax
the requirements on how well the calibration kit needs to be known, increasing
the measurement accuracy, especially when using a low-cost, inaccurate calibration kit.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/keysight.jpg" width="1600" height="1067" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;&lt;a href="https://www.keysight.com/fi/en/product/E5063A/e5063a-ena-vector-network-analyzer.html"&gt;Keysight E5603 VNA&lt;/a&gt;. 100 kHz to 18 GHz option has
    a list price of 53,000 € and this isn't even a high-end model.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There are better performing VNAs that would be perfect for my requirements, such as those from &lt;a href="https://coppermountaintech.com/"&gt;Copper Mountain&lt;/a&gt; or &lt;a href="https://www.keysight.com/fi/en/products/network-analyzers.html"&gt;Keysight&lt;/a&gt;, but even the cheapest &amp;gt;10 GHz VNAs are over ten thousand dollars.&lt;/p&gt;
&lt;p&gt;I was unable to find a cheap two-port VNA that would meet my requirements, but after
thinking it a bit, I was quite sure that I could make my own VNA that would have
better performance than any other cheap VNA currently available, even when
factoring in the higher prices at small prototype quantities.&lt;/p&gt;
&lt;h1 id="vna-architecture"&gt;VNA architecture&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna3_block_single_source.svg" width="669" height="498" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Generic block diagram of a four receiver VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A typical VNA consists of excitation and local oscillator signal sources. The source
is routed through a port switch to one port, with the other port being
terminated to 50 ohms. The signal then passes through directional couplers, which
sample the incident and reflected waves. These waves are mixed with the local oscillator to
convert them to low frequencies that can be sampled with an ADC. These measurements can
be used to calculate both the amplitude and phase of the reflected and
transmitted waves.&lt;/p&gt;
&lt;p&gt;A major issue with this architecture is the port switch. It requires &amp;gt;100 dB
isolation over the whole operational bandwidth of the VNA, which is very hard to
achieve, requiring that several RF switches in their separate shielded
enclosures are put in series. Especially at &amp;gt;10 GHz RF switches, shielded
enclosures, and amplifiers can get quite expensive.&lt;/p&gt;
&lt;p&gt;Traditionally, designing a wideband RF signal source has also been
a challenge. Often requiring multiple mixers, oscillators, filters, and frequency
multipliers to achieve high-quality signal over the entire VNA frequency range.
However, nowadays there are cheap PLL chips with integrated bank of VCOs that
can generate signals over very wide frequency range. For example, 
&lt;a href="https://www.ti.com/product/LMX2594"&gt;LMX2594&lt;/a&gt; is a 10 MHz to 15 GHz RF signal
source in a single package that costs $38 at large quantities.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna3_block.svg" width="474" height="485" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Block diagram of dual-source VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using integrated phase-locked loop (PLL) ICs for the source signal generation
makes it possible to remove the port switch and instead have separate signal
sources for each port. This is both cost-effective and simplifies achieving the
isolation requirement.&lt;/p&gt;
&lt;p&gt;The drawback is that variable attenuator for source power level control and
filter bank for filtering the harmonics needs to be duplicated for each port.
However, if low adjustable source power range is acceptable, the external variable
attenuator can be removed, and the internal power adjustment of the PLL chip can
be used instead. The LMX2594 output power can be adjusted by about 10 dB.&lt;/p&gt;
&lt;p&gt;Filter bank can also be removed if harmonics are acceptable. Ideally, harmonics
shouldn't affect the S-parameter measurements when measuring linear devices.
However, in practice there can be some non-linearity in the receiver that causes
&lt;a href="https://anritsu.typepad.com/vnareflections/2019/10/why-source-harmonics-are-important-when-selecting-a-vna-for-filter-characterization.html"&gt;harmonics to mix to the fundamental
frequency&lt;/a&gt;.
Including the filters would slightly improve the measurement quality.&lt;/p&gt;
&lt;p&gt;After looking at the prices of variable attenuators and RF switches at 15 GHz,
I decided that I'm fine with the harmonics and low output power adjustment
range.&lt;/p&gt;
&lt;h2 id="directional-couplers"&gt;Directional couplers&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/coupler.svg" width="219" height="105" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Directional coupler schematic.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;VNA needs directional couplers to sample the incident and reflected waves at
both ports. These couplers should also operate from 10 MHz to 15 GHz with good
directivity and low loss. The standard directional coupler used in many
commercial VNAs is a resistive directional bridge. It requires a balun, which in
this case is implemented with a coaxial cable surrounded by ferrite
beads to extend its operation to low frequencies.&lt;/p&gt;
&lt;p&gt;In my previous VNA, I used a similar directional coupler but it only had one
coupled port. I realized that in this application, when two couplers are needed that
sample reverse and forward waves, two couplers can be put back-to-back sharing
the same coaxial cable balun, which makes it smaller.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/coupler_breakout.jpg" width="1600" height="945" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Directional coupler breakout PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I made a breakout PCB of the coupler so I could measure it as its
performance is very important for the VNA. PCB material is FR4, which is quite
lossy at high frequencies, but proper RF material would be too costly.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/coupler_0402.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Measured S-parameters of the coupler. Ports 1 and
    2 is the thru path, and ports 3 and 4 are coupled ports.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I measured the coupler with my previous 6 GHz VNA, and the performance looks
good. It's a four port, but this is not a problem
and &lt;a href="https://scikit-rf.readthedocs.io/en/latest/examples/metrology/Measuring%20a%20Mutiport%20Device%20with%20a%202-Port%20Network%20Analyzer.html"&gt;only requires making multiple
measurements&lt;/a&gt;.
Loss is 3 dB at low frequencies and 5 dB at 6 GHz. Directivity is about 20 dB,
which is fine but could probably be improved with slightly tuning the resistor
values. Isolation between the coupled ports is acceptable, and there doesn't
seem to be issues with sharing the balun.&lt;/p&gt;
&lt;h2 id="receiver"&gt;Receiver&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/adl5961.png" width="760" height="1026" style="width: 25%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADL5961 block diagram. This would be an ideal
    receiver for the VNA if it wasn't for the price. It has integrated
    directional coupler and even LO frequency dividers and multipliers.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The receiver needs to first down-convert the RF signals from the directional
coupler and then sample it with an ADC. Finding a wideband mixer turned out to be
problematic. &lt;a href="https://www.analog.com/en/products/adl5961.html"&gt;ADL5961&lt;/a&gt; would be
an ideal choice as it's rated from 10 MHz to 20 GHz, it's designed for VNA
applications, and even includes a directional coupler on the chip. The drawback
is that it costs $200 for a single chip, and two of them (one for each port)
would cost more than all the other components combined.&lt;/p&gt;
&lt;p&gt;The other options are very limited. Many mixers function at high enough
frequencies, but almost all of them also have high minimum frequency. I could
have two mixers for low and high frequencies that are switched, but it would
cost too much. Some mixers could have enough conversion gain at low frequencies
even if operated out of spec, but I don't want to test them individually.
Another option would have been to make my own mixer with diodes, but this would
require a large amount of testing and the resulting circuit would likely be much
larger than the small commercial integrated circuits.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/adl5802.png" width="544" height="451" style="width: 25%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADL5802 dual channel mixer block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the end, I chose to use
&lt;a href="https://www.analog.com/en/products/adl5802.html"&gt;ADL5802&lt;/a&gt;, which is only rated
from 100 MHz to 6 GHz. Unfortunately, this limits the performance at high
frequencies when everything else would work fine. However, I don't see other cheap
options. They main benefit of it is that it's cheap ($12 / piece at high
quantities) and it has two mixers in one package, further driving down the cost.
The reason I chose to use it is that it does have some performance figures
listed at 8 GHz that suggests that it has 5 dB lower conversion gain than at
6 GHz. Even if it doesn't function well above 8 GHz, this should be good enough
as I mostly currently work at frequencies under 7 GHz.&lt;/p&gt;
&lt;h3 id="adc"&gt;ADC&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/receiver.svg" width="853" height="565" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Receiver dynamic range plot (not to-scale).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The dynamic range of the analog-to-digital converter (ADC) is critical because
it often limits the receiver's dynamic range. The mixer output can vary from the noise
floor (approximately -160 dBm/Hz) to about +10 dBm at maximum input power at
the 1 dB compression point. This wide dynamic range exceeds what most
ordinary ADCs can handle. The ADC's dynamic range depends on the noise in
a single sample and the number of samples averaged. Faster low-bit ADC can be
more accurate than slow many-bit ADC when many samples are averaged.&lt;/p&gt;
&lt;p&gt;The mixer output frequency should be at least a few hundred kHz to minimize the
effects of phase noise and 1/f noise. A higher intermediate frequency (IF)
allows for shorter measurement times, as at least few cycles of the IF must be
sampled. Some margin should be also left for the ADC's anti-alias filter
roll-off. A higher sampling rate also improves spectrum analyzer performance but
increases the ADC cost.&lt;/p&gt;
&lt;p&gt;The key performance metric for the ADC's dynamic range is &lt;a href="https://www.analog.com/en/resources/technical-articles/noise-spectral-density.html"&gt;noise spectral
density
(NSD)&lt;/a&gt;.
Many ADCs instead report signal-to-noise ratio (SNR) of a single sample instead.
Using the sampling rate, NSD can be calculated as:&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(\text{NSD} = -\text{SNR} - 10\log_{10}(f_s/2)\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(f_s\)&lt;/span&gt; is the sample rate. Typically its around -140 to -160 dBFS/Hz (noise
floor decibels relative to the ADC full-scale input at 1 Hz bandwidth). In
practice this figure tells the FFT noise floor of the ADC output as function of
measurement length. For example, with a 0.1-second measurement time and an NSD of -140
dBFs/Hz, the FFT noise floor is -130 dB below the full-scale non-clipping input.&lt;/p&gt;
&lt;p&gt;The ADC I chose to use is
&lt;a href="https://www.analog.com/en/products/ad9238.html"&gt;AD9238&lt;/a&gt; which has 40 MHz
maximum sampling frequency and an NSD of -143 dBFs/Hz. Since the ADC's dynamic
range is less than dynamic range of the mixer's output dynamic range, either
high input causes ADC saturation or the ADC's noise floor is above the mixer's
noise floor. In this case it's a bit of both.&lt;/p&gt;
&lt;p&gt;With an NSD of -143 dBFs/Hz, if the incident wave power is at -10 dBFs level at the ADC
input, the dynamic range for S21 measurement is at most 133 dBFs/Hz since
it's calculated as the ratio of port 2 received power divided to port 1 incident
power. With a 10 Hz IF bandwidth, the S21 measurement will have
a noise floor of -123 dB. If the source power is lower, the incident power
decreases, and the S21 noise floor increases.&lt;/p&gt;
&lt;p&gt;If the cost is not an issue, better choices are available. For example, the
&lt;a href="https://www.analog.com/en/products/ad9650.html"&gt;AD9650&lt;/a&gt; (105 MHz version) has
an NSD of -160 dBFs/Hz. It would increase the dynamic range by about 17 dB.
It's also about ten times more expensive, so the performance doesn't come cheap.&lt;/p&gt;
&lt;h2 id="fpga"&gt;FPGA&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna3_fpga_block.svg" width="549" height="625" style="width: 30%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FPGA digital signal processing block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Fast sample rate ADCs need an FPGA to handle the high amount of data they
produce. The same FPGA can also handle all the DSP and I/O control functions,
avoiding the need for adding a separate microcontroller.&lt;/p&gt;
&lt;p&gt;The digital signal processing needed inside the FPGA is deceptively simple. The
input signal from each of the four receivers is a signal at a few MHz frequency.
It's multiplied with a sin and a cos of the same frequency signal to get a zero
frequency I and Q output signals that are summed. At the end of the sampling
period, these I and Q accumulated sums are divided by the number of samples
summed to get the average value. This is equal to calculating just a single bin
of Fourier transform and is the optimal way of calculating magnitude and phase
of a signal at a known frequency.&lt;/p&gt;
&lt;p&gt;The rest of the logic on the FPGA is timing, switching I/O signals, and communicating
with the PC. All of the logic and calibration is implemented on PC as it's much
easier to do there.&lt;/p&gt;
&lt;h2 id="pcb"&gt;PCB&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna_pcb.jpg" width="1600" height="1159" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Partially assembled VNA PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;PCB has 6-layers and is made from FR4 material. FR4 is fine in this application
since distance from signal source to output port is quite short and there
aren't any sensitive circuits such as distributed filters that are sensitive
substrate material variations.&lt;/p&gt;
&lt;p&gt;For good isolation RF enclosure is necessary and it requires mounting holes and
contact surfaces on the PCB. Isolation was the biggest issue with my previous
VNA and I did everything to make sure that this time the leakage signal will be
below the noise floor. Due to the high frequency all the RF signals are routed
at the top layer, requiring routed channels in the shield to pass the signal via
the different enclosures.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/vna3_on.jpg" width="1600" height="1186" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Assembled VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I ordered the PCB assembled but I had to assemble some components such as
mixers, SMA connectors, and couplers myself. When considering a large scale
production the couplers are especially tricky as they require manually cutting
a coaxial cable, inserting ferrite beads into it, and the soldering it to the
PCB.  In this case it isn't an issue when I just make one PCB for myself, but
this would be difficult for mass manufacturing.&lt;/p&gt;
&lt;h1 id="cnc-machined-case"&gt;CNC machined case&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/cnc_case.jpg" width="1600" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;CNC machined case, upper half.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I first made &lt;a href="https://hforsten.com/img/vna3/3d_printed_shield.jpg"&gt;a 3D printed case lined with aluminium
foil&lt;/a&gt; for testing the electronics and
making sure the mechanical design fit the PCB. It had quite good isolation
after I figured how to get the aluminium foil lining to be tear free. Stability
and thermal properties of the CNC machined case should be much better, so
I ordered the same design machined from aluminium.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/cnc_case2.jpg" width="1600" height="1192" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;VNA with the aluminium case.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The case consists of upper and lower pieces with the PCB being sandwiched between
them. I designed it to be simple so that it can be manufactured on a 3-axis CNC
machine in a single pass and without any threads. The cost was $75 for the
case, $37 for shipping, and $29 for taxes. Not bad for a fairly large piece of
aluminium.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/cnc_case_leakage.png" width="826" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Uncorrected S-parameters with short on port 1 and
    load on port 2. 200 Hz IF bandwidth.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The leakage with the CNC machined case was surprisingly high when I first
assembled it. Uncorrected S21 was higher than -70 dB at 6 GHz. The culprit
turned out to be a gap between the edge launch SMA connectors and the
aluminum case. The gap between the connector and the case is very small, less
than 1 mm, but it works as an antenna just well enough that the coupling to the
other port was about -70 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/gasket.jpg" width="1892" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solder wick and aluminium foil used to seal the
    gap between the SMA connector and the aluminium case.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I used aluminium foil to plug the gap between the bottom case half and the
connector. A small folded up piece of foil placed under the PCB compresses
nicely and provides good RF seal. The upper half can't use the same method since
it would short the signal conductor. I used instead a small piece of solder wick
to seal the upper half gap.&lt;/p&gt;
&lt;p&gt;I knew that this connector gap could be an issue when I first designed the VNA,
but I decided to go with it because the alternative of using a bulkhead SMA
connector (the whole connector goes through the case) was just too much trouble.
Bulkhead connectors would need a hole and threads in the case in 90-degree angle
from the current machining direction. Bulkhead connectors would also need to be
soldered to the PCB when it's mounted in the case, making it inconvenient for
prototyping.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/cnc_isolation_10hz.png" width="768" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Uncorrected S-parameters with short on port 1 and
    load on port 2 with EM gasket. 10 Hz IF bandwidth.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After plugging the gap, the isolation is much better and leakage is below the
noise floor at all frequencies even with very low 10 Hz IF bandwidth (0.1
s measurement time per frequency point).&lt;/p&gt;
&lt;p&gt;The noise floor increases as the source frequency rises. This is because the
mixer's conversion gain decreases beyond 6 GHz. The dynamic range is still quite
good up to about 10 GHz, but starts to quickly decrease after that. While the
dynamic range was 120 dB at low frequencies, it's less than 70 dB at 15 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/ss_cal_sp_10hz.png" width="768" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Calibrated S-parameters with short circuits on both port 1 and
    port 2. 10 Hz IF bandwidth.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the calibration, the dynamic range is slightly lower at high frequencies
due to the losses that need to be calibrated out, but the difference isn't large
due to directional couplers still working with acceptable performance. While the
lower dynamic range at high frequencies might not be good enough for precision
measurements, it's still useful for many basic measurements.&lt;/p&gt;
&lt;h1 id="harmonic-mixing"&gt;Harmonic mixing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/lo_harmonics.png" width="826" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Reference receiver measurement at different LO
    harmonics (arbitrary scale).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Mixer is rated only up to 6 GHz operation and it has an integrated LO amplifier.
At high enough frequencies the LO amplifier's output power isn't high enough to
drive the mixer core. If the LO is driven at for example 1/3 of the desired
frequency, the fundamental frequency should be high enough to still pass through
the integrated LO amplifier and there should also be some harmonic mixing
products at the output caused by the third harmonic of the LO.&lt;/p&gt;
&lt;p&gt;I swept the source from 0.1 GHz to 15 GHz with LO set to different harmonics.
The results of the reference receiver power (arbitrary scale) are shown in
the image above. The result is magnitude of the mixer's output, which is
proportional to source power coupled into reference receiver and the conversion
gain of the mixer, so this measurement doesn't just measure the mixer. As
expected, the fundamental frequency results in the highest conversion gain at the mixer's
operational range. The IF signal is about 15 dB lower at 10 GHz, which is
still acceptable, but above that it starts to drop quickly. The output also
becomes very noisy, likely due to LO drive power being too weak. At around
12 GHz, using the LO at 1/3 frequency for 3rd harmonic mixing results in
higher output power. Other LO harmonics than 1st or 3rd results in lower conversion gain.&lt;/p&gt;
&lt;p&gt;The LMX2594 chip seems to have a 2 dB dip in output power from 1.25 GHz to 1.875 GHz
when the output frequency divider value is set to 6. This dip is visible in all
traces at that frequency range due to source power being lower. The same effect
due to LO source power can also be seen at higher frequencies at different
LO harmonics.&lt;/p&gt;
&lt;p&gt;Some VNAs use a similar trick with source harmonics to extend the maximum
frequency range. For example, to measure S-parameters at 20 GHz, set the source
to 10 GHz and use the 2nd harmonic of the signal as the test signal. The LO
would be set to (20 GHz + IF frequency)/3 = 6.67 GHz for the LO 3rd harmonic.
However, this method can't be used to measure any even slightly non-linear
devices, as the non-linearity of the device would cause the harmonics to change
due to the strong fundamental signal that is still present. Similarly, the
receiver's non-linearity can cause issues. Driving mixer LO with harmonics
doesn't suffer from the same issues.&lt;/p&gt;
&lt;h1 id="stability"&gt;Stability&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/short_temp.png" width="768" height="512" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Temperature of the FPGA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The board does get quite hot, I estimated that it dissipates about 10 W of
power. The FPGA's internal temperature sensor indicates a die temperature of 64
C (147 F). The case also gets uncomfortably warm to touch. It takes about 1 hour
to reach the equilibrium temperature.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/short_temp_stability.png" width="768" height="512" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Uncorrected S11 of short at 6 GHz as the VNA warms
    up.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Temperature has an effect on the measured S-parameters. With a short circuit at
port 1, uncorrected S-parameters measured at 6 GHz changed from -4.43 dB at room
temperature to -4.58 dB when warm (this includes VNA internal losses and
external 0.5 m long SMA cable loss). Source power should drop at high
temperatures, but since S11 is a ratio, it should cancel out. Incident and
reflected wave mixers are in the same package, well-matched to each other, and
at the same temperature, so their conversion gain change should also cancel out.
This just leaves the coupler as the likely cause the drift.&lt;/p&gt;
&lt;p&gt;One possible reason for thermal drift is that FR4 PCB dielectric constant and
loss tangent can have &lt;a href="https://www.ipc.org/system/files/technical_resource/E7%26S16_01.pdf"&gt;large temperature variation&lt;/a&gt;.
Copper resistance also increases with the temperature. The loss of
coaxial cable is about &lt;a href="https://www.gore.com/resources/technical-information-changes-insertion-loss-and-phase"&gt;7%
larger&lt;/a&gt;
at 60 C than at room temperature, and microstrip lines on the PCB likely have even
greater loss increase. Difference between
incident and reflected waves is that the incident does not go through the coupler
but reflected wave does, so it should be expected that the uncorrected S11
decreases slightly as temperature increases due to increased loss of the
coupler. Resistors in the coupler also change slightly with the temperature.&lt;/p&gt;
&lt;p&gt;I tried measuring the change in coupler's loss at different temperatures
using the coupler breakout board and heating it up with a hot air tool. I don't
have a thermal chamber at home, so the measurement accuracy is low but I did
observe about 0.1 dB change in S21 when heating it to hot-to-touch temperature.&lt;/p&gt;
&lt;p&gt;Another temperature effect is thermal expansion in coaxial cable and PCB, that
mainly affects the phase. Even if only the amplitude of the result is important,
phase accuracy is important since phase is used in calibration.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/stability_10mins.png" width="768" height="512" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Uncorrected S11 of short at 6 GHz after the VNA
    temperature has stabilized.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the temperature has stabilized, the measurements are quite stable. Keeping
everything absolutely still, drift in uncorrected short S11 measurements is
0.0001 dB RMS in 10-minute measurement with 100 Hz IF bandwidth. Noise in
a single measurement is 0.00006 dB RMS, limited only by the signal-to-noise ratio.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/vna3/sparam_cable_bend2.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;In practice, achieving this accuracy is challenging. Moving the SMA cable
changes uncorrected S11 by around 0.03 dB and it doesn't return to the original
level when the cable is returned to the original position. Bending the cable can
cause even larger changes. Connector repeatability, especially with low-quality
SMA connectors, also has an effect on the measurement accuracy. Professional
labs use expensive phase-stable cables and precision connectors for this reason.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/cal_kit.jpg" width="1785" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Self-made VNA calibration kit. Thru, short, open,
    and 50-ohm load.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For calibration kit, I'm using the same self-made short, open, and load that
I made for my previous VNA. The calibration standards are measured with
a commercial VNA that was calibrated with highly accurate calibration kit, and
as a result they should be fairly accurate. Thru is an ordinary SMA through
adapter, I don't have any measurement data of it, but that's not an issue since
I'm using unknown thru calibration algorithm, which, as the name suggests, does
not require a known thru standard.&lt;/p&gt;
&lt;h2 id="bandpass-filter"&gt;Bandpass filter&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/bpf.jpg" width="1785" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;6 GHz bandpass filter.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To test the accuracy of the VNA, I measured a 6 GHz bandpass filter with my VNA
that was calibrated with the self-made calibration kit. The results were compared to 
measurements taken with a commercial VNA, calibrated with a proper
calibration kit.&lt;/p&gt;
&lt;p&gt;The bandpass filter has
&lt;a href="https://product.tdk.com/en/search/rf/rf/filter/info?part_no=DEA165550BT-2230C2-H"&gt;DEA165550BT-2230C2-H&lt;/a&gt;
ceramic 6 GHz bandpass filter mounted on an FR4 PCB. Copper sheet was soldered over
it to create an enclosure.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/bpf_comparison_full.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Bandpass filter S-parameters compared.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The S-parameters measured with my VNA closely match with those from the commercial
VNA. I only plotted S21 and S11 to make the plot
clearer, but &lt;a href="https://hforsten.com/img/vna3/bpf_cal_s22.png"&gt;S12 and S22 also agree well&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/bpf_comparison_detail.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Bandpass filter S-parameters compared, passband
    detail.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Zooming into the passband, my VNA has less trace noise in the S21 than the
commercial VNA. The commercial VNA should be more accurate and I suspect that
this is caused by inaccurate thru standard definition in the SOLT calibration.
It's old enough that it doesn't have unknown thru algorithm built-in. Ignoring
the trace noise measured S-parameters agree with excellent accuracy.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/thru_solved_sp.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solved S-parameters of the thru calibration
    standard.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Unknown thru calibration solves for the thru S-parameters during the
calibration. Plotting the solved thru S-parameters, the measurement looks high
quality, but the performance of the thru doesn't look very good and it
definitely hasn't been designed to be used at these frequencies. Matching is
only -8 dB at 15 GHz. &lt;a href="https://hforsten.com/img/vna3/thru_solved_s21.png"&gt;S21 trace is also very
clean&lt;/a&gt; and looks very reasonable
indicating that the short, open, and load definitions are correct.&lt;/p&gt;
&lt;h2 id="calibration-algorithms"&gt;Calibration algorithms&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/bpf_cals3.png" width="2079" height="470" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Same measurement calibrated with different
    calibration algorithms.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Since I saved the raw uncalibrated measurements, I can do the calibration
afterwards using different calibration algorithms. I set the ideal thru standard
to be a 50-ohm line that is 65 ps long with linear attenuation of 0.1 dB
at 4 GHz. This is a simple transmission line model that agrees reasonably well
at low frequencies, but the loss and matching differ significantly at higher
frequencies. The delay should match well over the whole frequency range.&lt;/p&gt;
&lt;p&gt;The SOLT (Short-Open-Load-Thru) calibration algorithm is the default classic VNA
calibration algorithm that can also be used with a three-receiver VNA. It
requires that all the calibration standards are known accurately. Since the thru
standard has errors in this case, the solved S-parameters also have errors. Even
though the matching of the thru is -20 dB at 6 GHz, the errors it causes are
well above the measurement capability of the VNA.&lt;/p&gt;
&lt;p&gt;Unknown thru calibration is likely the best option when
calibrating to the end of SMA connectors. It only requires short, open, and load
standards to be known, while the thru standard can be completely unknown, as
long as it's reciprocal (S12=S21). However, this method requires a four-receiver VNA to 
measure the &lt;a href="https://ziadhatab.github.io/posts/vna-switch-terms/"&gt;switch
terms&lt;/a&gt;. This measurement is
not possible with a three-receiver VNA. When switch terms are known it reduces
the number of equations that needs to be solved in calibration by two, allowing
some calibration standard definitions to be relaxed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://scikit-rf.readthedocs.io/en/latest/api/calibration/generated/skrf.calibration.calibration.EightTerm.html"&gt;EightTerm&lt;/a&gt;
calibration is similar to SOLT, where all the calibration standards are assumed to
be known, However, it also includes switch terms, creating an overdetermined system of
linear equations. This system is solved least squares, making it a good choice if
all calibration standards, including thru, are known to high accuracy.&lt;/p&gt;
&lt;p&gt;Both unknown thru and EightTerm calibration are significantly more accurate than
SOLT because they have less unknowns to solve for. The thru standard is usually
not known with high accuracy in low-cost VNAs, and a four-receiver VNA capable
of measuring switch terms is required for these more advanced calibration
methods.&lt;/p&gt;
&lt;h2 id="trl-calibration"&gt;TRL calibration&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/trl_kit.jpg" width="1471" height="1600" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;TRL calibration kit and DUT. J1-J2 is thru, J3-J4
    is line, J5-J6 is another line with different connector footprint for
    testing, and J7-J8 is a diode that can be measured with the TRL calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;One especially useful calibration algorithm is thru-reflect-line calibration. It
requires a four receiver VNA, but it's also possible to use it as a second-stage
calibration with three receiver VNA. As the name implies, calibration standard
are not the usual short, open, and load. Instead they are two different length
transmission lines and reflect. Transmission lines need to have the same
propagation constant and optimally the length difference is 90 degrees.
Transmission line parameters don't need to be known and reflect can also be
unknown but should be identical in both ports.&lt;/p&gt;
&lt;p&gt;This is very useful calibration algorithm for measuring devices on PCB, as it
allows the VNA reference planes to be calibrated on the PCB right next to the
component being measured. It also solves for the transmission line propagation
constant and can be useful just for characterizing transmission lines without
requiring any calibration standards.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/trl_sp.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;TRL kit thru and line S-parameters calibrated to
    SMA connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I first measured the lines using unknown thru calibration calibrated to the SMA
connectors. This gives some indication on the loss and matching. If the
connectors are too reflective it can result in inaccurate calibration as the
reflections make it hard to measure the device, but this should be fine.&lt;/p&gt;
&lt;p&gt;Some slight ripple is visible in the S11 and S22 measurements, especially above
11 GHz where the 3rd LO harmonic is used. The main reason for the ripple below 11
GHz is due to the measurement cable stability. If I'm not careful with keeping
the cables stable the ripple can be even larger.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/trl_sp_s21.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;TRL kit thru and line S21 zoom.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Zooming into the S21 trace, it's very clean below 11 GHz. Above this frequency,
the 3rd harmonic LO starts to be used resulting in significantly larger trace
noise. Some of the trace noise at low frequencies can be caused by the
calibration kit definitions that uses measurements instead of models. Any noise
in the calibration kit measurement will be transferred to the measurements.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/line_time_step.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Time-domain impedance step response of the line.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using Fourier transform, &lt;a href="https://scikit-rf.readthedocs.io/en/latest/examples/networktheory/Time%20domain%20reflectometry%2C%20measurement%20vs%20simulation.html"&gt;the time-domain impedance step
response&lt;/a&gt; of the measurement
can be obtained. In this case, calculating the time-domain response of the line
shows the impedance discontinuities from the SMA connectors and it also gives an
estimate for the transmission line impedance, about 50.5 ohms.
The mathematical transformation from frequency to time-domain is much easier and 
accurate than trying to actually sample the reflected waveform at 15 GHz
sampling frequency.&lt;/p&gt;
&lt;p&gt;The quality of the time-domain transformed data is excellent. With my previous VNA
the same measurement would be too inaccurate to be useful.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/line_params.png" width="1441" height="470" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Transmission line parameters solved by the TRL
    calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;TRL calibration solves the transmission line propagation constant during the
calibration. It's useful to plot this as effective permittivity and loss per cm.
In this case reflect standard can be short calibration standard at the end of
the coaxial cable and no separate reflect standard is needed on the PCB.&lt;/p&gt;
&lt;p&gt;I also compared these values to microstrip line model using 4.6 substrate
dielectric constant and 0.015 loss tangent. These should be about what they are
supposed to be and it agrees somewhat okay. FR4 is notoriously frequency
dependent material and the model using non-frequency dependent parameters
doesn't match over the full frequency range. FR4 dielectric constant decreases
and the loss tangent increases as the frequency increases.&lt;/p&gt;
&lt;p&gt;This isn't quite ideal test setup for characterizing the transmission lines
since the length difference between thru and line is just 9 mm. This small length
difference leads to small differences in the S-parameters and 
makes the measurement sensitive to noise, measurement errors, and differences in
the connector S-parameters. Especially with these low-cost SMA connectors I'm
using there can be some small difference between thru and line SMA connectors,
which cause errors in the solved transmission line parameters. I don't think the
peak at 9 GHz for example is caused by VNA measurement accuracy, and it's more
likely to be due to differences in the SMA connector S-parameters. FR4
substrate also isn't uniform due to fiberglass wave construction and two
identical lines at different places in the weave can have slight differences.&lt;/p&gt;
&lt;p&gt;Repeatability also impacts the accuracy of TRL calibration. This TRL
kit was designed for frequencies up to 6 GHz, and the solved transmission line
parameters remain accurate up to that frequency. The TRL calibration should also
maintain its accuracy up to this frequency. Beyond that the calibration is
likely not as accurate.&lt;/p&gt;
&lt;h2 id="varactor-diode-measurement"&gt;Varactor diode measurement&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/varactor_trl_labels.jpg" width="2313" height="2133" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Varactor diode measurement setup.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With the TRL calibration it's possible to calibrate the VNA right next to the
component being measured on the PCB. This allows accurately characterizing SMD
components.&lt;/p&gt;
&lt;p&gt;The varactor diode on the PCB is
&lt;a href="https://www.skyworksinc.com/en/Products/Diodes/SMV2020-079LF"&gt;SMV2020-079LF&lt;/a&gt;.
It's a diode whose capacitance changes as the reverse bias voltage across the diode
is varied. They are used, for example, in voltage-controlled oscillators.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/trl_cal_nstd.png" width="768" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Normalized standard deviation of the TRL calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;TRL calibration accuracy depends on the phase shift between thru and line
standards. It's best when the phase shift is ±90 degrees and extremely poor when
the phase shift is 0 or 180 degrees. The theoretical effect on calibration
results can be quantified by calculating the normalized standard deviation of
the noise in result, normalized to the best case of 90-degree phase shift. In
this case, the best accuracy is around 4.6 GHz, and poorest at 9 GHz due to the
line length being 180 degrees at that frequency.&lt;/p&gt;
&lt;p&gt;I didn't have this good VNA when I made this PCB, so I didn't think to include
a second shorter line to extend the calibration maximum frequency. With this TRL
kit, measurement accuracy will be poor around 9 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/diode_meas_setup.jpg" width="1600" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Bias-T's added to VNA ports for biasing the diode.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To measure the diode S-parameters at different bias voltages, I added bias-T's to
both VNA ports. Adding them close to VNA is better for stability, and taping down
the SMA cables minimizes their movement during measurement, improving stability.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/varactor_0v_sp.png" width="711" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;S-parameters of the diode with 0V bias with TRL
    calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;9 GHz calibration singularity severely affects the results, but the S-parameters
are clean before and after it. TRL calibration accuracy is also poor at low
frequencies, but due to higher SNR, better stability, and better connector
repeatability at low frequencies, it doesn't cause similar issues.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/varactor_sp_sweep.png" width="1457" height="470" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Diode S21 and S11 measurements at 0 - 20 V reverse
    voltage.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I measured the diode at 0 to 20 V reverse bias voltage in 1 V increments. The
frequency range is limited to 7 GHz to avoid the calibration singularity. The
diode is expected to behave like a variable capacitor, with capacitance being
the largest at zero bias and decreasing as the reverse bias increases.&lt;/p&gt;
&lt;p&gt;This measurement would have requires flipping the DUT after every measurement if
I was using a 1.5-receiver VNA. It would have not only been very tedious, but
also terrible for measurement accuracy. However, with a proper two-port VNA, the
measurement setup can remain static and making the measurement is very quick
only taking few minutes.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/varactor_meas_vs_model_10v.png" width="768" height="470" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Measurement vs SPICE model at 10 V reverse bias.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The diode has a manufacturer supplied SPICE model. Comparing the measurements to
it, the results are quite close at low frequencies but some differences in S11
and S22 can be seen at higher frequencies.&lt;/p&gt;
&lt;p&gt;The results can be expected to be at least a little different since my
measurement includes PCB pads which are not included in the SPICE model.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna3/varactor_c_vs_datasheet.png" width="1445" height="470" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Capacitance vs reverse voltage. My measurement
    result vs datasheet plot.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When a capacitor is fitted to the measured S-parameters, the capacitance vs
reverse voltage graph is very similar to that on the datasheet. However, in my
measurement, the diode has few hundred femtofarad higher capacitance at low
bias. Some of this discrepancy could be caused by the PCB and it's also possible
that there is some manufacturing variation in the diodes.&lt;/p&gt;
&lt;p&gt;The diode measurement results have very low noise and the VNA can measure the
diodes low capacitance very accurately.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/vna3/vna3.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/vna3/schematic_top.png" width="1071" height="742" border="2" style="width: 50%; height: auto;"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px"&gt;Schematic of the VNA (click to open).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The designed VNA has excellent measurement quality, easily surpassing all of the
existing low-cost VNAs. It has four receivers which makes advanced calibration
methods, such as unknown thru possible. Isolation is excellent, over 120 dB, and
the source frequency range is 10 MHz to 15 GHz.&lt;/p&gt;
&lt;p&gt;The total cost of components in prototype quantities was $300, PCBs cost $100
for five pieces, and the CNC machined case was $75 (excluding taxes and
shipping). The couplers require some manual assembly, but it should be possible
to manufacture this at very low cost. I don't have plans at the moment to
manufacture these for sale. The schematic of the VNA is available above for
anyone interested in making their own VNA or just curious about the details.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Homemade polarimetric synthetic aperture radar drone</title><link href="https://hforsten.com/homemade-polarimetric-synthetic-aperture-radar-drone.html" rel="alternate"></link><published>2025-02-11T00:00:00+02:00</published><updated>2025-02-11T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2025-02-11:/homemade-polarimetric-synthetic-aperture-radar-drone.html</id><summary type="html">&lt;p&gt;Designing a cheap synthetic aperture radar mounted on FPV drone.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/drone_snow.jpg" width="1494" height="883" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar drone ready to take off from snow.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I have made several homebuilt radars and done some &lt;a href="https://hforsten.com/backprojection-backpropagation.html"&gt;synthetic aperture imaging
testing&lt;/a&gt; with them on the ground. I have wanted for a long time to put a radar on&lt;br /&gt;
a drone and capture synthetic aperture images from air. When I last looked at
this few years ago, medium sized drones with payload capability were
around 1,000 EUR and up. For example in &lt;a href="https://pure-oai.bham.ac.uk/ws/portalfiles/portal/136457382/Final_Version_TGRS.pdf"&gt;Low-Cost, High-Resolution, Drone-Borne SAR
Imaging&lt;/a&gt;
paper by A. Bekar, M. Antoniou and C. J. Baker, the imaging results look
excellent. They used DJI S900 drone with a list price of about 1,000 EUR. The
price for the whole system is quoted to be £15,000, which is a way too
high for my personal budget even just for the price of the drone. Many other
papers use similar style medium sized drones designed for carrying cameras and
they are usually equipped with RTK-GPS for accurate positioning.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/aliexpress.png" width="1031" height="504" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;One of many cheap Chinese FPV kits.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Recently small FPV drone prices have dropped a lot. Small 5 and 7 inch propeller
quadcopters can be bought for about 100 EUR from China (not including battery
and RC controller). Despite their small size they are able to lift about 1 kg or
even heavier payload which is plenty for a small radar.&lt;/p&gt;
&lt;p&gt;I bought the cheapest Chinese no-name 7-inch FPV kit and a small GPS+compass
module to support autonomous flying with the goal of making a light weight
synthetic aperture radar system that it can carry.&lt;/p&gt;
&lt;h1 id="synthetic-aperture-imaging"&gt;Synthetic aperture imaging&lt;/h1&gt;
&lt;p&gt;A single-channel radar can only measure the distance to a target and is unable
to detect the angle of the target. When multiple receiver channels are arranged
in a line, the signal travels slightly different distances to each receiver
based on the target's angle, causing phase shifts in the received signals. These
phase shifts allows calculating the angle of the target.&lt;/p&gt;
&lt;p&gt;Angular resolution (&lt;span class="math"&gt;\(\Delta \theta\)&lt;/span&gt;) of antenna depends on its size
approximately as: &lt;span class="math"&gt;\(\Delta \theta \approx \lambda/D\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is the
wavelength, and &lt;span class="math"&gt;\(D\)&lt;/span&gt; is the diameter of the antenna. For example to have
1 m resolution at 1 km distance requires 0.03° angular resolution with 6 GHz RF
frequency. This would require antenna size to be about 100 meters.&lt;/p&gt;
&lt;p&gt;Instead of making a single large antenna, it's possible to move a single radar
and take multiple measurements at different positions. If the scene remains
static, this approach yields the same results as having one many channel radar
system with big antenna. With synthetic aperture radar it's possible to attach
a single-channel radar to a drone, fly it while making measurements, creating
a large synthetic aperture that provides exceptional angular resolution.&lt;/p&gt;
&lt;h1 id="radar-design"&gt;Radar design&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/drone_dimensions.png" width="1287" height="617" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Dimensions of the drone frame. &lt;a href="https://www.printables.com/model/921700-geprc-mark-4-frame/files"&gt;(3d model source)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The design goal for the radar is to get the best imaging performance while being
able to fit into the FPV drone and achieving it with minimal budget (&amp;lt;500 EUR).
The budget limitation rules out using any low-loss RF materials and both
electronics and antennas should be implemented with lossy FR4 PCB material.&lt;/p&gt;
&lt;p&gt;The drone is quite small and this limits the maximum size of the radar. The
width of the frame is about 40 mm and propeller tip-to-tip distance is 50 mm
across the frame. Length is about 170 mm, which is much more than width and
means that ideally the radar is skinny. For example Raspberry Pi is 56 x 85 mm
which is too wide. The small size severely limits on what the radar can include.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/fmcw_vs_pulse.svg" width="1360" height="450" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Block diagrams of FMCW (left) and pulse radar
    (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There are several possible architectures for the radar. I previously made &lt;a href="https://hforsten.com/homemade-6-ghz-pulse-compression-radar.html"&gt;pulse
radar&lt;/a&gt; that is about 64 x 132 mm. That's little too
wide for the drone, but I believe it would be possible to shrink it a little.
Issue with that radar is that the maximum bandwidth is about 100 MHz and it's
limited by the ADC sampling rate. This corresponds to a range resolution of 1.5
m, which isn't quite high enough for a detailed image. It's hard to get much
larger ADC bandwidth on a reasonable budget and fitting high speed ADCs on the
limited space is also an issue. There's also a variation of pulse radar that has
two ramp generators, one for RX and one for TX. This results in low frequency IF
signal like with FMCW radar. This is an architecture that is often used on SAR
radars as it allows for high RF bandwidth without requiring high speed ADC.&lt;/p&gt;
&lt;p&gt;Since pulse radar with switched antenna can't transmit and receive at the same
time, pulsed radar maximum pulse length is limited by the time it takes for the
pulse to travel to the target and back. With for example 100 m minimum distance,
the maximum pulse length to not miss any part of the pulse is only 670 ns.
Because pulse radar needs to divide measurement time between transmission and
reception it reduces the average transmit power and decreases the
signal-to-noise ratio. Large number of very short pulses also complicates the
image formation. Time taken by the SAR image formation scales with the number of
pulses and very short maximum pulse length requires transmitting very large
number of them for a good SNR image.&lt;/p&gt;
&lt;p&gt;FMCW radar can transmit and receive at the same time and which improves the
signal-to-noise ratio. The maximum sweep length is only limited by the synthetic
aperture sampling speed requirements but it can be hundreds of µs. Unlike pulse
radar, there is also a minimum sweep length requirement, since reflected signal
is mixed to the transmitted signal it needs to be received while the sweep is
still being transmitted. Long sweep length allows collecting much more reflected
power per one sweep. Pulse radar could also use separate TX and RX antennas so
that this wouldn't be an issue, but that removes its advantages compared to
cheaper to implement FMCW radar. Separate transmit and receive antennas require
more space, but due to large maximum length it should be possible to fit two
small antennas side-by-side under the drone.&lt;/p&gt;
&lt;p&gt;In general FMCW radar has advantage for short range and slow moving platform
applications. Pulse radar is required when long range (more than few km) is needed.&lt;/p&gt;
&lt;h2 id="rf-design"&gt;RF design&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_fmcw_block.svg" width="690" height="339" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FMCW radar architecture.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the block diagram of the RF parts of the FMCW radar with dual-polarized
antennas. The sweep is generated by PLL, it's passed through a variable
attenuator and then amplified by the power amplifier. Most of it is passed to
the transmit antenna, polarization switch controls whether vertically or
horizontally polarized antenna is used. Part of the transmitted signal is
coupled to the receiver mixer, where it's mixed together with the received
reflected signal that has been amplified by the LNA. Receiver also has
a polarization switch, together with the transmit switch it allows the radar to
receiver and transmit any of the four combinations of polarizations. The mixer
outputs a low frequency signal that is amplified and then digitized by the ADC.
Some filtering is needed in the receiver to avoid large out of band signals and
ADC aliasing.&lt;/p&gt;
&lt;p&gt;DAC or DDS based sweep generation would likely be better than PLL. DDS phase
noise is often better and it can change frequencies essentially instantly
compared to PLL, but PLL is chosen because it's cheaper and requires less space. &lt;/p&gt;
&lt;p&gt;RF frequency is going to be around 6 GHz as this is the maximum frequency where
there are many cheap RF components for consumer applications. The highest output
power cheap power amplifiers at this frequency output around 30 dBm. Low noise
amplifiers for the receiver with 1 - 2 dB noise figure can also be obtained
cheaply.&lt;/p&gt;
&lt;p&gt;Receiver is direct conversion architecture and the mixer does not have any image
rejection. This causes both frequencies above and below the transmitted signal
to be converted to the same output frequency. This is not ideal as noise below
and above the instantaneous sweep frequency is received increasing the noise
floor by 3 dB. IQ sampling receiver that could reject the other sideband would
need two mixers and ADCs. For only 3 dB increase in the signal to noise
ratio, I didn't think it was worth the cost and PCB space.&lt;/p&gt;
&lt;p&gt;Polarization switches allow choosing which polarization is used to transmit and
receive. H is horizontal and V is vertical polarization. This allows measuring
four polarizations: HH, HV, VH and VV, where the first letter denotes TX
polarization and the second RX. Some targets reflect some polarizations more
than others and it is used in remote sensing to determine properties of
reflected targets. For example many smooth targets often reflect the same
polarization with shape of the target determining if it reflects more HH or VV
components. Forest and vegetation usually has higher cross-polarized HV and VH
component reflection compared to roads and bare ground due to multiple
reflections inside the vegetation.&lt;/p&gt;
&lt;p&gt;Although H and V antennas are drawn separately in the block diagram, this
doesn't mean that the system requires four antennas. It's possible to design
antenna with two ports, one which radiates H and the other V polarization. Dual
polarized antenna doesn't necessarily need any more space than single polarized
antenna.&lt;/p&gt;
&lt;p&gt;It would be possible to receive both H and V at the same time if the radar would
have two receivers. This would have some advantages, it would allow removing the
RX polarization switch which would decrease the losses and only the TX should be
switched allowing more time for each measurement which would also increase SNR.
It would also allow transmitting sweeps faster as there isn't need to multiplex
the receiver polarization switch. However, I didn't consider it being worth the
cost.&lt;/p&gt;
&lt;h3 id="tx-rx-leakage"&gt;TX-RX leakage&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/tx_rx_leakage.svg" width="530" height="469" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;TX-RX leakage can saturate the receiver on high
    powered FMCW radar if the leakage is too high.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;More RF power generally improves the signal-to-noise ratio, but since FMCW radar
transmits and receives at the same time, it's important to consider the TX-RX
leakage signal. The receiver must be sensitive enough to be able to detect
the thermal noise floor at -174 dBm/Hz without saturating due to leaked
RF power from the transmitter antenna. Typical maximum input power that
saturates the LNA is around -20 dBm. With +30 dBm transmitted power more than 50
dB isolation is needed between transmitter and receiver to prevent the receiver
saturation. Even more isolation might be required if some other receiver
component, such as ADC, saturates first. The variable attenuator before PA can be used to
decrease the transmit power in case high enough isolation antennas don't fit in
the drone. It also affects the receiver mixer's LO power, but mixers LO input
power range should be large enough for it to not be an issue.&lt;/p&gt;
&lt;h2 id="link-budget"&gt;Link budget&lt;/h2&gt;
&lt;p&gt;The equation for the received power at the receiver input can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$P_r = \frac{P_t G^2 \lambda^2 \sigma}{(4\pi)^3 r^4}$$&lt;/div&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(P_t\)&lt;/span&gt; is the transmitter power, &lt;span class="math"&gt;\(G\)&lt;/span&gt; is the antenna gain, &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is
wavelength, &lt;span class="math"&gt;\(\sigma\)&lt;/span&gt; is radar cross section of the target, and &lt;span class="math"&gt;\(r\)&lt;/span&gt; is range to
the target. This is the received power from one pulse. Synthetic aperture is
formed by sending multiple pulses while moving and these can all be coherently
summed together to increase the signal to noise ratio. If image is formed from
&lt;span class="math"&gt;\(n\)&lt;/span&gt; pulses, the received power can be multiplied by &lt;span class="math"&gt;\(n\)&lt;/span&gt; to get the received
power in the whole image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/ground_res.svg" style="width: 30%; height:
    auto;"/&gt; &lt;p style="font-size:13px"&gt;Radar measures radial distance δr, which
    is different from the ground resolution δx. δp is length of the area
    perpendicular to the antenna beam.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For a patch of ground, the reflectivity can be defined as: &lt;span class="math"&gt;\(\sigma = \sigma_0
A\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\sigma_0\)&lt;/span&gt; is reflectivity per unit area and &lt;span class="math"&gt;\(A = \delta x \delta y\)&lt;/span&gt;
is the area of the ground patch. &lt;span class="math"&gt;\(\delta x\)&lt;/span&gt; is the resolution in the
X-direction and &lt;span class="math"&gt;\(\delta y\)&lt;/span&gt; is resolution in cross-range direction. The radar
measures radial distance &lt;span class="math"&gt;\(\delta r = \delta x \cos \alpha\)&lt;/span&gt;, which causes the
ground resolution to depend on the grazing angle &lt;span class="math"&gt;\(\alpha = \sin{h/r}\)&lt;/span&gt;, with &lt;span class="math"&gt;\(h\)&lt;/span&gt;
being the radar's height above the ground.&lt;/p&gt;
&lt;p&gt;In this case &lt;span class="math"&gt;\(\delta x \approx \delta y \approx 0.3 m\)&lt;/span&gt;, depending on the radar
parameters, range, and imaging geometry. Reflectivity of the ground patch
depends on the material of the ground patch and the angle of illumination.
Reflectivity of ground is generally higher when it's illuminated at 90 degree
angle (in direction of the ground normal vector). This causes the specular
reflection to reflect back to the radar. At smaller angles there is still some
reflection back to the radar, but it decreases as the incidence angle decreases.
Typical ground reflectivity is around -20 to 0 dBsm (decibels square meter) with
moderate look angle. At very low incidence angle there is additional problems
with shadowing as there won't be any return from a target that radar doesn't
have an unobstructed line of sight.&lt;/p&gt;
&lt;p&gt;The minimum detectable power is limited by thermal noise of the receiver. It
can be written as &lt;span class="math"&gt;\(kTBF\)&lt;/span&gt;, where &lt;span class="math"&gt;\(k\)&lt;/span&gt; is &lt;a href="https://en.wikipedia.org/wiki/Johnson%E2%80%93Nyquist_noise#Derivation"&gt;Boltzmann
constant&lt;/a&gt;,
&lt;span class="math"&gt;\(T\)&lt;/span&gt; is receiver temperature, &lt;span class="math"&gt;\(B\)&lt;/span&gt; is the noise bandwidth and &lt;span class="math"&gt;\(F\)&lt;/span&gt; is noise figure
of the receiver. It's a common mistake to confuse the noise bandwidth &lt;span class="math"&gt;\(B\)&lt;/span&gt;
with RF bandwidth, but they are not related to each other. Noise bandwidth is
the minimum bandwidth where receiver can separate noise and signal from each other.
By taking Fourier transform of the input signal, we can discard all the
frequency bins that are beyond the signal we are currently looking at, and
noise at those discarded frequencies won't affect the detection capabilities of
the receiver. FFT resolution resolution is equal to &lt;span class="math"&gt;\(1/t_s\)&lt;/span&gt;, where &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is the
sweep length.&lt;/p&gt;
&lt;p&gt;Setting the received power &lt;span class="math"&gt;\(P_r\)&lt;/span&gt; equal to noise power and solving for
&lt;span class="math"&gt;\(\sigma_0\)&lt;/span&gt; we get noise equivalent sigma zero (NESZ) that is value often used
for comparing synthetic aperture radars.&lt;/p&gt;
&lt;div class="math"&gt;$$
\text{NESZ} = \frac{(4 \pi)^3 r^4 k F T \cos \alpha}{n \delta x \delta y G^2 \lambda^2 P_t t_s}
$$&lt;/div&gt;
&lt;p&gt;The number of pulses in image &lt;span class="math"&gt;\(n\)&lt;/span&gt; could also be written as &lt;span class="math"&gt;\(t_m \text{PRF}\)&lt;/span&gt;,
where &lt;span class="math"&gt;\(t_m\)&lt;/span&gt; is the measurement time and &lt;span class="math"&gt;\(\text{PRF}\)&lt;/span&gt; is pulse repetition frequency.
Or equivalently &lt;span class="math"&gt;\(l_m \text{PRF} / v\)&lt;/span&gt;, where &lt;span class="math"&gt;\(l_m\)&lt;/span&gt; is the length of the flown
track, and &lt;span class="math"&gt;\(v\)&lt;/span&gt; is the velocity of the drone during measurement. If the antenna
points at a constant angle during measurement (stripmap image) the number of
pulses in the image depends on the time that ground patch is illuminated by the
antenna beam and can be much smaller than the previous number if the antenna beam is
narrow. Quadcopter can easily fly pointing in arbitrary angle and it's possible to
constantly point the antenna at the target (spotlight imaging) and this
limitation doesn't necessarily apply in this case.&lt;/p&gt;
&lt;table style="width:80%"&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(P_t\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Transmitted power&lt;/td&gt;
&lt;td&gt;30 dBm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Antenna gain&lt;/td&gt;
&lt;td&gt;10 dBi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\lambda\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Wavelength&lt;/td&gt;
&lt;td&gt;5.2 cm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\delta x \delta y\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Image pixel resolution&lt;/td&gt;
&lt;td&gt;0.3 x 0.3 m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(T\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Receiver temperature&lt;/td&gt;
&lt;td&gt;290 K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(t_s\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Pulse length&lt;/td&gt;
&lt;td&gt;200 µs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(n\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Number of pulses in image&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(F\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Receiver noise figure&lt;/td&gt;
&lt;td&gt;6 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\alpha\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Grazing angle&lt;/td&gt;
&lt;td&gt;30 deg&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/nesz.svg" width="720" height="450" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Noise equivalent sigma zero vs range with above
    listed parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Plotting the NESZ as a function of range gives the above plot. There are some
parameters that can be adjusted, mainly sweep length and the number of sweeps in
image to slightly improve this figure. The requires NESZ for good quality image
depends on the actual reflectivity of the ground, but typically for satellite
based SAR NESZ is around -20 dBsm. With these parameters we should expect to see
to around 1 - 2 km with ok image quality. This is slightly optimistic for
drone SAR since 30 degree grazing angle at 2 km distance would require flying at
1 km altitude, which is much higher than is practical.&lt;/p&gt;
&lt;h2 id="pulse-repetition-frequency"&gt;Pulse repetition frequency&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/prf.svg" width="720" height="450" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Minimum alias-free pulse repetition frequency with
    6 GHz RF frequency and with different number of time-multiplexed channels.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Radar image formation relies on the phase information of the received signal.
If we consider a target that is 90 degrees from the antenna beam center (in the
direction of movement) to avoid phase ambiguity, the maximum phase difference
between two adjacent measurements needs to be less than 180 degrees. If the
movement is larger than this, there can be multiple targets at different azimuth
angles that have the same phase difference between measurements causing them to
overlap in the image. Since both transmitter and receiver antennas move,
a distance of a quarter wavelength between two measurements of the same target
results in a half-wavelength difference in the distance traveled by the signal
and a half-wavelength distance difference corresponds to 180-degree phase
difference. At this spacing, targets at ±90 degrees will have the same 180-degree
phase difference between measurements. If the measurement spacing is increased
further more of the image starts to alias.&lt;/p&gt;
&lt;p&gt;If the antenna is very directive then it's possible to use larger measurement
spacing. Directive antenna won't radiate to large angles that would alias, and
the more directive the antenna is, the larger the measurement spacing can be.
However, since the drone is space-limited and antenna directivity is related to
its size, it might not be possible to design a very directive antenna causing the
maximum measurement spacing to be around quarter wavelength.&lt;/p&gt;
&lt;p&gt;The common flying speed for a quadcopter is around 10 m/s, but flying speed can easily
be decreased if needed. With 6 GHz RF frequency, the quarter wavelength is 12.5
mm (0.5 inches) and 10 m/s flying speed means that pulse repetition frequency
needs to be at least 800 Hz. Since we have time multiplexed four different
polarizations, we need to be able to measure all of them in this time.&lt;/p&gt;
&lt;p&gt;PRF sets the requirement for maximum sweep length. With 4 * 800 Hz = 3.2 kHz
PRF requirement this leaves maximum of 312.5 µs per sweep. However, some time
needs to be reserved for time between sweeps due to limited locking time of the
PLL. The locking time of the PLL is around 20 - 30 µs, leaving 280 µs for the
maximum sweep length.&lt;/p&gt;
&lt;h2 id="required-adc-sampling-frequency"&gt;Required ADC sampling frequency&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/adc_fs.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Required ADC sampling rate vs maximum range and RF
    bandwidth for FMCW radar with 250 µs sweep length.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;FMCW radar mixes the received signal with a copy of the transmitted sweep,
resulting in a sine wave signal at the mixer output with frequency depending on
the range to the target. If the target is at distance &lt;span class="math"&gt;\(r\)&lt;/span&gt;, the IF frequency &lt;span class="math"&gt;\(f\)&lt;/span&gt;
can be calculated as: &lt;span class="math"&gt;\(f = \frac{2 B r}{c t_s}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(B\)&lt;/span&gt; is the bandwidth of
the RF sweep, &lt;span class="math"&gt;\(c\)&lt;/span&gt; is the speed of light, and &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is the sweep length. The
range resolution depends on the RF bandwidth &lt;span class="math"&gt;\(B\)&lt;/span&gt; as &lt;span class="math"&gt;\(\Delta r = \frac{c}{2B}\)&lt;/span&gt;.
For example, 150 MHz bandwidth is required for 1 m resolution, and 300 MHz
bandwidth results in 0.5 m resolution.&lt;/p&gt;
&lt;p&gt;If we have 300 MHz of RF bandwidth (0.5 m range resolution) and &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is 280 µs
as calculated earlier, then we can calculate the required ADC sampling speed
given the maximum target range we want to detect. For example, with 2 km
maximum range, the IF signal frequency is 14 MHz. The ADC sampling frequency
needs to be at least double this due to Nyquist sampling requirement, and some
additional margin is needed for the anti-alias filter roll-off. This results in
a minimum ADC sampling frequency of about 35 MHz. I chose to use 50 MHz
sampling frequency.&lt;/p&gt;
&lt;h2 id="fpga"&gt;FPGA&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_fmcw_digital.svg" width="920" height="739" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Block diagram of digital parts.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The amount of data and strict timing requirements of the sweep generation make
it difficult to handle with microcontroller and FPGA is necessary.
Microcontroller is useful for more complicated tasks such as communication with the
drone flight controller and ground station, configuring the radar, and writing
the data to a filesystem. I decided to use Zynq 7020 FPGA, the same one
I used in my &lt;a href="https://hforsten.com/homemade-6-ghz-pulse-compression-radar.html"&gt;previous pulse radar&lt;/a&gt;. It has FPGA fabric 
and a dual-core ARM processor in the same package. This FPGA is nominally 150 EUR
from ordinary distributors, but is available for fraction of that from Chinese
distributors.&lt;/p&gt;
&lt;p&gt;The drawback of this FPGA is that the microcontroller doesn't have many high
speed connections. For example SD-card and EMMC interfaces are limited to 25
MB/s, which is below the data rate of the ADC. It does have 1 Gbps Ethernet, but
using that would require adding a Raspberry Pi or similar computer, which
isn't possible due to size constraints. For instance, newer Ultrascale+ FPGAs
support SD-cards with 52 MB/s and EMMC at 200 MB/s speeds, but they cost around
500 EUR and are not available from Chinese low cost distributors.&lt;/p&gt;
&lt;p&gt;Zynq can have external DDR3 DRAM, but due to space limitations, it's not
possible to fit enough DRAM to store the whole measurement in memory. With
limitation of only one DDR3 module, the memory is limited to 1 GB, while size of
the measurement can be several gigabytes.&lt;/p&gt;
&lt;p&gt;This leaves only the option of implementing fast enough external communication
interface in the programmable logic side of the FPGA. Luckily &lt;a href="https://zipcpu.com/"&gt;Dan Gisselquist
(ZipCPU)&lt;/a&gt; has made GPL3-licensed &lt;a href="https://github.com/ZipCPU/sdspi"&gt;SD-card and EMMC
controller&lt;/a&gt; that supports faster high-speed
communication modes than the hard IP included with the ARM processor.&lt;/p&gt;
&lt;p&gt;At the time, sdspi controller hadn't been tested with real hardware at the
speeds I required. To make sure I have something working if I'm unable to get
the sdspi core working I connected the SD-card to the ARM processor's
integrated controller, which is limited to 25 MB/s, and EMMC memory to the
programmable logic side that is used with the sdspi controller. This way I'm
able to at least use the SD-card with the integrated controller if the sdspi
core isn't suitable, but this turned out to be unnecessary and the sdspi
controller worked fine. In future versions, it would be better to also connect
the SD-card to PL side using sdspi core for faster SD-card speeds.&lt;/p&gt;
&lt;p&gt;I also added FT600 USB3 bridge IC that can be used to connect FPGA to PC. This
is not needed for drone usage, but allows real-time connection to PC for other
applications.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_fmcw_fpga.svg" width="939" height="699" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FPGA program block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;FPGA functionality is quite simple at the block diagram level. The design
primarily consists of a few independent blocks wired with either DMA or AXI bus
to the processor. For radar operation, the radar timer block is important as it
switches internal and external signals during the measurement. It needs to be
implemented in the FPGA fabric to ensure that the timing is clock cycle accurate
to achieve phase-stable radar measurements. AXI bus is memory-mapped on the
processor side, allowing the radar to be controlled by writing values to fixed
memory addresses.&lt;/p&gt;
&lt;p&gt;A decimating FIR filter after ADC data input can be used to change the sample
rate of the ADC data. It can decimate by 1, 2, or 4. For long-range
measurements, the decimation should be disabled for the maximum IF
bandwidth. But for shorter-range measurements it makes sense to use higher
decimation value to decrease the amount of data that needs to be stored.&lt;/p&gt;
&lt;h1 id="pcb"&gt;PCB&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/pcb_blocks.png" width="1605" height="747" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Labeled PCB 3D model in KiCad. Some 3D models are missing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The PCB has six layers and is designed to be as compact as possible, with
components placed closely together to minimize size. Since one-sided assembly
is cheaper than assembling both sides, the bottom side is empty except for one
SD-card connector that I will solder myself.&lt;/p&gt;
&lt;p&gt;As with many of my previous radars, the RF part is a relatively small part of
the PCB and the overall design effort. Digital electronics and voltage regulators
take up the majority of the PCB space.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/pcb_complete2.jpg" width="1600" height="695" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Assembled PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The radar is designed to accept input voltage from 12 to 30 V and connect
directly to the drone's battery eliminating the need for an external DC/DC
regulator.&lt;/p&gt;
&lt;p&gt;Due to space constraints, there wasn't enough space to fit four SMA connectors
and I didn't want to use any miniature RF connectors. The top two connectors
are switchable TX outputs for H and V polarization antenna inputs, while the
bottom third one is RX input. The RX polarization switch is located on an
external PCB and connects to one of the three four-pin JST connectors on the
bottom right of the PCB. Another JST connectors is for flight controller's
serial port, and the third connector is currently unused, but it could be used to
connect for example GPS.&lt;/p&gt;
&lt;p&gt;There are two USB-C connectors: one for JTAG programming and
debugging of the FPGA, and the other connects to USB3 to FIFO bridge chip that enables
fast data transfer to PC. It isn't needed in drone use, but it's useful for
testing and other applications.&lt;/p&gt;
&lt;p&gt;PCB dimensions are 113 x 48 mm. Width is just skinny enough to fit on the drone,
while it could have been slightly longer.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/mouser.png" width="755" height="467" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;This component is not sale for individuals.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After ordering the PCB I made an order for the components from Mouser. The order
seemed to succeed fine and they accepted my money but I got the above email
afterwards telling me that they can't sell me one of the components. Reason
seems to be that the supplier of the component has forbidden them for selling it
to individuals. This was very frustrating as there was absolutely no warnings on
the website and I had already ordered the PCBs. I was unable to order this
component from anywhere, but I did find older obsolete PE43204 pin compatible IC
from obsolete component reseller. It's specified only for maximum of 4 GHz when
the original was up to 6 GHz but it does seems to have low enough loss at 6 GHz
to not cause too large issues.&lt;/p&gt;
&lt;p&gt;After &lt;a href="https://www.reddit.com/r/rfelectronics/comments/1dubvj7/psemi_forbidding_resellers_from_selling_to/"&gt;asking on
reddit&lt;/a&gt;
the reason is probably that the manufacturer of the component wants to know who
they are selling to, to avoid their components ending up in defense
applications. That's fine, but it would have been nice to know that in advance.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/soldered.jpg" width="1600" height="1105" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SD-card interposer PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I did make one mistake: SD-card pins are connected to 1.8 V I/O pins, while they
should be connected to 3.3 V I/O and SD-card didn't work with this lower voltage.
The radar could be used without SD-card by storing the data into EMMC instead
and then reading it through USB, but SD-card would be much easier to use.
I really didn't want to order another PCB to just fix this one mistake and
I managed to fix it by designing a small interposer PCB with level shifter that
I soldered on top of the previous SD-card footprint. I wrote about it in more
detail in a &lt;a href="https://hforsten.com/fixing-incorrectly-wired-sd-card-connector-with-interposer-pcb.html"&gt;previous post&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/heatsink.jpg" width="1500" height="1125" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Aluminium PCB heatsink under the radar PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Power amplifier can get quite hot if the transmit duty cycle is high. To keep it
cool I ordered custom aluminium substrate PCB that I bolted under the radar PCB.
Solder mask is removed under the PA and thermal pad is placed between the PCB
and heatsink. This cost only $4 for 5 pieces and works very well.&lt;/p&gt;
&lt;h1 id="drone-electronics"&gt;Drone electronics&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/fc.jpg" width="1600" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Speedybee F405 V3 flight controller.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Flight controller came with the drone kit. The included flight controller is
&lt;a href="https://www.speedybee.com/speedybee-f405-v3-bls-50a-30x30-fc-esc-stack/"&gt;Speedybee F405
V3&lt;/a&gt;.
This is a cheap low-end flight controller with 1 MB of flash. It does the job,
but I would recommend getting a little bit better flight controller with
2 MB of flash, the price difference isn't very large.&lt;/p&gt;
&lt;p&gt;There are several possible flight controller softwares. The three most used for
FPV drones are: &lt;a href="https://betaflight.com/"&gt;Betaflight&lt;/a&gt;,
&lt;a href="https://github.com/iNavFlight/inav"&gt;Inav&lt;/a&gt;, and
&lt;a href="https://ardupilot.org/"&gt;ArduPilot&lt;/a&gt;. The main differences of them are:
Betaflight focuses on fast-response manual flying and doesn't have autonomous
flight support, Inav shares lot of code with Betaflight and it includes some
autonomous flight support, and ArduPilot has the most advanced autonomous flight
capability with lot of features but it's more challenging to configure.&lt;/p&gt;
&lt;p&gt;I chose to use Ardupilot and found it to be excellent for this purpose. It has
very good IMU and GPS sensor fusion algorithm that is very helpful for
improving the position accuracy. The flight controller can communicate with the
radar through a serial port allowing it to enable and disable the radar during
autonomous mission and provide position information for the radar.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/gps.jpg" width="1600" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;GPS with integrated compass. It needs to be
    mounted far away from the battery leads to avoid magnetic fields causing issues
    with the compass.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;SAR imaging needs very accurate position information for proper image focusing.
Position information should be accurate within to a fraction of wavelength,
this is just few cm (1 - 2 inches) at this frequency. Many commercial SAR
imaging drones use RTK GPS with a second stationary GPS receiver on the ground
it's possible to obtain about 1 cm accurate positioning. The drawback
is that it's much more costly than regular GPS and RTK GPS receivers are
usually much larger than ordinary GPS receivers which makes it very difficult
to fit it in this drone.&lt;/p&gt;
&lt;p&gt;Good non-RTK GPS might be accurate to about 1 m accuracy. This large
positioning errors cause significant errors in the image if not corrected.
Luckily, it's possible to solve for position error from the radar data which is
called autofocusing. With drawback of needing more processing during the image
formation for autofocusing, it's possible to use regular GPS. Sensor fusion
with inertial measurement unit (IMU) can be used to improve accuracy of the
positioning and obtain position updates faster than the maximum of about 4 Hz that
is possible with only GPS.&lt;/p&gt;
&lt;p&gt;For autonomous flying the drone's flight computer also needs GPS, IMU and
compass. It would be a waste of space to have a second GPS and IMU for just the
radar and instead I'm relying on the flight computer to output its position
estimate to the radar through a serial interface.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_fmcw_drone.svg" width="930" height="360" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The drone is controlled with a radio controller. I use
&lt;a href="https://www.expresslrs.org/"&gt;ExpressLRS&lt;/a&gt; radio link, which is very common with
FPV drones. The drone also has a radio link to the ground control software
running on PC. This is needed for programming the autonomous mission parameters,
changing drone settings, it can be used to control the drone, and it displays
telemetry during the flight. Ground station can also be used to send messages to
radar through the flight controller and this allows programming the radar
parameters from the laptop.&lt;/p&gt;
&lt;p&gt;Typically Ardupilot has required using two radios. One for radio controller and
other for telemetry, but ELRS recently added &lt;a href="https://www.expresslrs.org/software/mavlink/"&gt;Mavlink
support&lt;/a&gt; that allows using
a single radio for both purposes. This is very convenient in this application.&lt;/p&gt;
&lt;h1 id="antennas"&gt;Antennas&lt;/h1&gt;
&lt;p&gt;In theory, the wider the antenna beam width is, the better the resolution of
SAR image is. &lt;a href="https://topex.ucsd.edu/rs/sar_summary.pdf"&gt;A famous result in SAR imaging&lt;/a&gt; is that the best possible
cross-range resolution in strip mode SAR (fixed antenna angle and straight
baseline) is &lt;span class="math"&gt;\(L/2\)&lt;/span&gt;, where &lt;span class="math"&gt;\(L\)&lt;/span&gt; is the length of the antenna. However, in
practice wider antenna beam isn't often better. Wider antenna beam means lower
gain which decreases the signal to noise ratio and limits the maximum range.
Antenna gain is squared in the link budget so halving the antenna gain, means
that number of pulses need to be quadrupled to get the same SNR.&lt;/p&gt;
&lt;p&gt;The cross-range resolution depends on the length of the baseline where the
target is visible and the wider the antenna beam is the longer this is. With
spotlight imaging, where antenna tracks the target, cross-range resolution is
not limited by the antenna beam width and spotlight imaging is easy with drone.
With drone SAR the maximum possible baseline length is often the limiting
factor for resolution as it's hard to fly very long track when limited by
visual line of sight.&lt;/p&gt;
&lt;p&gt;The azimuth angle resolution in spotlight imaging mode (or in stripmap mode
where antenna beam always covers the target) can be approximated as: &lt;span class="math"&gt;\(\Delta
\theta \approx \lambda/L\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is wavelength and &lt;span class="math"&gt;\(L\)&lt;/span&gt; is the length
of the track. Cross-range resolution can be obtained with: &lt;span class="math"&gt;\(\Delta
y = 2r \sin(\Delta \theta/2)\)&lt;/span&gt;, where &lt;span class="math"&gt;\(r\)&lt;/span&gt; is distance to the target.&lt;/p&gt;
&lt;p&gt;With drone SAR a big issue is how to fit large enough antennas on the drone.
Since the radar is FMCW, separate transmitter and receiver antennas are needed
further decreasing the space per antenna and low TX-RX leakage requires having
some distance between them.&lt;/p&gt;
&lt;p&gt;I have previously used &lt;a href="https://hforsten.com/horn-antenna-for-radar.html"&gt;self-made horn antennas&lt;/a&gt;
with my radars, but they are too big to fit on drone. The whole length of the
horn antenna is 100 mm, and even just the coaxial-to-waveguide transition is 25
mm long. This size makes it impossible to fit on the drone, which has only 50 mm
spacing between propeller tips. My previous horn antenna isn't dual-polarized,
but horn can be made dual-polarized easily by adding two feeds 90 degree apart.&lt;/p&gt;
&lt;p&gt;Patch antenna can be made much smaller since they are just copper on a PCB. They
can also be made dual-polarized with two feeds 90 degree apart. However,
a simple patch on 1.6 mm thick FR4 PCB has poor bandwidth, FR4 dielectric
inaccuracy can cause frequency shift, and the gain isn't very high. Gain can be
improved by making an array of patches, but with FR4 substrate the losses in
feeding network increase quickly.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/stacked_patch.svg" width="700" height="220" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Stacked aperture coupled patch antenna. Side (left) and top (right) views.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Neither horn nor patch antenna seemed suitable. After reading some scientific
papers I found this &lt;a href="https://digital-library.theiet.org/doi/10.1049/el%3A20010828"&gt;dual-polarized slot-fed stacked patch
antenna&lt;/a&gt; paper. It
consists of patch antenna that is fed by microstrip lines that couple to patch
through H-shaped slots in the ground plane. Two feed lines and slots 90 degree
apart can be used to make it dual-polarized. A second patch is suspended few mm
away from the first patch with air in between them. This structure can achieve
much wider bandwidth than a single patch making it tolerant to frequency shift
caused by inaccuracy of FR4 permittivity. The second patch also slightly
increases the gain.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/antenna_front.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;TX and RX patch fed horn antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;However, more gain would be good to have for improving the signal-to-noise
ratio. Sidelobes at 90 degree angle should also be lower to decrease TX-RX
leakage. To solve this I added a sheet metal horn structure around the antenna,
making it a stacked patch fed horn antenna. The horn increases the height by 10
mm, but even just a sheet metal wrapped around the patch gap increases the gain
and decreases sidelobes without increasing the height. I haven't found
a similar structure in any publications, but it wouldn't surprise me if some
exist, as it does seem straightforward.&lt;/p&gt;
&lt;p&gt;A pyramidal horn with filled-in corners would likely offer slightly higher gain
and be mechanically stiffer than this four-flap design. However, this was
easier for me to manufacture. I cut the copper sheet by hand with scissors and
soldered it to keep it together.&lt;/p&gt;
&lt;p&gt;This antenna has everything I wanted. It's dual-polarized, has very wide
bandwidth, good gain, isn't as tall as similar gain horn antenna fed with
coaxial-to-waveguide transition, and it's cheap to manufacture requiring just
two FR4 PCBs, some copper sheet and few bolts and spacers. FR4 is lossy and gain
would likely be around 0.5 - 1.0 dB higher if a proper low loss RF material was
used, but the cost would be about 100x higher in prototype quantities and it
doesn't make sense for me to spend so much more for so little improvement.&lt;/p&gt;
&lt;p&gt;Between the antennas is a small 0.25 x 0.5 wavelength wall that decreases TX-RX
coupling. I tested few different size walls and this small wall is more
effective than no wall and also more effective than taller walls.&lt;/p&gt;
&lt;p&gt;Not counting the SMA connectors, the height of the antenna is 18 mm, of which 10
mm is the height of the horn above the suspended patch. Total height including
the SMA connector is 28 mm. Patch substrate dimension is 45 x 45 mm and the horn
aperture is 65 x 65 mm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/antenna_back.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Backside of the antennas. Each antenna has two SMA
    connectors, one for H and other for V polarization.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Backside of the antennas is covered by copper sheet to decrease the backwards
radiation. This is needed because antennas are mounted right on top of the radar
PCB which doesn't have any shielding. Without shielding TX antenna backwards
radiation would increase the TX-RX coupling. Copper sheet is also inserted into
the wall between antennas and the tape keeps it in place. &lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/antenna_gain.svg" width="720" height="450" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simulated radiation pattern of the antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The simulated -3 dB beamwidth is 50/60 degrees in 0/90 degree angles. H and
V feed slots are rotated 90 degrees and the radiation pattern from the other
port is similar but rotated 90 degrees. Simulated peak gain is 10.0 dB.&lt;/p&gt;
&lt;p&gt;Sidelobe to 90 degree direction is about -10 dB. It's important for this to be
low to minimize TX-RX leakage.&lt;/p&gt;
&lt;p&gt;Since the antenna radiation pattern isn't quite symmetrical, mounting the other
antenna at a 90-degree rotation compared to the first one ensures good antenna
pattern matching between HH and VV polarizations. If the TX antenna transmits
H polarization from the first port, RX antenna receives H polarization from the
other port, and other way around for V, causing the two-way antenna pattern to
match between the both cases. However, HV and VH antenna patterns don't match
the HH and VV patterns, since in the cross-polarization case the same port is
used to transmit and receive on both antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/antenna_sp.svg" width="720" height="450" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Measured S-parameters of the antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There is a slight difference in the H and V port matching due to their slightly
different coupling slot sizes. Antenna useable bandwidth is from about 4.5 GHz
to 6.2 GHz which is more than enough for this application.&lt;/p&gt;
&lt;h1 id="mechanical-design"&gt;Mechanical design&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/blender.png" width="1227" height="703" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone mechanical model in Blender.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The drone needs some mechanical parts to hold the radar on the drone frame.  The
flight controller is mounted inside the frame, but there isn't enough room
inside it for the radar, so I designed a 3D printed mount that holds the radar
PCB under the drone frame. This mounting position also requires landing legs so
that the drone doesn't land on the radar. I designed it in Blender since I don't
know any mechanical CAD programs. It works just fine for these simple parts.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/assembly_radar_attached.jpg" width="1600" height="1151" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar attached under the drone.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The radar mount holds the radar PCB on the drone frame. It would be a good idea
to have some sort of weather proof enclosure for it, but I haven't got around it
yet. I added some material over the PCB to protect it in case the landing legs
fail.&lt;/p&gt;
&lt;p&gt;Radar holder attaches with four screws to the drone. Drone radio controller
antenna is visible at the bottom left. Propellers are collapsible, these make it
much easier to transport the drone as with these it fits in a backpack.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/assembly_switch.jpg" width="1600" height="1173" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Receiver polarization switch PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Antenna board is held with two bolts that allow its angle to be changed. Flight
controller serial port is attached to one of the JST connectors. There also
other JST connector from flight controller that is currently unused and just
held with tape in place.&lt;/p&gt;
&lt;p&gt;Transmitter has polarization switch and two SMA connectors on the PCB, but the
receiver polarization switch is on a separate PCB due to lack of space. I had
a mounting holes for it on the radar holder part, but the SMA cables are stiff
enough that I found it easier to just leave it hanging there. Polarization
switch connects to the radar PCB with another JST connector.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/assembly_legs.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone with landing legs.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Landing legs are 10 cm diameter carbon tubes with 3D printed TPU caps. Smaller
diameter tube would have likely been fine, these are very stiff and something
else would fail before them if they are stressed too much in landing.&lt;/p&gt;
&lt;p&gt;Radar is powered directly from the drone battery. XT60 splitter is used to
connect both flight controller and radar to the same battery.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/assembly_weight.jpg" width="1600" height="1353" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone balanced on a kitchen scale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Weight of the drone without battery is just 752 grams (1.66 lbs). I have two six
cell LiPo batteries, the smaller 1300 mAh capacity battery weights 196 grams and
the bigger 2200 mAh battery weights 322 grams. With the smaller battery the total
weight of the whole system is just 948 grams.&lt;/p&gt;
&lt;h1 id="image-formation"&gt;Image formation&lt;/h1&gt;
&lt;p&gt;Radar measures the distance and phase of each target. To convert these
measurements into a radar image, matched filtering can be used. For each pixel
in the image, generate a reference signal corresponding to what a target at that
position would reflect. Multiply the measured signal with the complex conjugate
of the reference signal for each measurement, then sum these products over
all measurements. When the measured signal closely matches the reference signal,
their product becomes large because the phases align. If it doesn't match,
the result of the multiplication is a complex number with a random phase, and summing
random complex numbers will average out generating a low response.&lt;/p&gt;
&lt;p&gt;The image formation can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$I = \sum_{p \in \mathcal{P}} \sum_{n=1}^N S_n(d(p,x_n)) \cdot H(d(p,x_n))^* $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(\mathcal{P}\)&lt;/span&gt; is the set of pixels in the image, &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the number of
radar measurements, &lt;span class="math"&gt;\(S_n\)&lt;/span&gt; is Fourier transformed measured IF signal &lt;span class="math"&gt;\(s_n\)&lt;/span&gt;,
&lt;span class="math"&gt;\(d(p,x_n)\)&lt;/span&gt; is the distance to location of pixel &lt;span class="math"&gt;\(p\)&lt;/span&gt; from radar position at that
measurement &lt;span class="math"&gt;\(x_n\)&lt;/span&gt;, and &lt;span class="math"&gt;\(H^*\)&lt;/span&gt; is complex conjugate of the reference function of
what target at that position in the image should look like (Fourier transform of
the radar IF signal from target at that position in the image).&lt;/p&gt;
&lt;p&gt;This is called backprojection algorithm. It's simple and doesn't make any
approximations or assumptions about flight geometry, but it's very demanding to
calculate. For example with 1 km x 1 km image with 0.3 m resolution and 10,000
radar sweeps, calculating the image needs &lt;span class="math"&gt;\((1000/0.3)^2 \cdot 10000 = 111 \cdot
10^9\)&lt;/span&gt; backprojections. This means over 100 billion complex exponentials and
square root calculations is needed for one image, and image size and number of
sweeps can be even larger in practice. There are some clever algorithms that can
be used to speed this up, but they often have approximations or only work with
linear flight tracks. One easy improvement that can be made without many
drawbacks, is to use polar coordinates instead of Cartesian coordinates, as that
requires less pixels in the image since angular resolution is constant while
cross-range resolution is better closer to the radar. Polar coordinate image can
then be afterwards interpolated to Cartesian grid.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;__global__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backprojection_polar_2d_kernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sweep_samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nsweeps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref_phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delta_r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theta0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dtheta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Nr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntheta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blockIdx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blockDim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadIdx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idtheta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntheta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntheta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Nr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idtheta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntheta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theta0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idtheta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dtheta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqrtf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nsweeps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Sweep reference position.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idbatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nsweeps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idbatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nsweeps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idbatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nsweeps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pz2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pos_z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Calculate distance to the pixel.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqrtf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pz2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;delta_r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Linear interpolation.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sweep_samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sweep_samples&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sweep_samples&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interp_idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interp_idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interp_idx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref_sin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref_cos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sincospif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref_phase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ref_sin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ref_cos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;complex64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ref_cos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref_sin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntheta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idtheta&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some years ago this would have been unbelievable amount of compute, but with
modern GPU this can be calculated in under a second. This problem is especially
well suited for GPU implementation since every pixel can be calculated
independently in parallel. Very straightforward CUDA kernel is able to calculate
220 Billion backprojections per second on RTX 3090 Ti GPU. This is very
respectable speed considering that each backprojection requires square root and
complex exponential (which can be calculated with just sin and cos). I'm sure
that someone experienced with CUDA programming could make this even faster as
this doesn't have any optimizations or approximations and is just the direct
algorithm implementation.&lt;/p&gt;
&lt;h2 id="autofocus"&gt;Autofocus&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/autofocus.svg" width="639" height="879" style="width: 30%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Autofocus block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Positioning accuracy of the GPS and IMU isn't good enough to form a high-quality
image. Ideally, the position should be known to a fraction of the wavelength,
but accuracy of the GPS isn't good enough. To achieve good image quality, an
autofocus algorithm is necessary to focus the radar image using information from
the radar data.&lt;/p&gt;
&lt;p&gt;The most commonly used autofocus algorithm is &lt;a href="https://ieeexplore.ieee.org/abstract/document/303752"&gt;phase gradient
autofocus&lt;/a&gt;. It's simple
and fast autofocus algorithm that works by taking an unfocused radar image as an
input and solving for a phase vector that when multiplied with the azimuth FFT
of the image gives a focused image. However, it doesn't work well in this case
since the azimuth beam is wide and the radar baseline is long causing the
focusing errors to be spatially dependent.&lt;/p&gt;
&lt;p&gt;I updated my &lt;a href="https://hforsten.com/backprojection-backpropagation.html"&gt;previous backpropagation autofocus&lt;/a&gt; to
use PyTorch and made some improvements. This autofocus algorithm works by
forming the radar image, calculating the gradient of the input velocity, and 
clipping the learning rate to limit the maximum position change to a predefined
value. The input velocity is then updated using a gradient descent optimizer.
I found that using a 3D position doesn't work well, as it often tends to just nudge
each position in random directions. Instead, using velocity and integrating it
to position seems to yield much better results. A small regularization term is
also included to minimize the distance between the optimized and original
positions, favoring smaller updates.&lt;/p&gt;
&lt;p&gt;Adjusting the learning rate based on the maximum position change makes it easier
to set the optimizer meta parameters. Instead of setting the learning rate
directly, maximum position update is given which is used to set the learning
rate.&lt;/p&gt;
&lt;p&gt;This is very general autofocus algorithm that makes no assumptions about the
radar system, scene, or the flight track. The obvious disadvantage is that it
requires forming the radar image many times making the already slow image
formation many times slower. Without the fast GPU image formation this would be
too slow to be useful.&lt;/p&gt;
&lt;p&gt;The autofocus algorithm is available on
&lt;a href="https://github.com/Ttl/torchbp"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/mission_planner.jpg" width="1209" height="1040" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Configuring the mission in ArduPilot Mission
    Planner.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The mission is programmed beforehand with the ArduPilot Mission Planner. The
drone will automatically fly the programmed waypoints, there are also commands
to set the ROI (region of interest) so that antenna always points towards it,
and the radar measurement is started with digicam configure command in the
mission. It's originally meant for configuring ordinary camera, but
I programmed the radar microcontroller to listen to it. Using an existing
command makes it easy to make the radar work with the existing ArduPilot software.&lt;/p&gt;
&lt;p&gt;Setting ROI, which is needed for spotlight imaging, needs a patch to ArduPilot
firmware. By default drone's front will always point towards the ROI and there
isn't a way to configure it to point the antenna towards ROI instead. The patch
is available on the &lt;a href="https://github.com/ArduPilot/ardupilot/pull/28486"&gt;ArduPilot Github as a pull request&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/action.jpg" width="1800" height="1910" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone in action.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The scene is a wide open field. There's about 1.5 km distance to the forest at
the antenna pointing direction. The drone flies at 110 m altitude in a straight
line for about 500 m at 5 m/s velocity. The radar was configured to transmit
only VV polarization with 400 µs long sweep, 500 MHz bandwidth, and 1 kHz
pulse repetition frequency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/raw_data.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Range compressed raw data.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The range compressed (Fourier transformed) captured data doesn't look very
impressive. It doesn't look anything like an image since due to the wide
antenna beam at each sweep many targets at different angles are captured.&lt;/p&gt;
&lt;p&gt;At the zero distance there is a large response from the TX-RX leakage, then the
next reflection is at 100 m distance from the ground. Even though the antenna
gain at directly below is much smaller than at the beam center, due to the angle
of the reflection and close distance, the reflection from directly below is very
large. At large distances reflections are mostly below the noise floor of
individual sweeps, but during image formation many sweeps are integrated
improving the signal to noise ratio. Some large individual objects are visible
and their distance to the radar changes as the drone moves.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar2_scatter.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Recorded drone position and antenna pointing
    vector from GPS and IMU. Note the unequal axes scale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Ideally the track should have been a straight line but there
is some disturbances due to for example wind. The drone is very light and even
a slight wind can easily affect it. The ROI was set quite far away there is only
few degree of change in the antenna pointing direction during the measurement.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_img2_polar.jpg" width="1579" height="848" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image without autofocus.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the processed SAR image without autofocus in pseudo-polar coordinates
that the image formation uses internally. It's pseudo-polar because angle axis
is in sine of radians instead of just radians, this is slightly more efficient
than ordinary polar coordinates. Image size is 6k x 20k pixels using 51,200
sweeps.&lt;/p&gt;
&lt;p&gt;Compared to the raw data it's a night and day. Various geographical features can
be now identified, but polar format makes it hard to compare to the map.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_img2_no_opt.jpg" width="962" height="843" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image without autofocus.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Cartesian coordinate image can be obtained by projecting the pseudo-polar image
to Cartesian grid. This is very fast operation compared forming the image
directly on the Cartesian grid. The image is also aligned so that north points
up using the drone's electronic compass measurements. Left corner is missing
a small patch of data due to the rotation.&lt;/p&gt;
&lt;p&gt;The resulting image is still quite blurry. Clearly only relying on the GPS and
IMU positioning isn't good enough and autofocus is needed to get a sharply
focused image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar_img2_opt_cart.jpg" width="962" height="843" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Autofocused SAR image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After applying 30 iterations of the minimum entropy gradient optimization
autofocus, the image quality is much better. Five iterations would have been
probably enough, but using more iterations does improve the quality slightly.
This does take several minutes since each iteration requires calculating forward
and backwards pass of the backprojection.&lt;/p&gt;
&lt;p&gt;Due to the low grazing angle, tall structures such as trees cast long shadows. The
image amplitude isn't normalized, which is why it's brighter closer to the
origin.  The antenna radiation pattern can be also visualized in the image. The
beam center is tilted slightly to the right, and the antenna gain at the left
side of the image is much smaller due to it being farther from the beam center,
causing it to be dimmer.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar2_img_detail_comparison.jpg" width="1478" height="726" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SAR image detail comparison. Without autofocus
    (left) and with minimum entropy optimization autofocus (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There is quite lot of detail in the resulting radar image when zooming in.
Comparing a 300 x 300 m patch, the autofocused image reveals surface details of
the field that was just blur in the image without autofocus.&lt;/p&gt;
&lt;p&gt;I also tried using phase gradient autofocus, but it doesn't work well in this
case. &lt;a href="https://hforsten.com/img/sar_fmcw/sar2_pga_detail.jpg"&gt;The result&lt;/a&gt; is very similar to
the image without autofocus.&lt;/p&gt;
&lt;p&gt;The three lines at the bottom left are power lines. They seem to be only visible
in the image when the radar is looking at them at 90 degree angle, at other angles
the reflectivity is so low that they are invisible.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar2_v_opt.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Original and optimized velocity.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Comparing the drone velocity before and after optimization the changes aren't
very large. Along the track and range direction velocity components are both
adjusted slightly and height direction velocity component is basically
unchanged.&lt;/p&gt;
&lt;h1 id="full-polarization-measurement"&gt;Full polarization measurement&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar3_map_1100.jpg" width="693" height="650" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Google maps screenshot of the SAR imaging area.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I also made another measurement at other location using all four polarizations.
The radar flies a linear track autonomously as before, but now the radar quickly
switches between each of the four polarization switch states. Sweep length was
reduced to 200 µs, pulse repetition frequency is 715 Hz for each
polarization, and other parameters are kept the same. Number of sweeps in the
image is the same 51,200.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar3_x4.jpg" width="721" height="705" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Four SAR images with different polarizations.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The four polarizations look very similar. The main differences are that
cross-polarization images (HV and VH) have weaker amplitude due to
cross-polarization component in general being smaller than the reflection of the
same polarization.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar3_pol_1100.jpg" width="905" height="854" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Polarized SAR image with autofocus.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Instead of looking at four different images for each polarization, it's common
to use RGB color channels for different polarizations in the same image. In this
colored image it's easier to visualize how each target reflects specific
polarizations. The ground is tinted purple, indicating that it
reflects VV and HH polarization better than the cross-polarized components. The same
can be seen on the buildings and in the light poles along the road. Forest areas
are colored white as they reflect all polarizations about equally. However,
since the effects of the antenna radiation pattern and possibly slightly
different losses for different polarization switch states are not calibrated
some of the observed differences could be attributed to the hardware. Better
accuracy measurements would require calibration.&lt;/p&gt;
&lt;p&gt;The area with bunch of points around (200, 500) meters is a some kind of garden
of small trees each surrounded with a metal wire mesh.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/sar3_scene.jpg" width="1200" height="900" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Picture at the ground at (-50, -80) m coordinates
    in the SAR image looking towards negative Y-axis.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There was slight amount of snow on the ground during the measurements. The
visible picture is from the top of the SAR image looking down. The small forest
on the left is the small patch of trees in the middle of the image.&lt;/p&gt;
&lt;h1 id="videosar"&gt;VideoSAR&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/videosar_mission.jpg" width="824" height="773" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Drone waypoints for the octagonal flight path. Red
    marker is the region-of-interest where drone points the antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The previous measurements synthesized one high-resolution image from a long
baseline. It's also possible to synthesize many images with small baselines from
one long measurement, and these many images can then be turned into a video.&lt;/p&gt;
&lt;p&gt;For the backprojection algorithm, the flight track doesn't need to be linear and for
this case I programmed the drone to fly octagonal track while pointing the
antenna at the octagon's center.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/sar_fmcw/12_21_pol.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Each frame has 1024 radar sweeps with 512 of them overlapping with the previous
frame.  Since each frame has less sweeps than the previous full images, the
frames are noisier and have worse angular resolution. The video is sped up by
about 10x. All four polarizations are used, and the image colorization is the
same as in the previous polarized SAR image.&lt;/p&gt;
&lt;p&gt;Frames are autofocused separately and there isn't any alignment of adjacent
frames, which causes the frames slightly wobble or jump around occasionally. Corners
of the octagon are especially challenging for the image formation since both
along- and cross-range positions need to be solved accurately for a good-quality
image. Angular resolution can also vary between frames as the baseline
length between the frames can vary, as only the number of sweeps is the same
between the frames.&lt;/p&gt;
&lt;p&gt;Natural targets such as the ground and forest look very similar at different frames,
but at several points in the video large reflections can be seen for example at
bridge and power lines when they are oriented at a 90-degree angle to the radar.
The bright spot that looks like it's moving at the bridge is just glint from the
railing. The mismatch between antenna patterns of different polarizations is
also visible as the same target can have slightly different color at the beam
center or at the edge.&lt;/p&gt;
&lt;h2 id="imaging-geometry"&gt;Imaging geometry&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/look_angle.svg" width="720" height="450" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar look angle with 120 m flying height.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Without special permits, it's allowed to fly the drone at a maximum altitude of
120 m. Usually, for SAR imaging, the look angle is around 10 to 50 degrees. If the
look angle is close to 90 degrees (i.e., looking straight down at the ground), the
reflected power is high, but the range resolution is poor as the distance to the
radar is almost the same for nearby locations. For low look angles the range
resolution is good, but due to the low grazing angle the reflected power back to
the radar is low. With extremely low look angle the reflected power can be about
10 to 20 dB lower than it would be compared to more usual around 45 degree look
angle reducing the maximum distance the radar can see.&lt;/p&gt;
&lt;p&gt;Another issue is shadowing caused by tall objects.  For instance, when flying at
120 m height, the grazing angle at 2 km distance is only 3.4 degrees. A 10
m tall tree casts 170 m long shadow at this low angle, making it impossible to
see any reflections from the ground after the tall object. This is clearly
visible in all of the measurements. Especially in the full-polarization
measurement only the tops of the buildings are visible at long distances.&lt;/p&gt;
&lt;h1 id="update-october-2025"&gt;Update October 2025&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/08_23_2_gopro.jpg" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Camera image of the scene.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/autofocus/08_23_2_autofocus.png" style="width: 80%;
    height: auto;"/&gt; &lt;p style="font-size:13px"&gt;SAR image of urban scene with new
    image processing software.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Since publishing this post, I have worked a lot on the image processing software
and now I can generate much better quality images. For more details see the
newer post: &lt;a href="https://hforsten.com/synthetic-aperture-radar-autofocus-and-calibration.html"&gt;Synthetic aperture radar autofocus and calibration&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/sar_fmcw/sar_fmcw.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/sar_fmcw/schematic_top.png" width="805" height="565" border="2" style="width: 50%; height: auto; border:2px solid black;" /&gt;&lt;/a&gt;
    &lt;p style="font-size:13px"&gt;Schematic of the radar (click to open).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The synthetic aperture radar drone can image at least up to 1.5 km and likely
even farther if flown higher. It weighs under 1 kg including the radar, drone,
and battery. The system can capture HH, HV, VH, and VV polarizations.
A gradient-based minimum entropy autofocus algorithm is capable of producing
good good-quality images with a wide antenna beam using only non-RTK GPS and IMU
sensor information. The total cost of the drone was about 200 EUR, 600 EUR for
two radar PCBs, and about 10 months of my free-time after work. I'm very happy
with the performance of the system considering its low cost.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Ttl/torchbp"&gt;Differentiable GPU image formation library is released on
Github&lt;/a&gt; under MIT license. Schematic of the
radar is also available above.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Fixing incorrectly wired SD card connector with interposer PCB</title><link href="https://hforsten.com/fixing-incorrectly-wired-sd-card-connector-with-interposer-pcb.html" rel="alternate"></link><published>2024-08-02T00:00:00+03:00</published><updated>2024-08-02T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2024-08-02:/fixing-incorrectly-wired-sd-card-connector-with-interposer-pcb.html</id><summary type="html">&lt;p&gt;Soldering a PCB directly on top of another to fix incorrectly wired SD card connector.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;A while ago ordered a PCB where I made a mistake of connecting SD card to 1.8
V logic level I/O pins instead of the correct 3.3 V. As a result the SD card in that
PCB didn't work. Otherwise the PCB was good and I didn't want to reorder the
whole PCB to fix this one mistake as it would have cost over 500 EUR.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/pcb_bottom.jpg" width="1600" height="1242" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Bottom of the PCB and the incorrectly wired
    SD card connector pads.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I had placed the SD card connector on the bottom of the PCB without many
components near it. The plan to fix it was to manufacture a small interposer PCB
with 1.8 V to 3.3 V logic level translator, SD card connector, and matching pads
on the bottom so that it can be soldered directly to the bigger PCB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/digilent_jtag.jpg" width="1280" height="1000" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Example of commercially sold PCB with castellated
    holes meant to be directly soldered on bigger PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Typically when soldering PCBs directly together castellated holes are used. They
are plated holes that overlap the edge of the PCB such that half of the hole is
routed out. This leaves nice plated edge to the PCB that is easy to solder.&lt;/p&gt;
&lt;p&gt;However, the issue with using castellated holes in this case is that pitch of
the SD card connector pads was only 1.1 mm which was too small for castellated
holes for any low cost PCB manufacturer. Another issue is that since the SD card
connector is at the edge of the PCB, the new interposer PCB would have needed to
stick out a little to make room for the castellated holes.&lt;/p&gt;
&lt;p&gt;The remaining option was to put SMD pads under the interposer PCB. With 4-layer
PCB it would be possible to mount the new SD card connector directly over the
old one and the level shifter could be next to the SD card connector. The
drawback of this approach is that since the pads are under the PCB it's not
possible to solder them with soldering iron and it would require using solder
paste and hot air tool.&lt;/p&gt;
&lt;p&gt;I did find some discussion if this was possible but I didn't find any actual
results, which motivated me to write this post.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/sd_card_fix_pcb.jpg" width="1901" height="714" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Interposer PCB bottom (left) and top (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the PCB I designed. It's a 4 layer PCB with mirrored SD card footprint
on the bottom that matches the footprint of the incorrectly wired SD card
connector. On the other side is bidirectional 1.8 V to 3.3 V level shifter
(&lt;a href="https://www.ti.com/product/TXS02612"&gt;TXS02612&lt;/a&gt;) and 1.8 V regulator. SD card
connector footprints are directly on top of each other to minimize the PCB area.&lt;/p&gt;
&lt;p&gt;Manufacturing, assembly, and shipping of 5 copies this PCB cost 43 USD.&lt;/p&gt;
&lt;h1 id="soldering"&gt;Soldering&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/solder_paste.jpg" width="1386" height="1094" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solder paste manually applied to the SD card
    connector pads.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I started with applying some solder paste to the pads on the bigger PCB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/wires.jpg" width="1600" height="1177" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Wires for positioning.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I found it hard to position the interposer PCB correctly because the pads are
completely covered by the interposer PCB. Since the SD card footprints were
exactly at the same location, the two holes in the footprint also overlapped and
could be used to position the interposer PCB. I cut some solid core wire and put
them in the holes.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/pcb_placed.jpg" width="1600" height="1283" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Interposer PCB placed with the guide wires.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using the solid core wires I was able to position the interposed PCB correctly.
This wouldn't have been possible if the SD card connector on the interposer would
have been soldered beforehand.&lt;/p&gt;
&lt;p&gt;I then heated the interposer with hot air to solder it. It was hard to see when
the solder paste had melted since the pads were completely covered by the
interposer, but I could see some flux bubbling from under the interposer and the
solder joints melting on top of the interposer PCB and I determined it was hot
enough. I had placed damp paper towel under the bigger PCB to avoid desoldering
components on it which worked fine.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/microscope.jpg" width="1081" height="1080" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Solder joints seen with a microscope.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Solder joints seem fine when viewed with microscope. Although my microscope's
image quality isn't too high, there is no visible short circuits and the
contacts seems good.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/soldered.jpg" width="1600" height="1105" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Interposer soldered.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I then soldered the SD card connector on the interposer PCB. It was easy
to solder because of the large pads and could be done easily with a soldering iron.&lt;/p&gt;
&lt;p&gt;With the added interposer PCB the SD card started to work.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/glued.jpg" width="1600" height="1216" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Super glue added.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I added some super glue to the sides for mechanical stability. I didn't think
only the solder joints could handle the mechanical stress of inserting and
removing the SD card.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sd_fix/fixed_2x.jpg" width="1600" height="1114" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Two fixed PCBs.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I also did the same procedure to another PCB which also worked fine.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Fixing incorrectly wired component can be fixed with interposer PCB if there is
enough extra space for the PCB to fit into. Soldering it can be difficult and
requires hot air tool. Fixing this PCB with the interposer cost about 50 EUR
compared to over 500 EUR for respin of the board.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Homemade 6 GHz pulse compression radar</title><link href="https://hforsten.com/homemade-6-ghz-pulse-compression-radar.html" rel="alternate"></link><published>2024-04-03T00:00:00+03:00</published><updated>2024-04-03T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2024-04-03:/homemade-6-ghz-pulse-compression-radar.html</id><summary type="html">&lt;p&gt;Designing a modern pulse compression radar that uses digital signal processing.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/fmcw_vs_pulsed_radar.svg" width="931" height="318" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FMCW and pulse radar architectures.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I have &lt;a href="https://hforsten.com/third-version-of-homemade-6-ghz-fmcw-radar.html"&gt;previously made several FMCW radars&lt;/a&gt; that
have worked well. FMCW (Frequency Modulated Continuous Wave) radar is quite easy
and cheap to make. It uses separate transmit and receive antennas, which avoids
the need for switching between receiving and transmitting. It mixes the received
signal with the transmitted signal, resulting in a low output frequency, making it
possible to use low-speed analog-to-digital converter (ADC). However, big,
serious radars typically aren't FMCW radars, instead they are pulse radars.
Switching one antenna between transmit and receive modes allows them to use just
one antenna. When an antenna diameter is measured in meters it matters a lot how
many are needed. Pulse radar can use large transmit power without worrying about
saturating the receiver, which is a big issue with FMCW radar. Pulse radar is
also better for measuring velocity of fast-moving targets as it can transmit
pulses more frequently, resulting in larger maximum unambiguous Doppler shift
it can measure.&lt;/p&gt;
&lt;p&gt;For these reasons, FMCW radars are usually used in short range applications such as
automotive radars and aircraft altimeters, while pulse radars are used mainly
for long-range applications such as weather radars, aircraft detection, and
synthetic aperture radar imaging from aircraft or satellite.&lt;/p&gt;
&lt;p&gt;Pulse radar is much more difficult to design than FMCW radar. To share one
antenna, very fast switching between transmit and receive is needed. Radar pulses
travel at the speed of light, and for example, if switching from transmit to
receive takes 1 microsecond, all the reflections from targets in 150-meter
distance would be missed during the switching time. Sharing one antenna
causes the radar to have a minimum detection distance, which can be hundreds of
meters which makes it unsuitable for short-range operation.&lt;/p&gt;
&lt;p&gt;Another difficulty is that pulse radar requires much faster ADC to capture the
received pulses. FMCW radar mixes transmitted and received waveforms which
results in a low-frequency sine wave for each target at the mixer output, for
short-range operation, it's possible to use ADC sampling frequency of 1 MHz or
even less while using hundreds of MHz of RF bandwidth. Pulse radar requires
much faster ADC, typically fast enough to sample the whole RF bandwidth of the
transmitted pulse. The range resolution of the radar is determined by the RF
bandwidth, and for useful range resolution ADC sampling rate should be hundreds
of MHz or even over 1 GHz. This fast ADCs are very expensive and require
expensive digital electronics to handle all the data.&lt;/p&gt;
&lt;p&gt;This article is about my experiences building a modern pulse radar utilizing
fast digital signal processing cheaply.&lt;/p&gt;
&lt;h1 id="pulse-compression-radar"&gt;Pulse compression radar&lt;/h1&gt;
&lt;p&gt;There are many kinds of pulse radars, and the one I want to make is a pulse
compression radar that supports arbitrary waveforms. Generating only linear
frequency sweeps could be simpler and sufficient for many practical
applications, but it wouldn't be as interesting.&lt;/p&gt;
&lt;p&gt;The requirement for arbitrary waveform means that there needs to be
a digital-to-analog converter (DAC) with large enough sampling rate to generate
the transmitted waveform. The receiver also needs an ADC with large enough sample
rate to sample the whole RF bandwidth.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulsed_rf_block.svg" width="614" height="366" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar RF side block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the block diagram of the radar. The architecture is very similar to
software-defined radio (SDR) and it could be used as a radio too. The radar has
two time-multiplexed receiver antennas with transmitter being shared with one of
them. I added the second receiver channel mainly because it was very cheap, it
only requires additional switch, LNA and SMA connector. The second receiver
channel makes it possible to use the radar also in FMCW mode.&lt;/p&gt;
&lt;p&gt;In a proper radar system some filtering would be useful at both transmitter and
receiver, but I left it out here to save money.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/zero_and_nonzero_if_transmitter.svg" width="1213" height="369" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Superheterodyne and zero-IF (direct conversion)
    transmitters. Superheterodyne mixing to IF frequency is done digitally.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;TX and RX are zero-IF architecture. This is not ideal from a performance point
of view but it's the cheapest option. The output of all mixers contains not only
the desired frequency-shifted signal but also local oscillator (LO) leakage and
image signals, which is the same signal as the desired one but mixed at the
opposite side of the LO frequency. If DAC generated the signal at offset
frequency it would be possible to filter out the unwanted frequencies at the
mixer output with a bandpass filter, but with zero-IF transmitter these unwanted
frequencies overlap the signal, making it impossible to filter them out with
a fixed filter.&lt;/p&gt;
&lt;p&gt;Similarly, these same nonidealities are also present at the receiver. These
nonidealities cause distortion of the received waveform, leading to increased
range sidelobes for each target.&lt;/p&gt;
&lt;p&gt;While superheterodyne architecture would provide better performance,
implementing it would require more hardware and my goal is to make a working
system with minimal budget. With zero-IF architecture many of the nonidealities
can be compensated sufficiently digitally. Predistorting the DAC output signal
can compensate for the mixer nonidealities, resulting in a clean output signal.
Receiver output signal can also similarly be modified digitally to remove many
of the nonidealities if they can be characterized to sufficient precision.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/complex_signal.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Complex linear frequency sweep signal in time
    domain and instantenous frequency.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The DAC outputs a &lt;a href="http://whiteboard.ping.se/SDR/IQ"&gt;complex IQ signal&lt;/a&gt; that is
modulated by the IQ mixer to the LO frequency and transmitted by the antenna,
transmit/receive switch is then switched to receive and reflected signal is
sampled by the receiver. Each target reflects some of the transmitted signal and
the reflected signal is a sum of the signals from each target.&lt;/p&gt;
&lt;p&gt;Complex IQ format allows representing both positive and negative frequencies at
the baseband. At the transmitter IQ modulator the I and Q signals are mixed
against LO and 90 degree phase shifted LO and summed. The result is
that positive baseband frequencies are shifted above the LO frequency and
negative frequencies at baseband below the LO frequency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulse_compression.svg" width="572" height="300" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Pulse compression of the received signal. By
    correlating with the reference signal the power from pulse is
    concentrated.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To get the target locations convolution is calculated against the transmitted
signal. At the time instance where there was a target the received
and transmitted signals correlate and convolution results is large, when there
isn't a signal there isn't a correlation and convolution result is small. In
practice the convolution is calculated using fast Fourier transform (FFT) as
that is faster in practice than calculating the convolution in time domain.&lt;/p&gt;
&lt;p&gt;In the above plot sidelobes can be seen around the two targets in the result.
These result from the convolution output not being completely zero when the
waveform isn't aligned. Multiplying the reference pulse by a windowing function
can be used to control the sidelobes of the convolution output. Windowing
function can also be applied to the transmitted pulse to further decrease the
sidelobes. Drawback of windowing is that it widens the mainlobe and results in
slightly worse range resolution. How much sidelobes are traded for resolution
can be controlled by the used windowing function.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/radar_processing.svg" width="1082" height="363" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Range-Doppler processing. Only the amplitude of
    the pulse is plotted in graph. Phase of the signal is important for Doppler
    FFT.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Besides the distance to the target, radar can also measure velocity of the
target from how phase of the received signal changes during many measurements.
By sending a burst of pulses and calculating FFT over the number of pulses
dimension, the targets are separated both in velocity and range in the resulting
range-Doppler map.&lt;/p&gt;
&lt;p&gt;Velocity could be also measured from change of distance, but the beauty of using
phase shift of the received signal is that velocity can be obtained from the
same measurement as the distance, multiple objects at the same range but with
different velocities can be separated, and the measurement accuracy is much
better. Detecting multiple objects at the same range with different velocity
is important for separating moving objects from stationary objects such as
ground, trees, and buildings that can have a large reflected signal that would
otherwise mask a small moving object.&lt;/p&gt;
&lt;p&gt;Measuring angle of the target would also be possible with multiple antennas, but
in this case with one antenna there isn't angle information.&lt;/p&gt;
&lt;h2 id="adc-and-dac"&gt;ADC and DAC&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adcs.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;2 channel LVDS interface ADCs with &gt;10 bits, sample rate vs price from Digikey.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;ADC sample rate is one of the most important parameters for the system as it
determines the maximum RF bandwidth that the system can receive. ADC sample rate
should be as fast as is affordable. In general, it's much easier to make the RF
side and DAC to have greater bandwidth than ADC, and it's ADC bandwidth that
limits the system.&lt;/p&gt;
&lt;p&gt;The requirement for ADC is having at least two channels, this is required for
IQ sampling, and LVDS output interface. Two one-channel ADCs could also be used
but it's disadvantageous from PCB area and routing perspective. The fastest ADCs
usually have JESD204B digital interface, the problem with it that it requires
high-end FPGA with high-speed serial transceivers and those are in general too
expensive for my budget. LVDS is the highest speed interface that can be
connected to regular FPGA I/O pins.&lt;/p&gt;
&lt;p&gt;In the above plot are all 2 channel ADC with LVDS interface and at least 10
bits. The best sample rate for price is
&lt;a href="https://www.ti.com/product/ADS4229"&gt;ADS4229&lt;/a&gt; with 250 MHz sample rate for 58
EUR / piece in single quantity. Even higher ADC sample rate would be very
desirable, but any higher sample rate than this would get much more expensive.
There is one two channel 8-bit ADC with 500 MHz sample rate for 73 EUR
/ piece, but it has 20 dB lower SNR than the 12-bit ADC and doubling the sample
rate would only give back 3 dB SNR. Low bit ADC would decrease the dynamic range
of the receiver making it more prone to saturation, and it would require more
gain before the ADC, so I decided against using it despite the higher sample rate.&lt;/p&gt;
&lt;p&gt;Suitable DACs are easier to find, and I chose to use
&lt;a href="https://www.ti.com/product/DAC3174"&gt;DAC3174&lt;/a&gt; two-channel 14-bit 500 MHz DAC
costing 33 EUR / piece. While the system bandwidth is limited by the ADC, it's
useful to have more than enough bandwidth on the DAC to make filtering easier.&lt;/p&gt;
&lt;h3 id="adc-filter"&gt;ADC filter&lt;/h3&gt;
&lt;p&gt;ADC requires anti-aliasing filter before it to limit the signal frequency to
below half of sample rate (&lt;a href="https://en.wikipedia.org/wiki/Nyquist_rate"&gt;Nyquist rate&lt;/a&gt;) to avoid aliasing. To get the largest usable bandwidth
the cutoff frequency of the anti-aliasing low-pass filters should be as close as
possible to the Nyquist rate, but this makes implementation of the filter
difficult as it needs to have very sharp cutoff.&lt;/p&gt;
&lt;p&gt;Filter should also have equal amplitude and group delay on the passband.
Amplitude requirement is easy to understand, we don't want to have different
frequencies attenuated different amounts. Group delay measures how much
different frequencies are delayed by the filter. If the group delay difference
is too large for different frequencies, the received pulse is distorted by the
filter decreasing its correlation to the reference pulse. In practice, this
shows up as higher sidelobes.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adc_lowpass_sim.png" width="1107" height="370" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADC lowpass simulation setup.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Source impedance of the IQ demodulator is 50 ohms differential. ADC input is also
differential, and its impedance varies with frequency having high impedance at
lower frequencies, but at higher frequencies the input capacitance is
significant. ADC datasheet provides a model for the ADC input which I added to
the simulation setup. It suggests adding a resistor at the input that sets
the input impedance, series resistors to limit ringing due to bond wires and
additional resistor and capacitor across inputs to filter sampling glitches.
While adding a 50 ohm resistor across the ADC input would be good for filter
design perspective it attenuates the signal too much as there already isn't
enough gain in the receiver and the IQ demodulator linearity decreases with low
output impedance. I added instead 200 ohm resistor to minimize the signal
attenuation. This makes the filter design challenging, as the high
load impedance requires using small capacitors and large inductors. Higher
impedance also increases the effect of sampling glitches, which are caused by
ADC input sampling capacitors rapidly sampling the input signal. Adding IF
amplifier would have made the filter design easier.&lt;/p&gt;
&lt;p&gt;100 nF series capacitors decouple the DC levels of IQ demodulator and ADC.
While it would be good to have DC coupled signals, the DC levels of IQ
demodulator and ADC are different and any significantly different DC levels
would limit the maximum AC signal range.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adc_lowpass_f.svg" width="720" height="360" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simulated frequency response of the ADC lowpass
    filter. Nyquist frequency marked with vertical line.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The cutoff frequency of the filter is set at 100 MHz and there should be -20 dB
attenuation at the Nyquist frequency of 125 MHz. Up to about 60 MHz both
magnitude and group delay are very good, above that it could be better but its
hard to improve with these constraints. There is some variation in the passband
magnitude that would have been smaller with 50 ohm impedance. Group delay is
relatively good at medium frequencies, at very low frequencies AC coupling
capacitor causes the delay to shoot up and near the cutoff frequency there is
a peak in the delay. The filter response can be compensated digitally if its
a problem.&lt;/p&gt;
&lt;h3 id="dac-anti-alias-filter"&gt;DAC anti-alias filter&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/dac_lowpass_f.svg" width="720" height="360" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simulated frequency response of the DAC lowpass
    filter. Nyquist frequency marked with vertical line.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Digital-to-analog converter is also a sampled system, and it has unwanted aliases
at the output that need to be filtered out. The sample rate of DAC is 500 MHz
which compared to required signal bandwidth of 100 MHz makes the filter design
much easier. The alias frequencies are in the range 400 to 500 MHz for 0 to 100
MHz signal.&lt;/p&gt;
&lt;p&gt;The filter is designed to have flat magnitude and group delay below 100 MHz and
in the simulator both look very good. Cutoff frequency is just above 100 MHz so
that the peak in group delay is above 100 MHz. The aliases are much further away
in frequency than with the ADC, and they are attenuated at least 65 dB more than
the signal. This amount of attenuation is more than enough for the DAC aliases
to not cause any issues, but it does mean that they are visible at the RF
output.  If the signal power is 30 dBm, then the image signal is about -35 dBm.
For proper radar the attenuation likely should be higher to avoid radiating
power at other than the allocated frequency band.&lt;/p&gt;
&lt;h2 id="fpga"&gt;FPGA&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/zynq-mp-core-dual.png" width="800" height="900" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Xilinx Zynq FPGA block diagram. The chip has
    two-core ARM CPU and programmable logic with fast interconnect between them.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using just a microcontroller isn't possible for this application. FPGA is
required for accurate timing of pulse generation and for managing the ADC and
DAC data. Accurate timing of pulse generation is critical for proper operation.
Switching between transmit and receive needs to be done quickly and accurately,
any timing error in pulse triggering or in the receiver will be visible as large
distance error.&lt;/p&gt;
&lt;p&gt;Pricing of the FPGAs is very bizarre. Looking at Digikey or other resellers many
of the suitable parts have prices starting in hundreds of dollars and
better ones can cost several thousand. However, the exact same parts can be
found for fraction of price from China. For some reason, Zynq 7020 is one of the
cheapest Zynq FPGAs in China available at $17, while the exact same part from
Digikey costs $173.&lt;/p&gt;
&lt;p&gt;Zynq 7020 has dual-core ARM-A9 CPU and typical FPGA programmable logic in the
same package. Having also a CPU core is useful as it can handle communication to
PC. It can also run Linux and I added SD-card for Linux file system if I want to
use it, but initially software is running without any operating system.&lt;/p&gt;
&lt;h1 id="digital-design"&gt;Digital design&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulsed_digital_block.svg" width="859" height="489" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Block diagram of digital interfaces.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With fast ADC and DAC moving a lot of data, it's important to consider whether the
system can keep up. In the above block diagram, digital interfaces between
important blocks have been drawn. The FPGA SoC consists of two parts: processing
system (PS), which is dual-core ARM A9 CPU, and programmable logic (PL), which is
programmable FPGA fabric. They are connected to each other through with four 
64-bit &lt;a href="https://en.wikipedia.org/wiki/Advanced_eXtensible_Interface"&gt;AXI&lt;/a&gt; buses.
Their clock frequency is configurable and, in this case, it's set to 130 MHz
which is near the upper limit that it can work. One AXI bus is reserved for ADC
Direct Memory Access (DMA) and other for DAC DMA, there's also a third, lower
speed AXI bus (not drawn in the diagram) for configuring the registers in the
programmable logic.&lt;/p&gt;
&lt;p&gt;A fast connection to the PC is needed to quickly transfer captured ADC samples.
Initially, the digital processing will be done on PC, but it should be
possible to do it on FPGA too for some applications. If the interface to PC is
much slower than the ADC data generation rate, it limits how often the radar can be
triggered. For target tracking, this means slower update rate of target
positions.&lt;/p&gt;
&lt;p&gt;1 Gbps Ethernet is the fastest interface to PC that can be easily connected to
this FPGA chip. USB 3 is another possible choice, offering 5 Gbps speed with
easy connection to PC, but it would require an external USB 3 transceiver chip
and more effort to make it work.&lt;/p&gt;
&lt;p&gt;The system has a single DDR3 DRAM chip that is connected to the PS side of the FPGA.
While the memory chip could be clocked faster, the memory interface speed is
limited by the FPGA memory controller to 1066 MHz. Memory bus width is 16-bits. 
The memory controller supports up to 32-bits, but it would require adding a second
DDR3 chip and the higher bandwidth is not necessary in this system.&lt;/p&gt;
&lt;p&gt;ADC samples are received by the PL side, which has a small FIFO buffer and DMA
controller transfer them to DRAM through the PL side. DAC also has its own DMA
channel, but DMA uses only one AXI bus limiting it to 8.3 Gbps, which is less
than what the DAC needs. It's also important to note that DDR3 bandwidth is less
than the sum of the DAC and ADC bandwidths, making streaming DAC samples from
DRAM while storing ADC samples at the same time impossible. For this reason,
there is a small 1 MB memory on the PL side that stores DAC samples. Pulse samples
are transferred from PC to DRAM through Ethernet, then DMA transfers them to the
small memory on the PL where they are transferred to the DAC every pulse.&lt;/p&gt;
&lt;p&gt;A small DAC memory limits the pulse length to 130 µs, but it's plenty for pulse
radar. A 130 µs pulse corresponds to 20 km minimum detection distance, and
typical pulse length is about 1 µs. The pulse could also be generated on PL,
eliminating the need for memory, but lookup table implementation makes it easier
to change pulse parameters such as windowing function, pre-distortion, and test
different types of pulse waveforms.&lt;/p&gt;
&lt;h1 id="rf-design"&gt;RF design&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulsed_pcb_3d_rf.jpg" width="2103" height="1056" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;RF parts take only a small portion of
    the PCB area. It's also a small amount of required work on the project
    although it seems like it should be the important part.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With the digital parts out of the way it's time to look at the RF side.
Designing the RF parts is relatively straight forward. Similar to my previous
radars, the operating frequency will be around 6 GHz. This is the highest
frequency with many off-the-shelf cheap components due to many consumer
applications.&lt;/p&gt;
&lt;p&gt;RF part consists of: IQ modulator, IQ demodulator, PLL for generating the LO
frequency, power amplifier, low noise amplifiers and switches.&lt;/p&gt;
&lt;p&gt;IQ modulator should have low LO leakage, high image rejection, enough output
power to drive PA without needing another amplifier, and baseband voltage level
compatible with the DAC output voltage range. There aren't that many
possible commercial chip alternatives and most of them are very similar in
performance. Same applies also to the IQ demodulator.&lt;/p&gt;
&lt;p&gt;Choosing a power amplifier was more difficult. While big, expensive radars often
have transmit power measured in kW or even MW, but that's unrealistic in this
case.  I would like to have at least 1 W peak RF power but there are
surprisingly few choices at 6 GHz band despite WLAN applications
that require power amplifiers. The best suitable amplifier I found was &lt;a href="https://www.skyworksinc.com/Products/Amplifiers/SE5004L"&gt;Skyworks
SE5004L&lt;/a&gt;, which has
2 W typical output 1 dB compression point and high gain of 32 dB, but its
documentation is severely lacking. There isn't any graph of gain vs frequency
and it requires some external components, but there aren't any values for them in
the datasheet. The solution for external components is found in the SE5004L-EK1
evaluation kit documentation, which has the schematic of the evaluation board of
this chip. It's also out of stock at the moment at common resellers although
it's available at some Chinese resellers. In the end I did decide to go with it
because there aren't many other cheap alternatives with enough output power.&lt;/p&gt;
&lt;p&gt;Switching speed of the T/R switch is very important, and it should have high
enough power handling capability to handle the 1 W power amplifier output
without blowing up or distorting the signal. Especially the fast switching speed
is a though requirement that rules out many options. I ended up choosing
&lt;a href="https://cdn.macom.com/datasheets/MASW-007588.pdf"&gt;MASW-007588&lt;/a&gt; switch that has
55 ns switching speed and 37 dBm 1 dB compression point. While 55 nanoseconds is fast,
in that time light travels 16.5 meters. There are better switches specifically
made for this kind of applications, but they are too expensive for my budget.&lt;/p&gt;
&lt;p&gt;Another option would be to use circulator instead of switch. This is
common for higher power radars as circulators can handle hundreds of Watts of
power, and there is no switching speed. There are some circulators for this
frequency, but big issue with them is that they are very large and much more
expensive than simple switch.&lt;/p&gt;
&lt;p&gt;The receiver should have enough amplification that the RF noise floor is above
the ADC quantization noise floor. The RF noise floor spectrum at the ADC input
can be calculated as &lt;span class="math"&gt;\(kT\)&lt;/span&gt;, where &lt;span class="math"&gt;\(k\)&lt;/span&gt; is the Boltzmann constant and &lt;span class="math"&gt;\(T\)&lt;/span&gt; is
temperature in Kelvin. This results in power density of about -174 dBm/Hz at
room temperature.&lt;/p&gt;
&lt;p&gt;LNA amplifies the thermal noise and adds some noise to it which is determined by
LNA's noise figure. Switches and PCB lines have some losses. IQ demodulator's
voltage conversion gain can be used to calculate the output voltage density at
the ADC input.&lt;/p&gt;
&lt;p&gt;ADC noise floor can be calculated from SNR specification, 69.4 dBFs (decibels
relative to full scale) in this case, sample rate 250 MHz, and maximum input
voltage 2 V peak-to-peak (0.707 Vrms). Noise is then 69.4 dB below 0.707 Vrms
maximum input voltage for each sample, and there are 250 million samples in one
second which equals bandwidth of one Hertz. This gives the ADC noise floor
density of -156 dBV/Hz.&lt;/p&gt;
&lt;p&gt;Calculating the RF noise floor at the ADC input after considering the whole
signal chain from starting from LNA gives noise floor of about -155 dBV/Hz. This
is barely not enough gain. RF noise floor should be much higher than the ADC
noise floor, typically about 10 dB, so that the ADC quantization noise doesn't
increase the noise of the whole receiver. An ADC driver amplifier could easily have
enough gain, but low-frequency amplifiers with high enough bandwidth are
surprisingly expensive. In the end, I just decided to have few dB higher noise
floor.&lt;/p&gt;
&lt;h1 id="maximum-detection-range"&gt;Maximum detection range&lt;/h1&gt;
&lt;p&gt;The maximum detection range of the radar can be calculated as following:&lt;/p&gt;
&lt;p&gt;The transmitter transmits a pulse of length &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; with average power of &lt;span class="math"&gt;\(P_t\)&lt;/span&gt;,
which is radiated by the transmitter antenna with gain &lt;span class="math"&gt;\(G\)&lt;/span&gt;.
The power density (&lt;span class="math"&gt;\(W/m^2\)&lt;/span&gt;) at distance &lt;span class="math"&gt;\(r\)&lt;/span&gt; can be written using &lt;a href="https://en.wikipedia.org/wiki/Friis_transmission_equation"&gt;Friis'
equation&lt;/a&gt; as &lt;span class="math"&gt;\(P_t G
/ (4 \pi r^2)\)&lt;/span&gt;. This power is reflected by a target with a radar cross-section of
&lt;span class="math"&gt;\(\sigma\)&lt;/span&gt; and some of it is reflected back to the radar. The received power depends
on the effective area of the receiving antenna: &lt;span class="math"&gt;\(P_r = P_t G A_e / ((4\pi
r^2)^2)\)&lt;/span&gt;. &lt;span class="math"&gt;\(A_e\)&lt;/span&gt; can be written in terms of the antenna gain as &lt;span class="math"&gt;\(A_e = \lambda^2
G / 4 \pi\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is the wavelength of the RF signal. The equation for
the received power at the receiver input can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$P_r = \frac{P_t G^2 \lambda^2 \sigma}{(4\pi)^3 r^4}$$&lt;/div&gt;
&lt;p&gt;This is the received power from one pulse. To increase the received power, multiple
received pulses can be coherently summed. It's important that the summation is
coherent so that the phases of the received pulses are aligned. In practice,
instead of summing, an FFT is used so that power from moving targets can be
coherently summed and separated from each other.&lt;/p&gt;
&lt;p&gt;To get the maximum detection range, we need to find the minimum
detectable received power. The detection performance is limited by the noise of
the receiver. Thermal noise density (W/Hz) of the receiver is &lt;span class="math"&gt;\(kT\)&lt;/span&gt;, where &lt;span class="math"&gt;\(k\)&lt;/span&gt; is
the &lt;a href="https://en.wikipedia.org/wiki/Johnson%E2%80%93Nyquist_noise#Derivation"&gt;Boltzmann constant&lt;/a&gt; and
&lt;span class="math"&gt;\(T\)&lt;/span&gt; is the receiver temperature in Kelvin. The receiver amplifies this thermal
noise and adds its own noise to it. The noise factor, &lt;span class="math"&gt;\(F\)&lt;/span&gt;, of the receiver is
how much higher the noise floor of the output is compared to theoretical thermal
noise floor if there wouldn't be any added noise. This can be calculated from
the receiver gain, RF amplifier's noise figure and ADC's noise floor.&lt;/p&gt;
&lt;p&gt;To get the noise floor, we need to multiply the thermal noise density &lt;span class="math"&gt;\(kT\)&lt;/span&gt; by the
receiver noise factor &lt;span class="math"&gt;\(F\)&lt;/span&gt; and the receiver's noise bandwidth &lt;span class="math"&gt;\(B\)&lt;/span&gt;. The correct
noise bandwidth to use is the minimum bandwidth after all the signal processing
which noise can't be separated from the signal. For example, by taking the Fourier
transform of the input signal, we can discard all the frequency bins that are
beyond where our signal is, and noise at those discarded frequencies won't
affect the detection capabilities of the receiver. Pulse compression ideally
collects all of the power of a pulse within a bandwidth of &lt;span class="math"&gt;\(1/t_s\)&lt;/span&gt; for a linear
frequency sweep. However, in practice, FFT windowing functions and any mismatch
between reference and received pulses will decrease this slightly.&lt;/p&gt;
&lt;p&gt;The minimum detectable signal should be higher than the noise floor by some
margin. The threshold value for detections can be chosen freely, but there is
a trade-off: if we accept detections that are only just above the noise floor,
occasionally some of them may be false detections resulting from noise
just happening to be above the detection threshold. The probability of false alarm
depends on the method used to estimate the signal-to-noise ratio of the
detection. For an ideal detector, the false alarm probability can be calculated
based on the probability that normally distributed noise is above the detection
threshold. Common threshold value is usually around 13 to 15 dB.&lt;/p&gt;
&lt;p&gt;At the maximum detection distance the received power is equal to minimum
detectable signal:&lt;/p&gt;
&lt;div class="math"&gt;$$\frac{n P_t G^2 \lambda^2 \sigma}{(4\pi)^3 r^4} = \frac{kTFS}{t_s}$$&lt;/div&gt;
&lt;p&gt;,where &lt;span class="math"&gt;\(n\)&lt;/span&gt; is the number of pulses, and &lt;span class="math"&gt;\(S\)&lt;/span&gt; is the detection threshold compared
to the noise floor. Solving for &lt;span class="math"&gt;\(r\)&lt;/span&gt; gives the maximum detection range:&lt;/p&gt;
&lt;div class="math"&gt;$$r_{\text{max}} = \sqrt[4]{\frac{n t_s P_t G^2 \lambda^2 \sigma}{(4\pi)^3 kTFS}}$$&lt;/div&gt;
&lt;table style="width:80%"&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(P_t\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Transmitted power&lt;/td&gt;
&lt;td&gt;30 dBm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Gain of antennas&lt;/td&gt;
&lt;td&gt;14 dBi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\lambda\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Wavelength&lt;/td&gt;
&lt;td&gt;5.2 cm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\sigma\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Target radar cross-section&lt;/td&gt;
&lt;td&gt;1 m²&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(T\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Receiver temperature&lt;/td&gt;
&lt;td&gt;290 K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(t_s\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Pulse length&lt;/td&gt;
&lt;td&gt;1 µs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(n\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Number of pulses in burst&lt;/td&gt;
&lt;td&gt;1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(F\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Receiver noise figure&lt;/td&gt;
&lt;td&gt;5 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(S\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Detection threshold&lt;/td&gt;
&lt;td&gt;15 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In the above table are estimations of the radar system parameters. Plugging
these values in the equation gives a maximum detection distance for target with
1 &lt;span class="math"&gt;\(m^2\)&lt;/span&gt; radar cross-section of 1200 meters. This might be slightly
optimistic, as there are losses in the cables to antennas, loss from antenna
efficiency, losses from mismatch, and atmosphere attenuation. However, the
maximum detection distance should still be about 1 km. At this maximum distance
the average received power from a target is equal to the minimum detection
threshold. Therefore, on average, a target at this distance is detected 50% of
the time. Due to normally distributed noise, there is a chance that a target at
shorter distance is not detected, and a target at longer distance could be
detected. However, because of the fourth power dependence of the received power,
the probability of detection drops quickly at larger distances.&lt;/p&gt;
&lt;h1 id="pcb-design"&gt;PCB design&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pcb_block.svg" width="519" height="289" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simplified PCB block diagram. PLL generates 6 GHz
    RF local oscillator and clock generator generates clocks for ADC, DAC and
    FPGA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Practical implementation of the system requires designing a printed circuit
board (PCB) that integrates all the components. The system has both RF and
high-speed digital circuits that require careful PCB routing to make sure that
they function correctly.&lt;/p&gt;
&lt;p&gt;The PCB has six layers, and I don't think the FPGA can be routed with any less 
layers. The material is standard FR-4, which isn't ideal for RF routing since it's
quite lossy, but it isn't a big issue in this case since the RF trace length is
kept very short.&lt;/p&gt;
&lt;h2 id="ddr3-routing"&gt;DDR3 routing&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_routing.png" width="887" height="443" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 routing implementation. Source: &lt;a href="https://docs.xilinx.com/v/u/en-US/ug933-Zynq-7000-PCB"&gt;UG933&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;DDR3 DRAM memory connected to the PS side of the FPGA runs at 533 MHz clock
frequency with two transfer per clock cycle. The memory uses the DDR3L standard,
which is a low-voltage version of the DDR3 standard with a 1.35 V operating
voltage instead of the normal DDR3 1.5 V supply voltage. While this isn't very
fast by the modern standards, it still requires some care with the routing.
Memory traces should be length matched, have correct characteristic impedance,
and be terminated properly to minimize reflections.&lt;/p&gt;
&lt;p&gt;The nominal characteristic impedance of DDR3 traces is 40 ohms. A shared address
bus is fly-by routed to all memory chips and terminated with a 40 ohm resistor
to VTT supply, which is at half of the memory supply voltage. Each memory chip
has its own data traces with on-chip termination. There are also few control
lines that are routed to all memory chips. With only one memory chip on the PCB,
the routing is much simpler.&lt;/p&gt;
&lt;p&gt;The memory bus can be simulated with circuit simulators before being
manufactured. Professional programs have ways to do finite element simulation of
the PCB, but this is quite difficult with open source software. FPGA and memory
chip driver and receiver electrical models are provided as
&lt;a href="https://en.wikipedia.org/wiki/Input/output_Buffer_Information_Specification"&gt;IBIS&lt;/a&gt;
files. I used &lt;a href="https://www.kicad.org/"&gt;KiCad&lt;/a&gt; to design the PCB and it's
supposed to include IBIS support but it was unclear how to use it. I ended up
using
&lt;a href="http://www.spisim.com/products/free-apps/spisim_ibis-a-free-web-app-for-simulating-ibis-using-free-simulators/"&gt;SPISim_IBIS&lt;/a&gt;
web app to convert the IBIS models to SPICE netlists and simulate them with &lt;a href="https://ngspice.sourceforge.io/"&gt;ngspice&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_spice.svg" width="393" height="189" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 memory routing simulation of a single trace.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I was interested in simulating if address bus termination resistors can be left
out in this case where there is only one memory chip, and it's mounted close to
the FPGA. I have seen this done on at least one FPGA development board, and it
would save some PCB space if termination resistors could be left out.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_term_40.svg" width="1024" height="768" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 databus with 40 ohm line and termination.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Normally, an eye diagram is used to analyze the timing margin of the memory bus.
However, it's not easy to simulate it with ngspice, so I just added a pulse
source and did a transient simulation plotting the voltage at the memory chip
input. With 120 ps line delay, 40 ohm line impedance, and termination resistance
the memory chip input voltage looks fine. High and low thresholds are 0.81 and
0.54 V according to the memory chip datasheet, and the signal looks very good in
the simulator.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_no_term.svg" width="1024" height="768" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 databus without termination resistors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Without termination resistors, the voltage looks fine from the threshold level
point of view. However, there is significant under and overshoot. Supply
voltages are 0 V and 1.35 V, and the memory chip input voltage overshoots by about
0.7 V, which is enough to forward bias the ESD (Electrostatic discharge)
protection diodes of the memory chip. This might be fine in practice, but memory
chip datasheet says that the overshoot should be limited to maximum of 0.4 V.
For this reason, I added the address line termination resistors.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_term_60.svg" width="1024" height="768" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 databus with 60 ohm line and 50 ohm
    termination resistors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While removing the termination resistors violates the datasheet guarantees, it's
possible in this case to use 60 ohm line impedance and 50 ohm termination
resistors with minimal difference in the signal integrity. The benefit of using
higher line impedance is that it results in narrower line on PCB allowing for
denser layout. A 40 ohm trace is 0.24 mm wide, while 60 ohm trace is 0.10 mm wide.
Using narrower trace also allows having more distance between different traces,
which decreases cross-talk between traces. 50 ohm termination resistor is
close enough to the trace impedance, and since 50 ohms resistors are needed on
other places on the PCB, using 50 ohm resistor allows removing one resistor
value from the bill of materials, making the assembly slightly cheaper.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ddr3_traces.png" width="3060" height="1640" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;DDR3 routing. FPGA on the right and DDR3 chip and
    the termination resistors on the left. Top left is the top layer and bottom
    right is the bottom one advancing horizontally.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the final DDR3 routing on all the PCB layers. Layers 2 and 5 are
ground, 4 is supply voltage, and others are reserved for signals. Two grounds are
needed for correct impedances on the top, middle, and bottom traces of the PCB.
With only one ground plane, the distance from the signal to ground would be too
large on either the top or bottom layer. Data bus traces are swapped within the byte
boundary to make the routing easier. The traces are length-matched with squiggly
lines, and some traces are manually drawn on the ground and supply layers to
decrease the size of slots in the planes due to vias. The trace matching
requirement is ±10 ps according to the Zynq PCB design guide, which is
approximately ±2mm in trace length. However, considering the faster memory chip
and having only one memory chip, the actual margin should be much greater. There
is also some delay difference inside the FPGA package which should be considered
in the length matching.&lt;/p&gt;
&lt;h2 id="transmission-line-termination"&gt;Transmission line termination&lt;/h2&gt;
&lt;p&gt;The T/R switch needs to be switched as fast as possible to minimize dead time
between transmit and receive, and the same applies for the IQ modulator enable pin.
The FPGA I/O pin driver strength can be controlled, and with the highest drive
strength it has a rise time of about 400 ps at the switch input in simulator.
However, few centimeters of PCB trace between the FPGA and switch input functions as
a transmission line, which has significant effect at these frequencies.&lt;/p&gt;
&lt;p&gt;The switch input pin is not matched to 50 ohms, and a typical CMOS input has
high input impedance. This causes reflections, which severely distorts the
switching waveform.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/switch_termination_sch.svg" width="459" height="250" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Termination of switch input with capacitor and
    resistor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To minimize reflections, the transmission line should be terminated to the
characteristic impedance of the transmission line, which is 50 ohms in this
case. Placing a 50 ohm resistor to ground near the switch input pin would
work, but it would sink DC current and cause the DC voltage to drop. Termination
to supply voltage has a similar issue except that now voltage can't reach 0 V.&lt;/p&gt;
&lt;p&gt;Termination with a 50 ohm resistor in series with a small capacitor solves the DC
level issue. Capacitor value should be tuned so that high-frequency reflections
are absorbed without affecting the low frequencies too much.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/switch_termination.svg" width="2048" height="768" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simulation of switch voltage with and without
    termination.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the simulated voltage at the switch input. Transmission line length was
300 ps, and termination capacitor was set to 12 pF. Without termination there is
significant over and undershoot, and a risk that the voltage drops below the
threshold voltage slowing the switching. With the termination, the waveform is much
cleaner.&lt;/p&gt;
&lt;h2 id="power-supply"&gt;Power supply&lt;/h2&gt;
&lt;p&gt;Analog electronic components are sensitive to supply voltage noise. This is
especially important for RF receiver with input signal at the level of the
thermal noise floor.&lt;/p&gt;
&lt;p&gt;Switching regulators have good efficiency, often around 90%, but their output has
switching noise that is significantly higher than the thermal noise floor. If
this noise isn't filtered properly, it will couple into the received and
transmitted waveforms and cause interference at the receiver. A linear regulator,
often called low-dropout regulator (LDO) for historical reasons, functions as 
a variable resistor, dissipating enough power to ensure that the output voltage
is at the correct level. The output noise is much lower, but if the voltage drop is
too large the efficiency is terrible.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ldo_psrr.png" width="523" height="343" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Power supply rejection rate (PSRR) of TPS7A7001 LDO.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To have both good efficiency and low noise, it's common to have a switching
regulator followed by an LDO to filter the switching noise. However, this
isn't enough filtering in this case. LDO filters well very low
frequencies, but it's filtering capability drops at the higher frequencies.
Above is the power supply rejection ratio (PSRR) of the LDO I'm using. For
example, with a 1 mV amplitude, 2.5 MHz signal at the LDO input is attenuated by
about 15 dB, resulting in about 200 µV amplitude signal at the output.&lt;/p&gt;
&lt;p&gt;The requirement for minimum power supply filtering can be obtained with few
assumptions about the coupling of the noise. The smallest signal level is at the
input of the receiver LNA. The thermal noise floor is -174 dBm/Hz at room
temperature. With a 10 ms measurement time, the bandwidth is 100 Hz. This
results in a maximum of -154 dBm power at the LNA input. At 50 ohm impedance,
this corresponds to 5 nV RMS voltage. If the LNA supply voltage is modulated by
noise, it affects the gain of the amplifier, and the supply voltage noise is
mixed to the RF signal. In practice, the allowed noise amplitude can be
larger since there is usually some power supply rejection at the LNA for
low-frequency supply voltage noise to the output RF frequency, but it's usually
not specified in the datasheet. With a 10 mV worst-case switching noise amplitude,
the required attenuation is 120 dB to reach the noise floor. LDO can be assumed
to filter about 10 dB, and we can assume another 10 dB power supply rejection
from the RF components, which sets the power supply filtering requirement to 100
dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ferrite_sim_sch.png" width="549" height="281" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Two-stage ferrite bead filter schematic. Capacitor
    parasitics drawn individually.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The required power supply filter can be designed with ferrite beads. They are
inductors that are lossy at high frequencies. A capacitor is needed after the
ferrite bead to complete the low-pass filter. The series resistance and
inductance of the capacitor are crucial at these frequencies, and they are
included in the schematic, assuming an SMD ceramic capacitor.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ferrite_sim_f.svg" width="720" height="360" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Ferrite bead filter frequency response.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The above filter achieves 100 dB attenuation at 1 MHz. The switching frequency is
2.5 MHz, and this filter works well at that frequency. However, it has a resonance at 30
kHz, which increases the noise at the output at that frequency. This is caused by
the ferrite bead behaving like an low-loss inductor at low frequencies which resonates
with the capacitor due to a lack of resistance that would dampen the resonance. It
can be fixed in two ways: adding resistance in series with the capacitor or
increasing the capacitance. Resistance could be also added in series with the
ferrite bead, but this is possible only if the DC current is small.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ferrite_sim2_f.svg" width="720" height="360" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Ferrite bead filter frequency response with 20 µF
    first capacitor and 200 µF second capacitor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With larger capacitors, the resonance is much smaller, and the attenuation
increases slightly.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/ferrite_istep.svg" width="720" height="360" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Time domain response to 1 A current step. One
    ferrite bead with 200 µF capacitance. The response is underdamped and
    increasing capacitor ESR would decrease the oscillation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;An important limitation of the ferrite bead filter is its time domain response. If
the output current changes quickly, the inductance of the ferrite bead tries to
keep the current through it constant, which means that the output capacitor needs
to supply the high-frequency current. If the output capacitor is small, it can't
supply the current, and the output voltage drops. If there isn't enough
resistance either in series with the ferrite bead or in series with the
capacitor, the output voltage oscillates before settling. Especially the power
amplifier that has high current draw needs a lot of capacitance to ensure that
the supply voltage doesn't drop as it's switched on.&lt;/p&gt;
&lt;p&gt;The time domain response can be improved by placing the ferrite bead before the
LDO. The LDO is then able to keep the output voltage constant while the input
voltage dips, but it needs to be ensured that the voltage after the ferrite bead
doesn't dip too low so that the LDO stays in regulation. I placed one ferrite
bead before the LDO and a second one before each analog component. The first one
filters the switching noise, and the second ferrite bead adds additional
filtering for each IC. Besides filtering the switching noise, the second ferrite
bead for each IC also improves the isolation between components, which is
desired between transmitter and receiver. Having a ferrite bead close to each
component also reduces the length of the trace that can work as an antenna to
pick up radiated noise.&lt;/p&gt;
&lt;p&gt;In total, the PCB has nine different supply voltages. There are six
supplies for FPGA and digital electronics: 1.0 V for FPGA core supply, 1.8 V,
2.5 V, and 3.3 V for various digital chips, 1.35 V and 0.675 V for DDR3 RAM. The
noise on these rails isn't too important for the system performance. Analog
electronics have low-noise 1.8 V, 3.3 V, and 5.0 V rails with linear regulators
and ferrite bead filtering.&lt;/p&gt;
&lt;h2 id="adc-and-dac-routing"&gt;ADC and DAC routing&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adc_traces.jpg" width="1081" height="1080" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADC data trace routing to FPGA. ADC footprint on
    the right side, FPGA out of view on the left side.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The ADC connects to the FPGA with a 12-bit wide LVDS bus. The ADC also generates a clock
signal that is center-aligned to the data. The sampling rate of the ADC is 250
MHz, and there are two channels with one channel's data on the rising edge and the other on
the falling edge of the clock. This data rate is too fast for the FPGA to capture
statically, requiring dynamic capture that uses adjustable delay lines to
correct for the signal delay programmatically. These delay lines also make the
length-matching requirement for the PCB routing quite loose.&lt;/p&gt;
&lt;p&gt;The DAC also has an LVDS interface but it operates at 500 MHz with 14-bits. This
FPGA doesn't have adjustable output delay lines, so the line lengths must be
length-matched to make sure that the DAC can capture the data. The DAC datasheet
provides setup and hold times for the interface, and plugging these values into
the FPGA synthesizer tool indicates that the timing can be met with ±25 ps trace
delay, which corresponds to about ±4 mm difference in the data trace lengths
compared to the clock trace. Even higher delay might work, but it's good practice
to match the interface as well as possible, especially since it can't be
adjusted in software like the ADC interface.&lt;/p&gt;
&lt;p&gt;On the FPGA side, it's important to set the supply voltages for the banks with
LVDS to 2.5 V with this FPGA part. For the receiver, only this voltage works
correctly with internal 100 ohm termination. Using internal 100 ohm
termination instead of external 100 ohm resistors on each data line makes the
routing easier and saves PCB space. The DAC LVDS transmitter also needs a 2.5
V supply voltage for both common mode and differential voltages to be compatible
with what the DAC expects.&lt;/p&gt;
&lt;h2 id="1-gbps-ethernet"&gt;1 Gbps Ethernet&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/rtl8211f_sch.png" width="1063" height="888" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Ethernet chip schematic connections. The chip
    requires several configuration resistors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The Ethernet interface needs an external PHY chip that is between the Ethernet
connector and the FPGA. The cheapest one I could find was Realtek RTL8211F,
which can be found for $1 in single quantities from China. While the RTL8211E version of
the chip is found on many FPGA development boards, the F version is much more
uncommon. The challenge with this chip is that officially the datasheet is
provided only under NDA. However, it is available from the Chinese resellers
with big "Confidential" and "Not for public release" labels. However, the
datasheet isn't quite clear on how it should be connected, and there aren't any
example schematics in it. Searching this chip on Google, I did find few
schematics of boards using it, which gave me some confidence that I can wire it
correctly. See the above schematic on how it should be wired for FPGA if you are
also looking to use it.&lt;/p&gt;
&lt;p&gt;Important note about the Zynq FPGA is that the Ethernet interface doesn't meet
the RGMII interface (FPGA to Ethernet chip interface) specifications when used
with 3.3 V supply voltage. Because of this, I had to set the FPGA PS side supply
voltage to 1.8 V, which requires adding level shifters for SD card and UART that
are powered from the same voltage.&lt;/p&gt;
&lt;h2 id="jtag-and-debug-uart"&gt;JTAG and debug UART&lt;/h2&gt;
&lt;p&gt;FPGA is programmed and debugged with JTAG connection. On development boards
there is usually a connector and external JTAG debugger is used to connect to
the development board. The official JTAG debugger is quite expensive with $270 list
price and I don't want to pay for one.&lt;/p&gt;
&lt;p&gt;FTDI makes FT2232H chip that can convert from USB to JTAG and UART. This can be
used to implement the JTAG interface cheaper. There used to be a drawback that
it wasn't supported by the Xilinx official tools which made debugging the design
much harder, but now it's &lt;a href="https://docs.xilinx.com/r/en-US/ug908-vivado-programming-debugging/Programming-FTDI-Devices-for-Vivado-Hardware-Manager-Support"&gt;officially
supported&lt;/a&gt;
if the EEPROM memory is programmed with tool provided by Xilinx.&lt;/p&gt;
&lt;p&gt;FT2232H also has UART output that is useful for debugging the ARM processor
code. Calling printf in the processor code prints characters to the debug UART.&lt;/p&gt;
&lt;h2 id="clock-generator"&gt;Clock generator&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/block_clock.svg" width="939" height="641" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Simplified block diagram of clock signals.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Accurate timing of the whole system is very important. Several clock signals are
unavoidable since the ADC runs on 250 MHz, the DAC runs at 500 MHz, and the FPGA
requires even lower clock frequency. The FPGA does have several phase-locked
loops that can be used to generate clocks, but accuracy of their output isn't
good enough. For a 100 MHz clock the tools predict a peak-to-peak jitter of 130
ps, while the clock generator chip has about 4 ps peak-to-peak jitter. ADC and
DAC require very clean clocks with minimal jitter, and any timing error on the
sampling clock &lt;a href="https://www.analog.com/en/resources/analog-dialogue/articles/the-easy-steps-to-calculate-sampling-clock-jitter-for-isolated-precision-high-speed-daqs.html"&gt;reduces the signal-to-noise
ratio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Everything involved in the radar signal generation or processing should run on
synchronized clock signals. For example, if the ADC and DAC would run with
completely unrelated clocks, the pulses wouldn't stay synchronized in phase as
the clocks would slightly drift. Phase drift would make coherent summing of
multiple pulses impossible and seriously harm the performance of the radar.&lt;/p&gt;
&lt;p&gt;There are two unrelated clocks on the PCB. PS side of the FPGA has its own 33
MHz crystal and it generates clocks for DDR3, CPU, and peripherals from
it. 133 MHz bus clock is also generated from it, which is passed to the programmable
logic side of the FPGA. The PL side uses an external clock generator chip
&lt;a href="https://www.ti.com/product/CDCM6208"&gt;CDCM6208&lt;/a&gt; to generate several 250 MHz and
500 MHz clocks from a single 25 MHz crystal. These clocks are all phase
synchronized to each other. The PS side's own independent clock
is that on power up the clock generator has not been programmed yet. The PS side
has its own independent clock, which is needed for programming the clock
generator. The independent clock domains of PS and PL don't cause issues
with proper clock domain crossings.&lt;/p&gt;
&lt;p&gt;The ADC outputs a 250 MHz clock with the data to the FPGA, which is
internally divided by two and used to clock the pulse timing logic. This
makes the FPGA logic also synchronized to the clock generator. Frequency
division is required because 250 MHz clock is too fast for the FPGA logic. The
clock division makes that for each 125 MHz clock cycle, two ADC samples are received
from both channels for total of 48 bits of data. There is a FIFO for clock
domain crossing to the PS side's 133 MHz, and DMA transfers the samples through
a 64-bit AXI bus to the DDR3 memory. 133 MHz is used because it needs to be faster
than the 125 MHz input clock and this clock needs to be generated by PS so it
can't be the PL 125 MHz clock.&lt;/p&gt;
&lt;p&gt;The FPGA needs to output a 500 MHz clock with the data to the DAC, and for this
purpose, a 500 MHz signal is routed to the FPGA. The FPGA has internal clock
generators, but they are not used for this purpose because their jitter is too
high. 500 MHz is too high frequency to route on the global clock network of the
FPGA, but it's possible to route it on the I/O clock network that is only routed
to the I/O buffers. That means no logic can be clocked at 500 MHz, but the chip
has &lt;a href="https://en.wikipedia.org/wiki/SerDes"&gt;serdes&lt;/a&gt; that can be clocked from the
I/O clock at each pin, which can take four bits at the rising edge of the
250 MHz clock and output them at both rising and falling edges of the 500 MHz
I/O clock.&lt;/p&gt;
&lt;h1 id="manufacturing"&gt;Manufacturing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/half_populated_pcb.jpg" width="1600" height="889" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Half populated PCB received from the PCB
    manufactuer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I ordered the PCB from a Chinese manufacturer, including assembly. They sent me
two assembled pieces and three empty PCBs. Some uncommon components were
not available for assembly, and I had to order those separately and solder them
myself. These included all the most expensive components such as ADC, DAC and
PLL. Luckily, the FPGA was available for assembly, which saved me the trouble of
soldering the large 484-pin BGA package myself.&lt;/p&gt;
&lt;p&gt;Quality of the PCB looks good, especially considering the price, which is only
a fraction of what it would have costed me locally. However, only one of the two
assembled PCBs worked out of the box because of soldering issue with one of
them.&lt;/p&gt;
&lt;p&gt;The suspiciously cheap $15 FPGA had equally suspiciously date and lot codes
covered (white rectangles on the FPGA chip in the picture above). I have
a development board of the same series chip with markings intact, so it definitely
shouldn't look like this. It did end up working, but I wonder what the
origin of these chips is.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/populated_pcb.jpg" width="1600" height="957" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Fully populated PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I soldered the rest of the components myself using solder paste and hot air
tool. It would be difficult to solder the QFN packages without hot air tool on
already populated board.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pcb_backside.jpg" width="1080" height="541" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Backside of the PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Two-sided assembly would have costed extra, so all the components are placed
only on the top side. There are some places for additional decoupling capacitors
on the bottom side just in case, but those were not needed.&lt;/p&gt;
&lt;h2 id="jtag-programmer"&gt;JTAG programmer&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/hardware_manager.png" width="445" height="335" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Vivado hardware manager. ARM processor, Zynq 7020
    FPGA connected to FTDI chip connected to localhost.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The first step to bring up the board is to program the FT2232H chip, 
which functions as JTAG programmer and serial port. Xilinx has
&lt;a href="https://docs.xilinx.com/r/en-US/ug908-vivado-programming-debugging/JTAG-Cables-and-Devices-Supported-by-hw_server"&gt;program_ftdi&lt;/a&gt;
tool that can program its EEPROM so that Xilinx tools recognize it. I first had
problems with the tool not recognizing the device. It failed to find the ftdi
device, even though I could see it in the Linux system log. After installing some
ftdi libraries and making sure that the official ftdi tools were able to read
the EEPROM, I was able to successfully program the EEPROM with the program_ftdi tool.&lt;/p&gt;
&lt;p&gt;Checking the Xilinx Vivado hardware manager, it's now able to find the Zynq 7020
FPGA. Programming and debugging the FPGA now works with the Xilinx tools.&lt;/p&gt;
&lt;h1 id="fpga-programming"&gt;FPGA programming&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/fpga_block.svg" width="960" height="519" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FPGA programmable logic block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;FPGA software consists of ADC and DAC LVDS interfaces, pulse timing that
enables and disables switches, PA, LNAs and other components at the right time,
AXI registers that enable the PS to configure the programmable logic, two DMA
channels for ADC and DAC samples, and SPI interfaces for ADC, DAC, PLL, and clock
generator.&lt;/p&gt;
&lt;p&gt;Most of the signal processing is done on the PC, and the FPGA mainly passes the
data around. However, it would be a good idea to have digital filtering and
decimation for the received samples on the FPGA. When the transmitted pulse
bandwidth isn't very large, for example when it isn't centered at zero
frequency, it's possible to do mixing digitally, filter the samples, and reduce
the sample rate. This would enable reducing the amount of data that needs to be
sent to PC and increase the frame rate of the radar.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/xapp1017.png" width="841" height="475" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;LVDS receiver. Source: &lt;a href="https://docs.xilinx.com/v/u/en-US/xapp1017-lvds-ddr-deserial"&gt;xapp1017&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;LVDS receiver is based on Xilinx appnote
&lt;a href="https://docs.xilinx.com/v/u/en-US/xapp1017-lvds-ddr-deserial"&gt;xapp1017&lt;/a&gt;. It
connects two delay lines and flip-flops to each LVDS lane with delay difference
set so that they sample the signal with 1/2 bit delay. State machine changes the
delays so that the master flip-flop samples at the center of the data eye. The
dynamically adjusted delay is able to compensate for PCB routing and FPGA
internal delay differences.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/radar_timer.png" width="657" height="482" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Part of the radar pulse timing circuit VHDL code.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The pulse timing circuit is just a counter with equality comparisons for each
possible event that can be programmed with AXI registers. The timing circuit is
triggered from the PS side of the FPGA, starting the counter that triggers
every subsystem on the FPGA and every external chip at the exact correct clock
cycle. It also has a loop functionality that can trigger the pulse multiple times
with precise repetition interval to support sending a burst of pulses.&lt;/p&gt;
&lt;p&gt;Accurate timing of the burst is essential for accurate target velocity
measurement. Any timing inaccuracy between ADC and DAC transfers to inaccuracy
in the measured distance. A single 125 MHz clock cycle timing error in ADC or
DAC triggering translates to a 1.2 m error in the measured distance.&lt;/p&gt;
&lt;h2 id="receiver-noise"&gt;Receiver noise&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulsed_sma_match.jpg" width="1600" height="638" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Testing the radar without antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For bench top testing I put matched loads at the antenna connectors, disabled
transmitter and recorded the ADC output. Ideally the recorded signal would be
noise and any signals visible are unwanted interference.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adcfft_5p8ghz.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADC output spectrum without signal, 5.80 GHz LO.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The length of the recording is 33 ms which is 8 million samples. The noise floor
average is -139 dBFs which is about what it should be. However, there are several
interference signals visible, the biggest are multiples of 25 MHz. Their
amplitude is about -100 dBFs which corresponds to about 15 µV RMS at ADC input,
so they aren't very large. DC offset of the ADC is also visible as very large
peak at zero frequency.&lt;/p&gt;
&lt;p&gt;The source of the interferences is fractional spurs caused by the PLL. They can
be changed by changing the PLL output frequency, PLL settings and LO input
reference frequency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adcfft_5p75ghz.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADC output spectrum without signal, 5.75 GHz LO.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The spurs are minimized when PLL output frequency is integer multiple of the PLL
reference clock. PLL reference clock is 250 MHz, but this is too high speed to
run the PLL phase detector, and it is divided by two by the PLL reference input
divider. With 125 MHz PLL reference clock setting the output frequency to 5.75
GHz makes it integer multiple and almost completely gets rid of the spurs. 5.875
is another close multiple that works well also with RF electronic side. There is
still a spur at -125 MHz, but this is expected as it is the phase detector
frequency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/adc_lowfrequencies.png" width="826" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;ADC output spectrum low frequencies.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Noise floor of the ADC is higher at low frequencies due to 1/f noise of the ADC.
Switching frequency of the DC/DC converters is 2.5 MHz and it's not visible at
the output spectrum plot, which means that the supply filtering works as
designed.&lt;/p&gt;
&lt;h2 id="transmit-power"&gt;Transmit power&lt;/h2&gt;
&lt;p&gt;The power amplifier I'm using has an integrated power detector. I set the DAC output
voltage to 85% of maximum amplitude, which is about the maximum amplitude it can
go while leaving some room for DC offset for LO leakage cancellation digital
predistortion. This should result in around +3 dBm output power from the IQ
modulator, and with 32 dB power amplifier gain it should be enough to drive the
PA into compression.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pdet_voltage.png" width="960" height="468" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;PA power detector voltage measured on
    oscilloscope.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The power detector pin waveform looks correct when measured on oscilloscope. It
has a series of 2 µs long pulses, which was the pulse width. Peak voltage is
1.72 V and about 0.32 V when not transmitting.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/pdet_datasheet_extrapolated.png" width="1039" height="529" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Power detector pin voltage vs output power from
    the datasheet.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Datasheet has a plot of expected power detector pin voltage vs output power at
different frequencies, but the plot doesn't go as high as I measured.
Questionable linear extrapolation gives around 33 dBm output power which is two
Watts. -1 dB compression point of the PA is specified to be 34 dBm typical, and
it looks like it's in compression as expected.&lt;/p&gt;
&lt;h2 id="calibration"&gt;Calibration&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/tx_waveform.svg" width="743" height="423" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Transmitted waveform.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With matched loads at the antenna connectors I recorded the leakage transmitter
signal through the T/R switch. The transmitted waveform is a 100 MHz bandwidth
1 µs long linear frequency sweep with 0.1 of the maximum DAC amplitude.&lt;/p&gt;
&lt;p&gt;The baseband frequency sweep signal can be written:&lt;/p&gt;
&lt;div class="math"&gt;$$ f(t) = \exp\left(j2\pi\left(\frac{B}{2t_s}t\right)t\right) $$&lt;/div&gt;
&lt;p&gt;,where &lt;span class="math"&gt;\(B\)&lt;/span&gt; is bandwidth, &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is the sweep length, &lt;span class="math"&gt;\(t\)&lt;/span&gt; is time, and &lt;span class="math"&gt;\(j = \sqrt{-1}\)&lt;/span&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/no_lo_cal_time.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Received frequency sweep without any correction. 128 overlapping waveforms.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The receiver was set to record 1 µs before and 2 µs after the transmitted
signal. 128 waveforms were transmitted with very good repeatability with all of
them plotted on top of each other on the graph. Ideally the received signal
would be attenuated, delayed and phase shifted copy of the transmitted signal,
but there is a clear difference between transmitted and received waveforms.&lt;/p&gt;
&lt;p&gt;The non-idealities identifiable from the time-domain data are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non-zero DC level before the pulse. This is caused by the DC offset of the
  ADC.&lt;/li&gt;
&lt;li&gt;Spike before 1 µs caused by the transmitter being switched on.&lt;/li&gt;
&lt;li&gt;Pulse has DC offset caused by the LO leakage from the transmitter, I signal
  has higher DC level than Q signal.&lt;/li&gt;
&lt;li&gt;Higher baseband frequencies are attenuated more causing a slight drop at the edges of the
  pulse envelope.&lt;/li&gt;
&lt;li&gt;Non-zero DC level after the pulse. Caused both by the ADC DC offset and ADC
  filter high-pass behaviour.&lt;/li&gt;
&lt;/ul&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/no_lo_cal_compress.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Compressed leakage signal.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the plot of the pulse compression output of the non-calibrated pulse
with -50 dB &lt;a href="https://www.mathworks.com/help/signal/ref/taylorwin.html"&gt;Taylor
window&lt;/a&gt; applied to the
reference pulse normalized to the peak level. The sidelobe level is -21 dBc,
which is far above the ideal level.&lt;/p&gt;
&lt;p&gt;The biggest error is caused by the LO leakage from the transmitter. LO leakage
from the transmitter is mixed down to DC at the receiver because they share the
same LO signal. Since DC level of the balanced linear frequency
sweep is non-zero, convolution with the reference sweep gives non-zero result
wherever there is a non-zero LO leakage that causes the flat correlation output
from 0.5 µs to 1.5 µs.&lt;/p&gt;
&lt;p&gt;To improve the sidelobe level LO leakage needs to be compensated. It can be done
by adjusting the transmitter waveform so that it has LO signal in opposite phase
that cancels the leakage signal. However, before LO leakage compensation ADC DC
offset should be compensated since received is used to measure the LO leakage
and DC offset of the ADC interferes with it.&lt;/p&gt;
&lt;p&gt;DC offset of the ADC is compensated by only triggering the receiver with
transmitter disabled. The only signal at the ADC is thermal noise and DC offset.
DC level can be measured and subtracted from all subsequent measurements.&lt;/p&gt;
&lt;p&gt;With ADC DC offset compensated the LO leakage can be measured by triggering the
sweep and outputting only zeros from DAC. Ideally there shouldn't be anything
transmitted, but due to LO leakage there is signal transmitted at LO frequency
which mixes down to DC at the receiver. DC level of the transmitted signal is
adjusted such that the ADC input is zero which results in zero LO leakage.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/compressed_lo_cal.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Compressed leakage signal after LO leakage
    compensation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With LO leakage compensation the impulse response looks much nicer. Sidelobe
level is -36 dB which is few dB above the ideal -42 dB. There is also a very
long -60 dB straight line after the sweep that is caused by the high pass
behaviour of the AC coupling capacitors between IQ demodulator and ADC.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/time_lo_cal.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Time domain leakage signal with LO compensations. DC level after the pulse is very slightly above zero on I channel.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The reason for the long flat part is that there is a non-zero DC level after the
sweep. Baseband frequency sweep has non-zero DC component and when it passes
through the high-pass filter it changes the output DC level. Convolution result
of frequency sweep with a constant results in non-zero output.&lt;/p&gt;
&lt;p&gt;The issue could be reduced by decreasing the high-pass filter cutoff frequency.
The AC coupling capacitor is only 100 nF which puts the high-pass cutoff
frequency at about 10 kHz. DC offset caused by the high-pass could be also
&lt;a href="https://ieeexplore.ieee.org/document/6755581"&gt;compensated digitally&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The decrease in amplitude as frequency increases is quite clear here. &lt;a href="https://www.analog.com/en/resources/technical-articles/equalizing-techniques-flatten-dac-frequency-response.html"&gt;DAC sinc
response&lt;/a&gt;
is compensated digitally, so that isn't the cause for the amplitude drop. The ADC
filter was supposed to be quite flat in amplitude, but during manufacturing
I had to substitute a different inductor than what I initially chose to use. The
substitute inductor has higher series resistance and amplitude isn't as flat.
I do have the correct inductors, but I haven't replaced them yet.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/sweep_offset_time.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;50 MHz frequency sweep with 25 MHz offset.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Other solution for the DC offset issue is modulating the frequency sweep so that
sweep doesn't include zero frequency, essentially using non-zero IF. Above is
time domain plot of received 50 MHz sweep with 25 MHz offset. Frequency sweeps
from 0 Hz to 50 MHz compared to -50 MHz to +50 MHz before.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/sweep_offset_compressed.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Compressed sweep with offset.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Calculating the pulse compression of the offset sweep gives much cleaner result.
The DC offset issue caused by the high-pass filter is completely removed.
Sidelobe level is still 2 dB higher than ideal but this is already quite nice
looking impulse response. Ideal sidelobes are higher than with 100 MHz sweep
because time-bandwidth product is lower and mainlobe is also widened because of
the lower bandwidth. The disadvantage of the modulated sweep is that maximum usable
bandwidth of the sweep is half of what can be used with a zero centered sweep.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/tx_tukey_0p1.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Transmitted signal with Tukey window with α=0.1.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Sidelobes caused by low time-bandwidth product of the pulse can be reduced with
transmitted pulse windowing. Window function reduces the effective bandwidth of
the transmitted waveform, so it increases the mainlobe width and reduces the
range resolution. Transmitter side windowing also decreases the average energy
per pulse as the waveform is tapered off at the start and end of the pulse which
decreases signal-to-noise ratio. One good windowing function for transmitter
side is &lt;a href="https://en.wikipedia.org/wiki/Window_function#Tukey_window"&gt;Tukey
window&lt;/a&gt;. It just
slightly tapers beginning and end of the waveform with middle being at the
maximum amplitude. Tukey window has a parameter α that can be used to control
how much it windows, with α=0 being equal to no windowing. With α=0.1 the pulse
energy, and the receiver SNR, is decreased by 0.8 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/rx_tukey_0p1.png" width="826" height="470" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Pulse compressed 50 MHz bandwidth pulse with 25 MHz offset frequency, 
    α=0.1 TX Tukey window, and -50 dB RX Taylor window.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Compared to the same pulse without TX window adding Tukey window to the
transmitter greatly decreases the far-away sidelobes. At 500 ns offset the
sidelobe level has decreased by about 20 dB. The measured sidelobe level is
slightly higher than what it should be ideally.&lt;/p&gt;
&lt;p&gt;Receiver and transmitter IQ imbalance isn't yet calibrated and there is some
frequency dependent distortion from the ADC and DAC filters. However, the
current level is good enough for now.&lt;/p&gt;
&lt;h2 id="tx-noise-leakage"&gt;TX noise leakage&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/tx_leakage.svg" width="715" height="460" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;If PA is not disabled during the reception noise
    from PA output leaks into receiver increasing the receiver noise floor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When switching to reception T/R switch is switched from transmitter to receiver.
If PA is kept enabled due to its high gain it has high enough output noise that
even when with attenuation from the switch isolation it's still larger than
the thermal noise floor of the LNA.&lt;/p&gt;
&lt;p&gt;T/R switch can be switched in about 50 nanoseconds but enabling and disabling
PA is much slower, it takes about 10 µs. Unfortunately this long PA
switching time means that when using a single antenna the receiver noise is
higher due to leaked PA noise.&lt;/p&gt;
&lt;p&gt;If the input to PA is thermal noise of 50 ohm resistor (-174 dBm/Hz) it's
amplified by PAs gain of 32 dB and it adds its own noise to it too. Usually 
amplifiers noise figure would be reported in the datasheet but this PA doesn't
have it listed. PA noise figure can be rather high, 5 - 10 dB wouldn't be too
unusual, as they usually aren't optimized to be particularly low noise. With
these figures the noise floor at the PA output is about -135 dBm/Hz. T/R switch
has limited isolation, exact value for leakage between these ports isn't
reported in the datasheet, but 26 dB is the reported typical isolation to the
antenna port and isolation between the input ports is usually little better.
This means that PA noise at the LNA input is about -165 dBm/Hz which is larger
than the thermal noise floor of -174 dBm/Hz and the PA noise limits the receiver
performance if it's not switched off.&lt;/p&gt;
&lt;p&gt;Noise figure of the receiver is about 5 dB, so the measured noise floor with
receiver connected to T/R switch should be about 5 dB higher, instead of
calculated 10 dB with noiseless receiver. Actually measuring the ADC noise floor
with PA on, when the receiver is connected to T/R switch the noise floor is 2.1 dB
higher than when it's connected to the other port. It matches well with the
theory considering the big uncertainties in all of the values.&lt;/p&gt;
&lt;p&gt;When using two antennas the second receiver switch can be switched to RX2 and
the LNA on the RX1 can be switched off which improves the isolation sufficiently
that PA leakage doesn't affect the receiver noise.&lt;/p&gt;
&lt;h1 id="target-detection"&gt;Target detection&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/detection.png" width="2478" height="470" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Detecting target from range-Doppler map with CFAR. Range-Doppler map (left), CFAR output (middle), sidelobes filtered out (right). Range on x-axis and Doppler velocity on y-axis.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To get from ADC samples to target detections some more software is required. In
general the transmitted signal is a burst of pulses and the first step is to
pulse compress each received pulse. After pulse compression the next step is to
take FFT over the number of pulses dimension. This sums the power from the
different pulses according to velocity of the target. This is called
range-Doppler processing and its output is a 2D image with range on one axis and
Doppler velocity on the other. Amplitude of each pixel corresponds to
the amount of power received at that distance and velocity.&lt;/p&gt;
&lt;p&gt;After range-Doppler processing the output is a 2D array of the received power
for each range-Doppler bin. To get to target detections we need to identify the
bins where there is a target. We also want to separate interesting
targets such as moving vehicles from non-interesting targets (clutter) such as
sidelobes, trees, ground, and other stationary targets.&lt;/p&gt;
&lt;p&gt;The targets in the range-Doppler map could be identified by the amplitude. If
a bin's amplitude is high enough above the noise floor then it likely
corresponds to a real target and is not just noise. The detection threshold, 
how much a target needs to be above the noise floor, needs to be chosen to
balance false alarm rate and missed detections. In general the noise floor power
isn't constant in the range-Doppler map. It can vary as function of time, there
can be sidelobes from other nearby targets and clutter, for example ground
reflections, can also be considered noise since we don't want to detect each
patch of ground as a target. Instead of setting a fixed noise floor it's
estimated by averaging nearby bins. For each pixel in the radar map, noise floor
is calculated by averaging nearby bins and if amplitude of the bin being tested
is larger than threshold times the calculated noise floor then we mark detected
target at that location. This is called CFAR (Constant False Alarm Rate)
algorithm.&lt;/p&gt;
&lt;p&gt;For high amplitude targets there are going to be false detections from
sidelobes. After the targets are detected we check if they correspond to
a sidelobe of a larger target and unmark it. This is simply done by checking if
there is a much larger target in same row or column. Target is also required to
have larger amplitude than adjacent bins, this causes only the peak location of
each compressed pulse to be detected.&lt;/p&gt;
&lt;p&gt;We now have a list of ranges and velocities for detected targets at the accuracy
of the radar resolution. Range and velocity measurement accuracy can be improved
by interpolating the peak location.&lt;/p&gt;
&lt;h1 id="target-tracking"&gt;Target tracking&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/kalman.svg" width="459" height="280" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Kalman filter for radar target tracking. Kalman
    filter predicts the next position of the target from the previous
    measurements including the uncertainty.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the detection pipeline we have a list of detections, some of which can be
false detections. To be able to track objects in time, detections need to be
associated with targets. Kalman filter is used to track each target's position
and velocity including uncertainty, and it provides a way to assign each
detection to specific target by considering probabilities that detection is from
that target.&lt;/p&gt;
&lt;p&gt;Tracking uses &lt;a href="https://stonesoup.readthedocs.io/en/latest/index.html"&gt;Stonesoup Python
library&lt;/a&gt;, which is
a library for general object tracking. Specifically radar tracking is heavily
based on the &lt;a href="https://stonesoup.readthedocs.io/en/latest/auto_tutorials/10_Simulation_%26_Tracking_Components.html#sphx-glr-auto-tutorials-10-simulation-tracking-components-py"&gt;StoneSoup
tutorial&lt;/a&gt;.
StoneSoup tutorial explains the tracking well, so I won't repeat it too much
here.&lt;/p&gt;
&lt;p&gt;The biggest change from the example is that example is for tracking object in 2D
with measurement providing it's 2D position but not velocity. Radar measures
distance and velocity of each target, but there is no angle information so only
1D tracking is possible.&lt;/p&gt;
&lt;p&gt;Transition model for the target is set as &lt;a href="https://stonesoup.readthedocs.io/en/latest/stonesoup.models.transition.html#stonesoup.models.transition.linear.ConstantAcceleration"&gt;constant
acceleration&lt;/a&gt;.
Kalman filter estimates acceleration from the measurements and the next
prediction for the target position is made assuming that acceleration is
constant.&lt;/p&gt;
&lt;h1 id="radar-measurements"&gt;Radar measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/horn_rust.jpg" width="1200" height="1279" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Horn antennas. The rust can't be good for efficiency.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I tested the radar by setting the radar on a side of a road and measuring
traffic passing by. Pulse length is 2 µs, the bandwidth is 150 MHz, the number of pulses
is 1024, RX length is 5 µs with 7 µs delay before the next pulse. I used two
antennas with separate TX and RX antenna. Antennas are &lt;a href="https://hforsten.com/horn-antenna-for-radar.html"&gt;horn antennas that
I made myself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;150 MHz bandwidth corresponds to 1 meter distance resolution. It's important to
note that resolution is not the same as accuracy. Resolution is how close two
point targets can be to be separated in the measurement. One target can be measured
with better accuracy than resolution with accuracy depending on signal-to-noise
ratio.&lt;/p&gt;
&lt;p&gt;12 µs time between pulses equals 83 kHz pulse repetition frequency. The pulse
interval determines the maximum unambiguous target velocity. Velocity
measurement is based on measuring phase change between pulses, and if target
moves at high enough speed that it moves several wavelengths between pulses,
there is no way for radar to know what that multiple is, causing the measured
velocity to be ambiguous.&lt;/p&gt;
&lt;p&gt;If the target moves half a wavelength between pulses, it causes a full wavelength distance change since the radar pulse goes from the radar to the target and back. At this speed, the phase
increases by a full wavelength at each measurement, which looks identical to if the
target was stationary. If we don't have information on which direction the
target is moving, we also need to consider that a signal increasing 90 degrees in
phase every measurement looks identical to a signal that decreases by 270 degrees
every measurement. The unambiguous velocity measurement range must be divided by
two for negative and positive velocities, resulting in velocity measurement range:&lt;/p&gt;
&lt;div class="math"&gt;$$v_\text{max} = \frac{\lambda}{4 t_d}$$&lt;/div&gt;
&lt;p&gt;,where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is RF wavelength and &lt;span class="math"&gt;\(t_d\)&lt;/span&gt; is pulse repetition interval.&lt;/p&gt;
&lt;p&gt;With 5.8 GHz RF frequency and 12 µs pulse repetition interval, the unambiguous
velocity measurement range is from -1077 m/s to +1077 m/s. This is over three
times the speed of sound, and there won't be any issues with velocity ambiguities
when measuring cars.&lt;/p&gt;
&lt;p&gt;The Doppler velocity resolution is the unambiguous velocity measurement range divided
by the number of pulses, which is 2155 m/s / 1024 = 2.1 m/s in this case. This is the
minimum velocity difference that two targets at the same range need to have to be
detected as two separate targets. As with the distance accuracy, the velocity
measurement accuracy for a single target is better than velocity resolution and
improves with signal-to-noise ratio.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/pulsed/pulsed_tie2_6.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Above is cellphone video synced with a radar range-Doppler map. CFAR
detections are plotted as red plus symbols on the range-Doppler map and 
listed in the order of decreasing SNR on the top right. On the radar image,
Y-axis is the Doppler velocity in m/s with negative values towards the radar,
X-axis is the distance in meters. The large line at the zero Doppler velocity is
reflections from stationary targets.&lt;/p&gt;
&lt;p&gt;On the list in the upper right, "frame" is the number of the sweep burst in the
radar measurement file, "t" is the time from the first frame, and "detections" is
the number of CFAR detections. Detections with a velocity less than 0.1 m/s are
filtered out to avoid marking every stationary object as a detection.&lt;/p&gt;
&lt;p&gt;Comparing the camera footage to the radar measurements it's easy to correlate
the radar detections to cars in the camera footage for close targets. There is
some shadowing as cars on the foreground block the view of farther away objects,
but the radar is able to detect objects not well visible in the camera footage
quite well. The radar can detect cars up to about 400 m, limited by the
line of sight. Beyond that the road turns and the view is blocked.&lt;/p&gt;
&lt;p&gt;The effect of the DC offset is also visible as very large sidelobes in the range
direction. These sidelobes decrease the ability to detect smaller objects near
larger ones. Especially towards the end the farthest away car is not always
detected by the CFAR as its amplitude isn't sufficiently larger than the
sidelobes overlapping it.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/pulsed/pulsed_tie2_6_tracker.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Above is the same measurement, but now with Kalman tracker. The tracker assigns
CFAR detections to targets with unique IDs. It's able to track multiple
targets, but shadowing and sidelobes cause it to not get enough detections
from further away blocked targets, and it loses track of them. The uncertainty
in their position increases so much that the track deletion threshold is
reached. When they become visible again, a new ID is assigned for them. The tracking
software could be improved to reduce this problem, but this is just a testing of
the radar and I don't want to spend too much time tuning it for this application.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/tie2_6_signal.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Received signal. 1024 overlapping pulses.
    Amplitude is normalized to full-scale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the received signal of all 1024 pulses from one measurement plotted on
the same graph. They overlap very well. There is a small change in the phase
during the measurement for moving objects, which is enough to separate the moving
objects from stationary ones. There is a large return from leakage and nearby
objects at the start, and the received signal from longer time delays that
correspond to farther away targets are much weaker.&lt;/p&gt;
&lt;h2 id="low-if-pulse"&gt;Low-IF pulse&lt;/h2&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/pulsed/pulsed_tie4_1.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;While using a large bandwidth sweep centered at DC works, sidelobes
caused by the high-pass filter are visible in the results. For second test, I set
the RF bandwidth to 75 MHz with 38 MHz modulation frequency so that the
frequency sweeps from 0.5 MHz to 75.5 MHz. Other parameters were kept the same.&lt;/p&gt;
&lt;p&gt;This time, as expected, the very wide sidelobes caused by the DC offset aren't
visible. Range resolution is only half of what is was previously, but it doesn't really
cause any issues with tracking of the cars. They are large enough that even with
a 2 meter range resolution, there isn't any issues with separating them.&lt;/p&gt;
&lt;p&gt;The frame rate is about only half of what is was before. The amount of data should
be the same, and I'm not really sure why it's so much slower this time?&lt;/p&gt;
&lt;p&gt;At the beginning, a second reflection of the passing car is visible at double the
distance and velocity. The radar signal reflects from car, to a sign that is
right next to me, back to car, and then is received by the radar. It's much
weaker in amplitude and its spread out which causes it to not be detected as
a target by CFAR.&lt;/p&gt;
&lt;p&gt;In this measurement, there's a cyclist coming towards the radar which is not
detected as a target. The reason for it is that the cyclist's speed isn't large
enough to separate it well enough from the stationary targets. When CFAR target
detection is calculated, all of the nearby stationary targets are included in
the noise floor calculation for low-speed targets. The large noise floor causes
that the small radar cross section of the cyclist isn't sufficiently above the
noise floor to be detected.&lt;/p&gt;
&lt;p&gt;For this application, a higher RF frequency would be beneficial. Doubling the RF
frequency would double the Doppler velocity bin separation and decrease the
maximum unambiguous Doppler velocity by two. Common police radar speed guns
operate at around 10 to 35 GHz, although nowadays
&lt;a href="https://en.wikipedia.org/wiki/Lidar"&gt;lidar&lt;/a&gt;, which operates near visible light,
is starting to be more common for traffic monitoring.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/detections_snr.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;SNR of detected objects as calculated by CFAR vs distance.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;SNR of the radar detections is quite good at this range. The maximum SNR at 450
m distance is around 35 dB, while just farther away at 550 m there are no
detections. This is because of line of sight, there is no clear path beyond 450
m. Radar SNR should decrease as fourth power of distance, which corresponds to
12 dB drop when the distance is doubled. The radar should be able to detect
traffic at even longer distances if there is a clear line of sight.&lt;/p&gt;
&lt;p&gt;From this measurement, it isn't clear if the radar link budget is as good as
designed, since the radar cross-section of the targets isn't known. Even
a typical car cross section can vary a lot depending on the model and the look
angle. The radar link budget could be verified by measuring a target with a known
radar cross section, typically a corner reflector. However, I don't have
a corner reflector. It wouldn't be too difficult to make one with few triangular
pieces of PCB, and it just would require some effort.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/pulsed/full_rd.png" width="826" height="512" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Full range-doppler map.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The unzoomed range-Doppler map shows how small the view on the videos is on the
Doppler axis. The maximum unambiguous velocity is over 1000 m/s. On the range
direction, negative distances correspond to pulses that arrive before the start
of the transmitted pulse. There shouldn't be any signal there except for
sidelobes from targets at positive distances. The noise floor drops at the edges
of the range direction because of zero padding in pulse compression.&lt;/p&gt;
&lt;h2 id="single-antenna"&gt;Single antenna&lt;/h2&gt;
&lt;p&gt;The previous measurements were made with two antennas, one transmitting and the
other receiving. In the next measurement, I have only one antenna that is switched
between transmit and receive modes. The pulse was set to the same parameters as the
low-IF measurement, except for pulse length, which was decreased from 2 µs to
1 µs to improve detection of close objects.&lt;/p&gt;
&lt;p&gt;With one antenna, receiver can only be switched on at the earliest just after
the end of the transmission. 1 µs at the speed of light is equal to 300 meters, but
radar signal needs to travel to the target and back, so minimum distance to
receive the full pulse is 150 meters. However, while it isn't possible to
receive the full pulse from shorter distances, it's possible to receive
a partial pulse. Pulse compression with only a partial pulse reduces distance
resolution and SNR, but it should allow detecting targets at much lower
distances.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/pulsed/pulsed_tie4_5_ch0.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Targets below 150 m distance can be detected but the range resolution worsen
quickly to unusable levels. Minimum range that radar can detect targets with
these settings is about 40 m. In this plot marker is drawn on the tracker
predicted location instead of CFAR detections as before.&lt;/p&gt;
&lt;p&gt;The tracker parameters were tuned a little bit for this measurement and the
tracking performance is better than in the earlier measurements.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/pulsed/pulsed_schematic.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/pulsed/pulsed_sch.png" width="805" height="565" border="2" style="width: 50%; height: auto; border:2px solid black;" /&gt;&lt;/a&gt;
    &lt;p style="font-size:13px"&gt;Schematic of the radar.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://hforsten.com/img/pulsed/pulsed_schematic.pdf"&gt;Schematic&lt;/a&gt; of the radar is 
available. It should be useful for also as a software-defined radio with some
modifications or reference for other applications that require FPGA. Firmware
and software isn't available at the moment, since I'm not sure if I should make
those public.&lt;/p&gt;
&lt;p&gt;Cost was 330 USD for PCB manufacturing and assembly of two PCBs and additional
225 EUR (240 USD) for components from Digikey that I soldered myself. This is
including 24% VAT and shipping costs. There aren't any similar commercial pulse
compression radars in the same price range and even software defined radios with
similar RF bandwidth are much more expensive.&lt;/p&gt;
&lt;p&gt;The designed radar is fundamentally similar to modern large radars. It utilizes
digital signal processing, supports arbitrary waveforms and has very large
maximum unambiguous target Doppler velocity due to high pulse repetition
frequency. Only the maximum range is shorter than large radars due to low output
power and small antenna.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Identifying Stable Diffusion XL 1.0 images from VAE artifacts</title><link href="https://hforsten.com/identifying-stable-diffusion-xl-10-images-from-vae-artifacts.html" rel="alternate"></link><published>2023-07-30T00:00:00+03:00</published><updated>2023-07-30T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2023-07-30:/identifying-stable-diffusion-xl-10-images-from-vae-artifacts.html</id><summary type="html">&lt;p&gt;The new SDXL 1.0 text-to-image generation model was recently released that generates small artifacts in the image when the earlier 0.9 release didn't have them.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/vae_comparison.png" width="1672" height="856" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;The same picture decoded with SDXL-VAE 0.9 (left)
    and 1.0 (right). Green and violet horizontal artifacts are visible near edges
    with the 1.0 VAE. Zoomed x8 from the original image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Lately AI generated images have been getting so good that they are hard to
distinguish from real photographs. Some of fake AI generated images have gone
viral on social media with some users thinking they are real. Some notable
examples are &lt;a href="https://www.cbsnews.com/news/pope-francis-puffer-jacket-fake-photos-deepfake-power-peril-of-ai/"&gt;Pope wearing puffer
jacket&lt;/a&gt;
and &lt;a href="https://arstechnica.com/tech-policy/2023/03/fake-ai-generated-images-imagining-donald-trumps-arrest-circulate-on-twitter/"&gt;Trump being
arrested&lt;/a&gt;.
There has been discussions on if AI generated content should be required to be
clearly identified by law. Currently many of the big companies building these AI
tools are trying to make sure that their tools are used responsibly by
watermarking the outputs or limiting what the tools are able to do.&lt;/p&gt;
&lt;p&gt;The new SDXL 1.0 text-to-image generation model was recently
&lt;a href="https://stability.ai/blog/stable-diffusion-sdxl-1-announcement"&gt;released&lt;/a&gt; that is
a big improvement over the previous Stable Diffusion model. When the 1.0 version
was released multiple people noticed that there were visible colorful artifacts
in the generated images around the edges that were not there in the earlier 0.9
release limited to research use. The cause was determined to be VAE (Variational
autoencoder) neural network that is responsible for encoding and decoding the
input and output images to latent space that the diffusion u-net works with. The
VAE encoder takes as input an RGB image and compresses it to latent space with
1/8 of original resolution with four channels. This smaller dimension makes the
diffusion u-net more efficient. VAE also includes a decoder that takes the
latent space representation and decodes it back to RGB image.&lt;/p&gt;
&lt;p&gt;On
&lt;a href="https://www.reddit.com/r/StableDiffusion/comments/15aqtuo/anyone_else_noticing_artifacts_in_the_10_vae/"&gt;social&lt;/a&gt;
&lt;a href="https://www.reddit.com/r/StableDiffusion/comments/15ao8v7/sdxl_10_on_comfyui_default_workflow_weird_color/"&gt;media&lt;/a&gt;
there have been claims that the new artifacts generated by the 1.0 VAE are
a watermark for detecting that images are AI generated but the Stability AI
staff haven't commented on it. The Stability AI's &lt;a href="https://github.com/Stability-AI/generative-models"&gt;official
code&lt;/a&gt; and &lt;a href="https://github.com/huggingface/diffusers"&gt;diffusers
library&lt;/a&gt; include &lt;a href="https://github.com/ShieldMnt/invisible-watermark/"&gt;invisible
watermark&lt;/a&gt; that is applied to
the generated images to mark them as AI generated, this was also included in the
earlier Stable Diffusion models. However, because the Stable Diffusion is open
source and watermark is not a part of the neural net model and is applied
afterwards to generated images it can be easily removed from the program. In
fact, two of the most popular UI's
&lt;a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui"&gt;A1111&lt;/a&gt; and
&lt;a href="https://github.com/comfyanonymous/ComfyUI"&gt;ComfyUI&lt;/a&gt; don't include this
watermark and it is not present in the majority of the Stable diffusion images
found in the wild.&lt;/p&gt;
&lt;p&gt;Initially only SDXL model with the newer 1.0 VAE was available, but currently
the version of the model with older 0.9 VAE can also be downloaded from the
Stability AI's &lt;a href="https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main"&gt;huggingface
repository&lt;/a&gt;.
The VAE is also available separately in its own
&lt;a href="https://huggingface.co/stabilityai/sdxl-vae"&gt;repository&lt;/a&gt; with the 1.0 VAE
available in the history.&lt;/p&gt;
&lt;h1 id="sdxl-10-vae-changes-from-09-version"&gt;SDXL 1.0 VAE changes from 0.9 version&lt;/h1&gt;
&lt;p&gt;Calculating difference between each weight in 0.9 and 1.0 VAEs shows that all
the encoder weights are identical but there are differences in the decoder
weights. It makes sense to only change the decoder when modifying an existing
VAE since changing the encoder modifies the latent space representation that
u-net works with which harms the quality of generated images.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/original_comparison.png" width="1540" height="512" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Original image (left), reproduction error of 0.9
    VAE (center) and 1.0 VAE (right). 256x256 original resolution.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Decoding 1024x1024 SDXL generated image resized to 256x256 image with both 0.9
and 1.0 VAEs and plotting the difference to the original image shows that in
both cases the largest error is around the edges. With 1.0 VAE there is a clear
stair case pattern around each edge. If you want you can view the images here:
&lt;a href="/img/sdxl-vae/original.png"&gt;original&lt;/a&gt;, &lt;a href="/img/sdxl-vae/img09.png"&gt;0.9&lt;/a&gt;,
&lt;a href="/img/sdxl-vae/img10.png"&gt;1.0&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/img10_diff_to_09_x4.png" width="1024" height="1024" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Difference of 1.0 encoded image to 0.9 encoded
    image. 256x256 original resolution.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This pattern is clearer when visualizing the difference between 0.9 and 1.0 VAE
processed images. There is a clear green and violet pattern around every edge in
the image.&lt;/p&gt;
&lt;h1 id="psnr-measurement"&gt;PSNR measurement&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/vae_report.png" width="848" height="515" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;VAE measurements from SDXL report.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Stability AI published a &lt;a href="https://arxiv.org/abs/2307.01952"&gt;report on SDXL&lt;/a&gt; that
includes performance comparisons of VAEs in SDXL and older Stable Diffusion
models on few different measures. Since there is a clear visual difference in
SDXL 0.9 and 1.0 VAEs there should be also a difference in the objective
measurements. &lt;a href="https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio"&gt;PSNR&lt;/a&gt;
is peak signal-to-noise ratio, it's SNR normalized to maximum signal dynamic
range. I downloaded the COCO2017 validation dataset that was used in the report
and ran the images through both VAEs and calculated the PSNR values on 256x256
resized images.&lt;/p&gt;
&lt;table style="width:30%"&gt;
&lt;tr&gt;
&lt;th&gt;VAE&lt;/th&gt;
&lt;th&gt;PSNR (dB)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 0.9&lt;/td&gt;
&lt;td&gt;25.74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 1.0&lt;/td&gt;
&lt;td&gt;25.37&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The 0.9 VAE had higher (better) PSNR than the 1.0 VAE included with SDXL model.
There is slight difference compared to the numbers presented in the SDXL report
but I assume that is caused by how images are cropped and resized. The newer 1.0
VAE has about 0.4 dB lower PSNR. Stability AI trained a new VAE from scratch for
SDXL to only slightly improve the performance compared to SD 2.X VAE. PSNR drop
from 0.9 to 1.0 VAE is bigger than the gain in that training.&lt;/p&gt;
&lt;h1 id="identifying-sdxl-generated-images-from-vae-artifacts"&gt;Identifying SDXL generated images from VAE artifacts&lt;/h1&gt;
&lt;p&gt;The 1.0 VAE artifacts are so visible that they can be spotted quite easily by
just zooming in to the image and examining edges visually. However it's
interesting to measure how easily the 1.0 VAE decoded images can be identified.
Instead of looking manually at hundreds of images I decided to train a very simple
neural network. Code is available at
&lt;a href="https://github.com/Ttl/sdxl_vae_detector"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I first encoded and decoded &lt;a href="https://cocodataset.org"&gt;COCO2017&lt;/a&gt; training set
images using 0.9 and 1.0 VAEs with 256x256 resolution and also saving the
resized original 256x256 image. Then I trained a very simple neural net
consisting of one convolution layer, global max pooling and two layer linear
network.  This is a very simple neural network and better performance could be
obtained with a larger network. Especially the global max operation is
problematic since only maximum of each convolution channel over the whole image
is available for the linear layers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VAEDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conv_chs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;linear_chs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conv_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conv2d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conv_chs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;valid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linear1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conv_chs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;linear_chs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linear2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linear_chs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conv_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linear1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linear2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/losses.png" width="833" height="331" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Training results. Grey curve is 1.0 VAE detector and blue 0.9 VAE. 1.0 VAE images are much easier to identify.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I trained the model two times trying to predict if image is decoded by 0.9 VAE
and the same with 1.0 VAE. The training losses are shown above. The grey curve
is trying to classify if the input image is generated by 1.0 VAE and blue the
same for 0.9 VAE. Accuracy/train is the proportion of correctly classified
samples in a batch and Loss/train is MSE loss. Training 20k steps with batch
size of 64 took about 15 minutes on RTX 2080S GPU.&lt;/p&gt;
&lt;table style="width:40%"&gt;
&lt;tr&gt;
&lt;th&gt;VAE detector&lt;/th&gt;
&lt;th&gt;COCO2017 validation accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 0.9&lt;/td&gt;
&lt;td&gt;82%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 1.0&lt;/td&gt;
&lt;td&gt;96%&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Testing the detectors on COCO2017 validation set at 256x256 resolution shows
that the 1.0 VAE encoded images are much easier to detect than 0.9 VAE encoded
images 96% vs 82% accuracy. The detector trying to detect 0.9 VAE images also
gets quite good accuracy of 82% which shows that even the 0.9 VAE without
clearly visible artifacts does have some features in the output that allows the
network to detect it. The detection ratio is based on simply rounding the
output to zero or one.&lt;/p&gt;
&lt;p&gt;A better detector should probably also try to detect the VAE artifacts after
the image has been compressed with various image compression algorithms as most
of the images on social media where it would be interesting to detect AI
generated images are further compressed.&lt;/p&gt;
&lt;h2 id="visualizing-the-convolution-kernels"&gt;Visualizing the convolution kernels&lt;/h2&gt;
&lt;p&gt;Since the network is so simple it is much easier to analyze what it's doing
compared to huge deep networks. To make visualizations even simpler I trained
the network with just 8 convolution output channels instead of 64. The
validation accuracy did drop to 90% for 1.0 VAE and 73% for 0.9 VAE indicating
that the other 56 convolution kernels are beneficial but most of the work can be
done with fewer kernels.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/conv_09_10.png" width="1712" height="472" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Detector input convolution kernels. 0.9 (left) and 1.0
    (right).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The eight input convolution kernels are plotted above for 0.9 and 1.0 VAE
detectors. They are quite different and it's clear that 1.0 VAE detector kernels
on the right is trying to detect the green and violet horizontal artifacts.&lt;/p&gt;
&lt;h2 id="generalization-to-larger-images"&gt;Generalization to larger images&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/sdxl-vae/ComfyUI_10_00002_.png" width="1024" height="1024" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Example SDXL output image decoded with 1.0 VAE. Some artifacts are visible around the tracks when zoomed in.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The training and validation images were all from COCO2017 dataset at 256x256
resolution. These are quite different from typical SDXL images that have typical
resolution of 1024x1024. I generated 200 images using SDXL with two different
prompts for photorealistic and illustration images that were decoded with both
VAEs. Detection threshold was set to 0.5, simply rounding the output to closer
detection. The number of false positives and false negatives could be adjusted
with different threshold parameter.&lt;/p&gt;
&lt;p&gt;1.0 VAE detector identified 187/200 of 1.0 decoded images correctly as being
decoded with 1.0 VAE and 198/200 of 0.9 decoded images correctly as not being
decoded with 1.0 VAE.&lt;/p&gt;
&lt;p&gt;0.9 VAE detector identified only 63/200 of 0.9 VAE decode images correctly.
Since 1.0 and 0.9 VAE errors are similar apart from the 1.0 artifacts its not clear
what it should report when tested on 1.0 decoded images but it identifies
158/200 of 1.0 decoded images as not being decoded with 0.9 VAE. The performance
is much worse than 1.0 VAE detector and below 50% random guessing chance.&lt;/p&gt;
&lt;table style="width:40%"&gt;
&lt;tr&gt;
&lt;th&gt;VAE detector&lt;/th&gt;
&lt;th&gt;LAION aesthetic subset accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 0.9&lt;/td&gt;
&lt;td&gt;41%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDXL-VAE 1.0&lt;/td&gt;
&lt;td&gt;94%&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;On a portion of &lt;a href="https://laion.ai/blog/laion-aesthetics/"&gt;LAION Aesthetic
dataset&lt;/a&gt; which consists of various
images from the internet with different compression algorithms and parameters,
the 1.0 VAE detector generalizes much better. In fact the 0.9 VAE detector gets
less than 50% them correct that could be achieved with random guessing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Ttl/sdxl_vae_detector/blob/7ef23ed47ff10856adc2f41c3e95e4330300f6da/vae_detector.py#L77-L109"&gt;A slightly more complicated
network&lt;/a&gt;
with about the same number of parameters gets over 99% correct when detecting
1.0 VAE images.&lt;/p&gt;
&lt;p&gt;It would be interesting to test if a better network trained with various
compressed images would be able to detect artifacts from 0.9 VAE decoded images.
1.0 VAE decoded images are clearly easy to detect.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Detecting and marking of AI generated content is currently a hot issue. While
Stability AI has not said if the SDXL outputs are watermarked on purpose, SDXL
1.0 neural net generates visible artifacts that makes it possible for generated
images be identified with good accuracy with a simple neural network. However
they can be identified also quite easily just by looking at them due to how
easily visible the VAE artifacts are. The earlier SDXL 0.9 model does not have
these same artifacts and identifying images generated by it is much harder.&lt;/p&gt;</content><category term="Programming"></category></entry><entry><title>Radar phase measurements</title><link href="https://hforsten.com/radar-phase-measurements.html" rel="alternate"></link><published>2023-05-04T00:00:00+03:00</published><updated>2023-05-04T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2023-05-04:/radar-phase-measurements.html</id><summary type="html">&lt;p&gt;Very small movement can be measured with radar by looking at the phase change of the received signal.&lt;/p&gt;</summary><content type="html">&lt;style&gt;
.image-swap {
  cursor: pointer;
}

.image-swap&gt;img, .image-swap:active&gt;img:first-child {
  display: none;
  pointer-events: none;
}

.image-swap&gt;img:first-child, .image-swap:active&gt;img:nth-child(2) {
  display: block;
  pointer-events: none;
}

.image-responsive {
  display: block;
  height: auto;
  max-width: 100%;
}
&lt;/style&gt;

&lt;div id="centered" &gt;
&lt;video width=50% controls autoplay loop muted&gt;
&lt;source src="https://hforsten.com/video/phase-meas/radar_phase_voltage.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/phase-meas/radar_phase_voltage.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Radar can measure distance, amplitude and phase of the reflected signal.
Distance to the target is usually the most important information. For a single
measurement phase is not very useful as it wraps around every half wavelength
distance to the target. However, with multiple measurements the change in the
phase can give valuable information. Radar imaging, both synthetic and real
aperture, relies on the phase information for the image formation. From the small
phase differences at different locations it's possible to determine the angular
location of the target.&lt;/p&gt;
&lt;p&gt;By observing the phase of multiple consecutive measurements, very small distance
changes can be measured. For example, with 6 GHz frequency the wavelength is 50
millimeters. Since the radar signal travels from radar to the target and back,
a change in the distance of half the wavelength is enough to cause the distance
traveled by the signal to be full wavelength. If over multiple measurements the
phase changes 360 degrees, we can then determine that the distance between radar
and target has changed by half wavelength, 25 mm with 6 GHz radar. For one
millimeter accuracy we need to be able to measure a phase difference of 14
degrees. This seems very doable accuracy, but what's the limit?&lt;/p&gt;
&lt;h1 id="radar-phase-measurement-accuracy"&gt;Radar phase measurement accuracy&lt;/h1&gt;
&lt;p&gt;Let's consider a radar transmitting a linear frequency sweep. It is processed
such that the result is an array of complex numbers representing the amplitude
and phase of reflections from targets in that range bin. Range bin size is the
range resolution of the radar. The amount of noise in one range bin depends on
the minimum separable frequency resolution. This frequency resolution comes from
the FFT resolution, which is one over the measurement time: &lt;span class="math"&gt;\(\Delta f = 1/t_s\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We are interested in the phase measurement accuracy of signal from one target in
one range bin. In that range bin we have ideal noiseless signal from the target
and some amount of noise that limits the phase measurement accuracy.&lt;/p&gt;
&lt;p&gt;If the signal-to-noise ratio of the target is &lt;span class="math"&gt;\(S\)&lt;/span&gt; then we can normalize by the
signal and say that the signal is equal to 1 and noise is from complex normal
distribution with mean of zero and standard deviation of &lt;span class="math"&gt;\(\frac{1}{\sqrt{2}S}\)&lt;/span&gt;
for both real and imaginary components with zero covariance.&lt;/p&gt;
&lt;p&gt;Then the question in mathematical terms becomes: what is the standard deviation of
the phase of &lt;span class="math"&gt;\(1+ \epsilon\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\epsilon\)&lt;/span&gt; is a sample from the complex normal distribution.&lt;/p&gt;
&lt;p&gt;Phase of a complex number &lt;span class="math"&gt;\(z\)&lt;/span&gt; can be calculated as
&lt;span class="math"&gt;\(\text{Arg}(z) = \arctan\left(\frac{y}{x}\right)\)&lt;/span&gt;, where &lt;span class="math"&gt;\(x\)&lt;/span&gt; is real part of &lt;span class="math"&gt;\(z\)&lt;/span&gt;
and &lt;span class="math"&gt;\(y\)&lt;/span&gt; is the imaginary part.&lt;/p&gt;
&lt;div class="math"&gt;$$\text{Arg}(1 + \epsilon) =  \text{Arg}(1 + \mathcal{N}(0, \sigma) + j\mathcal{N}(0, \sigma)) = \arctan\left(\frac{\mathcal{N}(0, \sigma)}{1 + \mathcal{N}(0, \sigma)}\right) \approx \mathcal{N}(0, \sigma)$$&lt;/div&gt;
&lt;p&gt;The above formula assumes that SNR is large so that &lt;span class="math"&gt;\(\mathcal{N}(0,
\sigma) \ll 1\)&lt;/span&gt;. Simplification also uses the fact that for small arguments
&lt;span class="math"&gt;\(\arctan(x) \approx x\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Using the relation of noise standard deviation to signal-to-noise ratio, the
result is that the standard deviation of the phase measurement is &lt;span class="math"&gt;\(\sigma
= \frac{1}{\sqrt{2}S}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(S\)&lt;/span&gt; is the signal-to-noise ratio.&lt;/p&gt;
&lt;h1 id="maximum-snr"&gt;Maximum SNR&lt;/h1&gt;
&lt;p&gt;Typically, at low signal strength thermal noise of the receiver is limiting the
signal-to-noise ratio. At very high signal level this might not be the case.
Any non-linearity, for example, from amplifier or ADC compressing, will generate
mixing products. Stability of the clocks is also another limiting factor, all
real oscillators output frequency drifts slightly.&lt;/p&gt;
&lt;p&gt;Using only the radar itself, it's possible to check if thermal noise or other
sources are limiting the SNR by pointing the radar at a large target (ceiling in
this case) and checking if the noise level at distances where there aren't any
targets changes as antennas are covered. If the phase noise is larger than
thermal noise then a large target's phase noise raises noise level also at other
distances. This doesn't directly tell about the SNR of the phase measurement,
since in that case we care about the noise at the same distance the target is
while in this measurement we compare signal strength to noise at different
distance.&lt;/p&gt;
&lt;p&gt;I set the radar to sweep from 5.4 GHz to 6.0 GHz in 1 ms. The target is
a ceiling 2.5 m away (including the cable lengths to the antennas). I added
attenuators to the TX output to limit RX from saturating. IF sampling frequency
was 500 kHz.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=50% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/phase-meas/phase_noise_meas.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/phase-meas/phase_noise_meas.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;In the above video the radar IF output spectrum is plotted while
I cover the antennas. X-axis is distance in meters and Y-axis is amplitude in
decibels (arbitrary reference). At the beginning the target amplitude at 2.5
m is about 58 dB while the noise floor at higher frequencies/distances is at
around -20 dB. When the TX antenna is covered the received signal strength drops
and amplitude of the target decreases by about 30 dB. Noise floor also drops by
10 dB.&lt;/p&gt;
&lt;p&gt;-30 dB level is the thermal noise floor and as the reflected signal strength
increases some other noise source raises the noise floor. It's unclear what
exactly is the source of the noise. Phase noise should be about 10 dB better
than this according to simulations, but with sweeping PLL it can be higher than
in steady state since phase locked loop is not stabilized. Some non-linearity
could also be the cause since received power is quite large.&lt;/p&gt;
&lt;p&gt;For the phase noise measurement accuracy, it's important to know that the maximum
SNR that can be measured with this radar is about 75 dB with these parameters.
Calculating the SNR from variance of the noise in the largest bin also gives
similar results.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/fmcw3_inside.jpg" width="1414" height="1024" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Radar and horn antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using my &lt;a href="https://hforsten.com/third-version-of-homemade-6-ghz-fmcw-radar.html"&gt;homemade FMCW radar&lt;/a&gt; I setup it up to measure its
phase measurement accuracy in the best possible condition. By pointing the
antennas at the ceiling, the received signal strength is maximized resulting in
the best possible SNR. Ceiling is also a stable target so that its possible to
assume that all the variance in the measurements are caused by the measurement
setup. To avoid saturating the receiver I had to add attenuators at the
transmitter output.&lt;/p&gt;
&lt;p&gt;Rather than increasing the sweep length, SNR can be also improved by averaging
multiple measurements. Making measurements faster also has advantage of being
able to detect faster changes.&lt;/p&gt;
&lt;p&gt;Frequency was swept linearly from 5.4 GHz to 6.0 GHz. The maximum sweep rate is
limited by the phase locked loop sweep rate to about 32 µs at this
bandwidth. I set the sweep length to 64 µs with 32 µs delay between sweeps for
total of 96 µs between measurements giving about 10 kHz measurement frequency.&lt;/p&gt;
&lt;p&gt;The phase measurement works by sending one frequency sweep which is mixed
against the transmitted sweep in the receiver. The resulting signal is sine wave
with frequency depending on the distance to the target. Normally in FMCW radar
Fourier transform would be used to get amplitudes of reflections from each
distance, but in this case where we are only looking at one distance we can
instead calculate only one bin of the FFT. The phase of the resulting complex
number is the wanted result.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=50% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/phase-meas/radar_phase2.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/phase-meas/radar_phase2.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;In the video above you can see the effect of moving the antennas on the measured
phase. At the start is some sinusoidal looking noise with small amplitude due to
measurement setup not being completely stable. Below is the picture of the
measurement with consistent scale. The noise level is not visible at full scale.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/lift.png" width="711" height="406" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Antenna movement measurement.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id="phase-accuracy"&gt;Phase accuracy&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/ceiling_phase.png" width="711" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar pointing at ceiling.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Keeping the radar stationary pointing at the ceiling should result in a constant
phase with small amount of noise. However, in the above plot there is clearly
drift in the measured phase. During the 40 s measurement the phase drifted 1.6
degrees corresponding to distance change of 116 µm. The drift is caused mainly
by temperature change, but also by oscillator frequency and supply voltages
drifting. If the signal would be constant with white noise, it would be
possible to average measurements indefinitely to obtain better accuracy. The
drift makes that impossible and after some amount of averaging, the accuracy
starts to get worse as the number of averages is increased.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/ceiling_phase1.png" width="711" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Radar pointing at ceiling, short
    timescale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;On the short timescale the drift is so small that the white noise from limited
SNR dominates the error.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/ceiling_phase_difference.png" width="711" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Phase difference between two successive measurements.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Plotting the phase difference between two successive measurements gives
very nice looking Gaussian distribution. The standard deviation is 0.00575 degrees.
Since standard deviation of difference of two normally distributed variables
from the same distribution is &lt;span class="math"&gt;\(\sqrt{2}\)&lt;/span&gt; times higher than single measurement we
can calculate that standard deviation of phase in single measurement is 0.0041
degrees. This corresponds to SNR of 80.0 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/ceiling_phase_allan.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Allan deviation of phase.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The optimal averaging time can be calculated with &lt;a href="https://en.wikipedia.org/wiki/Allan_variance"&gt;Allan
deviation&lt;/a&gt;. It's most commonly
used for clock stability measurements, but in RF electronics it's also used to
characterize radiometer accuracy with different integration times. In the above
plot is the Allan deviation of the phase measurement. X-axis the integration
time and Y-axis gives the standard deviation of noise at that integration time.
At low integration times the white noise dominates and averaging measurements
increases the measurement accuracy. At high integration times drift starts to
dominate and accuracy gets worse. The optimal integration time is around 5
milliseconds (50 measurements), which should give standard deviation of 0.0012
degrees corresponding to distance of &lt;em&gt;87 nanometers&lt;/em&gt;. Difference of two measurements
would have error &lt;span class="math"&gt;\(\sqrt{2}\)&lt;/span&gt; times larger of 0.0017 degrees corresponding to
distance of 123 nm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/ceiling_phase_diff_avg50.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Phase difference between two successive measurements. 50 averages / measurement.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Plotting the histogram of phase difference between consecutive averages of 50
measurements gives the above plot. The calculated standard deviation is 0.0017
degrees as was predicted from the Allan deviation plot.&lt;/p&gt;
&lt;h3 id="light-bulb-vibration"&gt;Light bulb vibration&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/light_vibration.png" width="768" height="470" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;Phase measurement of radar pointing to ceiling
    with nearby light bulb on.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While doing the radar measurements I had an issue of  extremely small but
consistent frequencies in the IF signal. First I thought that it was caused by
some timing issue in the radar hardware. For example, a slip of one clock cycle
between ADC and PLL sweep starts could cause a phase shift in the recorded
signal. However the phase shift was only about 0.04 degrees which is way below
what one clock cycle error should be able to cause. After some troubleshooting
I found out that turning the lights off would cause the signal to disappear. It
turns out that when I pointed the antennas to the ceiling a light bulb was
slightly in the antenna beam and the light bulb vibrates extremely slightly with
few micrometer displacement due to mains voltage and this small movement is large
enough to be detected by the radar. Any other small movements such as just being
close to the radar are also highly visible in the phase plot.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/phase-meas/light_vibration_fft.png" width="768" height="429" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px"&gt;FFT of the light bulb measurement.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Taking the Fourier transform of the measurement shows that there are a lot of
50 Hz harmonics which is the grid frequency here.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Radar is commonly known for measuring distance to targets with quite coarse
resolution, but using the phase information it can also measure chance in
distance extremely accurately. By observing the phase chance of consecutive
measurements, movements smaller than 1 µm can be detected in the best case.
Phase change can be used to measure small vibrations such as &lt;a href="https://hforsten.com/heartbeat-detection-with-radar.html"&gt;human heartbeat
and breathing rate&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>MIMO radar antenna arrays</title><link href="https://hforsten.com/mimo-radar-antenna-arrays.html" rel="alternate"></link><published>2021-05-15T00:00:00+03:00</published><updated>2021-05-15T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2021-05-15:/mimo-radar-antenna-arrays.html</id><summary type="html">&lt;p&gt;Short introduction to multiple input multiple output (MIMO) radar antenna arrays&lt;/p&gt;</summary><content type="html">&lt;style&gt;
.image-swap {
  cursor: pointer;
}

.image-swap&gt;img, .image-swap:active&gt;img:first-child {
  display: none;
  pointer-events: none;
}

.image-swap&gt;img:first-child, .image-swap:active&gt;img:nth-child(2) {
  display: block;
  pointer-events: none;
}

.image-responsive {
  display: block;
  height: auto;
  max-width: 100%;
}
&lt;/style&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;A radar with one transmitter and one receiver antenna can measure
distances to the targets based on the time of flight of the transmitted
electromagnetic wave. One transmitter and receiver isn't however enough to
measure angle in which the targets are. A simple way to get the angle
information is to use antenna with a very narrow beam and mechanically rotate
it. Mechanical rotation does have some drawbacks. It requires a large antenna to
achieve the narrow beamwidth, rotating the large antenna requires lot of
space and the whole field of view can't be imaged at the same time.&lt;/p&gt;
&lt;p&gt;Instead of rotating a single antenna, a radar with multiple stationary antennas
can be used to generate image of the targets. Phased array radars use multiple
antennas to form the radar image without needing to physically rotate. Because
the antennas can operate at the same time the image refresh rate is much faster.
MIMO radar is phased array radar with multiple transmitter and receiver
antennas. The more antennas are used the better the angular resolution of the
radar.&lt;/p&gt;
&lt;p&gt;I have been working with MIMO radars for few years. When I started I didn't know
much about them and there didn't seem to be much of introduction level material
at the time. Many of the existing material in my opinion are overly complex and
don't give good intuition about the subject. This post is my try to make
introduction level article about the MIMO radar antenna array design that
I wished I had when I started to design MIMO radars.&lt;/p&gt;
&lt;h1 id="antenna-array-beamforming-basics"&gt;Antenna array beamforming basics&lt;/h1&gt;
&lt;p&gt;Radar works by transmitting electromagnetic waveform from the transmitter
antennas which reflects from the targets and is received by the receiver
antennas. Based on the time delay between transmission and reception the distance
to the target can be calculated. This doesn't give any information about the
angle where the object is. To know the angle of the target multiple antennas are
needed. The antennas are at slightly different locations and thus receive the
signal at slightly different times. Based on the time differences at each
antenna the angle of the target can be solved. In practice instead of the
absolute time difference, which would required extremely good time resolution,
phase of the received signal is used.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/phased_array_1tx_2rx.svg" width="118" height="91" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Distance of the traveled radar waveform.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If we assume that the target is very far and the angle to the target from each
antenna can be assumed to be identical then the distances the radar waveform
travels to each antenna is summarized in the above figure. We will also make
another assumption that the distance difference to the target between the
antennas is less than range resolution of the radar. This assumption simplifies
the image formation as all signals will be in the same range bin after the image
formation.&lt;/p&gt;
&lt;p&gt;The distance traveled by the radar waveform is the distance from TX antenna to
target plus distance from the target back to the RX antenna: &lt;span class="math"&gt;\(r_{tx}
+ r_{rx1} = 2r_{tx} + d\sin \theta\)&lt;/span&gt;. With multiple receiver antennas at 
different distances to the transmitter the radar waveform travels slightly
different distance which can be used to solve for the angle of the target.&lt;/p&gt;
&lt;p&gt;The received signal from one target at the RX antenna is of the form: &lt;span class="math"&gt;\(f_{\text{rx}} = A
f_{rx0}\exp\left(\frac{2\pi j}{\lambda} d\sin \theta\right)\)&lt;/span&gt;. &lt;span class="math"&gt;\(A\)&lt;/span&gt; is the
amplitude of the received signal, &lt;span class="math"&gt;\(f_{rx0}\)&lt;/span&gt; is the time-delayed transmitter
waveform that would have been received at the transmitter antenna. Exponential
term is phase shift that depends on the distance to the target &lt;span class="math"&gt;\(r\)&lt;/span&gt; and wavelength
of the radiated signal &lt;span class="math"&gt;\(\lambda = \frac{c}{f}\)&lt;/span&gt;. Complex numbers are used to make
the analysis mathematically simpler, the actually measured signal are of course
real valued and only the real part of the expression would be measured.&lt;/p&gt;
&lt;p&gt;With one target and two receiver antennas at different distance the angle of the target can be easily
determined by comparing phase differences of the received signals at the two
receiver antennas. The phase difference of the received signals at two antennas at distances &lt;span class="math"&gt;\(d\)&lt;/span&gt; and
&lt;span class="math"&gt;\(2d\)&lt;/span&gt; from the transmitter is &lt;span class="math"&gt;\(\exp\left(\frac{2\pi j}{\lambda} d\sin\theta\right)\)&lt;/span&gt;, angle
of the target &lt;span class="math"&gt;\(\theta\)&lt;/span&gt; can be easily solved from the expression. For there to be
a single solution the distance &lt;span class="math"&gt;\(d\)&lt;/span&gt; needs to be less than &lt;span class="math"&gt;\(\lambda/2\)&lt;/span&gt;, otherwise
due to periodicity of &lt;span class="math"&gt;\(\sin\)&lt;/span&gt; same phase could be obtained at two or more angles.&lt;/p&gt;
&lt;h2 id="multiple-targets"&gt;Multiple targets&lt;/h2&gt;
&lt;p&gt;The situation is a little bit harder when there are multiple targets. Reflection signals from
the different targets sum at the receiver and only the summed signal can be
measured. With one target the amplitude of the signal was the same at all
antennas, but with multiple targets this is not necessarily the case.&lt;/p&gt;
&lt;p&gt;After the radar waveform &lt;span class="math"&gt;\(f_{rx0}\)&lt;/span&gt; is demodulated from the receiver signals,
each receiver's output is of the form &lt;span class="math"&gt;\(f_{rx,n} = A_n \exp{j \phi_n}\)&lt;/span&gt;. &lt;a href="https://en.wikipedia.org/wiki/Matched_filter"&gt;Matched
filtering&lt;/a&gt; is the optimal linear
filter to determine how likely it is that there is a target at each angle. In
this case the matched filter is the signal that would be observed from a single
target at the particular angle: &lt;span class="math"&gt;\(g_n(\theta) = \exp\left(\frac{2\pi j}{\lambda} d_n\sin\theta\right)\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The target distribution &lt;span class="math"&gt;\(t(\theta)\)&lt;/span&gt; can be calculated as: &lt;/p&gt;
&lt;div class="math"&gt;$$ t(\theta) = \sum_{n=0}^N
f_{rx,n}g_n^*(\theta) = \sum_{n=0}^N f_{rx,n}\exp\left(-\frac{2\pi j}{\lambda} d_n\sin\theta\right) $$&lt;/div&gt;
&lt;p&gt;Setting &lt;span class="math"&gt;\(f_{rx,n} = 1\)&lt;/span&gt; we get the target response of a single target at zero
angle.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/1xall.png" width="1536" height="946" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Antenna array detected target response with
    different number of RX antennas. Half wavelength spacing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above figure are some target responses with arrays having single
transmitter and different number of receivers. Many antennas are required to get
a decent angular resolution. Even with 16 receivers the -3 dB angular resolution
is just 6 degrees.&lt;/p&gt;
&lt;p&gt;Sidelobe level could be adjusted by weighting the terms in the sum. Without
weighting the first sidelobes are at -13 dB level.&lt;/p&gt;
&lt;h1 id="antenna-arrays-with-multiple-transmitters"&gt;Antenna arrays with multiple transmitters&lt;/h1&gt;
&lt;p&gt;For the MIMO principle to be possible we need to assume that receivers can
separate the signals from different transmitters. If they would transmit at the
same time the same waveform they would just sum up at the receivers and they
can't be separated. In practice the transmitters could transmit at different
times, at different frequencies or the waveforms are orthogonal and correlator
at the receiver can separate them.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/phased_array_2tx_2rx.svg" width="199" height="91" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Two transmitters and two receivers.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The above antenna configuration is similar to the previous one with one
transmitter and two receivers, but now a second transmitter has been added at
distance &lt;span class="math"&gt;\(2d\)&lt;/span&gt;. The TX1-RX1 and TX1-RX2 signals are the same as before and there
are also additional TX2-RX1 and TX2-RX2 signals. We can write &lt;span class="math"&gt;\(r_{tx2}\)&lt;/span&gt; as
a function of &lt;span class="math"&gt;\(r_{tx1}\)&lt;/span&gt; and distance between the transmitters &lt;span class="math"&gt;\(2d\)&lt;/span&gt; as &lt;span class="math"&gt;\(r_{tx2}
= r_{tx1} - 2d\sin\theta\)&lt;/span&gt;. The distance from TX2 to target to RX1 can be written
as: &lt;span class="math"&gt;\(r_{tx2} + (r_{tx1} + d\sin\theta) = 2r_{tx1} - d\sin\theta\)&lt;/span&gt;. Writing the
distances to target of all pairs gives the following table:&lt;/p&gt;
&lt;style type="text/css"&gt;
td
{
    padding:0 15px;
}
th
{
    padding:0 15px;
}
&lt;/style&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Antenna pair&lt;/th&gt;
&lt;th align="left"&gt;Traveled distance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;TX2-RX1&lt;/td&gt;
&lt;td align="left"&gt;&lt;span class="math"&gt;\(2r_{tx1} -d\sin\theta\)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;TX2-RX2&lt;/td&gt;
&lt;td align="left"&gt;&lt;span class="math"&gt;\(2r_{tx1}\)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;TX1-RX1&lt;/td&gt;
&lt;td align="left"&gt;&lt;span class="math"&gt;\(2r_{tx1} + d\sin\theta\)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;TX1-RX2&lt;/td&gt;
&lt;td align="left"&gt;&lt;span class="math"&gt;\(2r_{tx1} + 2d\sin\theta\)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/phased_array_1tx_4rx.svg" width="181" height="32" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;1TX-4RX array&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The 2TX-2RX array with four antennas gives identical length differences between
the antenna pairs to 1TX-4RX array with five antennas in the above figure. This
results in the identical formed radar image. If the TX2 antenna was moved to the
same position as RX2 the measured lengths would match perfectly to the 1TX-4RX
array. The constant offset in the measured distances doesn't matter for
determining the target angle and I didn't draw it like that because figure would
have been not as clear.&lt;/p&gt;
&lt;p&gt;Using two transmitters we have saved one total antenna. The savings are even
bigger with bigger arrays. &lt;strong&gt;MIMO array makes independent measurement for each
TX-RX pair, while antenna array with only one transmitter can make measurements
equal to the number of receiver antennas&lt;/strong&gt;. For example with 32 receivers and 32
transmitters each TX-RX pair gives independent measurement for total of 32*32
= 1024 measurements. Antenna array with only one transmitter would need 1024
receiver antennas to get the same number measurements and achieve the same
angular resolution.&lt;/p&gt;
&lt;h1 id="virtual-transceiver-array"&gt;Virtual transceiver array&lt;/h1&gt;
&lt;p&gt;In the last section 2TX-2RX array was found to behave similarly to 1TX-4RX array
when the antenna spacing were as given. If the antenna spacings would have been
different they would not have matched. Antenna arrays with a single transmitter
are easy to analyze. The problem is how to determine the correct antenna
spacings of the MIMO array to match regular antenna array?&lt;/p&gt;
&lt;p&gt;To ease the analysis of MIMO arrays it's possible to calculate virtual element
positions of the antenna array. Each virtual element is an overlapping
transmitter and receiver that only receives its own transmitted signal. This
virtual transceiver antenna array is easier to analyze. In practice for each
TX-RX pair we place a virtual transceiver antenna at half way between the both
antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/phased_array_virtual.svg" width="199" height="91" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;TX-RX pair and virtual antenna at half-way
    between them.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture is a TX-RX pair at distance &lt;span class="math"&gt;\(d\)&lt;/span&gt; and a virtual element half
way between them. The TX to target to RX distance is &lt;span class="math"&gt;\(2r_{tx} + d\sin\theta\)&lt;/span&gt;.
Distance from virtual element to target to virtual element is &lt;span class="math"&gt;\(2(r_{tx}
+ \frac{d}{2}\sin\theta) = 2r_{tx} + d\sin\theta\)&lt;/span&gt;, which is equal to the TX-RX
pair.&lt;/p&gt;
&lt;p&gt;One important detail about the transceiver elements is that while regular
antenna array spacing needs to be less than half wavelength to avoid aliasing,
transceiver element spacing needs to be less than quarter wavelength. This is
because transceiver element has both transmitter and receiver overlapping.
Moving the transceiver element by quarter wavelength away from the target
changes the distance to the target by half wavelength.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual2.png" width="1000" height="500" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;1TX-4RX and 2TX-2RX antenna arrays with virtual
    elements.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture are 1TX-4RX and 2TX-2RX array antenna locations with the
virtual elements marked. Despite having smaller physical size and less antennas
virtual element places are identical to the 1TX-4RX array.&lt;/p&gt;
&lt;h1 id="2d-mimo-antenna-arrays"&gt;2D MIMO antenna arrays&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual64.png" width="1000" height="500" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;1TX-64RX and 8TX-8RX antenna arrays.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above figure 1TX-64RX and 8TX-8RX arrays are compared. 8TX-8RX MIMO array
has less less antennas, is physically smaller and has the same virtual array as
the 1TX-64RX array. 1TX-64RX physical size is 31.5 wavelengths, 8TX-8RX size is
sligthly smaller 28 wavelengths. The Y-axis TX to RX distance is arbitrary and
doesn't have effect on the target detection. Target response at zero angle is
plotted below.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/target_response_1d_8x8.png" width="700" height="500" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;64 virtual element with λ/4 spacing target response at zero angle.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The angular resolution is about 1.6 degrees which starts to be a decent
resolution for a radar.&lt;/p&gt;
&lt;p&gt;However there is another MIMO array configuration that results in the same
virtual array but is physically even smaller. The trick is to split the RX array
in two and put them in the opposite sides of the TX array.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_8x8_split.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;8TX-8RX split RX MIMO array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The 8TX-8RX MIMO array in the above figure results in the exact same virtual
array as the two previous arrays, but the physical size is much smaller at only
17.5 wavelengths. This is almost two times smaller than 1TX-64RX array and in
fact &lt;strong&gt;as the number of elements increases the split RX MIMO array takes only half
of the space of equal array with one transmitter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are also other possible 8TX-8RX MIMO arrays that have the same virtual
array, but they are not as compact as the split RX array. However they might be
useful in some situations. In the figure below is one example:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_8x8_split2.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Alternative 8TX-8RX RX MIMO array.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="3d-mimo-antenna-arrays"&gt;3D MIMO antenna arrays&lt;/h1&gt;
&lt;p&gt;Previously the antenna arrays have been one dimensional linear arrays, the
obtained radar image has distance and azimuth angle to the target. They were
called 2D arrays as the radar image is 2D and has no inclination angle (target
height) component. To obtain the also the inclination angle and the full 3D
position of the targets the antenna array needs to be two dimensional.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_1x64_3d.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Dense 1TX-64RX 3D antenna array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With one transmitter the receivers need to be arranged in a dense grid in this
case 8x8 requiring total of 64 receiver antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/target_response_2d_1x64.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Target response of 1TX-64RX 3D array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The angular resolution is not that good since there is only eight antennas for
X and Y directions.&lt;/p&gt;
&lt;p&gt;One MIMO array that has the same virtual array can be achieved by arranging TX
and RX antennas in perpendicular rows:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_8x8_3d.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;8TX-8RX MIMO 3D antenna array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While this requires less total antennas it doesn't save space. Similar to split
RX 2D array the splitting of the antennas can be done with the 3D array to
achieve a box shaped array:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_8x8_box.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;8TX-8RX MIMO 3D box antenna array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The virtual element array is the same as before but now the physical size is
smaller. Similarly to the 2D case, as the number of elements increases the side
dimensions of box shaped MIMO array are half of the dense receiver array with
one transmitter.&lt;/p&gt;
&lt;p&gt;The box configuration does have one drawback that in the corners TX and RX
antennas need to be very close together, the distance is only 0.35 wavelengths.
In practice this can cause issues with large TX-RX coupling.&lt;/p&gt;
&lt;p&gt;The box array has a nice property that it can be tiled while still achieving
dense virtual element spacing without any gaps or overlapping elements:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_box_8x8x4.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Four tiled 8TX-8RX MIMO 3D box antenna arrays.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This is very useful in practice as it gives space to place components in a large
array and allows manufacturing the large array with smaller modules.&lt;/p&gt;
&lt;h1 id="non-equally-spaced-mimo-arrays"&gt;Non equally spaced MIMO arrays&lt;/h1&gt;
&lt;p&gt;All of the above arrays resulted in equally spaced virtual array. This is very
useful property in practice as it allows using Fast Fourier Transform in the
image formation greatly speeding it up. However non-equally spaced array is also
possible and it's possible to calculate their responses with the same equations.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_8x8_wide.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;8TX-8RX MIMO array with wider split RX spacing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If the antennas are not at exactly the right places the virtual array won't be
uniform.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/target_response_1d_8x8_wide.png" width="700" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Target response of the above array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Larger absolute size of the virtual array leads to narrower main lobe, but gaps
in the virtual array increase the side lobe levels.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/virtual_random_8x8.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Randomly placed 8TX-8RX MIMO array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture 8TX and 8RX antennas were placed randomly in -2 to +2
wavelength box following uniform distribution.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/mimo/target_response_2d_random_8x8.png" width="500" height="500" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Target response of the above array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Characteristic of randomly distributed antennas is narrow main lobe but high
side lobe level.&lt;/p&gt;
&lt;h1 id="receiver-antenna-virtual-array"&gt;Receiver antenna virtual array&lt;/h1&gt;
&lt;p&gt;It's possible to also make virtual array of only receiver antennas. In that case
virtual element positions are sum of position of each pair instead of average
when using transceiver elements. This kind of virtual array is usually more
common for example in text books. It doesn't matter which one is used when
analyzing the far field radiation pattern, but one case where it makes
a difference is MIMO SAR (synthetic aperture array). MIMO SAR virtual array can
be calculated by sum of individual virtual arrays only if transceiver elements
are used. I also think that transceiver array derivation is mathematically
cleaner.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;MIMO radar array with &lt;span class="math"&gt;\(N_{tx}\)&lt;/span&gt; transmitters and &lt;span class="math"&gt;\(N_{rx}\)&lt;/span&gt; receivers can achieve
angular resolution comparable to antenna array with one transmitter and &lt;span class="math"&gt;\(N_{tx}
N_{rx}\)&lt;/span&gt; receivers. With the right antenna distribution MIMO array can be made
half the size of regular antenna array while still achieving the same angular
resolution.&lt;/p&gt;
&lt;p&gt;The code for visualizing the antenna arrays is available
&lt;a href="https://gist.github.com/Ttl/3bb3e7c3213374f0dcb056b256795a76"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Backprojection Backpropagation</title><link href="https://hforsten.com/backprojection-backpropagation.html" rel="alternate"></link><published>2019-10-22T00:00:00+03:00</published><updated>2019-10-22T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2019-10-22:/backprojection-backpropagation.html</id><summary type="html">&lt;p&gt;Even better focused homemade SAR images with timedomain backprojection algorithm and automatic differentiation based autofocus.&lt;/p&gt;</summary><content type="html">&lt;style&gt;
.image-swap {
  cursor: pointer;
}

.image-swap&gt;img, .image-swap:active&gt;img:first-child {
  display: none;
  pointer-events: none;
}

.image-swap&gt;img:first-child, .image-swap:active&gt;img:nth-child(2) {
  display: block;
  pointer-events: none;
}

.image-responsive {
  display: block;
  height: auto;
  max-width: 100%;
}
&lt;/style&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;This post is continuation of my &lt;a href="https://hforsten.com/synthetic-aperture-radar-imaging.html"&gt;previous synthetic-aperture imaging
experiments&lt;/a&gt;. In the previous post I used omega-k
frequency domain algorithm for the image formation and automatic differentiation
based autofocusing with Tensorflow. I managed to get pretty well focused images
from bicycle mounted radar considering the total lack of any motion recording
data. The autofocus algorithm managed to improve it slightly, but it wasn't
perfectly focused.&lt;/p&gt;
&lt;p&gt;This time I'm implementing time-domain backprojection algorithm. It has slower
&lt;span class="math"&gt;\(O(n^3)\)&lt;/span&gt; time complexity compared to &lt;span class="math"&gt;\(O(n^2\log n)\)&lt;/span&gt; of the omega-k, but it
makes up for its slowness by its flexibility. Since omega-k is based on FFT it
requires the data to be sampled on a straight line with equal spacing. However
due to motion errors during the measurement this doesn't hold in practice. By
adjusting the phase of the signal it's possible to approximate the signal that
would have been recorded in slightly different position, but that approach has
it's limits. Backprojection algorithm works with any shape measurement path.&lt;/p&gt;
&lt;h1 id="backprojection-algorithm"&gt;Backprojection algorithm&lt;/h1&gt;
&lt;p&gt;Let's start the analysis of the algorithm with the radar signal model. My radar
is frequency modulated constant wave (FMCW) radar that transmits short frequency
sweeps. I won't repeat the derivation from &lt;a href="https://hforsten.com/synthetic-aperture-radar-imaging.html"&gt;the previous
post&lt;/a&gt;, you can check it out if you want to see the
details and I'll only give the resulting IF-signal for single target:&lt;/p&gt;
&lt;div class="math"&gt;$$ s(n, \tau) = \exp\left(-j \frac{4 \pi}{c} (f_c + \gamma \tau) d(\mathbf{x}, \mathbf{p}_n)\right), \quad -T/2 &amp;lt; \tau &amp;lt; T/2  $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(c\)&lt;/span&gt; is speed of light, &lt;span class="math"&gt;\(f_c\)&lt;/span&gt; is center frequency of the sweep, &lt;span class="math"&gt;\(\gamma = B/T
=\)&lt;/span&gt; sweep bandwidth / sweep length &lt;span class="math"&gt;\(=\)&lt;/span&gt; sweep rate, &lt;span class="math"&gt;\(\tau\)&lt;/span&gt; is time variable and
&lt;span class="math"&gt;\(d(\mathbf{x}, \mathbf{p}_n)\)&lt;/span&gt; is distance to the target at position &lt;span class="math"&gt;\(\mathbf{x}\)&lt;/span&gt; from the &lt;span class="math"&gt;\(n\)&lt;/span&gt;th
sweep at position &lt;span class="math"&gt;\(\mathbf{p}_n\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Let's define discrete version of the IF-signal &lt;span class="math"&gt;\(s[n, m] = s(n, m T_s)\)&lt;/span&gt;, where
&lt;span class="math"&gt;\(T_s\)&lt;/span&gt; is the sampling interval.&lt;/p&gt;
&lt;p&gt;The simplest way to generate a SAR image of radar measurements along some path
is to use &lt;a href="https://en.wikipedia.org/wiki/Matched_filter"&gt;matched filtering&lt;/a&gt;. For
each pixel in the output image it's possible to calculate what the measured IF
signal would have been if there was a target at that pixel. Matched filtering
the measured signal with the complex conjugate of the expected will give high
value when the measurement matches the expectation. Repeating the filtering
process for every pixel in the output gives the focused image. In equation form
the filtering for one pixel can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ I(\mathbf{x}) = \sum_{n=0}^{N-1}\sum_{m=0}^{M-1} s[n, m] s_\text{ref}[n, m] $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(s_{ref}[n, m]\)&lt;/span&gt; is the reference function which is complex conjugate of
the expected IF response:&lt;/p&gt;
&lt;div class="math"&gt;$$s_\text{ref}[n, m] = \exp\left(j\frac{4 \pi}{c}(f_c + \gamma m T_s) d(\mathbf{x}, \mathbf{p}_n)\right) $$&lt;/div&gt;
&lt;p&gt;This SAR focusing algorithm gives especially well focused images, there are no
interpolation steps, no approximations, it even works for cases where transmitter
and receiver antennas are not at the same position. The big problem however is
that it's extremely slow. For every pixel in the image it needs to go over every
measured data point. For X x Y image and N sweeps each M points long the time
complexity is in the order of &lt;span class="math"&gt;\(O(XYNM)\)&lt;/span&gt;, or just &lt;span class="math"&gt;\(O(n^4)\)&lt;/span&gt; for short as they all
scale linearly as the image size increases.&lt;/p&gt;
&lt;p&gt;There is an easy way to speed it up with some slight assumptions about the
measurement setup. If the reference function can be factored into two parts each
depenging on only &lt;span class="math"&gt;\(n\)&lt;/span&gt; and &lt;span class="math"&gt;\(m\)&lt;/span&gt; then the double sum can be factored into two sums.&lt;/p&gt;
&lt;div class="math"&gt;$$ I(\mathbf{x}) = \sum_{n=0}^{N-1} \exp\left(j\frac{4 \pi f_c}{c}d(\mathbf{x}, \mathbf{p}_n)\right) \sum_{m=0}^{M-1} s[n, m] \exp\left(j\frac{4\pi}{c} \gamma m T_s d(\mathbf{x}, \mathbf{p}_n)\right) $$&lt;/div&gt;
&lt;p&gt;The trick for faster algorithm is using FFT to calculate the inner sum once and
then using interpolation to index the right term for the outer sum.&lt;/p&gt;
&lt;p&gt;The definition of the inverse FFT is: &lt;span class="math"&gt;\(S[n, k] = \sum_{m=0}^{M-1} s[n, m] \exp\left(j 2 \pi
k m / M\right)\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To solve for the right index into the FFT we need to solve for &lt;span class="math"&gt;\(k\)&lt;/span&gt; in the equation:&lt;/p&gt;
&lt;div class="math"&gt;$$j 2 \pi k m / M = j\frac{4\pi}{c} \gamma m T_s d(\mathbf{x}, \mathbf{p}_n) $$&lt;/div&gt;
&lt;p&gt;Solving for &lt;span class="math"&gt;\(k\)&lt;/span&gt; and using the fact that &lt;span class="math"&gt;\(\gamma = B/T\)&lt;/span&gt; and &lt;span class="math"&gt;\(T_s = T / M =\)&lt;/span&gt;
sampling period gives:&lt;/p&gt;
&lt;div class="math"&gt;$$ k = \frac{2 B}{c} d(\mathbf{x}, \mathbf{p}_n) $$&lt;/div&gt;
&lt;p&gt;The backprojection algorithm can then be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ I(\mathbf{x}) = \sum_{n=0}^{N-1} \exp\left(j\frac{4 \pi f_c}{c}d(\mathbf{x}, \mathbf{p}_n)\right) S\left[n, \frac{2 B}{c} d(\mathbf{x}, \mathbf{p}_n)\right] $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(S[n, k]\)&lt;/span&gt; is the Fourier transform of &lt;span class="math"&gt;\(s[n, m]\)&lt;/span&gt; along the second axis.
A slight complication is that the new index isn't necessarily a whole number and
interpolation is required. The advantage of this form is that it completely
removes the inner sum and replaces it with FFT calculated in advance. As
a result time complexity of the algorithm improves from &lt;span class="math"&gt;\(O(n^4)\)&lt;/span&gt; to &lt;span class="math"&gt;\(O(n^3)\)&lt;/span&gt; and
in practice the new form is much faster.&lt;/p&gt;
&lt;h2 id="bistatic-correction"&gt;Bistatic correction&lt;/h2&gt;
&lt;p&gt;During this derivation it was assumed that transmitter and receiver antennas are
at the same position, but that isn't exactly the case with my radar.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/bicycle_radar_rack.jpg" width="1600" height="879" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Radar mounted on the bicycle. Note the separate
    transmitter and receiver antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As can be seen in the picture above I actually have separate transmitter and
receiver antennas. While the error from considering them to be located on the
same place isn't that large, it's not insignificant. Compensating for it in the
Omega-k algorithm would have been very difficult, but it's trivial in
backprojection algorithm. The distance that the radar measures is distance from
transmitter antenna to target to receiver antenna. Replacing the distance to the
target &lt;span class="math"&gt;\(p_n\)&lt;/span&gt; in the formula with average of distances from receiver and
transmitter antennas to the target gives the measured distance.&lt;/p&gt;
&lt;h2 id="velocity-correction"&gt;Velocity correction&lt;/h2&gt;
&lt;p&gt;During the measurement the distance to the target is actually not constant due
to movement of the platform. A correction for the movement was derived in the
previous post for Omega-k and similar correction can be derived for
the backprojection too.&lt;/p&gt;
&lt;p&gt;First we assume that velocity is constant during one sweep, which isn't really
unrealistic assumption as the acceleration would need to be absolutely enormous
to have any difference in the speed during the short sweep. The second assumption is
that velocity towards the target is constant during the sweep, this assumption
is equal to only correcting for the first order errors of the velocity. In
practice the non-linearity is so small with reasonable speeds that this isn't
a large assumption.&lt;/p&gt;
&lt;p&gt;Projection of the velocity &lt;span class="math"&gt;\(\mathbf{v}[n]\)&lt;/span&gt; to the distance vector gives the
velocity component towards the target:&lt;/p&gt;
&lt;div class="math"&gt;$$v_p[n] = \mathbf{v}[n] \cdot \frac{\mathbf{x} - \mathbf{p}_n}{\left|\mathbf{x} - \mathbf{p}_n\right|} $$&lt;/div&gt;
&lt;p&gt;Distance to the target during the sweep can then be written as: &lt;span class="math"&gt;\(d(\mathbf{x},
\mathbf{p}_n) + v_p[n] m T_s\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Substituting the new distance to the reference function gives:&lt;/p&gt;
&lt;div class="math"&gt;$$ s[n, m] = \exp\left(j\frac{4\pi}{c}\left(f_c d(\mathbf{x}, \mathbf{p}_n) + \gamma m T_s d(\mathbf{x}, \mathbf{p}_n) + f_c v_p[n] m T_s + \gamma m^2 T_s^2 v_p[n] \right)\right)$$&lt;/div&gt;
&lt;p&gt;The first two terms are the same as before, and the next two terms depending on &lt;span class="math"&gt;\(v_p[n]\)&lt;/span&gt; are new.
The last term with &lt;span class="math"&gt;\(m^2\)&lt;/span&gt; dependence is problematic since it prevents writing the
inner sum as FFT. Quadratic term could be ignored since it's typically much
smaller than the other velocity term. By dividing the quadratic velocity term with the
other term it can be shown that the quadratic term is at maximum &lt;span class="math"&gt;\(B / f_c\)&lt;/span&gt; of the other
term. It mostly matters when sweep bandwidth is significant fraction of the
operating frequency.&lt;/p&gt;
&lt;p&gt;Better estimate than ignoring the quadratic term can be obtained by fitting
a linear function to it (&lt;a href="https://scholarsarchive.byu.edu/etd/5587/"&gt;Stringham's PhD. thesis&lt;/a&gt;). Least square fit of linear function for &lt;span class="math"&gt;\(m^2\)&lt;/span&gt; over
interval 0 to &lt;span class="math"&gt;\(M-1\)&lt;/span&gt; can be obtained by minimizing the sum below for &lt;span class="math"&gt;\(a\)&lt;/span&gt; and &lt;span class="math"&gt;\(b\)&lt;/span&gt;:&lt;/p&gt;
&lt;div class="math"&gt;$$ \min_{a,b}\sum_{m=0}^{M-1} \left(m^2 - (a m + b)\right)^2 $$&lt;/div&gt;
&lt;p&gt;The solution can be found as &lt;span class="math"&gt;\(m^2 \approx (M - 1) m - \frac{1}{6} (M^2 - 3M + 2) \approx M m - \frac{M^2}{6}\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Substituting it into the quadratic term and simplifying gives: &lt;span class="math"&gt;\(\gamma m^2 T_s^2 v_p[n] \approx \frac{\gamma m T^2 v_p[n]}{M} - \frac{1}{6} \gamma T^2 v_p[n]\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As before the reference function can be divided into two parts and the inner sum
can be replaced with FFT:&lt;/p&gt;
&lt;div class="math"&gt;$$ I(\mathbf{x}) = \sum_{n=0}^{N-1} \exp\left(j\frac{4 \pi}{c} \left(f_c d(\mathbf{x}, \mathbf{p}_n) - \gamma T^2 v_p[n]/6\right) \right) S\left[n, \frac{2 B}{c} \left(d(\mathbf{x}, \mathbf{p}_n) + \frac{f_c v_p[n]}{\gamma}\right) + T v_p[n]\right] $$&lt;/div&gt;
&lt;p&gt;In the outer exponential &lt;span class="math"&gt;\(4\pi\gamma T^2 v_p[n]/6c = 4\pi B T v_p[n] / 6c\)&lt;/span&gt; is
normally small enough to be ignored.&lt;/p&gt;
&lt;p&gt;The difference to the case where radar was assumed to be stationary during the
sweep is just a slight adjustment to the index of the &lt;span class="math"&gt;\(S[n, k]\)&lt;/span&gt;.&lt;/p&gt;
&lt;h1 id="tensorflow-implementation"&gt;Tensorflow implementation&lt;/h1&gt;
&lt;p&gt;Implementation of the backprojection was made as a custom operation in
Tensorflow with both CPU and GPU support. The algorithm is excellent candidate
for GPU implementation as the parallelization is straightforward: each pixel in
the image can be calculated independently. Interpolation needed in the indexing
of the FFT result is implemented as simple linear interpolation. FFT is
calculated with configurable amount of zero-padding to smooth the output and
make the interpolation result more accurate. The result is acceptable even with
this simple interpolation unlike interpolation in Omega-k algorithm where
linear interpolation would cause visible artifacts in the image.&lt;/p&gt;
&lt;p&gt;For calculating gradients, a gradient operation also needs to be implemented.
Specifically operation calculating the chain rule needs to be implemented. The
operation takes as input the accumulated gradient from the earlier gradient
operations and calculates the gradient at the inputs of the operation.&lt;/p&gt;
&lt;p&gt;Gradients of complex numbers need some care in Tensorflow. Gradient of real function
&lt;span class="math"&gt;\(y(x)\)&lt;/span&gt; with respect to &lt;span class="math"&gt;\(x\)&lt;/span&gt; is calculated simply as &lt;span class="math"&gt;\(\frac{\partial y}{\partial
x}\)&lt;/span&gt;, but the formula is different if the function is complex valued. The short
version is that the gradient of complex valued function &lt;span class="math"&gt;\(f(x)\)&lt;/span&gt; with respect to
&lt;span class="math"&gt;\(x\)&lt;/span&gt; needs to be calculated as &lt;span class="math"&gt;\(\overline{\left(\frac{\partial f}{\partial x}
+ \frac{\partial \overline f}{\partial x}\right)}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\overline \cdot\)&lt;/span&gt; is
 complex conjugate. For more detailed explanation see &lt;a href="https://github.com/tensorflow/tensorflow/issues/3348#issuecomment-512101921"&gt;this
 discussion&lt;/a&gt;.
In this case the second term, conjugated partial derivate, is zero and the
result is the normal real gradient but conjugated. For simplicity velocity
correction terms are ignored but adding them is trivial. Using chain rule
gradient of the backprojection for one pixel of output can be calculated as:&lt;/p&gt;
&lt;div class="math"&gt;$$\frac{\partial I(\mathbf{x})}{\partial \mathbf{p}} = \sum_{n=0}^{N-1} \frac{\partial d(\mathbf{x}, \mathbf{p}_n)}{\partial \mathbf{p}} \exp\left(j\frac{4 \pi f_c}{c}d(\mathbf{x}, \mathbf{p}_n)\right)  \left( j\frac{4 \pi f_c}{c} S\left[n, \frac{2 B}{c} d(\mathbf{x}, \mathbf{p}_n)\right] + \frac{2 B}{c} \frac{\partial S}{\partial d(\mathbf{x}, \mathbf{p}_n)}\left[n, \frac{2 B}{c} d(\mathbf{x}, \mathbf{p}_n)\right] \right)$$&lt;/div&gt;
&lt;p&gt;The gradient computation needs gradient of &lt;span class="math"&gt;\(S\)&lt;/span&gt;. In the forward direction linear
interpolation was used to calculate the values in between the sample points and
it's natural to use the slope of the linear interpolation to define the
necessary gradient.&lt;/p&gt;
&lt;p&gt;The gradient operation calculating the chain rule is complex conjugate of the above
gradient times the accumulated gradient summed over all the pixels in the image.
If &lt;span class="math"&gt;\(E\)&lt;/span&gt; is some scalar output of the graph and &lt;span class="math"&gt;\(g\)&lt;/span&gt; is the accumulated gradient
from all the operations after the SAR image formation, the gradient of &lt;span class="math"&gt;\(E\)&lt;/span&gt; with
respect to the input positions can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ \frac{\partial E}{\partial \mathbf{p}} = \sum_\mathbf{x} g[\mathbf{x}] \overline{\left(\frac{\partial I(\mathbf{x})}{\partial
\mathbf{p}}\right)}$$&lt;/div&gt;
&lt;h1 id="autofocus"&gt;Autofocus&lt;/h1&gt;
&lt;p&gt;With the backprojection and its gradient operation implemented using Tensorflow
for autofocusing the generated image is easy. First calculate the SAR image and
it's entropy. Entropy of image generally correlates with how focused image is
with better focused images having lower entropy. Next calculate gradient of
entropy as function of the input position vector using automatic differentation.
A small step towards negative gradient vector should decrease the entropy
improving the focus of the image. The steps are repeated until entropy doesn't
decrease anymore.&lt;/p&gt;
&lt;p&gt;In practice there are some slight issues with the above algorithm. If position
of every sample can be optimized individually there are too many degrees of
freedom. There are many positions for
the samples that give a focused image with low entropy, but most of them have
some geometric distortions that don't correspond to the actual scene. For
example adding any position offset to all of the samples just moves the
resulting image. Backprojection algorithm requires specifying the pixel
coordinates beforehand and without any constraints the optimizer can just add
a large offset to every sample to move all the data out of the scene to minimize
the entropy. Some constraints need to be added for the sample positions to make
sure that the optimized image makes sense. Almost all SAR data is taken on
a straight path with constant velocity, so it's natural to add some constraints
to keep the positions close to a straight line with equal spacing.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar2/bp_parking2_100_geometric_errors2.png" width="435" height="814" style="width: 30%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Autofocused parking lot image with geometric
    erorrs due to unrestriected sample positions. There is a kink in the image
    near origin that shouldn't be there.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above image is an example of the scene with unrestricted position
optimization. The image looks focused but it has severe geometric distortions as
the parking spaces should be evenly spaced. See pictures at the bottom on how the
image should look like.&lt;/p&gt;
&lt;p&gt;I added two extra constraints for velocity. The first adds penalty if the range
direction of the velocity is too large and other adds penalty if the azimuth
velocity difference to its mean value is too large. With these constraints the
geometric errors are avoided in the cases I tested.&lt;/p&gt;
&lt;p&gt;Focusing data With 6444 sweeps each with 1000 samples each for 9666 x 1933 pixel
output image the FFT based Omega-k takes 56 ms for one forward iteration and
backprojection algorithm takes 436 ms. The time-domain backprojection algorithm
is almost ten times slower on this dataset but compared to the Omega-k algorithm
it's much more flexible. Omega-k algorithm requires the transmitter and receiver
to be at the same position and all samples to be recorded on a straight line
with equal distance in the direction of movement between them. Time-domain
backprojection can focus any kind of measurement configuration with possibly
unevenly samples positions and non-colocated transmitter and receiver antennas.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;p&gt;For good comparison with the Omega-k algorithm I focused the previously measured
data also with the backprojection algorithm. Autofocus was run for 100
iterations.&lt;/p&gt;
&lt;div id="centered" &gt;
     &lt;div class="image-swap" ontouchstart&gt;
        &lt;img style="max-width: 90%;" src="https://hforsten.com/img/fmcw3-sar2/bp_parking2_0_label.png"&gt;
        &lt;img style="max-width: 90%;" src="https://hforsten.com/img/fmcw3-sar2/bp_parking2_100_v2_label.png"&gt;
        &lt;p style="font-size:13px" &gt;Parking lot scene SAR image. Click/tap to see
        the autofocused version.&lt;/p&gt;
     &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Above is the image of the parking lot from the same data as I previously used
for the Omega-k algorithm post expect that the sampling rate is twice as large
as before avoiding some aliasing artifacts from before. Clicking/tapping and
holding shows the focused image. The difference the autofocus makes is big and
the autofocused image has excellent focusing.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar2/bp_parking2_v3_solved_v.png" width="709" height="480" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved velocity&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The solved velocity is almost constant, there are small oscillations in the
range direction and some slight variance in the azimuth direction.
There are some spikes that correspond to locations where there are large objects
that saturate the receiver. This method doesn't seem to give the best results
when receiver has saturated, some spreading of the big objects can be seen as
a result in the image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar2/bp_omegak_comparison.png" width="1002" height="938" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Comparison of backprojection and omega-k with and
    without autofocusing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For farther away objects the difference in focusing between the algorithms is
big. In the above comparison image both autofocus algorithms improve the
focusing for nearby targets, but only backprojection with autofocusing can also
focus the farther away targets.&lt;/p&gt;
&lt;div id="centered" &gt;
     &lt;div class="image-swap" ontouchstart&gt;
        &lt;img style="max-width: 80%;" src="https://hforsten.com/img/fmcw3-sar2/bp_park_0_labels.png"&gt;
        &lt;img style="max-width: 80%;" src="https://hforsten.com/img/fmcw3-sar2/bp_park_100_labels.png"&gt;
        &lt;p style="font-size:13px" &gt;Park scene SAR image. Click/tap to see
        the autofocused version.&lt;/p&gt;
     &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I also redid the other scene I measured the last time. The improvement from 
autofocus in that scene is also huge especially for objects farther away. The
objects farther away are still spread, but I'm not sure if they should be much
better than what they are now. There are lot of issues with occlusions in this
scene and some far away objects are visible for the radar only for a very short
time. See the images in &lt;a href="https://hforsten.com/synthetic-aperture-radar-imaging.html"&gt;the previous post&lt;/a&gt; for the
same data focused with Omega-k algorithm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar2/park_map_comparison.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar2/park_map_comparison.jpg" width="2152" height="862" border="2" style="width: 90%; height: auto; "border:2px solid black;""/&gt;&lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Comparison to Google maps image of the same location.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Light posts and tree trunks are very visible in the SAR image compared to the Google
maps image. Most of the details inside the park in the upper right can't be seen in the
SAR image since a metal fence blocks the most of the view from the radar signal.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I implemented a fast GPU based time-domain backprojection algorithm for
synthetic aperture radar image focusing with autofocus based on automatic
differentiation implemented as custom operation in Tensorflow. The autofocus
algorithm gives a big improvement to focusing of the data from my homemade radar
without any position measurement.  The code is available at my
&lt;a href="https://github.com/Ttl/fmcw3/tree/master/pc/sar"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Synthetic-aperture radar imaging</title><link href="https://hforsten.com/synthetic-aperture-radar-imaging.html" rel="alternate"></link><published>2019-08-13T00:00:00+03:00</published><updated>2019-08-13T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2019-08-13:/synthetic-aperture-radar-imaging.html</id><summary type="html">&lt;p&gt;Differentiable synthetic-aperture radar image formation with Tensorflow. Including very fast image formation and autofocusing utilizing GPU.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Few years ago I did some &lt;a href="https://hforsten.com/homemade-synthetic-aperture-radar.html"&gt;simple synthetic-aperture radar (SAR) imaging
experiments&lt;/a&gt; with the second version of my homemade
FMCW radar. Since then I made a much improved &lt;a href="https://hforsten.com/third-version-of-homemade-6-ghz-fmcw-radar.html"&gt;third version of the
radar&lt;/a&gt; but didn't do any SAR measurements due to the
amount of effort it would have required. I did have plans to do some SAR
experiments afterwards but it took until now to have enough time and
motivation.&lt;/p&gt;
&lt;p&gt;Synthetic aperture radar (SAR) imaging is a way to synthesize very large antenna
array by moving single antenna on a known path. If there are no moving targets
in the scene then one radar taking many measurements along a path gives the same
result as one ridiculously large radar that is as long as the movement path.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/sar_imaging.png" width="822" height="648" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SAR imaging of a single target. As the radar
    moves the measured distance follows a parabola.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If we move on a straight path while radar pointing 90 degrees from the direction
of the path measures a distance to the single target, we will find that the
measured distance follows a parabola. This follows directly from the Pythagorean
theorem. The SAR imaging problem is finding out the target position from the
measured distance data. Of course in a real scene we have multiple targets and
the solution isn't as simple as looking where the closest approach is as could
be done in the picture above.&lt;/p&gt;
&lt;h1 id="omega-k-algorithm"&gt;Omega-k algorithm&lt;/h1&gt;
&lt;p&gt;There are few different algorithms for solving this problem, but the one I'm
going to use is called Omega-k algorithm. It is a fast imaging algorithm
utilizing FFT which also makes it efficient to calculate on GPU. The derivation
mostly follows &lt;a href="https://ieeexplore.ieee.org/document/7878107"&gt;a paper by Guo and
Dong&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The radar I have is a frequency modulated constant wave (FMCW) radar. It
transmits a short frequency sweep. The transmitted waveform can be modeled as: &lt;/p&gt;
&lt;div class="math"&gt;$$ s_t(\tau) = \exp(j 2 \pi f_c \tau + \pi \gamma \tau^2),\quad -T_s/2 &amp;lt; \tau &amp;lt; T_s/2 $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(j = \sqrt{-1}\)&lt;/span&gt;, &lt;span class="math"&gt;\(f_c =\)&lt;/span&gt; RF carrier frequency, &lt;span class="math"&gt;\(\tau =\)&lt;/span&gt; time variable,
&lt;span class="math"&gt;\(\gamma = B/T_s =\)&lt;/span&gt; sweep bandwidth / sweep length &lt;span class="math"&gt;\(=\)&lt;/span&gt; sweep rate.&lt;/p&gt;
&lt;p&gt;The transmitted wave reflects off a target at some distance and is received after
time &lt;span class="math"&gt;\(t_d\)&lt;/span&gt;. Ignoring the amplitude, the received wave is a time-delayed copy
of the transmitted signal: &lt;span class="math"&gt;\(s_r(\tau) = s_t(\tau - t_d)\)&lt;/span&gt;. Signals from multiple
targets are summed.&lt;/p&gt;
&lt;p&gt;The receiver mixes the received signal with the transmitted signal. This mixing
is called dechirping and it removes the high frequency RF component. The result
is a low frequency signal, usually some few kHz to MHz and is easy to digitize
with low-cost ADC. With the complex signals we take complex conjugate of the
transmitted signal to get the low-pass product and the resulting mixing product is:&lt;/p&gt;
&lt;div class="math"&gt;$$ s_{\text{IF}}(\tau) = s_t(\tau - t_d) s_t^*(\tau) = \exp(-j 2 \pi f_c t_d
- j 2 \pi \gamma t_d \tau + j \pi \gamma \tau^2) $$&lt;/div&gt;
&lt;p&gt;During SAR measurement the radar repeats this measurement while moving on
a straight path with a constant speed. The position of the radar on the path is:
&lt;span class="math"&gt;\(x = v \tau + x_n\)&lt;/span&gt;, where &lt;span class="math"&gt;\(v\)&lt;/span&gt; is speed of the radar platform and &lt;span class="math"&gt;\(x_n
= v n T_p\)&lt;/span&gt;. &lt;span class="math"&gt;\(n\)&lt;/span&gt; is the index for measurements and &lt;span class="math"&gt;\(T_p\)&lt;/span&gt; is the transmit
repetition interval.&lt;/p&gt;
&lt;p&gt;If the radar target is at position &lt;span class="math"&gt;\((x_0, y_0)\)&lt;/span&gt; the distance to the target can
be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ R(x) = \sqrt{y_0^2 + (x_0 - x)^2} $$&lt;/div&gt;
&lt;p&gt;We set the y-coordinate of the path to be 0 and x position is limited to &lt;span class="math"&gt;\(-L/2
&amp;lt; x &amp;lt; L/2\)&lt;/span&gt;, where &lt;span class="math"&gt;\(L\)&lt;/span&gt; is length of the path.&lt;/p&gt;
&lt;p&gt;Since electromagnetic waves travel at the speed of light and radar signal needs
to travel to the target and back to the radar, we get expression
for received signal time delay &lt;span class="math"&gt;\(t_d = 2R(x)/c\)&lt;/span&gt;, where &lt;span class="math"&gt;\(c\)&lt;/span&gt; is the speed of light.&lt;/p&gt;
&lt;p&gt;The recorded signal can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ s_{\text{IF}}(\tau, x) = \exp\left(-j \frac{4 \pi}{c} (f_c + \gamma \tau) R(x)\right)
\exp\left(j \frac{4 \pi \gamma^2}{c^2} R^2(x)\right) $$&lt;/div&gt;
&lt;p&gt;The last term in the above expression is called residual video phase term and
it's an undesirable by-product from dechirping operation. It should be removed
before further processing by multiplying by &lt;span class="math"&gt;\(\exp(-j \frac{4 \pi \gamma^2}{c^2}
R^2(x))\)&lt;/span&gt;.  However this form is inconvenient because it depends on &lt;span class="math"&gt;\(R(x)\)&lt;/span&gt;. Using
the fact that &lt;span class="math"&gt;\(R(x) = c t_d / 2\)&lt;/span&gt; and that &lt;span class="math"&gt;\(t_d\)&lt;/span&gt; can be expressed in terms of
frequency of the IF signal: &lt;span class="math"&gt;\(f = -2 \gamma R(x) / c = -\gamma t_d \Rightarrow
t_d = -\frac{f}{\gamma}\)&lt;/span&gt; we can write the correction term as &lt;span class="math"&gt;\(\exp(-j \pi f^2
/ \gamma)\)&lt;/span&gt;. This form can be applied easily to the Fourier transformed signal.&lt;/p&gt;
&lt;p&gt;With RVP term removed the signal is:&lt;/p&gt;
&lt;div class="math"&gt;$$ s(\tau, x_n) = \exp\left(-j \frac{4 \pi}{c} (f_c + \gamma \tau) \sqrt{y_0^2 + (x_n - x_0 + v \tau)^2}\right) $$&lt;/div&gt;
&lt;p&gt;Ideally we would like to have the signal in form &lt;span class="math"&gt;\(\exp(-j 2 \pi f_y y_0)\exp(-j
2\pi f_x x_0)\)&lt;/span&gt;, then we could apply two dimensional inverse Fourier transform to
get a delta function centered at &lt;span class="math"&gt;\((x_0, y_0)\)&lt;/span&gt; focusing the image. Currently the
signal &lt;span class="math"&gt;\(s(\tau, x_n)\)&lt;/span&gt; is not in this form and inverse Fourier transform doesn't
give anything interesting. We need to find some processing steps to apply to the
signal to get it to the required form so that inverse Fourier transform can be
applied. The reason to look specifically for this kind of form is that FFT can
be performed very efficiently.&lt;/p&gt;
&lt;p&gt;As a first step, note that &lt;span class="math"&gt;\(\gamma\)&lt;/span&gt; has units of Hz/s and &lt;span class="math"&gt;\(\tau\)&lt;/span&gt; has units of s.
The product &lt;span class="math"&gt;\(\gamma \tau\)&lt;/span&gt; has units of Hz so it's a frequency. This product is actually
instantenous modulation frequency of the sweep. We do substitution &lt;span class="math"&gt;\(\gamma
\tau \rightarrow f_\tau\)&lt;/span&gt; to get rid of the time variable. &lt;span class="math"&gt;\(\tau\)&lt;/span&gt; range was &lt;span class="math"&gt;\(-T/2
\ldots T/2\)&lt;/span&gt; and the new range for &lt;span class="math"&gt;\(f_\tau\)&lt;/span&gt; is &lt;span class="math"&gt;\(-B/2 \ldots B/2\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="math"&gt;$$ S(f_\tau, x_n) = \exp\left(-j \frac{4 \pi}{c} (f_c + f_\tau) \sqrt{y_0^2 + (x_n - x_0 + \frac{v f_\tau}{\gamma} )^2}\right) $$&lt;/div&gt;
&lt;p&gt;Also instead of using frequency the math is cleaner and the implementation of
the algorithm is easier when using wavenumbers instead. We define range
wavenumber &lt;span class="math"&gt;\(K_r = K_{rc} + \Delta K_r\)&lt;/span&gt;. &lt;span class="math"&gt;\(K_{rc} = \frac{4\pi f_c}{c}\)&lt;/span&gt;, &lt;span class="math"&gt;\(\Delta
K_r = \frac{4\pi f_\tau}{c} = -\frac{2\pi B}{c} \ldots \frac{2\pi B}{c}\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="math"&gt;$$ S(K_r, x_n) = \exp\left(-j K_r \sqrt{y_0^2 + (x_n - x_0 + \frac{v c \Delta K_r}{4 \pi \gamma} )^2}\right) $$&lt;/div&gt;
&lt;p&gt;Next step is to do Fourier transform in azimuth direction (direction of the
movement) to move also the &lt;span class="math"&gt;\(x_n\)&lt;/span&gt; variable to frequency domain.&lt;/p&gt;
&lt;div class="math"&gt;$$ S(K_r, K_x) = \int_{-\infty}^\infty S(K_r, x_n) \exp(-j K_x x_n)\, dx_n  = \int_{-\infty}^\infty \exp(j\Phi(x_n))\, dx_n $$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(K_x = 2\pi f_x\)&lt;/span&gt; is wavenumber in the azimuth direction. This integral doesn't have
exact solution, but there is a method to calculate quite accurate approximation
using a method called principle of stationary phase (PSOP). Phase of the
function being integrated can be written as:&lt;/p&gt;
&lt;div class="math"&gt;$$ \Phi(x_n) = -K_r \sqrt{y_0^2 + \left(x_n - x_0 + \frac{v c \Delta K_r}{4 \pi \gamma} \right)^2} - K_x x_n $$&lt;/div&gt;
&lt;p&gt;If we plot the phase &lt;span class="math"&gt;\(\Phi(x_n)\)&lt;/span&gt; for some realistic values we get a plot that
looks something like below:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/phi_plot.png" width="709" height="480" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Phase and real part of the function being
    integrated.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There is one point where derivative of the phase is zero (stationary point) and
the function varies slowly, but away from that point the function is highly
oscillatory. As we integrate the function the oscillations far away from the
stationary point cancel out and mainly the area around the stationary point
contributes to the result of the integral.&lt;/p&gt;
&lt;p&gt;We can expand the function around the stationary point &lt;span class="math"&gt;\(\frac{d}{dx_n}\Phi(x_n) \rvert_{x_n=x_n^\star} = 0\)&lt;/span&gt;, as
&lt;span class="math"&gt;\(\Phi(x_n) = \Phi(x_n^\star) + 0 + \frac{1}{2}\Phi^{''}(x_n - x_n^\star)^2\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Plugging the Taylor expansion in to the integral we get:&lt;/p&gt;
&lt;div class="math"&gt;$$ \begin{aligned}S(K_r, K_x) &amp;amp;\approx \exp(j\Phi(x_n^\star)) \int_{-\infty}^\infty \exp\left(j\frac{1}{2}\Phi^{''}(x_n^\star)(x_n-x_n^\star)^2\right)\, d x_n \\
&amp;amp;= \exp(j\Phi(x_n^\star)) \int_{-\infty}^\infty \exp\left(j\frac{1}{2}\Phi^{''}(x_n^\star)s^2\right)\, d s \\
&amp;amp;= \exp(j\Phi(x_n^\star)) \sqrt{\frac{2\pi j}{\Phi^{''}(x_n^\star)}} \end{aligned}
$$&lt;/div&gt;
&lt;p&gt;Since &lt;span class="math"&gt;\(\Phi(x_n)\)&lt;/span&gt; is purely real function, if &lt;span class="math"&gt;\(\mu\)&lt;/span&gt; is sign of the
&lt;span class="math"&gt;\(\Phi(x_n^\star)\)&lt;/span&gt;, then the square root term can be written as
&lt;span class="math"&gt;\(\sqrt{\frac{2\pi}{|\Phi^{''}(x_n^\star)|}} exp(j\pi \mu/4)\)&lt;/span&gt;. The second
derivative contributes amplitude term and constant phase term, neither of them
which is important for focusing image which mainly depends on aligning the
phases. We have ignored the amplitude since beginning and it ends up being slowly
varying function so we will just approximate it away.&lt;/p&gt;
&lt;p&gt;The stationary point of the function &lt;span class="math"&gt;\(\frac{d}{dx_n}\Phi(x_n)
\rvert_{x_n=x_n^\star} = 0\)&lt;/span&gt; can be solved to be:&lt;/p&gt;
&lt;div class="math"&gt;$$ x_n^\star = x_0 - \frac{K_x y_0}{\sqrt{K_r^2 - K_x^2}} - \frac{c \Delta K_r
v}{4\pi\gamma} $$&lt;/div&gt;
&lt;p&gt;Plugging in the stationary point to the &lt;span class="math"&gt;\(S(K_r, K_x)\)&lt;/span&gt; equation above we get the
solution of the integral:&lt;/p&gt;
&lt;div class="math"&gt;$$ S(K_r, K_x) \approx \exp\left(j(-y_0 \sqrt{K_r^2 - K_x^2} - K_x x_0 + \frac{c \Delta K_r K_x
v}{4\pi\gamma})\right) $$&lt;/div&gt;
&lt;p&gt;The last term is phase offset caused by the movement of the radar during the
sweep. It can be removed by multiplying with exponential in the opposite phase.&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(x_0\)&lt;/span&gt; term is already in the correct form as it is multiplied only by &lt;span class="math"&gt;\(K_x\)&lt;/span&gt;, but
&lt;span class="math"&gt;\(y_0\)&lt;/span&gt; term depends on both &lt;span class="math"&gt;\(K_r\)&lt;/span&gt; and &lt;span class="math"&gt;\(K_x\)&lt;/span&gt;. &lt;span class="math"&gt;\(K_r, K_x\)&lt;/span&gt; dependence can be fixed
by making a substitution &lt;span class="math"&gt;\(\sqrt{K_r^2 - K_x^2} \rightarrow K_y\)&lt;/span&gt;. This step is
called Stolt interpolation as it is implemented by interpolating the data to
a new grid.&lt;/p&gt;
&lt;p&gt;After the Stolt interpolation the signal is in form:&lt;/p&gt;
&lt;div class="math"&gt;$$ S(K_y, K_x) = \exp(j(-K_y y_0 - K_x x_0)) $$&lt;/div&gt;
&lt;p&gt;Taking 2D inverse Fourier transform gives the focused image with delta function
centered at &lt;span class="math"&gt;\((x_0, y_0)\)&lt;/span&gt;.&lt;/p&gt;
&lt;h1 id="tensorflow-implementation"&gt;Tensorflow implementation&lt;/h1&gt;
&lt;p&gt;The Omega-k algorithm is mainly large FFTs and interpolation. Both can be
implemented well on GPU which requires large parallelism from the program. Well
written GPU implementation should be several times faster than CPU
implementation. For convenience I'll implement the algorithm using Tensorflow
library. Although it's most often used for training neural nets it can just as
well be used for other purposes.&lt;/p&gt;
&lt;p&gt;The derivation above was done using continous signals but in practice the radar
samples the signal with ADC resulting in discrete samples. Most of the above
derivation is still valid, some additional thought is needed for example 
in making sure that sampling grid is small enough to avoid aliasing.&lt;/p&gt;
&lt;p&gt;First let's define some parameters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tensorflow&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;tf&lt;/span&gt;

&lt;span class="c1"&gt;# Load captured data and parameters from the disk.&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# &amp;#39;data&amp;#39; contains the captured data in 2D array. &lt;/span&gt;
&lt;span class="c1"&gt;# First dimensions is index of the sweep on the path &lt;/span&gt;
&lt;span class="c1"&gt;# and second is raw values of the sweep from ADC.&lt;/span&gt;

&lt;span class="c1"&gt;# Platform movement speed during the measurement.&lt;/span&gt;
&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Samplerate of the digitized signal.&lt;/span&gt;
&lt;span class="n"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Sweep length.&lt;/span&gt;
&lt;span class="n"&gt;tsweep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tsweep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Bandiwdth of the sweep.&lt;/span&gt;
&lt;span class="n"&gt;bw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bw&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# RF center frequency of the sweep.&lt;/span&gt;
&lt;span class="n"&gt;fc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;f0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="c1"&gt;# Time between the sweeps.&lt;/span&gt;
&lt;span class="n"&gt;tdelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tdelay&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Sweep rate.&lt;/span&gt;
&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bw&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tsweep&lt;/span&gt;
&lt;span class="c1"&gt;# Number of captured sweeps.&lt;/span&gt;
&lt;span class="n"&gt;sweep_samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# Position difference between the captured sweeps.&lt;/span&gt;
&lt;span class="n"&gt;delta_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsweep&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tdelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;

&lt;span class="c1"&gt;# Wavenumber axes&lt;/span&gt;
&lt;span class="n"&gt;kx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;delta_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;delta_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;dkr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sweep_samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dkr&lt;/span&gt;
&lt;span class="n"&gt;ky0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;kx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;
&lt;span class="n"&gt;ky_delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;kr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Same spacing as kr to avoid aliasing during interpolation.&lt;/span&gt;
&lt;span class="c1"&gt;# Ky axis after interpolation.&lt;/span&gt;
&lt;span class="n"&gt;ky_interp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ky0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ky_delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Straight away one difference between the real data and the derivation is that my
radar doesn't have IQ sampling and the captured signal is purely real. We can
however easily generate the required imaginary part. If we take FFT of the
captured signal, since the signal is purely real positive and negative frequency
components are complex conjugates. However the complex signal should only have
negative frequency components (Negative because the delayed RF signal in the
receiver mixer is lower frequency than the LO signal). If we zero the positive
components and then take inverse FFT, the result is a complex signal with the
right properties. This transformation is called &lt;a href="https://en.wikipedia.org/wiki/Hilbert_transform"&gt;Hilbert
transform&lt;/a&gt;. We can also apply
windowing function in range direction and do the RVP term multiplication at the
same time. We do this step as a pre-processing step using numpy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hilbert_rvp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Last dimension is the range dimension&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[:,:&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# Zero the positive frequencies&lt;/span&gt;
    &lt;span class="c1"&gt;# Residual video phase term compensation&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ifft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Windowing function in range direction.&lt;/span&gt;
&lt;span class="c1"&gt;# Decreases sidelobes from FFT.&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hanning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# fs = Samplerate of the ADC.&lt;/span&gt;
&lt;span class="c1"&gt;# gamma = Sweep bandiwdth / length of the sweep.&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hilbert_rvp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The next step would be azimuth FFT but before that we will first zero pad the data
in azimuth direction because target azimuth positions can be outside the
endpoints of the movement path. Without zero padding those targets would alias
to locations inside the path.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add &amp;#39;zpad&amp;#39; zeros symmetrically to both sides of the azimuth axis.&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;zpad&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zpad&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;constant&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the pre-processing is done and the rest is done on GPU. The first step is
azimuth FFT which needs to be done in a slightly roundabout way due to
limitations of the function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a tensor from pre-processed data.&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complex64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Tensorflow FFT doesn&amp;#39;t have option to choose the axis to transform&lt;/span&gt;
&lt;span class="c1"&gt;# and it always calculates FFT over the last axis.&lt;/span&gt;
&lt;span class="c1"&gt;# Transpose to swap the axes before and after FFT to calculate FFT over&lt;/span&gt;
&lt;span class="c1"&gt;# the first axis.&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Shift frequency components so that zero-frequency is at the center.&lt;/span&gt;
&lt;span class="n"&gt;img1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;img2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Matched filter is used to correct for movement of the radar during the sweep.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Matched filtering to compensate for movement during the sweep.&lt;/span&gt;
&lt;span class="n"&gt;mf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dkr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complex64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
              &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complex64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After matched filtering we are supposed to do the Stolt interpolation. The data
is currently defined on &lt;span class="math"&gt;\(K_x, K_r\)&lt;/span&gt; axes and we need to interpolate it to &lt;span class="math"&gt;\(K_x,
K_y\)&lt;/span&gt; axes. In general the new &lt;span class="math"&gt;\(K_y\)&lt;/span&gt; axis points don't correspond exactly to the
points on the existing grid and we need to interpolate. The problem is that
there is no easy way to do the interpolation in Tensorflow. Simple interpolation
methods such as linear interpolation, while easy to implement, are not good
enough as they cause distortions in the frequency domain. The ideal interpolation
would be &lt;a href="https://en.wikipedia.org/wiki/Whittaker%E2%80%93Shannon_interpolation_formula"&gt;sinc interpolation&lt;/a&gt;,
but the formula needs multiplication for every sample in the signal to calculate
one output point resulting in a very slow &lt;span class="math"&gt;\(O(n^2)\)&lt;/span&gt; algorithm. A good compromise
between efficient algorithm and minimal frequency domain distortions is &lt;a href="https://en.wikipedia.org/wiki/Lanczos_resampling"&gt;Lanczos
interpolation&lt;/a&gt;. Instead of
interpolating with &lt;span class="math"&gt;\(\text{sinc}(x)\)&lt;/span&gt; that has infinite support, a kernel &lt;span class="math"&gt;\(L(x)\)&lt;/span&gt; with
finite support is used so that only nearby samples need to be considered in the
interpolation:&lt;/p&gt;
&lt;div class="math"&gt;$$ L(x) = \begin{cases} \text{sinc}(x)\, \text{sinc}(x/a) &amp;amp; \text{if}\ -a &amp;lt; x &amp;lt; a, \\
 0 &amp;amp; \text{otherwise} \end{cases}$$&lt;/div&gt;
&lt;p&gt;As far as I know there isn't really any easy way to implement it efficiently in
Tensorflow with the existing operations. Applying the formula for every point in the
image adds so many operations to the computation graph that it never finishes.
I ended up writing a custom operation in C++ for it with both CPU and GPU
implementations. The code for it is too long to include here but you can find it
in the &lt;a href="https://github.com/Ttl/fmcw3/tree/master/pc/sar/interp_op"&gt;Git
repository&lt;/a&gt;. After
compiling the operation and writing some Python code to interface with it the
Stolt interpolation can be written in a single line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;interp_op&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stolt_interp&lt;/span&gt;

&lt;span class="c1"&gt;# The original grid where the data is defined.&lt;/span&gt;
&lt;span class="n"&gt;ky&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;kr&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kx&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)[:,&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newaxis&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Stolt interpolation&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;#39;interp_order&amp;#39; is Lanczos kernel order &amp;#39;a&amp;#39;.&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stolt_interp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ky&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ky_interp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interp_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last step is to do 2D inverse FFT to generate the image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ifft2d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2 id="measurements"&gt;Measurements&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/bicycle_radar_rack.jpg" width="1600" height="879" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Radar mounted on the bicycle.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I mounted the radar on the rear rack of bicycle and pedaled the bicycle in
a straight line with constant speed to take the measurement. It's very important
that the path is known since any difference in the actual position and the one
used during the image formation leads to image quality degradation. The position
should be known within a fraction of wavelength to avoid any defocusing
errors. My radar works at 6 GHz which works out to few cm precision requirement
over about 200 m long path. This is probably not going to happen, but we are
going to see later how the introduced position errors can be at least partly
corrected.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking_lot_camera.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Photograph of the scene being imaged. Imaging
    path is on the footpath on right and the radar points to the parking lot
    on left.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture is the scene being imaged. It's a parking lot with lot of
pole-like targets that should be well visible in the generated image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_raw_data.png" width="1092" height="1003" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Raw data without any processing looks like this.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Raw captured data doesn't look like much. Sweep length is 1 ms, with 1 MHz
sampling rate, so each sweep has 1000 points and there are total of 6444 sweeps.
The output signal of the FMCW radar is a superposition of sine waves from each
visible target. Frequency depends on the distance to the target, closer it is
the lower the frequency. Amplitude depends on the amount of power reflected
which depends on the size, shape, material and distance to the target.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_raw_data_fft.jpg" width="1092" height="1003" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Taking FFT in range direction turns it into
    easier to read format with range on X-axis.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is FFT of the raw data in range direction. Range FFT is not part of the
image processing, but this is how non-imaging measurement would be processed.
FFT allows changing X-axis from wavenumbers to distance to the target.&lt;/p&gt;
&lt;p&gt;Running the Omega-k algorithm for the data generates the following image:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar/parking2_aspect.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_aspect.png" width="454" height="819" border="2" style="width: 40%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Generated SAR image of the parking lot. The
    camera photograph above was taken at (90, 0) looking towards (0,0).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The image is mostly focused, there is some visible spreading but at least most
of the targets can be recognized. There are some curved artifacts that I'm not
sure where they come from.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_foreground.png" width="1092" height="1003" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Generated SAR image of the parking lot. Zooming
    into the foreground.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When zooming into the foreground objects some smearing from movement
deviations is visible. These can be fixed with autofocusing algorithm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/sar_performance.png" width="709" height="480" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Image formation time on different programs using
    the above data from the parking lot. Program start-up and data preprocessing
    time is not included.&lt;/p&gt; &lt;/div&gt;

&lt;p&gt;There is absolutely huge speed up from utilizing GPU. Numpy implementation does the FFT
using Numpy and Stolt interpolation is coded in Python without vectorization.
Numpy version takes 22 min and 30 s to form the image of the above data most of
which is spent in the interpolation routine. TensorFlow CPU implementation
calculates everything on CPU, Stolt interpolation is a custom op coded in C++. It
is about 16x times faster than the Numpy implementation with most of the speedup
coming from the much faster C++ interpolation function. Image formation takes
1 min 20 s.&lt;/p&gt;
&lt;p&gt;TensorFlow GPU Python code is completely identical to the CPU version. It
calculates FFTs using nVidia's cufft library on GPU and interpolation is done
using custom CUDA kernel also on GPU. Image formation takes only 80 ms. The
speedup is absolutely huge being over 1000x faster than TensorFlow CPU and over
ten thousand times faster than the Numpy version.&lt;/p&gt;
&lt;p&gt;Speedup from GPU shouldn't really be this much. I think part of the reason is
that GPU implementation using nVidia's libraries is much more optimized than
whatever CPU implementation TensorFlow uses. CPU FFT doesn't seem to be
multithreaded, so just multithreaded FFT should speed it up by some small
factor.&lt;/p&gt;
&lt;h2 id="minimum-entropy-autofocus"&gt;Minimum Entropy Autofocus&lt;/h2&gt;
&lt;p&gt;There are some inevitable motion errors when moving the radar on a bicycle. If
there is a small deviation in direction of the antenna (Orthogonal to the
movement path) it results in phase shifting the signal. If movement errors are
known they can be corrected before the image formation by phase shifting the
signal in the opposite direction by the equal amount. We don't know the errors
but we can use TensorFlow to find the phase shift that minimizes some objective
function that corresponds to how well the image is focused.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/sar_entropy.jpg" width="1994" height="956" style="width: 65%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Comparison of the scene with and without added
    phase errors. The original scene that is visibly better focused also has
    lower entropy.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;One objective function that corresponds with focusing of the image is entropy
calculated as &lt;span class="math"&gt;\(-\sum_{i} p_{i} \log(p_{i})\)&lt;/span&gt;, where sum goes over all the pixels
in image that is normalized so that all pixels sum to one and are in range &lt;span class="math"&gt;\([0,
1]\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Autofocus can be implemented simply in the code by phase shifting the
measurement data at the beginning and adding optimizer that tries to minimize
the entropy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define variable.&lt;/span&gt;
&lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;phase&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;initializer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros_initializer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# Also pad the phase when padding the measured data.&lt;/span&gt;
&lt;span class="n"&gt;phase_padded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;zpad&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zpad&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;constant&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Apply the phase correction.&lt;/span&gt;
&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase_padded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complex64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Rest of the imaging&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Entropy calculation&lt;/span&gt;
&lt;span class="n"&gt;abs_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;abs_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs_img&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abs_img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abs_img&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abs_img&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;phase_smoothness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce_mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;phase_smoothness&lt;/span&gt;

&lt;span class="c1"&gt;# Define optimizer.&lt;/span&gt;
&lt;span class="n"&gt;opt_op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;train&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AdamOptimizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run the &lt;code&gt;opt_op&lt;/code&gt; until the entropy has stopped decreasing and as a result we
should have found the phase errors that minimize the entropy. It's not very
efficient since this method requires running the image formation several times,
but since the GPU implementation is so fast it is not an issue.&lt;/p&gt;
&lt;p&gt;I also found out that adding additional loss &lt;code&gt;phase_smoothness&lt;/code&gt; that penalizes
large discontinuities in the phase results in slightly better quality image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/sar_entropy_loss_graph.png" width="824" height="480" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Entropy during the optimization.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I ran the optimizer for around 100 steps, more than that will still decrease
entropy a little but there isn't really any significant visible changes in the
image.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar/parking2_aspect_autofocus.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_aspect_autofocus.png" width="454" height="819" border="2" style="width: 40%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Image after autofocus.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_background_comparison.jpg" width="1970" height="958" style="width: 65%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Autofocused image background compared to the
    original.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Difference between original and autofocused images is not huge, differences are
mainly bigger around objects that are farther away and are illuminated for
longer time.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar/parking2_map_comparison2.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/parking2_map_comparison2.jpg" width="1896" height="966" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;SAR image compared with Google maps satellite
    image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The generated SAR image matches pretty well with Google maps satellite image of
the same location. Google data is slightly older and since it was taken the
bottom building has been demolished. Cars are also obviously at different
places.&lt;/p&gt;
&lt;h2 id="second-scene"&gt;Second scene&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/park_camera.jpg" width="1600" height="900" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Photograph of the scene from the start of the
    track.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I also made measurements of a nearby park. There's a nice flat and smooth
sidewalk next to it that allows easy measuring in a straight long line.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar/road_sar4_autofocus.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/road_sar4_autofocus.png" width="537" height="819" border="2" style="width: 40%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Park image after autofocusing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At the bottom there are some big buildings that occlude anything behind them.
The reflection from them is large enough to saturate the receiver resulting in
some artifacts. The light poles, street signs and trees on the foreground are
very well focused, light poles very far away are spread a lot more. The problem
with at least some of the farther away light poles is that they are occluded by
other objects most of the time. The farthest light pole around 230 m is only
visible for around quarter of the measurement time but even with that amount of
observations it should still be sharper if the image was perfectly focused.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3-sar/road_sar4_foreground.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3-sar/road_sar4_foreground.png" width="376" height="819" border="2" style="width: 30%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Park image foreground objects. Visible image
    photograph above was taken from bottom left corner looking upwards.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The foreground objects are well focused. Above are some objects that are also
visible on the photograph above. The street curb is very reflective due to its
shape. Grass and asphalt surfaces have slightly different radar reflectivity and
the path to the zebra crossing is visible on the lower left corner.&lt;/p&gt;
&lt;p&gt;The biggest issue is the inaccurate position information. Accurate velocity
information should allow resampling the measured data to equal spacing and
autofocus should take care most of the remaining sideways movement error.&lt;/p&gt;
&lt;p&gt;Check out the code at &lt;a href="https://github.com/Ttl/fmcw3"&gt;GitHub&lt;/a&gt; or &lt;a href="https://hforsten.com/third-version-of-homemade-6-ghz-fmcw-radar.html"&gt;read how I made the radar
hardware&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Third version of homemade 6 GHz FMCW radar</title><link href="https://hforsten.com/third-version-of-homemade-6-ghz-fmcw-radar.html" rel="alternate"></link><published>2017-09-28T00:00:00+03:00</published><updated>2017-09-28T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2017-09-28:/third-version-of-homemade-6-ghz-fmcw-radar.html</id><summary type="html">&lt;p&gt;New and improved version of the 6 GHz FMCW radar with two receiver channels.&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/finished.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finished radar boards without antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://hforsten.com/6-ghz-frequency-modulated-radar.html"&gt;Previously&lt;/a&gt; I made
a simple frequency-modulated continuous-wave (FMCW) radar that was able to
detect distance of a human sized object to 100 m. It worked, but as it was made
with minimal budget and there was a lot of room for improvement.&lt;/p&gt;
&lt;h1 id="fmcw-radar-working-principle"&gt;FMCW radar working principle&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fmcw_block.svg" width="650" height="412" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FMCW radar block diagram&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If you have read my previous articles you should know how FMCW radar works, but
for completeness sake short explanation is given below:&lt;/p&gt;
&lt;p&gt;Frequency Modulated Continuous Wave (FMCW) radar works by transmitting a chirp
which frequency changes linearly with time. This chirp is then radiated with the
antenna, reflected from the target and is received by the receiving antenna.  On
the reception side the received signal that was delayed and undelayed copy of
the transmitted chirp are mixed (multiplied) together. The output of the mixer
are two sine waves that have frequencies of sum and difference of the waveforms.
The frequencies of the received signals are almost the same and the sum waveform
has frequency of about two times of the original signal and is filtered out, but
the difference waveform has frequency in kHz to few MHz range. The difference
frequency is dependent on the delay of the received reflection signal making it
possible to determine the delay of the reflected signal. The electromagnetic
waves travel at speed of light which allows converting the delay to distance
accurately. When there are several targets the output signal is sum of different
frequencies and the distances to the targets can be recovered efficiently with
Fourier transform.&lt;/p&gt;
&lt;h1 id="issues-with-the-previous-version"&gt;Issues with the previous version&lt;/h1&gt;
&lt;p&gt;The biggest issue with the previous version was noisy power supplies causing
spurs in the received signal, ADC sampling clock not being locked to PLL
reference clock and microcontroller being too slow.&lt;/p&gt;
&lt;p&gt;To save money I had chosen to use two buck converters to power all the digital
and analog components. Even though I chose the switching frequency of the
converters to be above the IF frequency, there ended up being some spurs also at
lower frequencies. Adding capacitance and swapping the inductors for better
shielded ones helped the problem but didn't completely solve it. The proper fix
is to add linear regulator after the buck converters to clean up the switching noise.&lt;/p&gt;
&lt;p&gt;The problem with separate clocks for ADC and PLL caused the sampling interval of
the ADC to vary between separate sweeps. The maximum offset was only +- half
a sample and it wasn't a big issue when only doing range measurements. However
when trying to measure heartbeat and respiratory rate the added phase noise from
the varying sampling interval caused some noise in the measurements. Noisy power
supplies also degraded the phase noise performance. The fix for this one is
pretty easy: Use the same clock for both ADC and PLL.&lt;/p&gt;
&lt;p&gt;The microcontroller I was using was pretty powerful compared to the cheapest
8-bit microcontrollers, but it was hopelessly underpowered to do any kind of
digital signal processing. All of processing power and internal memory was spent
in getting the samples from ADC to PC through USB fast enough. Sampling speed of
the ADC was 10 MHz and because of lack of DSP resources every sample needed to
be transferred to PC. Even the samples between the sweeps that didn't have any
useful data were transferred to PC only to be discarded later. With more
resources it would have been possible to do digital filtering, lower the
sampling speed and only transfer the useful samples.&lt;/p&gt;
&lt;p&gt;Microcontrollers don't really have enough processing power to do any kind of
non-trivial filtering. For example a 100 tap FIR filter requires 100
multiplications and 100 additions for every sample. Even if the multiplication
could be done in one clock cycle the maximum sample rate is too low to be
useful. The microcontroller should also have enough processing power to transfer
ADC samples and do communication and other logic. FPGA works much better for DSP
as the operations can be done in parallel.&lt;/p&gt;
&lt;p&gt;The last improvement is adding a second receiver antenna. When there are more
than one receiver antenna it is possible to determine the direction of arrival
of the received signal. If the return signal arrives in angle it is received at
different antennas at different times. The different arrival time causes a phase
shift in the IF signal which can be used to determine the direction.&lt;/p&gt;
&lt;h1 id="link-budget"&gt;Link budget&lt;/h1&gt;
&lt;p&gt;Since I'm not changing the transmitter, the maximum range performance should be
pretty identical to the last version when using the same horn antennas. When
smaller patch antennas are used as receiver antennas the range is going to
decrease because the gain and efficiency of the patch antennas is smaller.&lt;/p&gt;
&lt;p&gt;The maximum range to detect a target can be calculated using the radar equation:&lt;/p&gt;
&lt;div class="math"&gt;$$ R_{\text{max}} = \sqrt[4]{\frac{P_t G^2 \lambda^2 \sigma}{P_{\text{min}}(4
\pi)^3}} $$&lt;/div&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(P_t\)&lt;/span&gt; is the transmitted power, &lt;span class="math"&gt;\(G\)&lt;/span&gt; is gain of the antennas, &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is
wavelength, &lt;span class="math"&gt;\(\sigma\)&lt;/span&gt; is the radar cross section of the target and
&lt;span class="math"&gt;\(P_{\text{min}}\)&lt;/span&gt; is the minimum detectable signal power.&lt;/p&gt;
&lt;p&gt;Most of the values are easy to determine, but the minimum detectable power can
be tricky. Clearly the minimum power that can be detected depends on the noise
level of the receiver. Noise at the receiver input is RF thermal noise, which
can be calculated with &lt;span class="math"&gt;\(kTBF\)&lt;/span&gt;, where &lt;span class="math"&gt;\(k\)&lt;/span&gt; is the Boltzmann constant, &lt;span class="math"&gt;\(T\)&lt;/span&gt; is noise
temperature, &lt;span class="math"&gt;\(B\)&lt;/span&gt; is noise bandwidth and &lt;span class="math"&gt;\(F\)&lt;/span&gt; is the receiver noise figure.&lt;/p&gt;
&lt;p&gt;Determining the correct noise bandwidth to be used is critical and easy to get
wrong. Noise bandwidth is clearly smaller than the sweep bandwidth since the IF
filter filters most of the RF noise out. IF filter bandwidth determines the
noise power that makes it to the ADC, but that is not the noise bandwidth to be
used since some of the noise can be clearly filtered out after taking the FFT.
When FFT is taken of the IF signal the total RF noise is split equally to each
FFT bin. Bandwidth of one FFT bin is the noise bandwidth of the receiver and the
bandwidth that should be used in the calculation. Bandwidth of one FFT bin
depends only of the lenght of the FFT. With FMCW radar FFT length equals length
of one sweep, &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; and bandwidth of one FFT bin is &lt;span class="math"&gt;\(1/t_s\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;With sweep length of 1 ms and noise figure of 6 dB the RF noise floor at the
receiver input is -138 dBm. For the signal to be detectable it should be some
amount more powerful than the noise. Using 20 dB for the required
signal-to-noise ratio gives minimum detectable signal of -118 dBm.&lt;/p&gt;
&lt;table style="width:100%"&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(P_t\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Transmitted power&lt;/td&gt;
&lt;td&gt;15 dBm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Gain of antennas&lt;/td&gt;
&lt;td&gt;13 dBm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\lambda\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Wavelength&lt;/td&gt;
&lt;td&gt;5.2 cm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(\sigma\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Target radar cross-section&lt;/td&gt;
&lt;td&gt;1 m²&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Plugging the values above to the radar equation the maximum range that radar can
see a human sized 1 m² cross-section target can be solved to be 320 m. The
target size does have a big effect, for example a bird sized target with
cross-section of 0.01 m² can be detected only below 102 m. The transmitted power
in the table is somewhat pessimistic to include losses from antennas, cables and
PCB.&lt;/p&gt;
&lt;p&gt;This would be correct for radar looking at air, but when the radar antennas are
looking at a low angle there is going to be problem with clutter that can
decrease the SNR. When angle of the antennas is low and the radar is on a flat
ground there is going to be returns from the ground from basically every range.
Returns from other objects such as trees, buildings and other objects can also
overlap the target signal decreasing the signal to noise ratio.&lt;/p&gt;
&lt;h2 id="receiver-design"&gt;Receiver design&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/receiver_block_labels.svg" width="172" height="107" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simplified radar block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The previous range calculation assumed that the receiver has sufficient dynamic
range to detect the RF noise floor, linearity is good enough. Care should be
taken especially with the dynamic range of the receiver since reflections from
targets near the antennas are going to have much larger power than the noise
floor and the receiver should be able to resolve both returns at the same time.&lt;/p&gt;
&lt;p&gt;Noise figure of the receiver should be kept as low as possible which can be done
by choosing a low noise amplifier with low noise figure and high gain. Higher
the gain of the LNA is the smaller the noise contributions of the following
stages. A drawback of having high gain is that the input compression point is
lowered as either the LNA output stage, mixer or IF amplifier starts to saturate
earlier due to higher output power of the high gain LNA. If the input
compression point is too low receiver can saturate just from the leakage power
between the transmission and receiver antennas. In saturation the receiver
doesn't work linearly anymore and many harmonics are generated that are detected
as false targets. It's very important to avoid saturating the receiver and this
is one of the major issues when using single antenna that is shared between
transmitter and receiver. Radar with a low input compression point has higher
minimum detection distance below which the returns from the targets saturate the
receiver. Choosing the LNA gain ends up being a compromise between noise and
input compression point.&lt;/p&gt;
&lt;p&gt;It's important to also take into account the noise figure of the low frequency
amplifier driving the ADC and noise floor of the ADC. Op-amps typically can't be
analyzed using noise figure because ports aren't matched to 50 ohms. The right
way is to convert the RF noise to V/&lt;span class="math"&gt;\(\sqrt{\text{Hz}}\)&lt;/span&gt;. However comparing the
input noise voltage density of the op-amp to noise voltage density of 50 ohm
resistor gives a close enough value for hand calculations. Usually op-amps have
quite high noise figure due to the resistors used to set the gain. NF of ten to
twenty dB is usually a good guess.&lt;/p&gt;
&lt;p&gt;ADC has quantization noise and input noise that results in limited dynamic
range. Datasheet of the ADC lists the SNR which is calculated by measuring
a maximum amplitude sine wave, taking FFT and then dividing the signal power by
noise power in all other FFT bins. The dynamic range can be increased with
oversampling and filtering. The noise floor after taking the FFT is considerably
better than the SNR number listed in the datasheet because noise at different
frequencies can be separated.&lt;/p&gt;
&lt;p&gt;The dynamic range of the ADC with the FFT processing gain can be calculated with:&lt;/p&gt;
&lt;div class="math"&gt;$$\text{SNR} + 10 \log_{10}(f_s t_s)$$&lt;/div&gt;
&lt;p&gt;where SNR is the SNR of the ADC listed in the datasheet, &lt;span class="math"&gt;\(f_s\)&lt;/span&gt; is the sampling
frequency and &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is sweep length. LTC2292 ADC that I used has listed SNR of
71.3 dB and sampling frequency is 40 MHz. This gives ADC dynamic range of 117
dB with 1 ms sweep.&lt;/p&gt;
&lt;p&gt;To make sure that the ADC doesn't limit the system performance the RF noise
floor after the ADC should be above the ADC noise floor. The input referred RF
noise power at the LNA input is &lt;span class="math"&gt;\(kTBF = kTF/t_s\)&lt;/span&gt;. This is amplified by the LNA and mixer
by &lt;span class="math"&gt;\(G_{\text{LNA}}G_{\text{mixer}}\)&lt;/span&gt;. After the mixer the RMS output voltage can
be calculated as &lt;span class="math"&gt;\(V = \sqrt{Z_{\text{load}} P}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(Z_{\text{load}}\)&lt;/span&gt; is the
mixer output impedance (200 Ω on the mixer I'm using). This voltage is amplified
by the IF amplifier by &lt;span class="math"&gt;\(G_{IF}\)&lt;/span&gt;. Next the RMS voltage should be converted to
peak-to-peak value by multiplying by &lt;span class="math"&gt;\(2\sqrt{2}\)&lt;/span&gt; and then referenced to maximum
ADC voltage range. Finally by taking &lt;span class="math"&gt;\(20\log_{10}\)&lt;/span&gt; we will get the noise floor in
dBFs units (relative to ADC maximum input signal). Putting it all together in one equation gives:&lt;/p&gt;
&lt;div class="math"&gt;$$ P_{\text{noise}} = 20\log_{10}( 2\sqrt{2}G_{\text{IF}}\sqrt{G_{\text{LNA}} G_{\text{mixer}} kTF Z_{\text{load}}/t_s }/V_{\text{ref}} )$$&lt;/div&gt;
&lt;table style="width:100%"&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(V_\text{ref}\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;ADC maximum peak-to-peak voltage&lt;/td&gt;
&lt;td&gt;2 V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G_\text{IF}\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;IF amplifier gain&lt;/td&gt;
&lt;td&gt;20 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G_\text{LNA}\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;LNA gain&lt;/td&gt;
&lt;td&gt;20 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(G_\text{mixer}\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Mixer power gain&lt;/td&gt;
&lt;td&gt;0 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(k\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Boltzmann constant&lt;/td&gt;
&lt;td&gt;1.38064852×10&lt;sup&gt;-23&lt;/sup&gt; m&lt;sup&gt;2&lt;/sup&gt; kg s&lt;sup&gt;-2&lt;/sup&gt; K&lt;sup&gt;-1&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(T\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Noise temperature&lt;/td&gt;
&lt;td&gt;290 K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(Z_\text{load}\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Mixer output impedance&lt;/td&gt;
&lt;td&gt;200 Ω&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(F\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Receiver noise figure&lt;/td&gt;
&lt;td&gt;6 dB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class="math"&gt;\(t_s\)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Sweep length&lt;/td&gt;
&lt;td&gt;1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Plugging the above values into the equation gives the noise floor as -102 dBFs.
This is 15 dB over the ADC noise floor and the receiver performance is not
limited by the ADC.&lt;/p&gt;
&lt;h1 id="fpga-digital-signal-processing"&gt;FPGA Digital signal processing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fpga_dsp.svg" width="259" height="80" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of FPGA DSP.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;ADC sampling speed is fixed at 40 MHz, but this is much more than needed. IF
filter bandwidth is only 2 MHz so from about 2 MHz to 40 MHz there is only noise
which should be filtered out. The reason for such high sampling speed is
avoiding aliasing and SNR increase from oversampling. Sampling rate needs to be
dropped to make the amount of data manageable. Without decimating the data rate
would be 120 MBps, which exceeds the USB 2.0 datarate and is very challenging to
store anywhere.  Dropping the sample rate to 2 MHz and increasing bit resolution
to 16 bits results in much more manageable 8 MBps data rate. When samples
between the sweeps are dropped the data rate further reduces by ratio of sweep
length to time between the sweeps.&lt;/p&gt;
&lt;p&gt;Data rate reduction could be obtained by reducing the ADC sampling speed or
dropping some of the ADC samples, but these methods don't get the SNR gain from
the oversampling. Averaging of the samples could be used, but this corresponds
to filtering with FIR filter with constant 1 coefficients and is not spectrally
very nice and will cause aliasing. The correct way is to use FIR filters to
filter out the frequencies that would alias and then decimating (dropping
samples). This method will give the SNR improvement from oversampling and avoids
aliasing of higher frequencies.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fir20.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Frequency response of the first FIR filter.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Big advantage of digital filtering compared to the analog filtering is that it
can be made as accurate as needed, it's noiseless and doesn't require any
additional hardware. Above is the frequency response of the first 120 tap FIR
filter in the block diagram. This high order would be infeasible to make using
analog components but digitally implemented it's not a problem. By combining
the decimation and filtering the implementation can be made much more
efficiently compared to the naive implementation. High order filter has
advantage that transition band can be made very small thus resulting in less
wasted spectrum.&lt;/p&gt;
&lt;p&gt;The two channel 120 tap FIR filter needs 10 DSP slices when decimating at 20.
The other filters are implemented as one filter with reconfigurable coefficients
and only require 2 DSP slices. All the filters need a total of 12 DSP slices out
of 45 available DSP slices on the FPGA I'm using. Amount of DSP slices required
could be dropped even further by increasing the clock speed, but there is no
need for it since there is still plenty of room left.&lt;/p&gt;
&lt;h1 id="beamforming"&gt;Beamforming&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/phased_array.svg" width="72" height="106" style="width: 20%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Antenna array.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Two antennas receiving a reflection from the same target will have slightly
different phase shift on the received signal due to the small angle dependent
difference in the distance to the target. The path length difference between the
antennas is &lt;span class="math"&gt;\(d\sin\theta\)&lt;/span&gt;, where &lt;span class="math"&gt;\(d\)&lt;/span&gt; is the distance between the antennas and
&lt;span class="math"&gt;\(\theta\)&lt;/span&gt; is angle of the target. Phase shift of the received signal is
&lt;span class="math"&gt;\(d\sin\theta/\lambda\)&lt;/span&gt; where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is the RF wavelength.&lt;/p&gt;
&lt;p&gt;If the antenna outputs are just summed together the targets straight ahead will
sum in the same phase while the targets from angle where &lt;span class="math"&gt;\(d\sin\theta/\lambda= -1\)&lt;/span&gt; 
will be summed in the opposite phases and will cancel each other. Angles between
these extremes will experience different degrees of interference.&lt;/p&gt;
&lt;p&gt;Beam forming can be done by phase shifting the signals before summing them. When
the phase shift &lt;span class="math"&gt;\(\phi\)&lt;/span&gt; is chosen such that &lt;span class="math"&gt;\(\phi = -d\sin\theta\)&lt;/span&gt; the peak of the
antenna pattern is shifted to angle &lt;span class="math"&gt;\(\theta\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To generate multiple beams for generating an image the simple way would be to
repeat the summing of signals for different phase shifts. A more efficient way
to synthesize multiple beam is possible using Fourier transform. For radar
signals we want to first take FFT of the each ADC signal to move to frequency
domain. Each bin of the FFT corresponding to different distances has amplitude
and phase of the echo signal. Next all the Fourier transformed signals are placed in
array and a second FFT is taken across the antenna dimension. This is
equivalent to summing the signals with different phase shifts. Zero padding
the array before taking the FFT is necessary to get some resolution to the
output. Otherwise we would only get same number of beams as there are antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/af_2.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Array factor with two antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above plot is the array factor of the two antenna array. This is the beam
pattern assuming that the antenna patterns is omnidirectional. The pattern of
the real array can be calculated by multiplying the array factor with the
radiation pattern of single antenna.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/af_8_hamming.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Array factor with eight antennas. Hamming window
    weighthing.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With eight antennas the angle resolution would be much better and of course even
more antennas would give even better angle resolution. Eight receiver channels would
increase the cost and power consumption by quite a bit, but it would be possible
to have eight antennas that are connected to two receiver channels with
a switch. All antennas could then be sampled in four separate sweeps.&lt;/p&gt;
&lt;p&gt;Because the array beam width with only two antennas is so poor it's hard to see
the angle information on the plots. The angle visualization can be improved by
multiplying the measurement with a windowing function that is shifted to the
peak location. If there is only one target in the range bin it works well, but
if there are multiple targets then the peak is somewhere between them and the
windowing makes it look like that there is only one target. There are issues
especially with walls and other very wide targets that are reduced to one target
by the windowing.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/angle_window.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Angle windowing function.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="designing-pcb"&gt;Designing PCB&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw3/fmcw3.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fmcw3_schematic.png" width="983" height="684" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for PDF.&lt;/p&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;The PCB was designed for the same OSH park 4 layer process that I have used
many times with my earlier projects. It is the only cheap PCB manufacturing
process with better material than FR-4. The material is FR408 which is like
FR-4, but losses are lower and the relative permittivity is more tightly
controlled.&lt;/p&gt;
&lt;p&gt;Compared to the previous version the PCB is much bigger. Especially the FPGA
requires a huge amount of space. Power supply is more complicated because FPGA
requires several voltages and there are now several linear regulators on board
to clean up the switching noise. Of course the second receiver channel also
takes lot of space.&lt;/p&gt;
&lt;p&gt;Some things however are simpler than before. Previously the IF filter had
several op-amps and had variable gain. Now there is only one amplification
stage, because I actually calculated that multiple stages are not needed.
Variable gain has also been removed and instead ADC dynamic range is maximized
so that there is enough dynamic range to not need it.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fmcw3_layout.png" width="1345" height="588" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Layout in KiCad.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I placed an SD-card holder on the PCB for use without a PC but I haven't
actually programmed the FPGA to use it yet. There are also two headers that are
connected to FPGA IO pins. I don't have any definite use for them, but some
examples I had in mind are connecting an inertial measurement unit for SAR
motion correction, connecting control signals for use without PC and controlling
external antenna switches.&lt;/p&gt;
&lt;h1 id="soldering"&gt;Soldering&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/stencil2.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Applying solder paste with stencil.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Although I tried to keep the PCB simple it still ended up having about 350
components and many of them are in hard to solder packages.&lt;/p&gt;
&lt;p&gt;I start with the more complicated top side. I used to solder the bottom side
first with some of the previous projects, but it's easier to apply the solder
paste on the top side when there aren't any components on the bottom side. The
solder paste is applied using a stencil from &lt;a href="https://www.oshstencils.com/"&gt;OSH
stencils&lt;/a&gt;. I bought a cheaper Polyimide stencil
this time instead of more expensive stainless steel one and I have to say that
the stainless steel stencil works much better.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/paste.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste on PCB. &lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/components.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Components placed on paste.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The empty QFN footprint on the bottom right is for a second IF amplifier stage.
I added it just in case, but didn't end up needing it.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/ready.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;After soldering and adding connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Like all my previous projects I soldered the PCB myself using my &lt;a href="http://hforsten.com/toaster-oven-reflow-controller.html"&gt;reflow
oven&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/qfn_sideways.jpg" width="640" height="480" style="width: 30%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Quality wasn't perfect this time and I had to fix
    some components by hand later.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When there are so many components there are bound to be some that don't get
soldered correctly. One of the components was soldered incorrectly probably due
to uneven solder paste placement. I fixed this component by hand using hot air.&lt;/p&gt;
&lt;h1 id="antennas"&gt;Antennas&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/horns.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;The old horn antennas I made previously.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I have the &lt;a href="http://hforsten.com/horn-antenna-for-radar.html"&gt;horn antennas that I made
previously&lt;/a&gt;, but there are only
two of them and they are not very suitable as receiver antennas due to their
large size. Because the antennas are large they need to be placed far away from
each other. When distance between the receiver antennas is more than
&lt;span class="math"&gt;\(\lambda/2\)&lt;/span&gt; the phase shift of the received signals exceeds 180 degrees for some
angles. The resulting IF signal is identical to some smaller angle causing
aliasing.&lt;/p&gt;
&lt;h2 id="patch-antenna"&gt;Patch antenna&lt;/h2&gt;
&lt;p&gt;To avoid angle aliasing I made new patch antennas that are placed &lt;span class="math"&gt;\(\lambda/2\)&lt;/span&gt;
away from each other. Due to budget constraints, the PCB material is ordinary
FR-4. It has high losses at RF frequencies and causes the antenna to have low
efficiency. Relative permittivity of the FR-4 is not very well controlled and
varies between the manufacturers and different manufacturing runs. Change in the
relative permittivity results in shifting of the antenna operating frequency.
Unfortunately any better materials are much more expensive. FR-4 PCBs can be had
for less than 10 € from China, while the same PCBs with better material would cost
at least 100 €.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/antennas.jpg" width="1600" height="1075" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;2x5 patch array (left) and patch fed horn (right). The thin copper sheet horn has taken some damage.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The antenna consists of five patch antennas in a line with the center antenna
being fed from SMA connector through a via underneath the board. Compared to
a single element patch antenna the five element array radiates much less in the
direction of the array, while in the other direction beam width is very similar
to one element patch antenna. The antenna is meant to be mounted such that in
the horizontal plane there is a wide beam width for good angle coverage and
narrow beam width in the vertical direction to minimize the ground returns.&lt;/p&gt;
&lt;p&gt;I also made a horn antenna fed by a patch element. Compared to the previous
waveguide fed horn antennas the operating bandwidth is much smaller and the
efficiency is lower. The advantage is that the construction is much simpler and
the antenna is much easier to mount because of its flat bottom side.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/patch_sparam.png" width="826" height="450" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured patch array and patch fed horn S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The measured S-parameters of the antennas look good except that the operating frequency
has shifted to lower frequencies. The design frequency was 5 GHz ISM band (5.725
- 5.875 GHz), but the realized operating frequency of the manufactured antennas is about
150 MHz lower. The reason for the frequency shift is different relative
permittivity of the substrate material in simulations and manufactured antennas.
I made one patch antenna on FR-4 before and I measured that it had relative
permittivity of 4.20 which I used for simulating the new antennas. However based
on the measurements FR-4 on the new antennas has relative permittivity of about
4.58.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;h2 id="noise-floor"&gt;Noise floor&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/fmcw3_attenuators.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Attenuators on transmitter and receivers.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Earlier I calculated that the RF noise floor should be about -102 dBFs. The
noise floor can be measured easily by attaching attenuators to transmitter and
receiver. ADC noise floor is harder to measure because I can't disable or
disconnect the receivers. I could desolder some components or add a short
circuit, but I'm not going to bother.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/noise_floor.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the measured receiver power spectrum in dBFs (relative to the maximum
ADC input voltage). Calculation was -102 dBFs and the measurement is -99 dBFs on
the first channel and -100 dBFs on the second one. Measurement agrees very well
with the calculation considering that the gains of the components can vary few
dB.&lt;/p&gt;
&lt;p&gt;At low frequencies/distances there is some signals visible. The lowest bin is DC
and is probably caused by the IF amplifier and ADC DC offset and FFT windowing. At
little bit higher frequencies there are some genuine targets probably from
ceiling and walls. If I wave my hand over the radar I can see the targets
moving. It seems that the microstrip lines on the board radiate enough that
radar can detect some nearby targets without antennas. A similar issue could be
seen with my &lt;a href="http://hforsten.com/video/vna2/vna_leakage_crop2.m4v"&gt;homemade VNA&lt;/a&gt;.
Unlike with the VNA, isolation is not too important with FMCW radar because it
will only cause targets at very low distances.&lt;/p&gt;
&lt;h2 id="park"&gt;Park&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/park.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Image of the scene.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/park_time_domain.png" width="1032" height="562" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time-domain plot of the received signals.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/park_patch_range.png" width="1032" height="562" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time-range plot captured with the patch antennas. Previous sweep is subtracted from the current one to remove fixed targets.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a time-range plot of me riding a bicycle in a circle in front of the
radar. The circle part of course is not visible on this plot as angle of the
target is not shown.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls&gt;
&lt;source src="https://hforsten.com/video/fmcw3/park_patch.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/fmcw3/park_patch.ogg" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;With the added angle dimension visualizing the data now requires a video. Above
is a video of the same measurement that was plotted above. This time fixed
targets were not removed and lightning posts and trees are visible as fixed
targets. Without the angle information the fixed targets would have just
cluttered the plot but now the video looks quite clear even without any
additional post processing.&lt;/p&gt;
&lt;p&gt;You can tell from the video that I start and end the video at left side of the
radar (positive cross range). Walking in front of the radar at the beginning
shadows the background targets and also causes some clipping at IF due to the
strong reflections.&lt;/p&gt;
&lt;p&gt;The angle resolution is not that good but this is expected because there are
only two receiver antennas. Windowing used for visualizing the angle of target
causes the wide bushes at the background seem to oscillate wildly because of
small changes in the received reflections. Because returns at different angles
are nearly equal small noise can flip the peak to different location.&lt;/p&gt;
&lt;p&gt;In the video it's clear that magnitude of reflections around the center is much
bigger than at the edges. This is mostly because of the narrow beam width of the
transmitting horn antenna. Receiver antennas will also have less gain at high
angles further dropping received reflection magnitude.&lt;/p&gt;
&lt;p&gt;The lamp posts in the picture are also clearly visible on the video and don't
move around as much as the bush. When I go near them in the same range bin the
location seems to jump around. The problem is that when I am at the same range
as them the angle windowing still assumes that there is only one target and puts
the target center somewhere between the lamp post and me.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls&gt;
&lt;source src="https://hforsten.com/video/fmcw3/park_patch_mti.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/fmcw3/park_patch_mti.ogg" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Above is the same video as previously, but with the previous sweep subtracted
from the current one to remove fixed targets. Subtracting the fixed targets
highlights some of the problems with the angle visualization even more.&lt;/p&gt;
&lt;h2 id="traffic-speed"&gt;Traffic speed&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/bridge.jpg" width="1600" height="900" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Image of the scene.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There was a nice bridge near me that went over a busy road so I decided to test
how well the radar can see cars. Cars are have pretty big radar cross section
and should be visible very far.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw3/bridge_range.png" width="1032" height="562" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time-range plot of traffic with fixed targets removed.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The above time-range plot is recorded with a single horn antenna so there is no angle
information. Clutter is removed by subtracting the previous sweep from the
current one leaving just the moving objects. Without the clutter reduction there
are so many targets that it's &lt;a href="https://hforsten.com/img/fmcw3/bridge_range_clutter.png"&gt;hard to see anything&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The range was previously cut off because there wasn't any far away targets, but
this time I'm plotting the full range. Decimate by two filter was active during
this capture to limit the data rate and it limits the range to 250 m with 300
MHz sweep. Without decimating the range would be 500 m, but there isn't much
point in capturing that since the line of sight from the bridge is less than
that. SNR is still good at 200 m so it should be able to see much farther.
At far ranges the drop in SNR is caused by the decimation filter filtering also
little bit below the Nyquist frequency to avoid aliasing.&lt;/p&gt;
&lt;p&gt;From the plot it can be seen that during the capture there five cars going away
from the radar and one car and one bicyclist coming towards. Car speeds can be
calculated from the graph by simply dividing the traveled distance by time it
took to travel it. There are better ways for measuring the speed, but I'm too lazy
to implement them. The solved speeds are around 60 to 70 km/h. The speed limit
is 80 km/h so all of them seem to be driving little below the speed limit, but
I guess that makes sense as there are traffic lights just after me.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The changes in the newest version fixed the problems in the previous version and
the performance of the radar seems very good. Two receiver channels allows
determining the angle of the target, but the angle resolution with only two
receiver antennas is not too good. The angle resolution is limited by the
physics and getting more resolution would need more antennas. Multiple switched
receiver antennas are on my todo list.&lt;/p&gt;
&lt;p&gt;If you are interested in taking a more detailed look, all hardware design files,
firmware and processing software are available at
&lt;a href="https://github.com/Ttl/fmcw3"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>TRL measurements with homemade VNA and open source software</title><link href="https://hforsten.com/trl-measurements-with-homemade-vna-and-open-source-software.html" rel="alternate"></link><published>2017-05-08T00:00:00+03:00</published><updated>2017-05-08T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2017-05-08:/trl-measurements-with-homemade-vna-and-open-source-software.html</id><summary type="html">&lt;p&gt;Measuring transmission line parameters of 50 ohm microstrip on OSH park 4 layer process and S-parameters of 1 nF SMD capacitor.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I have been using the OSH Park's 4 layer process a lot on my own projects. It
has FR408 substrate that has better controlled permittivity and lower losses
than ordinary FR-4 that other low cost PCB manufacturers use. In my opinion
currently it is the best low cost process for making RF PCBs.&lt;/p&gt;
&lt;p&gt;My previous boards have worked pretty well, but I decided to make a test board
that I can use to characterize the process better.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_board_labels.jpg" width="1600" height="1198" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Test board for TRL calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture is the test board that I made. It has two 50 ohm microstrip
lines of different length, one open microstrip line, one microstrip line
terminated with 50 ohm resistor and line with 0402 footprint that I populated
with a 1 nF capacitor. I'm using this same type of capacitor as a general DC
blocking capacitor in my VNA so I'm interested in finding out how it performs at
high frequencies.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/board_block.png" width="1598" height="1700" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of the board.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using this board and TRL calibration, effect of the connectors can be calibrated
out resulting in accurate measurements of the transmission line parameters.
Accurate measurement of the capacitor is also obtained, which is useful in
a system design. Other 0402 footprint passives can also be populated in place of
capacitor to measure them at high frequencies.&lt;/p&gt;
&lt;p&gt;If you are not familiar with TRL calibration it is a different method for
calibrating a VNA. The normal SOLT calibration uses Short, Open, Load and Thru
calibration standard measurements and sets the measurement reference plane to
the end of the coaxial connector. Calibration standards need to be known
accurately or otherwise there will be errors in the measurements.&lt;/p&gt;
&lt;p&gt;TRL calibration uses at least two lines of different lengths and one reflect. No
short, open or load standards are needed and unlike standards used with SOLT
calibration, lines or reflect don't need to be fully known. Line lengths need to
be known, but their electrical behaviour doesn't need to be known accurately.
Phase of the reflection coefficient of reflect standard needs to be known within
+- 90 degree accuracy. Real reflection coefficient and propagation constant of
the transmission lines will be solved during the calibration.&lt;/p&gt;
&lt;p&gt;After the TRL calibration reference plane is placed after the SMA connectors on
the board. The exact reference plane location is in the middle of the shortest
line. For example the capacitor test structure on the board would include the
SMA connectors if measured using the SOLT calibration, but with TRL calibration
the reference planes are right next to the capacitor pads.&lt;/p&gt;
&lt;p&gt;There are few limitations on the TRL calibration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Length difference
 between the lines should ideally be a quarter wavelength. If the measured
 phase is a multiple of 180 degrees, the resulting system of equation is singular
 and calibration fails. This results in a limited bandwidth for the calibration
 but it can be extended by using multiple lines with different lengths.&lt;/li&gt;
&lt;li&gt;Reference impedance of the calibration is the characteristic impedance of the
   transmission lines and not 50 or 75 ohms as is usual. The measured
   S-parameters are referenced to this possibly frequency dependent and complex
   impedance. If the characteristic impedance is not known accurately, the
   measured results might not be very useful.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="propagation-constant"&gt;Propagation constant&lt;/h1&gt;
&lt;p&gt;Propagation constant of the transmission line characterizes how phase and
amplitude of a wave traveling in a transmission line varies as a function of
distance. During the TRL calibration propagation constant is solved and it is
one of the outputs of the calibration. Propagation constant varies significantly
as a function of frequency and thus can't be really called a constant but that
is the established term.&lt;/p&gt;
&lt;p&gt;S21 coefficient of a transmission line with length &lt;span class="math"&gt;\(l\)&lt;/span&gt; can be calculated using
propagation constant as: &lt;span class="math"&gt;\(S_{21} = e^{-\gamma l}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(\gamma = \alpha
+ j \beta\)&lt;/span&gt; is the propagation constant.&lt;/p&gt;
&lt;p&gt;Real part &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt; of the propagation constant causes loss and imaginary part
&lt;span class="math"&gt;\(\beta\)&lt;/span&gt; causes phase shift. Units of &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt; are in Nepers/meter, which can be
converter to more practical units of dB/m with equation &lt;span class="math"&gt;\(20\log_{10}(e)\alpha\)&lt;/span&gt;.
&lt;span class="math"&gt;\(\beta\)&lt;/span&gt; includes the frequency dependence and looks like a very steep straight
line when plotted as is. It can be normalized by dividing by &lt;span class="math"&gt;\(2\pi f\)&lt;/span&gt;, but
I think that effective permittivity of the transmission line is a more
useful quantity.&lt;/p&gt;
&lt;p&gt;Effective permittivity of the transmission line is related to the speed of
electromagnetic wave on the transmission line via equation: &lt;span class="math"&gt;\(v
= \frac{c}{\sqrt{\epsilon_{\text{eff}}}}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(c\)&lt;/span&gt; is the speed of light.
Wavelength of the electromagnetic wave in a transmission line can be calculated
using effective permittivity with: &lt;span class="math"&gt;\(\lambda = \frac{v}{f}
= \frac{c}{f\sqrt{\epsilon_{\text{eff}}}}\)&lt;/span&gt;. Since many RF circuits need transmission
lines with accurately known length in wavelengths, knowing the effective
permittivity accurately is very useful.&lt;/p&gt;
&lt;p&gt;Effective permittivity of the transmission line depends on the permittivities of
the surrounding materials. With stripline or coaxial cable where the signal
conductor is completely surrounded by one material the effective permittivity of
the transmission line is equal to the permittivity of the surrounding material.
If the TRL calibration would be done for a stripline, the effective permittivity
calculated from the solved propagation constant would directly give the
permittivity of the material. With microstrip some of the field travels also
on the air and the effective permittivity is somewhere between permittivities of
substrate material and air. The exact value depends on the line geometry.&lt;/p&gt;
&lt;p&gt;Effective permittivity can be calculated from the propagation constant with:&lt;/p&gt;
&lt;div class="math"&gt;$$ \epsilon_{\text{eff}} = - \left(\frac{\gamma}{2\pi f/c}\right)^2 $$&lt;/div&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/measurements.jpg" width="1151" height="1200" style="width: 30%; height: auto;"/&gt;
&lt;/div&gt;

&lt;p&gt;All of the following measurements are done using the second version of my
&lt;a href="http://hforsten.com/improved-homemade-vna.html"&gt;homemade VNA&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="unknown-thru"&gt;Unknown thru&lt;/h2&gt;
&lt;p&gt;Before doing the TRL calibration let's first do a regular calibration to the SMA
connectors to see what the S-parameters look like with the SMA connectors.&lt;/p&gt;
&lt;p&gt;I'm using &lt;a href="http://scikit-rf-web.readthedocs.io/"&gt;scikit-rf&lt;/a&gt; Python library for
doing the calibration. In the library there are several possible calibration
methods to choose from. There is ordinary
&lt;a href="http://scikit-rf.readthedocs.io/en/latest/api/calibration/generated/skrf.calibration.calibration.SOLT.html"&gt;SOLT&lt;/a&gt;
that most of the commercial VNAs have builtin, but also several more advanced
calibration routines. This time I'm using
&lt;a href="http://scikit-rf.readthedocs.io/en/latest/api/calibration/generated/skrf.calibration.calibration.UnknownThru.html"&gt;UnknownThru&lt;/a&gt;
calibration, because I know the short, open and match standards I'm using
well because I have measured them with a commercial VNA, but I don't have a
measurement of the through standard. Unknown thru calibration doesn't requires
knowledge of the thru standard S-parameters so it is a good calibration to use
in this case.&lt;/p&gt;
&lt;p&gt;Below is the code for doing UnknownThru calibration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;skrf&lt;/span&gt;
&lt;span class="c1"&gt;#Use skrf plot style&lt;/span&gt;
&lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stylely&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;#Load the measured uncalibrated S-parameter files&lt;/span&gt;
&lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;load_load.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;oo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;open_open.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;short_load.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;load_short.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;through&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;through.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sw_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sw_terms.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;switch_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sw_terms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sw_terms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Network to plot&lt;/span&gt;
&lt;span class="n"&gt;dut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;#Calibration kit measurements&lt;/span&gt;
&lt;span class="n"&gt;o_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../cal_kit/open.s1p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../cal_kit/short.s1p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;l_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../cal_kit/load.s1p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;through&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;

&lt;span class="c1"&gt;#Interpolate the calibration standard S-parameters&lt;/span&gt;
&lt;span class="n"&gt;o_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o_i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s_i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;l_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;l_i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Make two-port networks from two one-ports&lt;/span&gt;
&lt;span class="n"&gt;oo_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;two_port_reflect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ls_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;two_port_reflect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sl_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;two_port_reflect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Make ideal through Network&lt;/span&gt;
&lt;span class="n"&gt;through_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;through&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;through_i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;through_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnknownThru&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;measured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;oo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;through&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;ideals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;oo_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sl_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ls_i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;through_i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;n_thrus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;isolation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;switch_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;switch_terms&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_cal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot_s_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The measured networks follow the std1-std2 naming rule, where std1 is the
standard connected to port 1 and std2 on port 2. Measurements of open, short and
load are needed on the both ports. Standards are usually measured as one port on
both ports, but in this case standards are connected to both ports and they are
measured as a two port. Through also needs to be measured, but with this
calibration real through S-parameters don't need to be known. Through can even
be a line on the board being measured or any other transmissive circuit that
has S21 equal to S12.&lt;/p&gt;
&lt;p&gt;Switch terms are needed to account for reflection coefficient of the port switch
inside the VNA that causes different termination depending on the measurement
direction. They are measured with the ports connected together using any
transmissive standard. They can't be calculated from the S-parameters and
instead raw receiver measurements are required. It's not an issue with my
homemade VNA since accessing the raw receiver outputs is easy, but it can be
tricky with some older VNAs. They can also be extracted from SOLT measurements
if the receiver measurements are not possible.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_through_sma.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Short line calibrated to SMA connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_through_sma_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close up to S21 of short line.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the shortest 5mm long line on the board calibrated to the SMA
connectors. It is matched quite well which suggest that the SMA connector
matching is also good and that the line is not at least very far off from 50
ohms. Loss is smaller than 0.5 dB even at 6 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/through_sma.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved SMA through S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/through_sma_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close up to S21 of SMA through.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are the solved S-parameters of the SMA through
(right side on &lt;a href="https://hforsten.com/img/vna/cal_kit2.jpg"&gt;this picture&lt;/a&gt;) that I used for the calibration.
Loss is okay, but matching is not spectacular. I guess the through is not
exactly 50 ohms. Same results are obtained if the line on the board is used as
a through in the calibration.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_line_sma.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Long line calibrated to SMA connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_line_sma_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close up to S21 of the long line.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Longer 18mm line however isn't performing as well as the shorter one. There are
resonances at 4.2 and 5.8 GHz causing increased losses. I measured also another
identical board and it has the same resonances. I'm not exactly sure why it
happens and I haven't investigated it much yet. Maybe the bottom ground plane
works as an antenna? Board has 4 layers and the bottom three layers have
a ground plane. The two bottom ground planes are connected to the microstrip
ground plane only near the SMA connectors and it is possible that the bottom
ground plane is resonating.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/cap_sma.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Series capacitor test structure calibrated to SMA connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/cap_sma_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close up to S21 of the capacitor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Capacitor test structure doesn't have resonance at 4.2 GHz, but maybe there is
a small resonance at 5.8 GHz? Compared to the through line, capacitor test
structure has little higher losses as is expected.&lt;/p&gt;
&lt;h2 id="trl"&gt;TRL&lt;/h2&gt;
&lt;p&gt;There are three different TRL calibrations in the scikit-rf: Ordinary TRL
calibration (TRL), Least squares multiline TRL (MultilineTRL) and NIST Multiline
TRL (NISTMultilineTRL).&lt;/p&gt;
&lt;p&gt;MultilineTRL is just an alias of TRL, but difference between the TRL and
NISTMultilineTRL calibrations is that they behave differently when using
multiple lines. Ordinary least squares TRL uses the same system of equations
with multiple lines as with just one, but solves it with least squares. It is
simple to implement but doesn't avoid singularities if any pair of lines has
phase difference that is multiple of 180 degrees. NIST multiline TRL combines the lines more
intelligently avoiding the singularities as long as there is one pair of lines
that has non-singular phase difference. It allows very wideband calibration
even with just a few lines if their lengths are chosen correctly.&lt;/p&gt;
&lt;p&gt;In this simple case with one through and one line there shouldn't be difference
in the accuracy but I'm using NISTMultilineTRL because it has few features that
the TRL class doesn't have.&lt;/p&gt;
&lt;p&gt;Below is the code for calibrating the uncorrected measurements:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;skrf&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;span class="c1"&gt;#Use the skrf plot style&lt;/span&gt;
&lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stylely&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;#Load the uncalibrated S-parameters&lt;/span&gt;
&lt;span class="n"&gt;trl_thru&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trl_through.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trl_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trl_line.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trl_open1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trl_open_load.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trl_open2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trl_load_open.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#Assemble the two reflection measurements into same network&lt;/span&gt;
&lt;span class="n"&gt;trl_open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;two_port_reflect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trl_open1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trl_open2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trl_dut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trl_cap.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sw_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sw_terms.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;switch_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sw_terms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sw_terms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Measurement with load on both ports for isolation calibration&lt;/span&gt;
&lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;load_load.s2p&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cal_trl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NISTMultilineTRL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;measured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trl_thru&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trl_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trl_line&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;Grefls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;#Estimate of the reflection coefficient&lt;/span&gt;
        &lt;span class="n"&gt;er_est&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Estimated effective permittivity&lt;/span&gt;
        &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;13e-3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;#Line lengths&lt;/span&gt;
        &lt;span class="n"&gt;gamma_root_choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;real&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Assumes that lines are lossy&lt;/span&gt;
        &lt;span class="n"&gt;switch_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;switch_terms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;isolation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ll&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Plot the calibrated capacitor S-parameters&lt;/span&gt;
&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_cal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trl_dut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot_s_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_line_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;13 mm long line TRL calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The 13 mm long line still has the resonance after the calibration. The resonance
will also be visible in all the other measurements since the line with resonance
is used in calculating the calibration error coefficients. S11 and S22 are
perfect because reference impedance of the TRL calibration is equal to the
characteristic impedance of the lines.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_cap.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Series capacitor test structure calibrated with
    TRL.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_cap_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Series capacitor S21 detail.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the capacitor test structure this time calibrated with TRL. With the
unknown thru calibration S21 of the capacitor was -0.4 dB at 4 GHz, but with TRL
calibration the S21 is only -0.1 dB as the connector losses are calibrated out.&lt;/p&gt;
&lt;p&gt;Self resonance frequency of this capacitor is 380 MHz. Equivalent series
resistance (ESR) of the capacitor and pads can be determined from the loss at
this frequency. Since at self resonance frequency inductive and capacitive
reactances cancel out, the capacitor looks like a small valued resistor. S21 of
a series resistor can be solved as: &lt;span class="math"&gt;\(S_{21} = 2Z_0/(2Z_0+ R)\)&lt;/span&gt;. S21 of the
capacitor is 0.022 dB dB at 380 MHz, giving a series resistance of 0.26
Ω assuming 50 ohm reference impedance. But the reference impedance in this
measurement is really the characteristic impedance of the lines. It is probably
close to 50 ohms, but characteristic impedance needs to be determined more
accurately if a more accurate estimate of ESR is needed.&lt;/p&gt;
&lt;p&gt;The solved effective permittivity and line attenuation can be plotted with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;
&lt;span class="normal"&gt;9&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Effective permittivity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;er_eff&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Attenuation dB/m&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log10&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/er_eff.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved effective permittivity.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/att_dbm.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved line attenuation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The resonance at 4.2 GHz affects both effective permittivity and attenuation,
but below 4 GHz both should be correct. Effective permittivity is quite noisy, but
it appears to be around 2.84 at high frequencies and more at lower frequencies.&lt;/p&gt;
&lt;p&gt;Line attenuation is around 4.3 dB/m at 1 GHz and 11.6 dB/m at 3 GHz. Using
a calculator the theoretical values are 4.9 dB/m at 1 GHz and 11.9 dB/m at
3 GHz.&lt;/p&gt;
&lt;p&gt;Accuracy of the calibration can be estimated using the normalized standard
deviation plot. It is normalized such that when one through and one line
calibration is the most accurate, the phase difference is 90 degrees, the
normalized standard deviation is one. When the phase difference goes further
from 90 degrees accuracy decreases and the normalized standard deviation
increases.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Normalized standard deviation&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nstd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/nstd.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Normalized standard deviation.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As a rule of thumb phase difference between through and line should be between
20 and 160 degrees for an accurate calibration. In the normalized standard
deviation plot this corresponds to about three. From the above plot it is clear
that at around below 500 MHz the accuracy is much worse than this. To improve
the accuracy a longer line that is 90 degrees at low frequencies should be
added.&lt;/p&gt;
&lt;h1 id="characteristic-impedance"&gt;Characteristic impedance&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/Transmission_line_element.svg" width="310" height="158" style="width: 30%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Transmission line lumped element model. Source:
    &lt;a href="https://commons.wikimedia.org/wiki/File:Transmission_line_element.svg"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Transmission line can be modeled using sections of lumped elements. Each section
represents a short section of transmission line. One section has series resistor
and inductor and parallel capacitor and conductance.&lt;/p&gt;
&lt;p&gt;Using the lumped section model propagation constant of the transmission line can
be defined as:&lt;/p&gt;
&lt;div class="math"&gt;$$ \gamma = \sqrt{(R + j\omega L)(G + j \omega C)} $$&lt;/div&gt;
&lt;p&gt;and characteristic impedance as:&lt;/p&gt;
&lt;div class="math"&gt;$$ Z_0 = \sqrt{\frac{R + j \omega L}{G + j \omega C}} $$&lt;/div&gt;
&lt;p&gt;or written using the propagation constant:&lt;/p&gt;
&lt;div class="math"&gt;$$ Z_0 = \frac{\gamma}{G + j \omega C} \approx \frac{\gamma}{j\omega C} $$&lt;/div&gt;
&lt;p&gt;Typically conductance (G) of the transmission line is very close to zero if the
substrate is not conductive and it can be approximated to be zero. Because
propagation constant is solved during the TRL calibration characteristic
impedance of the transmission line can be determined if the capacitance per
length of the transmission line is known.&lt;/p&gt;
&lt;p&gt;C could be measured directly, but it is hard to measure accurately since
capacitance of a typical transmission line is very low.
&lt;a href="https://www.ece.ncsu.edu/erl/html2/papers/paulf/1993/NCSU-ERL-PAULF-93-04.pdf"&gt;Here&lt;/a&gt;
is a good paper of the possible methods for measuring it.&lt;/p&gt;
&lt;h1 id="measuring-characteristic-impedance"&gt;Measuring characteristic impedance&lt;/h1&gt;
&lt;p&gt;There are several ways to measure the characteristic impedance of the microstrip
lines. The first most simple way would probably be trying to curve fit to the
measured lines, but the problem is that we can't fit to the TRL calibrated
measurements because TRL calibration's reference impedance is the characteristic
impedance of the lines that we want to measure. Fitting to the measurements
corrected to the SMA connectors is possible, but accuracy is limited by the
matching of the connectors.&lt;/p&gt;
&lt;p&gt;Better method without extra measurements is to first calibrate measurements to
the SMA connector for example with unknown thru calibration and then do TRL
calibration as a second level calibration. Now the TRL calibration error terms
represent the S-parameters of the circuit between the reference planes of the
calibrations. Unknown thru reference plane was at the SMA connectors and TRL
calibrations reference plane is after the SMA connector on the PCB, thus the
error terms of the TRL calibration are the S-parameters of the connector. Port
impedances of the error terms are 50 ohm from the unknown thru and
characteristic impedance of the line on the TRL side. If the SMA connector is
short and well matched then the error terms only include the impedance change.
Modeling the error terms as an ideal impedance transformer the TRL reference
impedance can be solved. There are even enough degrees of freedoms to add
a shunt capacitance to the model and still solve the characteristic impedance.
For overview of this method see &lt;a href="http://www.ursi.org/proceedings/procGA02/papers/p0956.pdf"&gt;this
paper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can get the error parameters of the calibration as follows from the
calibration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;s21&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward reflection tracking&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;connector_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;\
            &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward directivity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;s21&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;\
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward source match&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]]&lt;/span&gt; \
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connector_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SMA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot_s_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;VNA calibration can't solve for both S21 and S12 of the error network and
instead only the product S12*S21 is solved, which is called reflection tracking.
For passive circuit S12 = S21 and we can take a square root of the reflection
tracking to solve for S12 and S21. Taking square root leaves 180 degree phase
ambiquity, but that is not important when only looking a the magnitudes.
Directivity is S11 and source match is the S22.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/sma_connector.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMA connector S-parameters from the error terms.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the plot of the S-parameters of the connector from the error terms.
Port 1 is the connector side and port 2 is the board side. Port 1 reference
impedance is the reference impedance of the unknown thru calibration which is 50
ohms in this case.  Port 2 reference impedance is the TRL calibration reference
impedance which is the characteristic impedance of the lines that we don't know.
At low frequencies we would expect the connector to be matched well, but S11 is
only -22 dB suggesting that characteristic impedance of the line is not 50 ohms.&lt;/p&gt;
&lt;p&gt;Using scikit-rf we can solve the characteristic impedance with the following
code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;coefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward reflection tracking&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward directivity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coefs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forward source match&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s2t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;t11&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;50.&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;t22&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;50.&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;xpad_est&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;t11&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;t22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;#Estimated pad capacitance&lt;/span&gt;
&lt;span class="n"&gt;cpad_est&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;f_ghz&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;1e9&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;xpad_est&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Plot the characteristic impedance&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0 from error terms&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/error_term_z0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Characteristic impedance solved from the error
    terms.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The method is only going to be accurate at low frequencies and in the above plot
we can see that the estimated characteristic impedance of the lines is around 57
ohms. Accuracy is not very good and issue with low frequency measurements is
that characteristic impedance increases as the frequency decreases. Reason for
the increase can be found from the lumped element model characteristic impedance
definition:&lt;/p&gt;
&lt;div class="math"&gt;$$Z_0 = \sqrt{\frac{R + j \omega L}{G + j \omega C}}$$&lt;/div&gt;
&lt;p&gt;As &lt;span class="math"&gt;\(\omega \rightarrow 0\)&lt;/span&gt;, &lt;span class="math"&gt;\(Z_0 = \sqrt{R/G}\)&lt;/span&gt;. &lt;span class="math"&gt;\(G\)&lt;/span&gt; is usually much smaller than
&lt;span class="math"&gt;\(R\)&lt;/span&gt; and &lt;span class="math"&gt;\(Z_0\)&lt;/span&gt; approaches a large value. At high enough frequencies &lt;span class="math"&gt;\(Z_0 \approx
\sqrt{L/C}\)&lt;/span&gt;, which is constant.&lt;/p&gt;
&lt;p&gt;Better estimate for the characteristic impedance can be obtained by solving for
the capacitance per unit length and using the solved propagation constant from
the TRL calibration to estimate the characteristic impedance. This assumes that
&lt;span class="math"&gt;\(G \approx 0\)&lt;/span&gt;, which should be true when substrate is not conductive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C0 from error terms&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Capacitance [F/m]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/c0_from_error_terms.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved capacitance per unit length.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At low frequencies the capacitance per unit length is about 99 pF/m, but the
obtained value is not very accurate because accuracy of the calibration is poor
at low frequencies.&lt;/p&gt;
&lt;p&gt;Assuming that the capacitance per unit length doesn't vary as a function of
frequency, we can use the capacitance solved at low frequencies for solving the
characteristic impedance over the whole frequency range. This is usually good
approximation to make as long as the relative permittivity of the substrate
doesn't vary much as a function of frequency.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;z0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;99e-12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0 from C0 and $\gamma$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0 [$\Omega$]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/z0_from_c0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Characteristic impedance from C0 and propagation
    constant.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The trace is quite noisy and the resonance around 4.2 GHz in the line affects the
propagation constant solution causing also the characteristic impedance to be
solved incorrectly, but below 4 GHz the solution should be correct. At very low
frequencies it can be seen that the characteristic impedance rises as is
expected and after 1 GHz it is practically constant. From the plot
characteristic impedance of the lines can be estimated to be about 56.7 Ω.&lt;/p&gt;
&lt;h2 id="second-method"&gt;Second method&lt;/h2&gt;
&lt;p&gt;The previous method assumed that the SMA connector was well matched. A better
method that doesn't assume can be used if line there is a terminated line on the
board.&lt;/p&gt;
&lt;p&gt;At low frequencies parasitics of the termination can be assumed to be low and
impedance of the termination is the same as its DC resistance that can be
measured very accurately with a multimeter. When the termination is measured
with a VNA the expected reflection coefficient is:&lt;/p&gt;
&lt;div class="math"&gt;$$\Gamma = \frac{R_{\text{load}} - Z_0}{R_{\text{load}} + Z_0}$$&lt;/div&gt;
&lt;p&gt;This equation can be solved for &lt;span class="math"&gt;\(Z_0\)&lt;/span&gt;, but it is again better to instead solve
for capacitance per unit length because &lt;span class="math"&gt;\(Z_0\)&lt;/span&gt; is not constant at low
frequencies.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;#Calibrated termination&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_cal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trl_load&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot_s_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Solve for Z0&lt;/span&gt;
&lt;span class="n"&gt;r_load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;50.2&lt;/span&gt; &lt;span class="c1"&gt;#Measure DC resistance of the termination&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_cal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trl_load&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s11&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;z0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r_load&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0 from termination&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_ghz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;#Solve for C0&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imag&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r_load&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C0 from termination&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_load.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured S11 of the termination&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are the TRL calibrated S-parameters of the termination. At low frequencies
the S11 should be very low if &lt;span class="math"&gt;\(Z_0=R_{\text{load}}\)&lt;/span&gt;, but the measured S11 is
only -22 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/z0_term.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved Z0 using termination.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/c0_term.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved capacitance using termination.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Solved Z0 seems to be around 57 Ω agreeing with the first method. Solved
capacitance per unit length is about 98.8 pF/m, when previous method gave 99
pF/m. Now that we have gotten the same characteristic impedance using two
methods we should be pretty confident in the result.&lt;/p&gt;
&lt;h1 id="renormalized-trl-calibration"&gt;Renormalized TRL calibration&lt;/h1&gt;
&lt;p&gt;Finally the reference impedance of the TRL calibration be renormalized to 50
ohms by giving the capacitance per unit length to the calibration routine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NISTMultilineTRL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;measured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trl_thru&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trl_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trl_line&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;Grefls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;#Estimate of the reflection coefficient&lt;/span&gt;
        &lt;span class="n"&gt;er_est&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Estimated effective permittivity&lt;/span&gt;
        &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;13e-3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;#Line lengths&lt;/span&gt;
        &lt;span class="n"&gt;gamma_root_choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;real&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Assumes that lines are lossy&lt;/span&gt;
        &lt;span class="n"&gt;switch_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;switch_terms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;isolation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;c0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;98.8e-12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;z0_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="c1"&gt;#New reference impedance&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#Plot the smoothed characteristic impedance&lt;/span&gt;
&lt;span class="c1"&gt;#http://scipy.github.io/old-wiki/pages/Cookbook/SavitzkyGolay&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Z0 [$\Omega$]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Frequency [Hz]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_ghz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;savitzky_golay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cal_trl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]),&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/z0_smooth.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Characteristic impedance of the transmission
    lines. Smoothed using Savitzky Golay filter.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The resonance at 4.2 GHz gives incorrect results around that frequency, but the
characteristic impedance can be expected to be constant at high frequencies.
Reading from the plot the characteristic impedance seems to be around 56.7 Ω.&lt;/p&gt;
&lt;p&gt;The line was supposed to be 50 ohms, but characteristic impedance seems to be
over 10% bigger than that, so where's the problems?&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/my_photo-82.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Photo of the line with calibers for scale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The first thing to check is that the manufactured dimensions of the line match
the designed dimensions. In the picture is the microstrip line with calibers set
to 1.00 mm for scale. In the image distance between the caliber jaws is 118
pixels, while line is 32 pixels wide. Converting this to mm gives line width of
about 0.27 mm. The designed width was 0.34 mm so the manufactured line is
thinner, which raises the characteristic impedance. Inputting the measured width
to microstrip characteristic impedance calculator with nominal 35 µm trace
thickness, 170 µm substrate height and substrate dielectric constant of 3.66
give characteristic impedance of 57.0 ohms.&lt;/p&gt;
&lt;p&gt;The observed increase in the characteristic impedance can be fully explained by
overetching of the lines. Overetching is about 35 µm, which is not bad
considering the price of the PCBs. I have seen bigger overetching on much more
expensive PCBs.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_load_z0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Termination S11.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is S11 of the termination with renormalized calibration. Now the low
frequency reflection coefficient is small as expected. At high frequencies
the reflection coefficient is not so good, but this is an issue with the
termination and not measurement.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/sma_connector_z0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMA connector S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Plotting the solved SMA connector S-parameters from the error terms now gives
the true 50 ohm normalized S-parameters. This time the connector matching looks
much better. S11 and S22 are below -25 dB up to 4 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/sma_connector_z0_s21.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMA connector S21.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Loss of the SMA connector is also very small.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_cap_z0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Capacitor S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Capacitor S-parameters don't look that different from before.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/trl_cap_s21_z0.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Capacitor S21.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At self-resonance frequency S21 is about 0.025 dB, when previously it was 0.022
dB. This gives ESR of 0.29 Ω, when without renormalizing it was estimated to be
0.26 Ω. Error is not very big in this case.&lt;/p&gt;
&lt;p&gt;Part number of this capacitor is GRM155R71H102KA01J and Murata makes its
S-parameter measurements available on their
&lt;a href="http://ds.murata.co.jp/software/simsurfing/en-us/index.html"&gt;website&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/trl/cap_sparam.png" width="804" height="372" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Capacitor S-parameters from manufacturer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;My measurements include the PCB pads which cause some extra capacitance at high
frequencies, but S-parameters measured by the manufacturer look very similar to
what I measured. The manufacturer has measured the ESR to be 0.277 Ω which
agrees really well with my measurement of 0.29 Ω.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Even though my VNA is much less accurate than commercial VNAs it is accurate
enough for many useful measurements. I tried the same measurement also with the
first version of the VNA and due to the much worse isolation the results were
unusable.&lt;/p&gt;
&lt;p&gt;The board had unfortunate resonance at 4.2 GHz, which I think comes from the
poor grounding of the multiple ground planes. I think I'll need to order
a new PCB with better grounding.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Improved homemade VNA</title><link href="https://hforsten.com/improved-homemade-vna.html" rel="alternate"></link><published>2017-03-13T00:00:00+02:00</published><updated>2017-03-13T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2017-03-13:/improved-homemade-vna.html</id><summary type="html">&lt;p&gt;Second version of the homemade 30 MHz - 6 GHz VNA with improved performance&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_2.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Last year &lt;a href="http://hforsten.com/cheap-homemade-30-mhz-6-ghz-vector-network-analyzer.html"&gt;I made a simple vector network
analyzer&lt;/a&gt;
for measuring S-parameters of microwave circuits that I'm making at
home. Budget was very small and it was mostly a proof of concept for a low cost
homemade VNA.&lt;/p&gt;
&lt;p&gt;If you don't know what a vector network analyzer is I recommend that you read
the previous post first, but in short it is a test device that can be used to
measure magnitude and phase of transmitted and reflected power of a circuit. For
example amount of power reflected back from antenna should be low at its working
frequency.&lt;/p&gt;
&lt;p&gt;While it worked and I could use it to measure one and two-port S-parameters
measurement accuracy was not very good. One port measurements worked reasonably
well but the biggest problem with two port measurement accuracy was leakage
between the test ports. To even calibrate the instrument I had to use an exotic
16-term calibration that can compensate for different leakage paths between
the receiver channels.&lt;/p&gt;
&lt;p&gt;Lack of isolation between the ports was the biggest issue, but there were also
many other smaller issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Receiver noise figure was very high since there
  wasn't any amplification before the mixer. Increased noise figure resulted in
  noisy measurement and reduced dynamic range.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linearity of the receiver was not very good. This was partly caused by the bad
  choice of IF amplifier and partly because I used a balanced mixer without
  a balun. Mixer is recommended to be driven with differential input, but
  wideband baluns were too expensive. Driving the mixer with other input
  terminated was possible but linearity, conversion gain and noise figure
  suffered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Directional couplers were integrated on the PCB and to save PCB area I made
  them as small as possible. At low frequencies coupling was very low and the
  signal to noise ratio at the receiver was not very good. At high frequencies
  directivity and matching of the couplers started to worsen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In theory even if the matching, directivity or leakage were high they can
  be corrected by the calibration. In reality there is some amount of drift and
  noise in the instrument that causes the vector error correction to not be exact
  and some amount of error shows up in the results. There was enough drift,
  non-linearity and noise that there were significant errors in the measurement
  results even after the calibration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Microcontroller was not fast enough to do any signal processing and all the
  signal processing needed to be done on the PC. This required transferring raw
  ADC values through the USB connection to the PC for processing.
  While the PC should be fast enough to process the samples without delaying the
  next measurement, in practice my code was written in Python which just wasn't
  fast enough to do all the signal processing without delaying the measurement.
  Microcontroller was also busy with reading the ADC and didn't have time to do
  anything else at the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In total there were enough room for improvement that I wasn't be happy with
the board and decided to make an improved version that tried to fix the problem
points as well as I could while still keeping to cost low.&lt;/p&gt;
&lt;h1 id="single-receiver-vna"&gt;Single receiver VNA&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vna_block.png" width="2761" height="1972" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of the previous VNA version.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the block diagram of the previous version of VNA. Commercial VNAs
use four separate receivers with their own mixers and ADCs to maximize the
isolation between the receiver channels and improve the measurements speed.
I would also like to use the four receivers, but the reality is that multiplying
the receiver cost by four is too much for my budget.&lt;/p&gt;
&lt;p&gt;The isolation between the receiver channels can be improved by improving the
isolation of the receiver SP4T switch. On my previous VNA I used a single
&lt;a href="http://www.psemi.com/products/rf-switches/pe42441"&gt;PE42441&lt;/a&gt; SP4T switch. It was very
cheap, but the isolation is not good enough for VNA use. Commercial VNAs have
isolation at least more than 100 dB between the receivers, but this switch has only
40 dB isolation at 6 GHz.&lt;/p&gt;
&lt;p&gt;VNA should much higher isolation than the isolation of the component being
tested and for example the isolation of the receiver switch couldn't be measured
on the previous version of my VNA since leakage between the receiver channels is
the same order as signal passing through the switch being measured. This is
a general issue with test equipment: Its performance should be better than
device being measured.&lt;/p&gt;
&lt;p&gt;In addition to the receiver SP4T switch, port switch also had a single SP2T
switch that didn't have good enough isolation. Source switch was
&lt;a href="http://www.psemi.com/products/rf-switches/pe42423"&gt;PE42423&lt;/a&gt;, which is marketed
as having an exceptional isolation of 43 dB at 6 GHz. While it is a very good
isolation for a single SP2T switch it is not enough for VNA usage.&lt;/p&gt;
&lt;p&gt;Achieving a isolation of more than 100 dB on a single PCB is hard and
that is why professional RF test equipment have each system shielded from each other.
If several blocks are made on the same PCB a milled aluminium case is placed on
top of the PCB that isolated the blocks from each other and the environment.
Aluminium block can also serve as a heat sink and reduce the temperature drift of
the instrument. However I can't afford any custom milled aluminium pieces and
have to manage without them.&lt;/p&gt;
&lt;p&gt;I did however put a shield around the receiver that can be attached using clips
so that it can be removed if needed. Shielding source, switches, couplers, FPGA
and power supply would have also been a good choice, but it would have required
too much space on the PCB so I decided to leave them out.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_block.png" width="2733" height="2175" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of new VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a block diagram of the new VNA design. It is very similar to the block
diagram of the previous version presented above. Port and receiver switches are
changed to multiple SP2T switches in series as a single switch can't provide the
necessary isolation. A low noise amplifier is added before the
mixer to reduce the noise figure of the receiver.&lt;/p&gt;
&lt;p&gt;Balun is also added before the mixer to increase its performance. Mixer is
designed to be used with a balun that converts the single-ended RF signal to
differential, but a wideband balun was too expensive. Only Minicircuits sells
a wideband enough balun. It costs 8 € a piece with minimum order of 20 pieces
plus shipping costs. Ordering the baluns would have cost more than all the other
components combined, so I decided to leave it out and accept the performance
loss in the first version. However I found a third party seller that sells the
same balun for little extra at low quantities. With balun input return loss,
conversion gain, noise figure and linearity of the mixer are improved.&lt;/p&gt;
&lt;p&gt;Even though the block diagram looks very similar, there are also other big
changes: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Microcontroller is replaced by FPGA which can do the digital signal
  processing on the board reducing the computing power required from the PC.
  On-board processing is much faster and the measurement speed should increase
  as it isn't necessary to transfer all the ADC samples to PC anymore.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ADC was previously a 12-bit ADC integrated on the microcontroller that was
  sampled at 10 MHz. It is now replaced by a 14-bit external ADC that is sampled
  at 40 MHz. This should greatly increase the dynamic range of the receiver.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Directional couplers were integrated on the PCB in the previous version but
  now they are on separate PCBs. This allows experimenting with different
  couplers and improving them without replacing the whole VNA.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Receiver is shielded from the other components. To improve the isolation from
  source to receiver, receiver is heavily isolated from everything else. RF
  signals are routed on the internal layers of the PCB with top and bottom
  ground planes to shield the signals as much as possible.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/receiver_labels.jpg" width="1600" height="1210" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Receiver components labeled.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a picture of the finished receiver section with different components
labeled.  Shield requires lot of space on the board, but it didn't affect the
final PCB size too much, because SMA connectors and power supply limit the
minimum board size.&lt;/p&gt;
&lt;h1 id="design"&gt;Design&lt;/h1&gt;
&lt;h2 id="schematic"&gt;Schematic&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/vna2/vna2.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_sch.png" width="1047" height="724" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;&lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for PDF.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Mostly due to addition of FPGA number of sheets in the schematic almost doubled
from 8 in the first version to 15 in the new version. BOM has 522 components,
but it also counts for example mounting holes, RF footprints and test points
that are PCB features. There are also many empty footprints that are not meant
to be populated just in case. For comparison the previous version had 342
components in the BOM.&lt;/p&gt;
&lt;h2 id="fpga"&gt;FPGA&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_fpga.png" width="2396" height="1103" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simplified FPGA block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Most of the high level logic, such as calculating the S-parameters from receiver
readings and deciding the frequency points in the sweep, are done on the PC.
On-board FPGA is responsible for low level processing such as toggling the IO
lines based on the commands from the PC and calculating the IQ values from the
ADC samples.&lt;/p&gt;
&lt;p&gt;Digital signal processing works in the same way as the first version. ADC
samples are divided in two branches where they are multiplied by &lt;span class="math"&gt;\(\cos(2\pi
f t)\)&lt;/span&gt; and &lt;span class="math"&gt;\(\sin(2\pi f t)\)&lt;/span&gt;. Frequency of the digital LO is the same as the
output of the mixer, 2 MHz in this case. This results in the signal being mixed
down to DC. Next average of the samples is taken resulting in two numbers I and
Q.&lt;/p&gt;
&lt;p&gt;Alternate way of thinking the DSP is that multiplying by sin and cos and then
taking averages is equal to one frequency bin of Fourier transform:&lt;/p&gt;
&lt;div class="math"&gt;$$ X_k = \frac{1}{N}\sum_{n=0}^{N-1} x_n e^{-2\pi i k n/N} $$&lt;/div&gt;
&lt;p&gt;This is the optimal way to measure amplitude and phase of a signal with known
frequency affected by gaussian noise.&lt;/p&gt;
&lt;h2 id="directional-couplers"&gt;Directional couplers&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/bridge2.png" width="1426" height="610" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Resistive bridge coupler schematic. R1 and R5 are
    termination resistances of source and load and are not on the board. Z is
    the unknown impedance being measured. Vc voltage varies depending on the Z.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The previous directional couplers were stripline coupled line couplers that had
two closely placed lines on internal layer of the PCB. It did work quite
well at about 2-4 GHz but at lower frequencies the lines were very short
compared to the wavelength of the signal and coupling was very low. This
caused the receiver input power to be very low at low frequencies leading to
very noisy measurements. Above 4 GHz directivity of the couplers wasn't very
good. Ideally the coupler measuring the reflected signal wouldn't couple any
signal going from the source to the test port and only couple the signals
returning from the device under test going towards the source. In practice
directional couplers have finite directivity and some of the signal passing
from source to DUT is also coupled to the receiver. This directivity error
must be measured during the calibration and removed from the measurements.
Subtraction of the directivity error isn't perfect and it is preferred
if it is low enough so that potential errors between the measured and real
directivity doesn't cause big error in the measurement result.&lt;/p&gt;
&lt;p&gt;For the new directional couplers I decided to change the architecture to
resistive bridge coupler as it can give very flat coupling over very wide
bandwidth from kHz frequencies to over 10 GHz. As the name says it is based on
resistors that don't have frequency dependence like coupled lines and the
frequency response is only limited by parasitics. Commercial VNAs often
have resistive bridge couplers to reach the kHz frequencies where transmission
line based couplers aren't practical.&lt;/p&gt;
&lt;p&gt;A resistive bridge is basically a variation of &lt;a href="https://en.wikipedia.org/wiki/Wheatstone_bridge"&gt;Wheatstone
bridge&lt;/a&gt;. A coaxial balun is
inserted to the bridge to allow connecting a single ended load. Normally the
coaxial cable should be long compared to the signal wavelength, but lower
frequency limit of the balun can be extended by adding ferrite beads around the
coaxial cable. Ferrite beads attenuate the common mode signals at low
frequencies and allow using a much shorted coaxial cable.&lt;/p&gt;
&lt;p&gt;Resistor values are also modified a little compared to the normal Wheatstone
bridge circuit, so that through loss is lower and coupling loss is higher. See
&lt;a href="http://www.ke5fx.com/Broadband_Coupler_Dunsmore.pdf"&gt;this paper&lt;/a&gt; for a good
overview on how this coupler architecture works.&lt;/p&gt;
&lt;p&gt;Design of my coupler is based on &lt;a href="http://ieeexplore.ieee.org/document/7345756/"&gt;this IEEE paper&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_pcbs.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Directional coupler PCBs.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_coax.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Short piece of coaxial cable for balun.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_smds.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMD resistors are soldered upside down to
    minimize parasitics.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At least in theory SMD resistor soldered upside down should have little bit
lower parasitic inductance than when it's soldered the right way. The resistive
element is on top side of the resistor and by mounting it upside down it is
closer to the PCB ground plane lowering its inductance.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_finished.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Two finished couplers with ferrite beads around
    the coaxial cable.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_ref_s12.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured S-parameters of the coupler.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_s11_ref.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured S-parameters of the coupler.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above plots are measured S-parameters with a commercial VNA up to 10 GHz.
Coupler was measured as a two port with third port terminated with a high
quality termination from the VNA calibration kit.&lt;/p&gt;
&lt;p&gt;S12 trace is the through path, S13 is the coupled path and S23 is the isolated
direction. Directivity is S13 - S23 and it is better than 25 dB up to 5.5 GHz
and about 15 dB after it.  Matching is very good at low frequencies but starts
to worsen as the frequency increases. Through loss is about 1.5 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_s21_ref_log.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured S-parameters of the coupler. Logarithmic
    scale.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Low frequency performance can be more easily seen when the X-axis is changed to
logarithmic. Ferrites on the coaxial balun extend the low frequency range and
the coupler still has about 5 dB directivity at 300 kHz.&lt;/p&gt;
&lt;p&gt;Overall I'm very happy with the performance of the coupler. Matching,
directivity and flatness of the coupling are all much better than the coupled
line coupler on the previous version of the VNA. Using these couplers should
result in much more accurate measurements.&lt;/p&gt;
&lt;h1 id="soldering"&gt;Soldering&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/bare_pcb.jpg" width="1600" height="1550" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Bare PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/pcb_mount.jpg" width="1600" height="1438" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PCB secured for solder paste stenciling.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Like the first version, PCB is from &lt;a href="https://oshpark.com/"&gt;OSH Park&lt;/a&gt;. OSH Park
offers 4-layer FR408 substrate PCB that has lower loss and better controlled
dielectric constant at high frequencies. It seems to currently be the only cheap
non-FR4 process and if you have read my other posts I have used it a lot.&lt;/p&gt;
&lt;p&gt;Previously I have made the backside of the PCB first, but I have found that it
makes applying the solder paste on the front side more difficult as the board
won't sit straight on the table. This time I decided to do the front side with
difficult components first and do the backside by hand later. Having a flat
backside without components helped with securing the PCB and the stenciling
result was better than the last time. Stainless steel stencil should also help
with the solder paste application and should result in better defined edges of
the deposits than polyimide stencils I have used before. Stencil is from &lt;a href="https://www.oshstencils.com"&gt;OSH
stencils&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/stencil_paste.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste on stencil.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/stencil_paste2.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;After spreading the paste.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/bga_paste.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste on BGA footprint.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/source_paste.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste on RF source components.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Solder paste stenciling was very successful. Edges of the paste deposits are
very well defined.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/bga_side_paste.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;BGA placed on the solder paste. Ball pitch is
    1 mm, which is very easy to position even by hand.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Xilinx FPGA comes in a 256 ball package with 1 mm ball pitch. It is very
big pitch for a BGA package and makes it very easy to solder at home. There is 
also comfortable amount of room to route all the signals even on a low cost PCB
like this.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/components_placed.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Components placed on the paste.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/pcb_oven.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PCB in the reflow oven.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I used &lt;a href="http://hforsten.com/toaster-oven-reflow-controller.html"&gt;the same old toaster
oven&lt;/a&gt; for reflow that
I have made all my other projects with. Green PCB on the back holds the
temperature sensor.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/hello_world.jpg" width="1600" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Hello world!&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I made a quick bitfile for FPGA that just blinks the LED and everything worked
the first time.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;h2 id="receiver"&gt;Receiver&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/adc_output_zoom.png" width="816" height="600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;ADC output waveform.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;External 14-bit ADC makes a big difference to the receiver dynamic range
compared to the previous 12-bit ADC integrated in the microcontroller. Mixer
output frequency is programmable and is set to 2 MHz in the above plot. Exact
frequency isn't important, but there are some limits for setting the frequency.
At low frequencies there is a dithering signal and more noise and at high
frequencies linearity of the IF amplifier and ADC isn't as good. At some point the
anti-aliasing filter will also start to attenuate the signal. 2 MHz is a good
compromise.&lt;/p&gt;
&lt;p&gt;Sampling frequency of the ADC is 40 MHz. In theory it could be much lower
without aliasing and high sample rate ADC costs more so why not use for example
10 MHz ADC instead? There are three reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To simplify the clock distribution FPGA, PLLs and ADC use the same clock
   signal. 40 MHz is a good value for all of them. If different clock
   frequencies were used, clock dividers or PLLs would be need to be added.
   FPGA could use separate clock, but PLL reference clock and ADC sampling clock
   need to be synchronized.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;High sampling speed allows using simpler anti-aliasing filter.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sampling speed can be traded for accuracy. When two consecutive ADC samples
   are averaged dynamic range increases by 3 dB. Quadrupling the sampling speed
   and taking average of four samples increases dynamic range by 6 dB, which
   equals increase of one bit in the sample depth. Dynamic range of 40 MHz
   14-bit ADC is equivalent to 10 MHz 15-bit ADC or 2.5 MHz 16-bit ADC.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/adc_fft.png" width="816" height="600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FFT of 5000 ADC samples. Y-axis is dB full scale. Maximum non-clipping input sine wave would be 0 dB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is an FFT of 5000 ADC samples which is the maximum I can store at the
FPGA and transfer to the PC at the moment. USB is not fast enough to
continuously stream the ADC samples. Noise floor is about -105 dBFs. 5000 samples
corresponds to 8 kHz wide FFT bin and sampling time of 125 µs. Every doubling of
the sampling time increases the dynamic range by 3 dB. With 10 ms sampling time
(100 Hz IF bandwidth) dynamic range is 125 dB and 0.1 s (10 Hz IF bandwidth)
gives a dynamic range of 135 dB.&lt;/p&gt;
&lt;p&gt;For comparison the previous version had dynamic range of 90 dB with sampling
time of 400us. Due to needing to transfer all the samples to PC this setting was
about as fast 5 ms sampling time on the new version. Because of better ADC,
receiver LNA and on-board processing the dynamic range increased by at least 30
dB for the same measurement speed.&lt;/p&gt;
&lt;h2 id="one-port-s-parameters"&gt;One port S-parameters&lt;/h2&gt;
&lt;p&gt;Making accurate one port S-parameter measurements is much easier than two port
measurements. Error model is much simpler and isolation isn't as big of
a problem. One port measurement can be completely corrected with three parameter
error network, while two port error network has at least nine parameters.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/cal_kit.jpg" width="1600" height="1157" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Homemade SMA calibration kit. Open, short and
    load.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/cal_kit_snp.png" width="711" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured calibration kit S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Previously I didn't have information about the true S-parameters of the
calibration kit and had to make a guess that decreased the accuracy of the
measured S-parameters.&lt;/p&gt;
&lt;p&gt;I measured the S-parameters of the calibration kit with commercial VNA
calibrated with a very accurate and expensive calibration kit. This basically
transfers the calibration of the expensive kit to my homemade kit and allows me
to make reasonable accurate measurements with the homemade kit.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/cal_kit.png" width="653" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Uncalibrated S-parameters of the calibration
    standards.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are the raw uncalibrated measurements of the calibration standards
measured with my VNA. Load is measured to be -30 dB because of the limited
directivity of the couplers. Ripple is caused by the variation in the source
matching. Open and short are not centered at 0 dB, because of loss of the
couplers. All these errors can be determined from the measurements and
corrected.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/error_terms.png" width="768" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solved error terms.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Compared to the &lt;a href="https://hforsten.com/img/vna/uncalibrated.png"&gt;same measurement with the previous
version&lt;/a&gt; error terms are now
clearly much smaller. New couplers have better directivity and matching reducing
the ripple in open and short measurements and improving the dynamic range of
the load measurement.&lt;/p&gt;
&lt;h3 id="patch-antenna"&gt;Patch antenna&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_patch_pic.jpg" width="824" height="479" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Picture of the patch antenna and simulated and
    measured S11 traces.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a simple patch antenna designed to work at 5.8 GHz. "patch_cal" trace
is the measured S11 and "patch_simulation" is the S11 from EM-simulator.
Measured S11 agrees very well with the simulated one and the trace is very clean.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/patch_zoom.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Zoom into patch antenna S11 trace.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Trace noise in the patch antenna S11 measurement is about 0.1 dB, which is
probably caused by the variation of the leakage from source to receivers.
Below 700 MHz performance is much better because isolation is much higher at low
frequencies.&lt;/p&gt;
&lt;h3 id="horn-antenna"&gt;Horn antenna&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_horn_left.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;6 GHz radar horn antenna compared to commercial
    VNA measurement.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a S11 measurement ("horn_left_cal" trace) of my homemade &lt;a href="https://hforsten.com/horn-antenna-for-radar.html"&gt;horn
antennas&lt;/a&gt; compared to
a measurement made with a commercial VNA ("horn_left_ref" trace). Compared to &lt;a href="https://hforsten.com/img/vna/horn_left_s11.png"&gt;the measurement with last
version&lt;/a&gt; traces are now much closer. Most
of the improvement is from measuring the calibration kit.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_horn_right.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Other horn antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The other horn antenna also agrees quite well.&lt;/p&gt;
&lt;h2 id="two-port-s-parameters"&gt;Two port S-parameters&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_1.jpg" width="1600" height="1101" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Resistive bridge couplers on port 1 (down). Stripline
    coupled line coupler on port 2 (up).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I didn't have enough of the new resistive bridge couplers for both ports. So for
two port measurements I had to put coupled line coupler from the previous
version on the second port. It reduces the accuracy, but at least it allows me
to make measurements.&lt;/p&gt;
&lt;p&gt;Previous version had such high leakage between the channels that I had to use
a special 16-term calibration that also calibrates for all the possible leakage
paths. This version has better isolation and I'm able to use the normal SOLT
calibration with isolation calibration (Both ports terminated).&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_att_20db.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;20 dB attenuator S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are the measured S-parameters of 20 dB attenuator. I measured the same
attenuator also with the old VNA and using the same 12-term calibration quality
was &lt;a href="https://hforsten.com/img/vna/att_20db_solt.png"&gt;pretty terrible&lt;/a&gt;. With 16-term
calibration that includes additional leakage paths &lt;a href="https://hforsten.com/img/vna/att_20db_lmr16_solt.png"&gt;I managed to measure
it&lt;/a&gt;. There's a clear difference in
the S11 and S22 accuracy between the new and the old versions. Ripple in the
traces is unphysical and they should be smooth.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_att_20db_zoom.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;20 dB attenuator S-parameters. S21, S12 detail.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the same measurement zoomed to the S12 and S21 traces. Ripple
increases as the frequency increases due to the leakage being bigger at high
frequencies. At low frequencies there's also some difference between the S21 and
S12 traces due to different couplers on different ports. Port 1 had the new
resistive bridge couplers while port 2 had coupled line coupler that has very
low coupling at low frequencies which decreases the measurement accuracy.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler_ports.jpg" width="1600" height="945" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Resistive bridge coupler with ports labeled. Port
    3 is terminated with load.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I made three bridge couplers, two are used on the port 1 so one is left to be
measured. Above is a picture of the coupler with ports labeled. In the picture
port 3 is terminated with load so that it can be measured with two port VNA.
Port 1 to port 2 is the through path with low loss. Port 1 to port 3 is coupling
path that has 16 dB loss. Port 2 to port 3 is the isolated path with low
coupling.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler2_13_comp.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Coupler port 1 to port 3, with port 2 terminated.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture are the two port S-parameters of the coupler measured with
my 300 € VNA calibrated with 5 € homemade calibration kit ("coupler2_13_term_cal" traces) 
and with commercial 100 000€ VNA calibrated with 5 000€ calibration kit
("coupler2_13_ref" traces). While my measurements are much noisier the traces agree
very closely.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/coupler2_23_comp.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Coupler port 2 to port 3, with port 1 terminated.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Port 2 to port 3 measurement also agrees well. If you compare these measurements
to one presented before, you notice that the directivity is not as good. Reason
is that these measurements have been made with very cheap termination that has
higher reflection coefficient. In the directivity measurement the terminated
port reflects some power and measured S21 is bigger than it would be with
a perfect termination.&lt;/p&gt;
&lt;h2 id="isolation"&gt;Isolation&lt;/h2&gt;
&lt;p&gt;Isolation performance of the VNA didn't turn out to be as good as I hoped.
I knew before that I wouldn't get the best performance without very good
shielding, but I wanted to avoid it as good shielding is too expensive. It seems
that most of the leakage is radiative leakage from source to receiver, but there
is also more leakage between the receiver channels than expected. I did try to
find and fix some of the worst unintentional antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/rx2_amp_shdwn_leakage.png" width="816" height="600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Port 2 reference channel (RX2) measured with
    source on port 1. With and without 10 pF capacitor on amplifier shutdown
    trace.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Source amplifier has a shutdown pin that is connected to FPGA and routed on top
layer of the PCB for a long distance. I placed a 100 ohm resistor near the
amplifier pin to add some loss to the line. However it turns out that at about
5 GHz the shutdown pin trace is the biggest leakage source. After adding a 10 pF
capacitor from the trace to ground leakage dropped by 20 dB at 5 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/amp_shdwn2.jpg" width="640" height="480" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Amplifier shutdown trace, series resistor and the
    added capacitor. Skinny trace on the right is the shutdown trace. Source
    amplifier is the IC on bottom left.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna2/vna2_hand_over_couplers.png" width="816" height="600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Port 2 reference channel (RX2) measured with
    source on port 1. With and without hand over the coupler cables.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At about 700 MHz there is another peak in the leakage plot. This one seems to be
caused by the external couplers. If I place my hand over both of the couplers
leakage is attenuated by about 20 dB.&lt;/p&gt;
&lt;p&gt;There are many more similar unintentional antennas and I can't fix all of them.
I guess the only way to improve the isolation is to shield every subsystem and
preferably even make them on different PCBs.&lt;/p&gt;
&lt;h1 id="stability"&gt;Stability&lt;/h1&gt;
&lt;div id="centered" &gt;
&lt;video width=80% autoplay="autoplay" loop&gt;
&lt;source src="https://hforsten.com/video/vna2/vna_leakage_crop2.m4v" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/vna2/vna_leakage_crop2.ogv" type="video/ogv"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;On the above video you can see how waving my hand over the board affects the
receiver readings. Source frequency is 6 GHz and it is connected to port
1 causing the high readings on the port 1 channels RX1 and A. Test cables are
open so ideally RX2 and B channels shouldn't have any power entering them.
However due to leakage some power is detected at RX2 and B channels. Moving my
hand either raises or lowers the detected leakage based on whether the reflected
signal from my hand is in or out of phase with the other leakage paths.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% autoplay="autoplay" loop&gt;
&lt;source src="https://hforsten.com/video/vna2/vna_leakage2_crop.m4v" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/vna2/vna_leakage2_crop.ogv" type="video/ogv"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;S11 measurement is also affected by the isolation. The absolute effect is
smaller because signals are bigger, but I can affect the S11 readings by about
0.01 dB from about 0.5 m away by moving my hand over the VNA. IF bandwidth was
100 Hz.&lt;/p&gt;
&lt;p&gt;Trace noise is also visible in the plot at the beginning. It is about 0.002 dB
which is quite good but due to the poor isolation trace noise in the
measurements is much higher. With a good isolation trace noise would be about
this level also on the measured S-parameters.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The new version of the VNA is much more accurate than the previous version.
Receiver is very accurate and has good dynamic range, but like the last version
isolation limits the performance. A metal case around the PCB would help with
stability and should increase the measurement accuracy. With case, shields
around the individual subcircuits and receiver on a separate PCB measurement
accuracy should be much better.&lt;/p&gt;
&lt;p&gt;All the design files are available at &lt;a href="https://github.com/Ttl/vna2"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>RF power detector and scalar network analyzer</title><link href="https://hforsten.com/rf-power-detector-and-scalar-network-analyzer.html" rel="alternate"></link><published>2016-10-25T00:00:00+03:00</published><updated>2016-10-25T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2016-10-25:/rf-power-detector-and-scalar-network-analyzer.html</id><summary type="html">&lt;p&gt;Next on the list of homemade RF test equipment is RF power meter. I decided to make one to calibrate the output power of my VNA and to also measure output power of the previous radar projects.&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/finished.jpg" width="1600" height="903" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finisihed power detector.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Continuing with the self-made RF test equipment theme I decided to next make an
RF power meter. Accuracy of the finished power detector should be good enough
for calibrating the output power of my VNA and measuring the output power of my
radars. I'll be happy with +- 1 dB accuracy and sometimes even a worse accuracy
is useful in measuring if there is any signal at all. USB connection is nice to
have so that I can connect the meter to computer and for example automatically
measure power of the VNA over the whole frequency range without having to
manually write down all the measurement results.&lt;/p&gt;
&lt;h1 id="schematic"&gt;Schematic&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/detector/detector.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/detector/sch.png" width="1081" height="764" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for PDF.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Power detector could be done simply with a diode followed by capacitor
similar to peak detector circuit used at lower frequencies. Diode will rectify
the RF signal and generates a DC voltage which is proportional to the RF power.&lt;/p&gt;
&lt;p&gt;While it would be possible to design the detector using a discrete diode and matching
circuit, it is much cheaper, easier and accurate to use integrated circuit
already containing the necessary circuit, matching circuit and temperature
compensation. Existing ICs are also very well characterized so hopefully I don't
need to calibrate the power sensor myself.&lt;/p&gt;
&lt;p&gt;Searching on Digikey brings up several power detector ICs with most suitable
looking to be
&lt;a href="http://www.analog.com/en/products/rf-microwave/rf-power-detectors/non-rms-responding-detector/ad8319.html"&gt;AD8319&lt;/a&gt;.
It works from 1 MHz to 10 GHz, has good accuracy, dynamic range and temperature
stability. Package is 8-pin SMD package that can fit on a 2 layer PCB. Using
2 layer PCB instead of 4 would halve the price of the PCB.&lt;/p&gt;
&lt;p&gt;I chose to use
&lt;a href="http://www.nxp.com/products/microcontrollers-and-processors/arm-processors/lpc-cortex-m-mcus/lpc-cortex-m0-plus-m0/lpc1100-cortex-m0-plus-m0/scalable-entry-level-32-bit-microcontroller-mcu-based-on-arm-cortex-m0-plus-m0-cores:LPC11U68JBD48"&gt;LPC11U68&lt;/a&gt;
microcontroller to interface to the computer.  It is very cheap, has USB,
doesn't need external programmer and I'm already familiar with some of the other
LPC family processors so the programming should be easy. It is available in
48-pin QFP package which also can be routed on 2 layer PCB.&lt;/p&gt;
&lt;p&gt;Only a voltage regulator IC is needed to complete the design. Having had a good
experience using &lt;a href="http://www.ti.com/product/LP5907"&gt;LP5907&lt;/a&gt; regulator in my VNA
I decided to use the same regulator on this project. It has very good power
supply rejection ratio and low noise for the price.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sma_connector.jpg" width="1918" height="995" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;RF traces on PCB. SMA connector on right,
    matching components and input to power detector IC.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;On a 4 layer board SMA connector needed a &lt;a href="http://hforsten.com/6-ghz-frequency-modulated-radar.html#sma-connector-interface"&gt;cutout under the signal
pad&lt;/a&gt;
because 50 ohm microstrip line is only 0.34 mm wide, while signal pad is 1.5 mm.
Without the cutout, pad would work as very low impedance transmission line causing
reflections. Cutout removes the closest ground under the pad raising its
impedance. 2 layer board is so thick that the 50 ohm microstrip is about 3 mm
thick and instead ground plane on the top side needs to be brought closer to the
signal pad to decrease the impedance of the line.&lt;/p&gt;
&lt;p&gt;3 mm microstrip line would be very wide and make it hard to interface with
components. Changing microstrip to grounded CPW with 0.2mm ground gap can be used to
reduce the line width to 1.5 mm. While it's still quite wide, it's easier to
interface with components than 3 mm line.&lt;/p&gt;
&lt;h1 id="manufacturing"&gt;Manufacturing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/pcb.jpg" width="1328" height="759" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Bare PCB from &lt;a href="https://oshpark.com/"&gt;OSH
    Park&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/paste.jpg" width="1600" height="938" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Manually dispensed solder paste on PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/components.jpg" width="1600" height="930" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Components added on top of solder paste.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I didn't feel like I needed a stencil on this simple board, so I dispensed the
solder paste manually. I did have few short circuits on the microcontroller pins
that I fixed manually, but otherwise it worked fine. Soldering was done on my
&lt;a href="http://hforsten.com/toaster-oven-reflow-controller.html"&gt;reflow oven made from toaster oven&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;p&gt;I don't have a reference power detector to measure the absolute accuracy of the
detected power, but I can use my VNA to generate a low frequency signal and
measure its voltage with oscilloscope and power with power detector. Power of
the signal can be calculated from the oscilloscope voltage measurement and it
should be more accurate than the power detector reading.  Power detector reading
can then be adjusted so that it shows the same power. This method only corrects
the reading at one frequency, but datasheet of the power detector has
calibration curves that show how the power changes as function of frequency.
There's still issue with frequency dependent losses on the PCB, but those can be
assumed to be small. As a result power detector should be sort of accurate.&lt;/p&gt;
&lt;p&gt;Power detector has limited range of powers that it can measure accurately. I can
use my VNA to also measure the linearity range of the power detector. VNA has
&lt;a href="http://www.psemi.com/newsroom/new-products/927482-pe43711-glitch-less-digital-step-attenuator"&gt;PE43711&lt;/a&gt;
0 - 31.25 dB variable attenuator that controls the output signal power.
Attenuator is much more linear than the power detector, so measuring the output
power of the VNA with different attenuation settings should reveal how good the
power detector linearity is.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/ext_pdet_0.1.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;VNA power sweep at 100 MHz measured with power detector. 20 dB attenuator was used at the detector input.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Power detector is connected to the output of the VNA with 20 dB fixed attenuator
in between to limit the power to detector. I wrote a quick Python script to step
the attenuator values and measure the power at the output of the VNA using power
detector. At 100 MHz the linearity seems to be quite good. Since there is a 20
dB attenuator before the power detector, maximum output power of the VNA is
about 13 dBm at this frequency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/ext_pdet_1.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At 1 GHz power output of the VNA drops a little bit and the linearity can be
seen to drop a little bit below -40 dBm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/ext_pdet_6.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At 6 GHz the output power of the VNA drops further. Now the maximum output power
is under 0 dBm. It could be raised few dBm by increasing the output power of the
PLL, which was set to medium power setting for this measurement. Below -45 dBm
reading starts to significantly deviate from linear.&lt;/p&gt;
&lt;h1 id="input-matching"&gt;Input matching&lt;/h1&gt;
&lt;p&gt;Input matching of the detector chip itself is very high impedance, so the
matching suggested by the datasheet is 50 ohm resistor to ground before the
detector input. I used the suggested matching on my PCB since I wanted it to
have a good matching over wide bandwidth.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/detector_s11.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Input reflection coefficient of the power
    detector measured with my VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While the matching at low frequencies is very good, at high frequencies it
quickly becomes very poor. At 4.5 GHz matching is only -3.4 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/matching_block.png" width="938" height="243" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of the input matching.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Cause of the poor matching at high frequencies is the TX line 2 before the power
detector input. At low frequencies length of the transmission line is
electrically short and looking from the SMA detector board looks like high
impedance power detector in parallel with 50 ohm resistor, which is close to 50
ohms giving good matching. At high frequencies the transmission line isn't
anymore electrically short and when it is 90 degrees long the high impedance of
power detector looks like a very small resistance (Transmission line works as
a &lt;a href="https://en.wikipedia.org/wiki/Quarter-wave_impedance_transformer"&gt;quarter-wave
transformer&lt;/a&gt;).
When a short circuit is in parallel to the 50 ohm resistor, result is still
a short circuit resulting in a poor matching.&lt;/p&gt;
&lt;p&gt;At 6 GHz 90 degrees long grounded CPW transmission line is about 7 mm long.
Trace from resistor to the detector input is about 4 mm long and there are also
1.6 mm tall vias from component ground pad to bottom ground of the PCB. Power
detector also has some transmission line inside it from wirebonds inside the
package and the total transmission line length is more than 90 degrees at high
frequencies.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/det_model.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulation model and measurement of the input
    matching.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After fitting the second transmission line parameters to the measured results
the simulated input matching agrees very close to the measured matching. Power
detector was simulated as 1000 ohm resistance, which is correct at low
frequencies. At high frequencies resistance is about 100 ohms which explains
the slightly better measured results.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/pdet_layout.png" width="685" height="447" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Layout of the RF input.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Looking at the PCB layout of the power detector there are few ways to modify it
to improve the matching.  Especially RF grounding of the power detector isn't very
good.  On 2 layer board vias are 1.6 mm tall, which is a long distance at high
frequencies. One way to improve the grounding would be to connect top side
grounds of the input transmission line to directly to the ground pad of the
component. This way ground current doesn't have to go through vias. Although
even with this change matching wouldn't still be that good at high frequencies.
Input DC block capacitor could also be changed to 0201 package so that 50 ohm resistor
can be mounted closer to the input.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/pdet_layout_v2.png" width="672" height="454" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Improved layout.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In above picture the grounding of the component is improved by connecting the
top side ground to component ground pad (Both sides of the pin 1).&lt;/p&gt;
&lt;h1 id="scalar-network-analyzer"&gt;Scalar network analyzer&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sna_block.png" width="1160" height="502" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Scalar network analyzer block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using two directional coupler, two power detectors and RF signal source a scalar
network analyzer can be made. Two directional couplers are connected so that
other measures output power of the signal source and other measures the
reflected power from the device under test. Ratio of the reflected and
transmitted power gives the reflection coefficient of the device being tested.&lt;/p&gt;
&lt;p&gt;I don't really need a scalar network analyzer since I have a VNA that has much
better accuracy. It is however a nice project and I have all the necessary parts
to make it already, so why not?&lt;/p&gt;
&lt;p&gt;Similarly to the &lt;a href="http://hforsten.com/cheap-homemade-30-mhz-6-ghz-vector-network-analyzer.html"&gt;VNA I made
before&lt;/a&gt;
it can be used to measure reflection coefficient of the device under test.
Difference between vector and scalar network analyzer is that scalar network
analyzer can only measure the magnitude of the reflection coefficient while
vector network analyzer can also measure the phase of the reflected signal.
Measured phase can be used to calibrate the instrument enabling it to measure
with much higher accuracy, as the scalar network analyzer can't measure phase it
can't be calibrated the same way VNA can be and as a result it isn't going to be as
accurate.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sna.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Scalar network analyzer connection.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I have already made a double directional coupler board with the same directional
coupler that was used in my VNA. VNA can be used as
a signal source and of course the power detectors are the ones I have just made.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sna_standards.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Load, open and short measured using scalar
    network analyzer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are measured results of short, open and load. Ideally open and short would
be straight lines at 0 dB and load would have very low reflection coefficient.
Clearly this is not the case here and especially short and open aren't 
straight lines, but instead have high ripple on them.&lt;/p&gt;
&lt;p&gt;Directivity of the directional coupler is about 20 dB, which means that
transmitted power going through the directional coupler measuring the reflected
power will couple to the power detector with level of 20 dB less than if it
would be going the right way. This means that even if the DUT absorbs the signal
perfectly without reflecting anything, reflected power is measured to be 20 dB
below the transmitted power due to directivity error. This is exactly what is
observed when measuring the load.&lt;/p&gt;
&lt;p&gt;Ripple in short and open measurements is also caused by directivity error of the
directional couplers. Some of the transmitted power from signal source is
coupled to the power detector measuring the reflected power and some of the
reflected power is measured by the power detector measuring the transmitted
power. If the right way coupled and wrong way coupled signals are in phase they
will add up and power detector measured bigger power than without the
directivity error. If the phases are opposite, signals partially cancel and
measured power is lower. As the phase of the reflected signal varies as
a function of frequency it causes the observed ripple pattern on the plot.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_forward.png" width="768" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured S-parameters of the coupler board from
    input to coupling port.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;At low frequencies the open and short measurements are very noisy and load seems
to have very poor matching. These are caused by the too low coupled power to the
power detectors. Coupler board I'm using has low coupling at low frequencies and
dynamic range of the power detectors isn't good enough to measure the low power
and it shows the noise floor.&lt;/p&gt;
&lt;p&gt;Ideal open and short have reflection coefficients of 1 and -1. All of the power
is reflected and phase difference between them is 180 degrees. When average of
short and open measurements is taken the directivity error is cancelled. As the
standards have high reflection coefficient and they don't absorb any power,
their averaged measurement measures the losses in the measurement system.
Subtracting the average of open and short measurements from other measurements
can then be used to correct for the losses in system. It will also correct for
the mismatch in sensed power of the two power detectors.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sma_reflection_tracking.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Reflection tracking error calculated from the
    previous measurements by averaging open and short measurements.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/standards_tau.png" width="960" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Same measurement as above, but with reflection
    tracking corrected.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Even with the reflection tracking corrected, ripple on the traces doesn't go
anywhere because directivity and matching errors remain uncorrected.
Without measuring the phase of the signal they can't be corrected. Accurate
measurements using scalar network analyzer require good directivity and good
matching from all the components.&lt;/p&gt;
&lt;h2 id="measuring-sma-dipole-antenna"&gt;Measuring SMA dipole antenna&lt;/h2&gt;
&lt;p&gt;In order to have something to test the scalar network analyzer with I made
a simple half-wave dipole antenna by soldering two wires on SMA connector.
Target frequency is 2 GHz, which means that the length of the quarter-wave long
wires should be about 37.5mm. Really the wires should be little shorter due to
gap between the wires on connector increasing the antenna length by few mm.
Thickness of the wires also causes some electric field to fringe also to the ends of
the wires increasing their electrical length. It's expected that resonance
frequency is little lower than 2 GHz due to these effects.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/dipole_connector.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;End-launch SMA connector from China. Not very
    high quality.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/dipole_wires.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Wires are ordinary single-core copper wire.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/dipole.jpg" width="1600" height="1200" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finished SMA dipole antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sma_dipole_loss.png" width="1032" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured return loss of the SMA dipole.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Measuring the return loss with the scalar network analyzer connection described
above gives the above plot of the return loss. While the quality of the
measurements isn't very good it can be seen that there seems to be resonance
little below 2 GHz as expected and also other resonance around 5.5 GHz.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/detector/sma_dipole_vna.png" width="826" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMA dipole measured with VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For comparison above is the same SMA dipole measured with my VNA. Even though it
has similar directional coupler used with the scalar network analyzer
measurements, results are much better because of vector error correction
allows correcting for the matching and directivity errors.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Power detectors seem to work as designed except that input matching isn't very
good at high frequencies. Absolute accuracy is questionable as I don't have
a reference to compare it to.&lt;/p&gt;
&lt;p&gt;Scalar network analyzer can't really compare to vector network analyzer. It can
be made for much cheaper though and could be good enough for measuring antennas
for hobbyist use.&lt;/p&gt;
&lt;p&gt;You can find the hardware drawing, firmware and PC software on &lt;a href="https://github.com/Ttl/detector"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Cheap homemade 30 MHz - 6 GHz vector network analyzer</title><link href="https://hforsten.com/cheap-homemade-30-mhz-6-ghz-vector-network-analyzer.html" rel="alternate"></link><published>2016-08-02T00:00:00+03:00</published><updated>2016-08-02T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2016-08-02:/cheap-homemade-30-mhz-6-ghz-vector-network-analyzer.html</id><summary type="html">&lt;p&gt;Vector network analyzers are used to measure high frequency circuits, unfortunately they are too expensive for student budget so I decided to make one myself for small budget.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Vector network analyzer (VNA) are used to measure scattering parameters of high
frequency circuits. When frequency is high enough the reflections of the waves
start to matter and distributed effects need to be taken into account. VNA can
be used to analyze reflection and transmission coefficients of circuits at high
frequencies.&lt;/p&gt;
&lt;p&gt;For example ideally antenna would radiate all the energy it gets, but all
antennas reflect some of the energy back to the source and only radiate energy
at certain frequencies. With VNA amount of energy reflected as function of
frequency can be measured. Amplifiers also reflect some energy from both input
and output and have some amount of gain. All of which can be measured using VNA.&lt;/p&gt;
&lt;p&gt;Unfortunately VNAs are often very expensive and way out of my budget. Newest
cutting edge VNAs with very wide frequency band can have insanely high cost. For
example starting price of Anritsu's 110 GHz VectorStart ME7838A VNA is
&lt;a href="http://www.prnewswire.com/news-releases/anritsu-company-expands-measurement-capability-of-vectorstar-110-ghz-broadband-vna-system-159639545.html"&gt;$575,850&lt;/a&gt;.
Even used VNAs for lower frequencies are often several thousand dollars. At ebay
cheapest used 6 GHz two port VNAs seem to sell for about 2,000€, still way more
than I'm willing to pay.&lt;/p&gt;
&lt;p&gt;Since I can't afford even a used VNA I decided to make one myself with a budget
of 200€, tenth of what they cost used and about 1/100 of what they cost new. Of
course it isn't going to be as accurate as commercial VNAs, but I don't need that
high accuracy and it's a good learning experience anyway.&lt;/p&gt;
&lt;h1 id="block-diagram"&gt;Block diagram&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vna_general_block.png" width="956" height="1026" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;General block diagram of VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="math"&gt;$$ S_{11} = \frac{\text{A}}{\text{RX1}} $$&lt;/div&gt;
&lt;div class="math"&gt;$$ S_{21} = \frac{\text{B}}{\text{RX1}} $$&lt;/div&gt;
&lt;p&gt;So how does VNA measure reflection and transmission of signals? Operating
principle is simple, but implementation is more challenging. Theoretically VNA
consists of signal source that is used to excite the device under test (DUT),
two directional coupler per port that measure transmitted and reflected waves
and detectors at the end of the couplers that can measure both amplitude and
phase of the signals.&lt;/p&gt;
&lt;p&gt;Signal source generates a test signal which is routed to one of the ports. Part
of the signal is coupled by the receiver directional coupler and its phase and
amplitude are measured. Rest of the signal goes out of the VNA port and into the
device under test. Some of the signal is reflected back to the source port and
it is measured by another directional coupler. Ratio of reflected power to
transmitted power is used to calculate the reflection coefficient of the DUT.&lt;/p&gt;
&lt;p&gt;Non-reflected part of the signal goes through the DUT and can either be
attenuated or amplified after passing through the device. When the test signal
comes out of the DUT, part of the signal is coupled by the directional coupler on
the second port and its phase and amplitude are measured. Rest of the signal
passes to the termination where it is absorbed. Transmission coefficient is
calculated as ratio of received power to transmitter power.&lt;/p&gt;
&lt;p&gt;When measurement is repeated with the source switch connected the other way,
reflection and transmission coefficients of the DUT can be measured from the
other direction.&lt;/p&gt;
&lt;p&gt;However in practice measurement isn't so simple. Biggest difference is length of
the transmission lines inside the VNA and cables connecting the DUT causing loss
and affection the measured phase. At 6 GHz wavelength on PCB is about 3 cm. For
the phase difference between receivers to negligible distances from source,
couplers and DUT should be much smaller than that. Especially cables connecting
the VNA to the DUT need to be much longer than that so that device can be
connected. There is also losses on cables, couplers and transmission lines
inside the VNA. Directional couplers aren't perfectly directional and they
couple also some signal coming from the other direction. Source and load
matching aren't going to be perfect and will also reflect some signal back.
There are also reflections from the internal components of the VNA. All of the
errors also have some frequency dependence.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vna_general_block_errors.png" width="956" height="1026" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram with some of the error sources
    drawn.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;However situation isn't hopeless as all of the error terms can be solved from
measurements of devices with known reflection and transmission coefficients. When error
terms are known, real reflection and transmission coefficients can be solved
from the measurements. Usually very accurately characterized
short, open and load standards are measured on both ports and through line is
used to calibrate the transmission from port to port.&lt;/p&gt;
&lt;h2 id="four-receiver-vna"&gt;Four receiver VNA&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vna_4receiver.png" width="2761" height="2536" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of common two port, four receiver VNA. Most of the commercial VNAs work like this.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If you want to see a more detailed block diagram of VNA, take a look at for
example PNA-X Service Manual N5242-90001. On page 119 there is a very detailed
block diagram of the RF parts. Above is a simplified version of the diagram.&lt;/p&gt;
&lt;p&gt;Source is implemented using a phase locked loop and often frequency multipliers are
used to reach the higher frequencies. To keep the output power level constant as
a function of frequency, output power after the output amplifier is measured and
attenuator before the amplifier is adjusted until the sensed power is correct.&lt;/p&gt;
&lt;p&gt;Power coupled into the directional couplers is high frequency and it needs to be
mixed down before it can be detected. &lt;a href="https://en.wikipedia.org/wiki/Superheterodyne_receiver"&gt;Super heterodyne
receiver&lt;/a&gt; with one
intermediate frequency is often used receiver architecture that avoids
complications with mixing straight to the DC. In this case the signal exists
only at one frequency and this allows setting the intermediate frequency very
low, about few MHz, and doing the final mixing digitally. Digital mixing has
advantage over analog implementation in that while no analog component can be
perfect, digital mixing can be made as accurately as needed. Analog mixers add
noise, phases of the LO signals of two mixers aren't perfectly equal,
performance varies as a function of temperature and operating voltage and so on.
None of these errors exist with digital mixing and measured result is much more
accurate.&lt;/p&gt;
&lt;p&gt;While this is a good architecture for making a VNA, it has a drawback of needing
many expensive components. 30 MHz - 6 GHz mixer costs about 10€, high
accuracy ADCs about 10 - 20 €, fast microcontroller, or better, FPGA is needed
to interface to the ADCs, control switches, toggle other signals and communicate
with computer. Just these components cost at least 100 € and many more
components are still needed such as PLLs, oscillators, filters, PCBs, power
converters and so on. Whole board would be way too expensive so something has to
be removed to save money.&lt;/p&gt;
&lt;h1 id="single-receiver-vna"&gt;Single receiver VNA&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vna_block.png" width="2761" height="1972" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of my VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Most radical way to simplify the block diagram is replacing the receivers with
single receiver and SP4T switch. This removes three ADCs, mixers and filters
while adding a single switch. Signal processing is also simplified since
now we must only measure one ADC instead of four. This change does have some
drawbacks. Firstly the SP4T switch isn't perfect and it will have some leakage
between the receiver channels. In theory it can be calibrated out, but it will
reduce dynamic range of the measurements. Secondly previously all of the four
channels could be measured at the same time, but now only one channel can be
measured at once. This increases the time required to measure a single
frequency sweep by four times.&lt;/p&gt;
&lt;p&gt;Local oscillator can also be simplified as harmonics and exact power level
doesn't matter that much as long as it is withing the specifications of the
mixer.&lt;/p&gt;
&lt;p&gt;In practice leakage from the SP4T switch is going to be a problem with
calibration. Normal VNAs use high quality components and crosstalk between the
ports can be assumed to be non-existent. With this architecture unless care is
taken to minimize the crosstalk (which would require increased cost),
it can't be assumed to be zero. Normal calibration procedures are unable to
correct for it and there will be errors in the final measurements. There are
more complicated special calibration procedures that can correct the leakage,
so calibrating is still possible.&lt;/p&gt;
&lt;h1 id="design"&gt;Design&lt;/h1&gt;
&lt;h2 id="directional-coupler"&gt;Directional coupler&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler.png" width="722" height="232" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Coupled line coupler. When wave is passed from
    port 1, most of it goes through to the port 2 and some of it will be coupled to
    the port 3. When wave is passed from port 2, most of it will again go
    through to port 1, but some of it will couple. This time the coupled wave
    will be absorbed by the termination resistor and ideally nothing is detected
    on port 3.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The heart of the VNA are the directional couplers. In theory they can be made
simply with two lines side by side with small gap between them. When a high frequency
wave passes through one of the lines some signal couples to the nearby line. The
coupled wave also prefers to go in one direction and ideally nothing would go to
the other direction, but in reality there is small signal also to the other
direction. Ratio of how much power goes to right direction compared to the power
going to the wrong direction is called directivity. A good coupler can have
directivity of more than 30 dB (one thousandth of power going to the wrong
direction).&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler.jpg" width="640" height="353" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Microstrip coupler from my radar.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;How does the directivity affect the accuracy of VNA? If the directivity is 0 dB,
then the coupler can't really be called directional anymore and transmitted and
reflected waves can't be separated. Result is that VNA can't measure anything.
If the directivity is poor, but above 0 dB, there is some error in the
measurements. It can be calibrated out, but the dynamic range of receiver is
limited and accuracy is reduced. So for accurate VNA we need to have as high
directivity as possible.&lt;/p&gt;
&lt;p&gt;Turns out that stripline coupler, where lines are on the internal layers of the
PCB, can be made with higher directivity than microstrip coupler where lines are
on the top side of the PCB. I don't really know the exact reason myself, it has
something to do with that on microstrip one side is PCB with high relative
permittivity and other side is air with lower permittivity. Stripline on the
other hand is embedded inside PCB and so it has same material on both sides.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/cst_coupler.png" width="1082" height="532" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;3D simulation model of two stripline directional couplers.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_sparam_sim.png" width="826" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated coupling in forward (S31) and reverse (S41) directions. S-parameters are plotted with &lt;a href="http://scikit-rf-web.readthedocs.io/"&gt;scikit-rf&lt;/a&gt; python package.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Simulated directivity is 20 dB at low frequencies and 30 dB at high frequencies.
Coupling decreases very rapidly at lower frequencies due to coupling lines being
electrically very short compared to wavelength. Coupling at low frequencies can
be improved by making a multi-stage coupler. I decided to not make one, because
it would have required more PCB area and even single stage couplers are pretty big.
As a result of it dynamic range at low frequencies is going to be poor. I'm more
interested at high frequencies so reduced accuracy at lower frequecies doesn't
matter too much.&lt;/p&gt;
&lt;p&gt;There is still a potential problem with making a connection from microstrip
trace on the top side to internal layer of the PCB. A via is needed, but does it
work with low enough reflections at these frequencies?&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/vias.png" width="1068" height="545" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;3D simulation model of via.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I made a simulation of a via structure with one signal via and two ground vias
close to it. It's important to add ground vias close to the signal via so that
ground current can also change layers without needing to go too far.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/via_sparam.png" width="826" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated return loss of the vias. Insertion loss
    is about 0.4 dB.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;According to the simulations there doesn't seem to be any problems with this
kind of via connection and return loss is very good. I even simulated it up to 16
GHz and according to the simulation it still works well at that frequency.&lt;/p&gt;
&lt;h2 id="source-and-local-oscillator"&gt;Source and local oscillator&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/pll.png" width="813" height="434" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PLL block diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To accurately generate frequencies at GHz range a phase locked loop is needed.
It can generate accurate signals using a feedback loop that compares output
frequency of voltage controlled oscillator (VCO) divided by N to stable low
frequency reference clock.  Feedback loop tries to make these frequencies equal
by adjusting the tuning voltage of the VCO. Result is that output frequency of
the VCO is N times the reference clock frequency. By cleverly changing the
divider value fractional division values can be realized.&lt;/p&gt;
&lt;p&gt;Problem with wideband signal generation using PLL is that good quality voltage
controlled oscillators are usually not very wideband. To generate signals over
 wide bandwidth there are several different options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frequency multipliers and dividers can be used to extend frequency range.
Disadvantage of this approach is that they both generate high number of
harmonics so filtering is needed.&lt;/li&gt;
&lt;li&gt;Two PLL outputs can be mixed together to generate sum and difference
  frequencies. This also generates harmonics and needs a second PLL and mixer.&lt;/li&gt;
&lt;li&gt;Multiple switched VCOs can be used. This requires logic for choosing a correct
  VCO and of course many VCOs are needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily this problem has already been solved and there are several commercial
PLL chips with integrated VCO bank and output frequency dividers. Some suitable
ones are
&lt;a href="http://www.analog.com/en/products/rf-microwave/pll-synth/plls-w-integrated-vcos/adf4355.html#product-overview"&gt;ADF4355&lt;/a&gt;
which can generate output frequencies from 54 MHz to 6.8 GHz and
&lt;a href="https://www.maximintegrated.com/en/products/comms/wireless-rf/MAX2871.html"&gt;MAX2871&lt;/a&gt;
which can generate frequencies from 23.5 MHz to 6.0 GHz.&lt;/p&gt;
&lt;p&gt;MAX2871 is a cheaper choice and it has a suitable frequency range, so I choose
to use that. It has multiple VCOs and frequency dividers. Especially at lower
frequencies frequency dividers can generate high harmonics and some filtering is
needed to clean the signal. Because frequency band is so wide multiple filters
are needed to cover it all without passing harmonics. I decided to use four
filters working at 0 - 1.1 GHz, 1.1 - 2.1 GHz, 2.1 - 4.2 GHz and 4.2 - 6.0 GHz.
More would be better especially at lower frequencies, but they would soon become
too expensive compared to the advantage of having them. Four is a good choice
from cost perspective, since RF switches with more than four poles are often
more expensive than switches with less poles as there are less use for such
switches. Connecting multiple switches in series is possible, but would need
more PCB area.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/source_block.png" width="1470" height="1152" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;More detailed block diagram of the source.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the filters there is power leveling circuits consisting of variable
attenuator, RF amplifier and power detector.&lt;/p&gt;
&lt;p&gt;Variable attenuator is
&lt;a href="http://www.psemi.com/newsroom/new-products/927482-pe43711-glitch-less-digital-step-attenuator"&gt;PE43711&lt;/a&gt;
that can be configured to have attenuation from 0 dB to 31.75 dB in steps of
0.25 dB.&lt;/p&gt;
&lt;p&gt;RF amplifier needs to be very wideband, I chose to use
&lt;a href="http://www.ti.com/product/TRF37A75"&gt;TRF37A75&lt;/a&gt; RF amplifier. It's a cheap 40 to
6000 MHz amplifier with 12 dB gain. Gain is very stable as function of
frequency, it varies only about 3 dB over the whole frequency range. Output
matching however could be better as it is only -7 dB at 6.0 GHz and reflections
from the amplifier output will cause some errors in measurements. Now that
I think it would have been a good idea to add some places for matching
components so that matching could be improved afterwards, but it's too late for
that now.&lt;/p&gt;
&lt;p&gt;At the amplifier output there is a power detector connected using a 100 ohm
resistor. The resistor works as a -10 dB coupler and capacitor is used for DC
blocking.&lt;/p&gt;
&lt;p&gt;Microcontroller is used as a part of the feedback loop. It could be done using
operational amplifier based circuit, but it can't be used in this case. Power
level of the source must remain constant during the measurement of different
receiver channels or else there will be uncorrectable errors in the
measurements.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/pll_pcb_labels.jpg" width="1600" height="1427" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PLL, filters and power leveling circuits on PCB. PLL has its own voltage regulator to reduce interferences. Big component under the detector text is actually ADC. Detector is the small 1.2 x 0.8 mm black box right of it.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a picture of the source components on PCB. Especially around the
amplifier area the components couldn't be placed more closely together. I even
placed some DC blocking capacitors at 45 degree angle so that I could save few
mm of space. I decided to add power leveling only after routing the rest of the
PCB so space got little tight.&lt;/p&gt;
&lt;p&gt;Local oscillator uses another MAX2871 chip to generate signal for the receiver
mixer. Filtering this signal isn't necessary as harmonics don't degrade
performance of the mixer. Power leveling isn't needed either as long as the
power level stays within correct range. For accurate frequency generation it's
important to use same reference clock for both source and LO. ADC sampling clock
should also be derived from the same reference for the best accuracy. If they were
to use different references, they could drift relative to each other. Using same
reference means that even if the reference drifts, it will cancel out after
sampling as all the frequencies drift the same amount.&lt;/p&gt;
&lt;h2 id="receiver"&gt;Receiver&lt;/h2&gt;
&lt;p&gt;Receiver consists of SP4T switch, mixer, local oscillator, anti-aliasing filter
and ADC.&lt;/p&gt;
&lt;p&gt;Switch causes crosstalk between the channels reducing the accuracy of
measurements. A very high isolation switch would be ideal for the best performance.
Low loss is also welcome as high loss reduces the dynamic range, but high
isolation is more important. Switch also needs to be absorptive instead of
reflective on non-switched ports and of course work from 30 MHz to 6 GHz.&lt;/p&gt;
&lt;p&gt;It would also be possible to use three SP2T switches and it would probably give
better isolation. However single SP4T switch is simpler and optimizing
performance isn't that critical. It's better to keep design simple at this
point and optimize performance in next revisions.&lt;/p&gt;
&lt;p&gt;I chose to use Peregrine
&lt;a href="http://www.psemi.com/products/rf-switches/pe42441"&gt;PE42441&lt;/a&gt; SP4T switch that
has about 40 dB isolation. It does affect the accuracy, but hopefully not too
much. Commercial VNAs have often very high &amp;gt;100 dB isolation
between the ports, so this is very poor compared to them.&lt;/p&gt;
&lt;p&gt;To my knowledge there is only one possible affordable mixer that can be used
over the whole frequency range. It is the same
&lt;a href="http://www.analog.com/en/products/rf-microwave/mixers/single-double-triple-balanced-mixers/adl5801.html"&gt;ADL5801&lt;/a&gt;
mixer that I used on my radar.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/mixer_balun.png" width="332" height="176" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Recommended mixer input connection.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Mixer has a balanced input and datasheet recommends using a balun to convert
single-ended signal to differential signal for mixer input. Issue is that
most of the baluns are narrowband, while this application needs a balun capable of
operating over the whole frequency range from 30 MHz to 6 GHz. There is one
balun from MiniCircuits that is capable of operating over the whole frequency
range, but it costs 7€ a piece and I would need to order it directly from
MiniCircuits.&lt;/p&gt;
&lt;p&gt;Other option is to leave the balun out and drive the mixer single endedly. Other
unused output is connected to ground through a capacitor. Since single ended
input impedance of mixer is 25 ohms and the system impedance is 50 ohms, return
loss of the mixer is going to worsen. Conversion gain and linearity will also
suffer. It does however save 7€ (+shipping) and PCB area so it seems
worth it.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/filter.png" width="961" height="508" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;IF filter.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;LO frequency is set to 2 MHz below (or above, doesn't matter) source frequency.
This gives a 2 MHz IF signal which is easy to sample and filter. On &lt;a href="http://hforsten.com/homemade-synthetic-aperture-radar.html"&gt;my
radar&lt;/a&gt; filter could
have been more aggressive and this time I added some optional filtering
components that can be added if needed. Differential amplifier buffers the
signal for ADC and slightly amplifies it. Final RC filter before ADC further
attenuates high frequencies above sampling frequency to avoid aliasing.&lt;/p&gt;
&lt;p&gt;DITHER signal is a noise signal that can be added to ADC input. Why add
deliberately noise to the signal? Consider the case where signal to be measured
is so small that peak-to-peak value is less than one ADC least significant bit.
Then the ADC might only output constant value and nothing is detected. If a noise
signal is added then some changes are always seen at the ADC output. This time
the signal to be measured can affect the output value of ADC. If noise and
signal have different frequencies then the noise signal can be filtered out.
Since signal affected some of the output values it can be detected after
filtering. So by adding noise dynamic range of the ADC was increased.&lt;/p&gt;
&lt;p&gt;Find out more at this &lt;a href="http://www.analog.com/library/analogDialogue/archives/40-02/adc_noise.html"&gt;Analog devices
article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Good idea, but turns out that due to leakage between the channels there is
always enough signal to be detected so it isn't needed.&lt;/p&gt;
&lt;h2 id="digital-logic"&gt;Digital logic&lt;/h2&gt;
&lt;p&gt;Microcontroller (or FPGA) is needed on board to handle communication with
computer and control all the devices. Since only one ADC is needed with
the switched receivers I decided to save money and look for a microcontroller
with integrated ADC.&lt;/p&gt;
&lt;p&gt;Most suitable one I could find was &lt;a href="http://www.nxp.com/products/microcontrollers-and-processors/arm-processors/lpc-cortex-m-mcus/lpc-cortex-m4/lpc4300-cortex-m4-m0/32-bit-arm-cortex-m4-plus-2-x-m0-mcu-282-kb-sram-ethernet-two-hs-usbs-80-msps-12-bit-adc-configurable-peripherals:LPC4370FET100"&gt;NXP
LPC4370&lt;/a&gt;.
It has one ARM-Cortex M4 core and two Cortex-M0 cores, high speed (480 Mb/s) USB
support and 80 MHz 12-bit ADC. Maximum clock frequency is 204 MHz, so it should
be fast enough.&lt;/p&gt;
&lt;p&gt;This part has the fastest integrated ADC that I have seen on any microcontroller.
External ADCs with same clock rate and bit depth cost more than this whole
microcontroller, so the cost savings are considerable. It's available in
 hobbyist unfriendly 100 and 256 ball BGA packages.&lt;/p&gt;
&lt;h2 id="voltage-regulation"&gt;Voltage regulation&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/power_supplies.png" width="1420" height="920" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of power supplies.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For high accuracy measurements it's important that there is no noise on the
power supply that can make its way into the signal path. For my radar I made
a mistake of using switching mode power supply for analog parts and ADC. While
the noise was not very high, it was still detectable.&lt;/p&gt;
&lt;p&gt;This time I decided to pay extra attention into the power supply design to
minimize interferences. Still I didn't want to go too far, since high quality
components can be expensive. For example shielding the RF parts would be
helpful, but would cost too much.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/supplies.png" width="1018" height="691" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Switching mode power supplies.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Power consumption of the board is about 3 W. Power consumption limit from USB
bus is 0.5 W, so external supply is needed. 12 V is common AC adapter output
voltage so I chose to use that as input voltage. There is some filtering right
after the DC plug after which power goes to two L7980 switching mode power
converters. Special feature of L7980 regulator is that switching frequencies of
two different regulators can be synchronized by connecting two pins on the
regulators together. Switching frequencies will synchronize in such a way that
phase difference between the regulators is 180°. This results in lower input
ripple as current draw from the input filtering capacitors is spaced apart.
Having synchronized switching frequency is useful as there is noise only at one
frequency on both 5V and 3.3 V rails.&lt;/p&gt;
&lt;p&gt;If the frequencies wouldn't be synchronized mixing products of the different
switching frequencies could be created. One path which this can happen is that if
the power inductors are placed close to each other magnetic fields on the
inductors can couple and switching noise is injected between them causing sum
and difference terms of the switching frequencies to be detected at outputs of
both of the regulators. Another path that switching noise can travel is through
the shared input. As regulator switches it draws current and input voltage
decreases a little bit. Because regulators share input voltage, noise is injected
to output of the other regulator.&lt;/p&gt;
&lt;p&gt;Care must also be taken when connecting a linear regulator after a switching
regulator. Goal of this connection is usually to have good efficiency of the
switching regulator and low noise of the linear regulator. Noise reduction can
be much less than thought if power supply rejection rate (PSRR) of linear
regulator isn't high enough at the switching frequency.&lt;/p&gt;
&lt;p&gt;For example if we place
&lt;a href="http://ww1.microchip.com/downloads/en/DeviceDoc/20001826C.pdf"&gt;MCP1700&lt;/a&gt; 3.3
V linear regulator after switching power supply that has a switching frequency
of 100 kHz we will find that adding the linear regulator had barely any effect.
Switching noise after the regulator is same as before it.&lt;/p&gt;
&lt;p&gt;If we replace the linear regulator with
&lt;a href="http://www.ti.com/lit/ds/symlink/lp5907.pdf"&gt;LP5907&lt;/a&gt; the switching noise after
the linear regulator is gone. What's the difference between these two linear
regulators?&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/psrr_mcp1700.png" width="733" height="484" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PSRR of MCP1700.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/psrr_lp5907.png" width="814" height="549" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PSRR of LP5907.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Looking at the power supply rejection rate plots on the datasheets of the
devices we find that at 100 kHz MCP1700 has PSRR of about 0 dB. All the noise at
this frequency just passes through. LP5907 has PSRR of -60 dB. For example
a very large 0.1 V noise at the input would only be 100 µV at the regulator
output.&lt;/p&gt;
&lt;p&gt;MCP1700 does still have use cases. It is much cheaper and would be a good choice for
powering digital systems where noise doesn't matter that much.&lt;/p&gt;
&lt;h1 id="schematic-and-layout"&gt;Schematic and layout&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/vna/vna_sch.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/vna/sch.png" width="1338" height="929" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for PDF.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/layout.png" width="843" height="912" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Layout in KiCad.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;PCB was designed with &lt;a href="http://kicad-pcb.org/"&gt;KiCad&lt;/a&gt;. New push-and-shove router
is really helpful when routing a tight board like this.&lt;/p&gt;
&lt;p&gt;This PCB is a personal record in saving PCB area. Size of the PCB is 52 mm x 57
mm and it has over 300 components mounted on both sides. It could have been made
even smaller if it wasn't for the big directional couplers. Stripline
construction requires that layers above and below it are left empty wasting
a lot of space.&lt;/p&gt;
&lt;h1 id="soldering"&gt;Soldering&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/pcbs.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Fresh PCBs. Manufactured by &lt;a href="https://oshpark.com/"&gt;OSH Park&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/backside.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;First I soldered the passive components on the
    backside.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/stencil.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Stencil on the top side. Stencil is made by &lt;a href="https://www.oshstencils.com/"&gt;OSH stencils&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/paste.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste spread on PCB.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/components.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Components placed by hand.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/soldered.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;After few minutes in
    oven components have been
    soldered.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/working.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Connecting power for the first time.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;On first power up everything seemed to work fine. Although I found out that
I had made a mistake in switching regulator compensation network design and both
supplies had about 0.1 V switching noise riding on them. After recalculating and
correcting the component values everything seemed to work fine. I could program
the microcontroller, turn on the PLLs and see the correct looking IF signal with
oscilloscope on the mixer output. It was working fine for few days, but suddenly
though I couldn't communicate with the microcontroller anymore. I suspected
an issue with soldering and I tried to reball the BGA and resolder it but it didn't
help. I could communicate with the microcontroller again, but it would get hot
and randomly hang when communicating through USB. I didn't find any mistakes on
the layout.  I also tried to remove and reball BGA on evaluation board of the
same microcontroller and it started to have similar symptoms so I was pretty
sure that there was something wrong with my soldering. Or maybe with the Chinese
BGA balls I was using?&lt;/p&gt;
&lt;p&gt;Unfortunately I didn't manage to get the BGA resoldered correctly and after few
tries the solder mask of the BGA footprint was looking pretty damaged from
desolderings.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/ruined_pcb.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Ruined PCB after removing the components.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;So after letting the board be for few months I managed to find motivation to
make another one. I desoldered the most expensive components and made a new
board. This time I managed messed up soldering of the LO PLL package. I had
broken my soldering iron while desoldering some of the components earlier and using
my old unregulated iron I managed to lift data input pad of the LO PLL while
cleaning the solder from the pads. I managed to solder a 0.1 mm diameter jump
wire to QFN pad and the board could still be used.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/jump_wire.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Jump wire soldered on the side of QFN.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Microcontroller still heated up more than seemed normal. It wouldn't run
stable at full operating frequency, but after reducing the frequency from 204
MHz to 108 MHz it seemed to be working, although still running hot. I still
couldn't find any issues on the layout though so maybe there is still some
issues with soldering?&lt;/p&gt;
&lt;h1 id="signal-processing"&gt;Signal processing&lt;/h1&gt;
&lt;p&gt;Firmware is based on the &lt;a href="https://github.com/mossmann/hackrf"&gt;HackRF&lt;/a&gt; and
&lt;a href="https://github.com/airspy/firmware"&gt;AirSpy&lt;/a&gt; firmwares. Thanks to these open
source software already implementing many of the features I needed, firmware
development was much easier than if I would have neede to start from scratch.&lt;/p&gt;
&lt;p&gt;In short, the sampling part of the firmware works by first setting the source and
LO frequencies based on the commands from computer. RF outputs and amplifier are
enabled, port switch, source filter and source attenuator are set to the correct
values and receiver SP4T switch is set to the first channel.&lt;/p&gt;
&lt;p&gt;Then computer sends a command to start sampling. DMA is configured to transfer
16k samples from ADC to memory and raise interrupt when done. As DMA moves
samples in the background SP4T switch is switched so that every channel is
sampled. When DMA complete interrupt is raised samples are sent to the computer.
If more averaging is needed sampling is repeated and measurements are averaged.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/adc_samples.png" width="888" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Samples on computer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Computer program gets the recorded samples that look like in the plot
above. In this case two port measurement is made so all four channels are
sampled (In one port measurement sampling two channels is enough). First
measurement is the port 2 reference channel RX2. Second is port 1 reflection
channel A. Third is port 2 reflection B and fourth is port 1 reference RX1.&lt;/p&gt;
&lt;p&gt;In this measurement we can see that source is connected to port 2 as port 1
channels RX1 and A only show leakage, while there is big signal on port 2
channels RX2 and B.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/rx2_fft.png" width="888" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FFT of the RX2 channel samples.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Taking FFT of the RX2 channel samples reveals that the signal quality is quite 
good. ADC on the microcontroller also seems to perform surprisingly well.
Y-axis units are in decibels full-scale, input sine wave with maximum amplitude
without saturation would have an amplitude of 0 dB.&lt;/p&gt;
&lt;p&gt;Signal level is -12 dB and noise floor near it is around -90 dB. Some
noise can be seen near DC and bigger spurs at 3.6 and 4.0 MHz. High frequency
spikes are likely result of mixer non-linearities. 4.0 MHz seems to be second
harmonic of the IF signal, but I'm not sure where the 3.6 MHz comes from.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/a_fft.png" width="888" height="600" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FFT of the A channel samples.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Taking FFT of the A receiver samples reveals that some signal is detected even
though on the plot amplitude looks very low. Measured signal level is -50 dB
full-scale. This time signals at 3.6 and 4.0 MHz have disappeared which makes
sense if their level as mixer products is dependent on the signal level. However
at 2.6 MHz there is a small spur. It can be seen on the first FFT too, although signal
leaks little over it making it hard to see. Noise floor is at about -90 dB.&lt;/p&gt;
&lt;p&gt;From the FFT plots magnitude and phase of the IF signal could be extracted, but
there is no need to calculate all of the frequency bins when just one is needed.&lt;/p&gt;
&lt;p&gt;&lt;span class="math"&gt;\(k\)&lt;/span&gt;th frequency bin of discrete Fourier transform of signal &lt;span class="math"&gt;\(x\)&lt;/span&gt; is defined as:&lt;/p&gt;
&lt;div class="math"&gt;$$ X_k = \frac{1}{N}\sum_{n=0}^{N-1} x_n e^{-2\pi i k n/N} $$&lt;/div&gt;
&lt;p&gt;, where &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the length of the signal. We can use this definition to calculate
coefficient of only one frequency bin. Time complexity is O(n) instead of O(n log(n))
of FFT, so in theory it should be faster.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/lo_iq.png" width="888" height="600" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Generated digital second LO I and Q signals. In this plot frequency is lower than 2 MHz to make the shape more clear.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Two sine waves are generated at the signal frequency with phase difference of 90
degrees. Multiplying slices of the signal with samples from one channel and same
index slices of two sine waves and taking separately averages of the results
gives real and complex part of the IQ sample. Taking slice also of the LO signal
is important for correct phase measurement. Absolute phases of the signals will
vary as there isn't a reference channel to compare phase. However it isn't
needed because S-parameters are defined as ratios of the measurements and
relative phases are repeatable.&lt;/p&gt;
&lt;p&gt;Next the port switch is switched and receivers are measured with source
connected to the other port. After getting the IQ samples from all of the
channels S-parameters can be calculated as ratios of calculated IQ sample of
different receivers:&lt;/p&gt;
&lt;div class="math"&gt;$$ S_{11} = \frac{\text{A}^1}{\text{RX1}^1} $$&lt;/div&gt;
&lt;div class="math"&gt;$$ S_{21} = \frac{\text{B}^1}{\text{RX1}^1} $$&lt;/div&gt;
&lt;div class="math"&gt;$$ S_{12} = \frac{\text{A}^2}{\text{RX2}^2} $$&lt;/div&gt;
&lt;div class="math"&gt;$$ S_{22} = \frac{\text{B}^2}{\text{RX2}^2} $$&lt;/div&gt;
&lt;p&gt;Superscript marks the port source is connected to.&lt;/p&gt;
&lt;p&gt;We aren't however done yet. If we plot the S-parameters now we see that they
don't look like they are supposed to. Reason is that there are many error
sources in the measurements. Some of which are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reflections from the components inside VNA and external cables.&lt;/li&gt;
&lt;li&gt;Directivity error from directional couplers.&lt;/li&gt;
&lt;li&gt;Leakage between the receiver channels.&lt;/li&gt;
&lt;li&gt;Attenuation and length of the cables and VNA internals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Measuring the S-parameters of open circuit gives the following plots.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/open_sparam_db.png" width="711" height="480" style="width: 50%; height: auto;"/&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/open_sparam_phase.png" width="711" height="480" style="width: 50%; height: auto;"/&gt;
&lt;/div&gt;

&lt;p&gt;Ideally open circuit should have reflection coefficient of 0 dB and phase of the
reflection should be 0 degrees.&lt;/p&gt;
&lt;p&gt;To get accurate measurements errors must be characterized and
mathematically removed from the measurements.&lt;/p&gt;
&lt;h1 id="one-port-calibration"&gt;One port calibration&lt;/h1&gt;
&lt;p&gt;Calibrating one port measurements is much easier than two port as there are not
as many error sources. Only one of the ports will be used and receivers on the
second port are not measured at all. Result of the measurement is reflection
coefficient of the device under test.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/oneport_cal.png" width="1399" height="626" style="width: 80%; height: auto;"/&gt;
&lt;/div&gt;

&lt;p&gt;We can model the one-port VNA as an ideal VNA in series with error adapter.
&lt;span class="math"&gt;\(a_0\)&lt;/span&gt; and &lt;span class="math"&gt;\(b_0\)&lt;/span&gt; are the measured RX1 and A channels. Measured reflection
coefficient can be calculated simply as &lt;span class="math"&gt;\(b_0/a_0\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;In the error adapter &lt;span class="math"&gt;\(e_{00}\)&lt;/span&gt; term is the directivity error of the directional
couplers. &lt;span class="math"&gt;\(e_{11}\)&lt;/span&gt; includes errors from port matching and &lt;span class="math"&gt;\(e_{10}e_{01}\)&lt;/span&gt; term is
tracking error. It models different frequency responses of the
detectors, for example phase error from cables is corrected by this term.
&lt;span class="math"&gt;\(\Gamma\)&lt;/span&gt; is the actual reflection coefficient of the device under test and what
we want to measure.&lt;/p&gt;
&lt;p&gt;For good overview of VNA calibration methods see &lt;a href="http://emlab.uiuc.edu/ece451/appnotes/Rytting_NAModels.pdf"&gt;this
document&lt;/a&gt; and &lt;a href="http://na.support.keysight.com/pna/help/latest/S3_Cals/Errors.htm"&gt;this
page&lt;/a&gt; for
more detailed explanations of the error terms.&lt;/p&gt;
&lt;p&gt;Error adapter has three unknown terms so by measuring three known devices we can
solve for the error terms. With the solved error terms we can solve for the
actual reflection coefficient of the DUT.&lt;/p&gt;
&lt;p&gt;The only problem is that three accurately known devices are needed. If the
actual reflection coefficients are different from the ones used in calibration,
there will be errors in the measurements.&lt;/p&gt;
&lt;p&gt;Accurate calibration standards are really expensive as they require very high
precision construction. At ebay professional calibration standards with SMA
connectors sell for 300 € to 5000 €. Even the cheapest ones cost more than
I have so far spent on this project.&lt;/p&gt;
&lt;p&gt;Following the example of &lt;a href="http://www.qsl.net/in3otd/electronics/VNA_calkit/SMA_female.html"&gt;IN3OTD who made simple calibration
standards&lt;/a&gt;,
I decided to make calibration standard myself.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/cal_kit2.jpg" width="1600" height="1008" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Homemade SMA calibration kit. Open, short, 
    match and through standards.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I took three through hole SMA female connectors. Cut off legs from one to make
open, one was filled with solder to make short and match was made by soldering
two 0805 0.1% 100 ohm resistors in parallel to the last one. Through standard is needed
for two port calibration and it is a regular SMA female-to-female adapter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/oneport_standard_measurement.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measuring the calibration standards.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/horn_left.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measuring a horn antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/uncalibrated.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Uncalibrated responses of calibration standards
    and horn antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above plot are the uncalibrated S-parameters of calibration standards and
horn antenna. With little imagination it can be seen that horn antenna looks
like open circuit at low frequencies and is well matched above 5 GHz. This plot
also tells that internal errors are biggest between 4 and 5 GHz as the ripple there has
largest magnitude.&lt;/p&gt;
&lt;p&gt;Calibration is done using existing one port calibration routine in scikit-rf
Python library.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/horn_left_s11.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Input return loss of horn antenna measured with
    this VNA compared to measurements from professional grade VNA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the input return loss of horn antenna after calibration. Now the trace
is much cleaner compared to the uncalibrated one. On blue is the return loss
measured with this VNA and on red is same antenna measured using professional
grade VNA made by Agilent. Exact model I used is now obsolete, but similar one
would cost about 15,000€ as new. My VNA is able to make this measurement quite
accurately considering the price difference. There are small differences in the
plots, likely because of my inaccurate calibration standards.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/horn_right_s11.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Return loss of the other horn antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Other horn antenna measurement also agrees with the measurement made with
Agilent's VNA.&lt;/p&gt;
&lt;h1 id="two-port-calibration"&gt;Two port calibration&lt;/h1&gt;
&lt;p&gt;Two port calibration is much more difficult than one port calibration because
there is also additionally problem with calibrating the leakage away. Most
commonly used error model is 12-term error model where both ports have 6 error
terms, but this error model doesn't model leakage paths between different
receivers and thus it can't remove errors caused by the leakage from
the measurements.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/12term_error1.png" width="686" height="508" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;12 term error model. &lt;a href="http://emlab.uiuc.edu/ece451/appnotes/Rytting_NAModels.pdf"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/12term_error_model.png" width="840" height="578" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;12 term error adapter parameters. Same model is used for the other port. &lt;a href="http://emlab.uiuc.edu/ece451/appnotes/Rytting_NAModels.pdf"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Calibration standards needed for this model are open, short, match and through.
Open, short and match are measured at both ports one at a time and one port
calibration is performed on both of the ports. Through measurement is used to
calibrate the transmission from port to port. In the model there is a &lt;span class="math"&gt;\(e_{30}\)&lt;/span&gt;
term that models port to port leakage, this isn't however solved in the
&lt;a href="http://scikit-rf-web.readthedocs.io/"&gt;scikit-rf&lt;/a&gt; package I'm using to
calibrate. It is often assumed to be zero as good VNAs have very high
isolation between the ports.&lt;/p&gt;
&lt;h2 id="measuring-20-db-attenuator"&gt;Measuring 20 dB attenuator&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/att_20db.jpg" width="1600" height="1200" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;20 dB attenuator connected for measurement.
    Female-to-female SMA adapter is used on the male connector to connect to SMA
    cables.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To test the accuracy of 12 term SOLT (short, open, load, through) calibration
I measured a 20 dB attenuator.&lt;/p&gt;
&lt;p&gt;According to the
&lt;a href="http://www.aliexpress.com/item/SMA-Attenuator-SMA-Plug-to-Jack-2Watt-DC-18Ghz-20db/562129682.html"&gt;seller&lt;/a&gt;
this attenuator should have attenuation of 20 dB +- 2% and return loss better
than -17 dB. However it is a cheap Chinese one so who knows what it really looks
like?&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/att_20db_solt.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the SOLT calibrated S-parameters of the attenuator. Matching seems to
be very good and it seems to fulfill the -17 dB specification. At low
frequencies measured attenuation is -19.5 dB which is just outside the promised
2% accuracy. Slight offset might be caused by inaccurate calibration standards.
At low frequencies traces look okay, but at high frequencies there is weird
ripple which is also seen in S11 and S22 measurements. I assume this is due to
leakage which isn't corrected by this model.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/16term_error_model.png" width="842" height="575" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;16 term error model. &lt;a href="http://emlab.uiuc.edu/ece451/appnotes/Rytting_NAModels.pdf"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To get more accurate measurements, error model is needed that takes into account
the leakage terms between all of the receivers. 16-Term error model is a complete
error model for two port VNA. It includes all the possible paths the signal can
travel. Using this model on well isolated VNA causes more harm than benefit due
to increased numerical errors if the real values of the leakage paths are below
the measurement capability.&lt;/p&gt;
&lt;p&gt;I decided to use a calibration method called
&lt;a href="http://ieeexplore.ieee.org/xpl/login.jsp?tp=&amp;amp;arnumber=598439&amp;amp;url=http%3A%2F%2Fieeexplore.ieee.org%2Fxpls%2Fabs_all.jsp%3Farnumber%3D598439"&gt;LMR16&lt;/a&gt;.
Due to increased number of unknowns in 16-term error model more measurements are
needed. This method needs three different calibration standards: Through, match
and reflect. Reflect can be any reflecting standard, match is assumed to be
perfect and through is assumed to be lossless and well matched. Five different
measurements are needed to get enough equations to solve for the unknown error
terms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Through (Ports connected together with through standard)&lt;/li&gt;
&lt;li&gt;Match-Match (Match on both ports)&lt;/li&gt;
&lt;li&gt;Reflect-Reflect (Reflect on both ports)&lt;/li&gt;
&lt;li&gt;Reflect-Match (Reflect on port 1, match on port 2)&lt;/li&gt;
&lt;li&gt;Match-Reflect (Match on port 1, reflect on port 2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LMR16 is a self-calibration procedure that can either determine the correct
length of the through standard when reflect standard is given or the other way.
This is convenient feature since I don't know the electrical length of the
through, but short and open models are reasonable accurate according to the one
port measurements.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/att_20db_lmr16_solt.png" width="826" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;20 dB attenuator S-parameters with LMR16
    calibration.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the plot of the same 20 dB attenuator measurement, but this time
calibrated using LMR16 calibration. Most of the ripple on the S21 and S12 plots
has disappeared. Doing a secondary calibration using 12-Term SOLT further
slightly improves the response as LMR16 assumes match standard to be perfect and
SOLT calibration corrects for the non ideal match.&lt;/p&gt;
&lt;p&gt;Response looks like it should. Insertion loss is very stable as a function of
frequency and both ports are well matched. Some ripple is seen on S11 and S22
traces that looks like it shouldn't exists. Measured reflections are similar
level as leakage so maybe there is some residual error left. I'm not sure why it
exists, but some possible reasons I can think of are: inaccurate calibration
standards and components heating up during the measurements and their parameters
changing. &lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/solved_through.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
&lt;/div&gt;

&lt;p&gt;Solved through length is equal to 41.1 ps delay corresponding to about 8.7 mm long
coaxial cable assuming that through material is PTFE.&lt;/p&gt;
&lt;h2 id="measuring-coupler"&gt;Measuring coupler&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_sma.jpg" width="1600" height="929" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Coupler with SMA connectors.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To have something to measure I made a coupler board with SMA connectors.
Coupler has four ports, but VNA only has two so I used SMA terminations on the
two unused ports.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_through.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
&lt;/div&gt;

&lt;p&gt;In above plot through path is measured with terminations on the coupled line
outputs. SMA connector and coupler seem to be well matched. Return loss is
better than -17 dB over the whole range. &lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_through_insertion_loss.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close-up to the insertion loss.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Insertion loss of the coupler is also reasonable. About -1.3 dB at 6 GHz.
Measured trace is also decent quality compared to the S21 trace with 12-term
calibration. There is about 0.1 dB peak-to-peak ripple on it.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_forward.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured forward coupling.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Next measurement is coupling in the forward direction. VNA was connected to the
two rightmost ports while other two were terminated. Coupling at low frequencies
is very low as was simulated. Peak coupling is -13 dB, when -15 dB was the
simulated value.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/vna/coupler_reverse.png" width="768" height="480" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Measured reverse coupling.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Coupling in the reverse side however is higher than expected. It doesn't seem
that the directivity is as good as simulated. Some of it is because termination
on the through port causes reflections. It can be seen on the S11 that return
loss is much higher than when through was measured indicating that termination
isn't well matched. Better termination or mathematically compensating for it
would be needed for more accurate measurement.&lt;/p&gt;
&lt;p&gt;It is however possible that the directivity at high frequencies isn't as good as
simulated when looking at the uncalibrated values. Amplitude ripple is highest
between 4 and 5 GHz where directivity is low according to the measurements.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Looking at the uncalibrated values internal errors of the VNA seem to be quite
big at higher frequencies. Sweet spot for minimum errors and good coupling seems
to be around 2 - 3 GHz and at this range VNA gives the best accuracy. With
correct calibration procedure the errors can be calibrated out and resulting
measurement are quite high quality when cost of the board is considered.
Performance seems to be good enough for non-professional use. Simple
measurements such as antenna return loss measurements can be performed with high
accuracy.&lt;/p&gt;
&lt;p&gt;I'm not selling these, but if you are interested in making your own VNA or are
just interested in looking at the design files all hardware, firmware and
processing software is available at &lt;a href="https://github.com/Ttl/vna"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Heartbeat detection with radar</title><link href="https://hforsten.com/heartbeat-detection-with-radar.html" rel="alternate"></link><published>2016-04-05T00:00:00+03:00</published><updated>2016-04-05T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2016-04-05:/heartbeat-detection-with-radar.html</id><summary type="html">&lt;p&gt;Phase of the radar baseband signal is very sensitive to movement allowing to detect small movements caused by breathing and heartbeat.&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Frequency modulated constant wave (FMCW) radar return signal from target is
a sine wave and its frequency depends on the distance to target. Phase of this
signal is very sensitive on the distance to the target.&lt;/p&gt;
&lt;p&gt;By analyzing phase variation of the radar return signal very small movements can
be resolved. Movements less much smaller than wavelength of the radar can be
resolved, but downside is that two different targets can't be resolved any
better than radar normally can (tens of cm to meters).&lt;/p&gt;
&lt;p&gt;If you aren't interested in the mathematics about how this works you can scroll down and
watch videos instead.&lt;/p&gt;
&lt;h1 id="mathematics"&gt;Mathematics&lt;/h1&gt;
&lt;p&gt;Let &lt;span class="math"&gt;\(x(t)\)&lt;/span&gt; be the transmitted linearly increasing frequency sweep from &lt;span class="math"&gt;\(f_c -B/2\)&lt;/span&gt; to
&lt;span class="math"&gt;\(f_c + B/2\)&lt;/span&gt; in &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; seconds:&lt;/p&gt;
&lt;div class="math"&gt;$$ x(t) = \cos(\phi(t)),\quad -t_s/2 \leq t \leq t_s/2 $$&lt;/div&gt;
&lt;div class="math"&gt;$$ \phi(t) = 2\pi f_c t+\pi \frac{B}{t_s}t^2$$&lt;/div&gt;
&lt;p&gt;&lt;span class="math"&gt;\(f_c\)&lt;/span&gt; is the sweep center frequency, &lt;span class="math"&gt;\(B\)&lt;/span&gt; is sweep bandwidth and &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; is sweep length.
Amplitude of the signal is not important in this analysis and is set to 1.&lt;/p&gt;
&lt;p&gt;Transmitted signal travels at speed of light and reflects of the target at
distance &lt;span class="math"&gt;\(r\)&lt;/span&gt; and is received after time &lt;span class="math"&gt;\(\tau = \frac{2r}{c}\)&lt;/span&gt;, where &lt;span class="math"&gt;\(c\)&lt;/span&gt; is
speed of light.&lt;/p&gt;
&lt;p&gt;Received signal is mixed with a copy of the transmitted signal which results
in difference and sum of frequencies of the signals. Sum term is too high frequency
(about twice the carrier frequency) and is filtered out. Difference term is low
frequency, usually few kHz, and contains the information about the target:&lt;/p&gt;
&lt;div class="math"&gt;$$ f(t) = \cos(\phi(t) - \phi(t - \tau)) = \cos\left(2\pi\left(\frac{B\tau}{t_s}t - \frac{B \tau^2}{2t_s} + f_c\tau \right)\right) $$&lt;/div&gt;
&lt;p&gt;First term with &lt;span class="math"&gt;\(t\)&lt;/span&gt; dependency is frequency of the signal and last two terms are
phase. When Fourier transform is taken frequency of the signal can be found out giving the
distance to the target. Resolution of radar as defined by minimum distance of two
targets such that they are detected separately follows from the resolution of
Fourier transform. Length of Fourier transform is same as sweep length &lt;span class="math"&gt;\(t_s\)&lt;/span&gt;.
Resolution of discrete Fourier transform is sampling frequency &lt;span class="math"&gt;\(f_s\)&lt;/span&gt; divided by number
of samples &lt;span class="math"&gt;\(N\)&lt;/span&gt;: &lt;span class="math"&gt;\(\Delta f = f_s/N\)&lt;/span&gt;. Because the signal is &lt;span class="math"&gt;\(t_s\)&lt;/span&gt; long, there are &lt;span class="math"&gt;\(f_s
t_s\)&lt;/span&gt; samples. Frequency resolution can be improved by increasing the
sweep length, but increasing the sweep length also decreases the frequency
from the target. Changes balance out exactly and range resolution is independent of the
sweep length:&lt;/p&gt;
&lt;div class="math"&gt;$$ \frac{B2\Delta r}{ct_s} = \Delta f = \frac{1}{t_s} \Rightarrow \Delta
r = \frac{c}{2B} $$&lt;/div&gt;
&lt;p&gt;The phase terms also contain information about distance to target. &lt;span class="math"&gt;\(\pi \frac{B
\tau^2}{t_s}\)&lt;/span&gt; term is called residual video phase term and due to &lt;span class="math"&gt;\(\tau^2\)&lt;/span&gt; term is
very small and can often be ignored. Second phase term on the other hand can
be expanded as:&lt;/p&gt;
&lt;div class="math"&gt;$$ f_c\tau = f_c \frac{2 r}{c} = \frac{2 r}{\lambda} $$&lt;/div&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(\lambda\)&lt;/span&gt; is the wavelength of the sweep center frequency. Changing
the distance to the target by half wavelength is enough to wrap the phase around.
Phase noise of the radar determines the resolution the phase can be determined,
but usually it should be possible to detect phase within few degrees.&lt;/p&gt;
&lt;p&gt;At 5.6 GHz wavelength is 54 mm and assuming that minimum of 1 degree change
in phase can be detected it gives the minimum detectable movement of 75 µm.&lt;/p&gt;
&lt;p&gt;One drawback of using phase is that while it can sense small changes in
distance, it can't resolve closely spaced targets any closer than is possible by
analyzing the frequency term since Fourier transform is needed to get the phase.
This means that if two equally reflecting targets are moving in opposite
directions close together, then their movements cancel each other and radar
doesn't detect either of them.&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=50% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/microdoppler/radar_phase.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/microdoppler/radar_phase.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;In video above I'm moving the antenna a short distance and in the upper right
corner there are plotted raw signal from ADC and Fourier transform of the same
signal. Phase of the signal changes very rapidly, but the frequency stays
unchanged at this scale due to the limited resolution of the radar.&lt;/p&gt;
&lt;h1 id="breathing-and-heartbeat-detection"&gt;Breathing and heartbeat detection&lt;/h1&gt;
&lt;div id="centered" &gt;
&lt;video width=50% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/microdoppler/breathing.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/microdoppler/breathing.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Above is a short video of me sitting still facing the radar. First I'm breathing
normally and then I hold my breath for short time.&lt;/p&gt;
&lt;p&gt;Radar sends sweeps with linearly increasing frequency at 1 kHz repetition
frequency. For every sweep the sampled signal is Fourier transformed so that
returns from targets at different distances can be separated. Then phase of one
of the FFT bins at distance where I'm sitting at is saved. Same processing is
done for all of the sweeps and unwrapped phases are plotted to give the plot on
the video.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/microdoppler/md_fig_full.png" width="1032" height="597" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Full unwrapped phase from the recorded data.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the image of the measured phase. At the beginning where I'm walking
phase wraps around multiple times and the breathing signal can't be seen at this
scale.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/microdoppler/md_fig_breathing.png" width="1032" height="597" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Zoomed in to breathing&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is zoomed in to remove the walking part so that the breathing signal can be
seen. Breathing amplitude is about 25 degrees, which equals 1.8 millimeters with
the 5.6 GHz center frequency and breathing frequency is 0.25 Hz (15.3
breaths/min).&lt;/p&gt;
&lt;p&gt;When I'm holding my breath signal is not completely flat, but instead a periodic
signal can be seen that is caused by heart beat moving my chest a tiny amount.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/microdoppler/md_fig_heartbeats.png" width="1032" height="597" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Zoomed in to heartbeats.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/microdoppler/md_fig_heartbeats_zoom.png" width="1032" height="597" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Zoomed in further to heartbeats.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Amplitude of the heartbeat signal varies a little bit because of random motions.
Heartbeat frequency is 1.1 Hz (65 bpm).
Amplitude is about 1 degree which translates to motion of 75 µm.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/microdoppler/radar_phase_noise.png" width="1032" height="597" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Same measurement with empty room.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the result of same measurement but with empty room for measuring the
phase noise. Measured noise is much lower than the heart beat signal and we can
be confident that it is not noise. It should be noted that measured signal was
filtered digitally with 10 Hz low pass filter to decrease the noise. Unfiltered
phase noise at full bandwidth is about 1 degree RMS.&lt;/p&gt;
&lt;p&gt;In the phase noise plot small amount of drift can be seen. Source of this drift is probably
clock drift between PLL reference clock and ADC clock. Clocks are drifting tiny
amount because especially the RF board uses lot of power for its small size
which heats it up and when crystal is heated its frequency changes slightly.
They should be derived from same clock so they wouldn't drift respect to each
other, but I didn't realize it when drawing the schematics of the radar. Luckily
drift is very minor and it doesn't cause many problems in normal operation.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Homemade synthetic aperture radar</title><link href="https://hforsten.com/homemade-synthetic-aperture-radar.html" rel="alternate"></link><published>2015-10-14T00:00:00+03:00</published><updated>2015-10-14T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2015-10-14:/homemade-synthetic-aperture-radar.html</id><summary type="html">&lt;p&gt;Second version of the 6 GHz FMCW radar now with SAR images&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/radar.jpg" width="1600" height="1072" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finished radar boards without antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://hforsten.com/6-ghz-frequency-modulated-radar.html"&gt;Previously&lt;/a&gt; I made
a very simple frequency-modulated continuous-wave (FMCW) radar that was able to
detect distance of a human sized object to 100 m. It worked, but as it was made
with minimal budget there was a lot of room for improvement.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/nonlinear_sweep.png"/&gt;
    &lt;p style="font-size:13px" &gt;Non-linear frequency sweep causes decreased
    resolution.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;One of the bigger problems was that voltage controlled oscillator (VCO)
generating the high frequency output signal was driven directly from the
digital-to-analog converter (DAC) of
the microcontroller. VCO tuning voltage and output frequency relation is not
linear and using a linear ramp as a tuning voltage generates slightly
non-linear frequency sweep. If the frequency sweep is not linear it generates
non-constant mixing tone at the baseband. FMCW radars use Fourier transform of the
baseband signal to find the distance to the target and when the received tone is
not constant it spreads the power in the frequency domain and target resolving
resolution decreases.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/vco_vtune.png" width="800" height="611" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;VCO's output frequency as a function of the tuning
    voltage. Ideally this would be linear.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A linear sweep can be generated by pre-distorting the VCO tuning voltage so that
result is a linear sweep. Drawback of this method is that it requires knowing
the exact VCO voltage/frequency characteristics which varies to some degree as
a function of temperature and from component to component. Other way is to use
&lt;a href="https://en.wikipedia.org/wiki/Phase-locked_loop"&gt;a phase locked loop&lt;/a&gt; (PLL) that uses a frequency divider to measure generated RF
frequency and compares it to accurate low frequency reference oscillator.
A feedback loop then adjusts VCO tuning voltage so that divided frequency equals
the reference oscillator frequency, this forces the RF signal frequency to be
division amount times the reference oscillator frequency. Sweeping the
divider value in small fractional steps can be used to generate a very linear
sweep. Alternatively reference oscillator frequency can be swept while keeping the
divider constant.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/self_mixing.png"/ width=50%&gt;
    &lt;p style="font-size:13px" &gt;Mixer output of the old radar when antennas are replaced with
    loads. Waveform is caused by the LO leaking to RF input port of the mixer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Receiver also had few problems. First being that baseband low frequency
amplifier just didn't have enough gain to amplify low power echoes from far away
limiting the maximum range. Other receiver problem was DC mixing product 
changing during the sweep and creating AC signal.&lt;/p&gt;
&lt;p&gt;Receiver LNA had a gain of 13 dB and mixer after it had a conversion loss of 8.5
dB. This only leaves 4.5 dB of gain at RF section and causes the baseband low
frequency signals to have very low amplitude. Since the receiver is a direct
conversion receiver, LO oscillator leaking to RF input port of the mixer is
going to cause a mixing product at DC. Power amplifier output power and mixer conversion
gain vary with frequency causing the DC offset term to also vary. It creates
an AC signal with frequency equal to sweep repetition frequency. This frequency
is around 1 kHz and is amplified by the baseband amplifier. Since RF gain is
low, this leakage signal can have bigger amplitude than the received signal
saturating the baseband amplifier. A better mixer with more
isolation, PA with less gain variation, more gain at RF and high-pass filter
after mixer can be used to minimize this effect.&lt;/p&gt;
&lt;p&gt;Last problem was with the microcontroller. It was not fast
enough to transfer all the ADC samples to PC through USB. It was able to
transfer only 25 sweeps/second while radar would take 500 sweeps/second.  It
probably would have been possible to improve firmware but strict timing
constraint for generating the VCO ramp signal would had made it hard.&lt;/p&gt;
&lt;p&gt;Because of the various problems with the old design I decided to make a new
radar that fixed the problems.&lt;/p&gt;
&lt;h1 id="new-radar"&gt;New radar&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/new_block.png" width="909" height="427" style="width: 65%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Block diagram of the new design.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The new radar design is very similar to the old one on system level, but with
few improvements. It functions using same principle: Transmitted linear sweep
reflects from target and is received at the receiver. There it is multiplied
with copy of transmitted sweep. Since electromagnetic radiation travels at speed
of light, there is a time difference between received reflection and transmitted
sweep.  Multiplication in mixer generates a low frequency signal that has
a frequency proportional to travel time of signal. Fourier transforming the
mixer output signal gives frequencies in the signal and it can be used to
resolve more than one target at the same time.&lt;/p&gt;
&lt;p&gt;Biggest difference is addition of PLL that linearizes the transmitted sweep. PLL
used is
&lt;a href="http://www.analog.com/en/products/rf-microwave/pll-synth/adf4158.html"&gt;ADF4158&lt;/a&gt;,
it is designed especially for radars and it can be programmed to generate
accurate sawtooth and triangular ramps.&lt;/p&gt;
&lt;p&gt;Other changes include: much faster microcontroller, better receiver, better
baseband filtering and various other small improvements.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/pcb.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Bare PCBs.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Design is split it two boards. One has digital functions and other high
frequency parts.
One of the reasons was that if I made a mistake with the PCBs, with two board
design I could probably only order one new board and save money. Two boards also
reduce the noise going from digital board to RF board.&lt;/p&gt;
&lt;p&gt;RF board needs to be four layered, because 50 ohm transmission lines would be
too wide on 2 layer board because of thicker substrate. Four layer board is also made out of better
material with lower loss and more stable parameters. Digital board
could be made with two layers to save money as four layer board costs two
times as much as two layer board for same area.&lt;/p&gt;
&lt;p&gt;But it turns out that two layer board would need to be bigger as compact BGA
components can't be routed on two layer board and bigger QFP packaged
microcontroller would be needed. QFP takes more than four times as much area as
BGA, so using BGA on four layer board turns out to be cheaper. It also enables
packing the other components closer and results in better routing with less
interference due to having more layers available.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/fmcw2_sch.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/schematic.png" width="1233" height="855" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for PDF.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id="digital-board"&gt;Digital board&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/mcu_pcb.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu_pcb.png" width="1005" height="905" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Microcontroller PCB in KiCad without zone fills.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;First I was thinking of having FPGA for signal processing, but they are
expensive and don't have easy and cheap way to add high-speed USB communication.
I ended up using &lt;a href="http://www.nxp.com/products/microcontrollers/product_series/lpc4300/"&gt;NXP
LPC4320&lt;/a&gt; 204 MHz ARM microcontroller. It has interesting
serial GPIO (SGPIO) device that has several shift registers that can be
configured very extensively. SGPIO can be used to implement communication
protocols such as UART and SPI, generate PWM signals, interface to parallel
output ADC which I'm using it for and various other things. SGPIO subsystem can be clocked up to 102 MHz allowing
for very fast data transfer from ADC. Microcontroller also has high-speed (480
Mbit/s) USB, so this time the transfer rate shouldn't be a problem. It's also
one of the cheapest microcontrollers with high-speed USB costing 7.2 € at
low quantities at Digi-Key.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sgpio.png" width="803" height="434" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SGPIO slice block diagram. One slice can be
    configured as input or output, generate interrupts and clock source can also
    be configured. Slices can also output to other slices.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/mossmann/hackrf"&gt;HackRF&lt;/a&gt; uses the same microcontroller for
same reasons. Since HackRF is open source I'm able to use its software with
small modifications. Existing software really helps especially as I don't have
debug connector on board making it hard to see what the microcontroller is
doing.&lt;/p&gt;
&lt;h2 id="rf-board"&gt;RF board&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/rf_pcb.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rf_pcb.png" width="1415" height="736" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;RF PCB in KiCad without zone fills.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;RF board is based on the
&lt;a href="http://www.analog.com/en/products/rf-microwave/pll-synth/adf4158.html"&gt;ADF4158&lt;/a&gt;
PLL chip. It takes a 30 MHz reference clock and RF output from VCO as inputs.
VCO output frequency is divided by programmable divider on the chip and divided frequency is
compared to reference clock. Chip then outputs analog voltage to VCO's tuning
pin to makes the divided RF frequency equal to reference clock. Since low
frequency reference clock and divider can be made very accurate and stable, PLL
chip makes the VCO output frequency to also be very accurate.&lt;/p&gt;
&lt;p&gt;This chip is made especially for radar applications. Its divider value can
be fractional so that RF frequency can be stepped in smaller steps than
multiplies of the reference clock frequency. It also has internal function that
can automatically step the divider value so that it generates linear sweeps
ideal for radar without needing constant control from the microcontroller.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/coupler.jpg" width="640" height="353" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Mixer LO coupler. A small portion of the power
    amplifier output is coupler to mixer. Small footprint on right side is for
    50 ohm resistor terminating the other branch of the coupler.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/coupler_sparam_cst.png" width="1056" height="536" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Coupler simulated S-parameters. S11 = input from
    PA, S31 = Coupler power to mixer and S21 = Power passed to antenna.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Previous radar used passive mixer with high local oscillator power requirement. &lt;a href="https://en.wikipedia.org/wiki/Wilkinson_power_divider"&gt;Wilkinson
divider&lt;/a&gt; after the PA was used to divide output power equally to antenna and
mixer. Mixer wouldn't have needed that much power and it caused reduced
efficiency and lower radiated power.&lt;/p&gt;
&lt;p&gt;New coupler couples much less power to the mixer (-16 dB = 3%) and much more is
passed to the antenna. Simulated power loss from power amplifier to antenna
connector is only 0.23 dB (5%).&lt;/p&gt;
&lt;p&gt;Reason for using sharp 90 degree bends with part of the corner cut off is that these corners are
designed to minimize reflections of the bend. These kinds of corners are called &lt;a href="http://www.microwaves101.com/encyclopedias/mitered-bends"&gt;mitered bends&lt;/a&gt;.
Large diameter smooth bends could also have been used, but they take more space.
Mitered bends are also used on the &lt;a href="https://hforsten.com/img/fmcw2/mixer_traces.jpg"&gt;long trace&lt;/a&gt; to mixer local oscillator input.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/lnas.png" width="826" height="639" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Low noise amplifiers. Receiving antenna
    SMA-connector on the top right, balun and mixer input at the bottom.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This time there is additional amplifier after the LNA to increase the gain
before the mixer. Active mixer I'm using has a higher noise figure requiring more gain
than the previous passive mixer to achieve low system noise figure. Noise figure
of the system can be calculated using &lt;a href="https://en.wikipedia.org/wiki/Friis_formulas_for_noise"&gt;Friis
formula&lt;/a&gt;. If only the
first LNA would have been used noise figure of the system would have been 6 dB.
With two LNAs noise figure is 2 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mixer.png" width="765" height="826" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;ADL5801 active mixer.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Instead of passive mixer like last time with high LO power requirement, this
time mixer is active. It requires less LO power which enabled using coupler
instead of power divider. Conversion gain is also bigger which increases the
output signal level at the baseband. Better isolation between LO and RF ports
also reduces the DC-offset term.&lt;/p&gt;
&lt;p&gt;Possible downside of using active mixer when downconverting to low frequencies
is higher &lt;a href="https://en.wikipedia.org/wiki/Flicker_noise"&gt;flicker noise&lt;/a&gt; from
higher amount of transistors needed compared to passive mixer. At lower
frequencies transistors generate much more noise than at high frequencies and
below some process dependent corner frequency this noise behaves like 1/f
increasing sharply when frequency decreases. MOSFET transistors typically have
much higher corner frequencies, sometimes several megahertz in case of small
integrated transistors. Bipolar transistors usually behave much better and they
have corner frequency typically few kilohertz. If the mixer was made with
MOSFETs it might have high 1/f noise causing the noise figure calculations 
to actually be much higher. Mixer I used was made with SiGe process which uses
bipolar transistors. It doesn't have low frequency noise characteristics in the
datasheet, but it probably has low enough corner frequency to not matter at this
application as the baseband signal frequencies are around 10 kHz to 1 MHz.&lt;/p&gt;
&lt;h2 id="range-compensation-high-pass-filter"&gt;Range compensation high-pass filter&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/bb_filter.png" width="1012" height="606" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Baseband filter schematic. IF is output from
    mixer, IFF is input to ADC driver.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Received power by radar decreases as a fourth power of distance. This causes
various problems, such as doubling the maximum detection distance requires 16
times more transmission power. Other problem occurs when there are several
targets large distance apart. Target close to the radar reflects much more power
than the farther target. Fourth power means when target is 10 timer farther,
power received is ten thousand times smaller. Real hardware has limited dynamic
range making it possible that close target with more powerful return signal
masks the weak signal from farther target.&lt;/p&gt;
&lt;p&gt;Because the frequency of the target in FMCW radar depends linearly from the
distance to the target, it is possible to use high-pass filter to equalize the
signals from far apart targets. If signal after the mixer is filtered using 
40 dB/decade high-pass filter, responses from same sized targets from different
distances will ideally have same amplitude.&lt;/p&gt;
&lt;p&gt;In schematic above is a 40 db/decade high-pass filter implementing the range
compensation. The first stage is LC-lowpass filter to filter out the high
frequency noise before the first amplifier stage. After AC-coupling capacitors
for removing the DC-offset is the first amplifier stage that amplifies the signal before 
more processing. Next stage is a 2nd order Sallen key high pass filter that
generates the 40dB/decade slope. Output then goes to digital board which has
a variable gain stage and ADC driver.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/bb_filter_freq2.png" width="1175" height="563" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Baseband filter frequency response.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Downside of this approach is that at low frequencies baseband gain is much lower
and it can even attenuate the signal. RF noise is still same at all frequencies, so low
baseband gain reduces the dynamic range at low frequencies. In the end it
doesn't really matter that much since at low frequencies dynamic range was
already very high due to high received power.&lt;/p&gt;
&lt;p&gt;Below is the same scene is simulated with and without the range compensation filter showing how big effect it can have.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rx_signal.png" width="1032" height="342" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated received signal after mixer from
    several same sized targets at various distances.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rx_signal_fft.png" width="1032" height="342" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FFT of received signal. Target very close to radar reflects so much more
    power that targets farther away are not seen. X-axis is frequency in Hz.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/filtered_rx_signal.png" width="1032" height="342" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Same signal after range compensation filter.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/filtered_rx_signal_fft.png" width="1032" height="342" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;FFT of filtered signal. Targets farther away are now also visible.&lt;/p&gt;
&lt;/div&gt;

&lt;h3 id="soldering-digital-board"&gt;Soldering digital board&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu_backside.jpg" width="1600" height="1221" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Backside has only few passives and is soldered by hand.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Backside is soldered first. It has only light components that stay in place by
surface tension of the solder even while upside down during reflowing of the top
side.  Board-to-board connector is soldered after the top side is done, it is so
tall that it would raise the board and make applying solder paste on the top side harder.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu_soldermask.jpg" width="640" height="480" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;100 ball BGA footprint. Solder mask is applied slightly offset.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu_stencil.jpg" width="1600" height="1296" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Spreading solder paste using stencil.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/bga_before.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Close up of the BGA before reflow.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I managed to put the BGA pretty much perfectly on place, there's maybe about 0.1
mm of positioning error, but surface tension of the solder can correct it. Even
bigger positioning error should be fine as long as balls don't bridge the solder
paste drops.&lt;/p&gt;
&lt;p&gt;Outline of the BGA on solder screen is very useful for positioning
it. Without any visible guide on the board positioning would be very challenging
as the pads are under the chip.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/bga_reflow.jpg" width="640" height="480" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;After reflow.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After reflow solder joints look good. It looks like the solder balls on the BGA
also reflowed and combined with solder paste on the PCB. If only the solder
paste reflow with balls staying solid everything might work for a while, but the
contact between ball and small amount of reflowed solder paste isn't very
reliable.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu.jpg" width="1600" height="1250" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;After reflow.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Chip on left is flash memory for firmware, center is microcontroller, right is
ADC and below it is op-amp to drive it. Unpopulated component on the bottom left
is USB power switch for USB on-the-go. I decided to leave it out since it can be
switched from the microcontroller. If it is enabled while connected to computer
it would short the on-board 5V to computers USB power line, which is why it's better to leave
it out until everything else is working.&lt;/p&gt;
&lt;p&gt;Two very small surface mount switches are reset button and USB bootloader
button.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/mcu_leds.jpg" width="1600" height="1083" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Microcontroller working. Other LED is controlled
    by the microcontroller.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Advantage of two board design is testing in parts. If there was a mistake in
power supply circuit that would provide too much voltage, at least the other
board would be saved.&lt;/p&gt;
&lt;h3 id="soldering-rf-board"&gt;Soldering RF board&lt;/h3&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rf_back.jpg" width="1600" height="859" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Backside of RF board.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To save money I didn't order stencil on bottom sides of the boards.
There are only passive components with leads visible that are
easy to solder without stencil. On top side are all the difficult to solder
components which are much easier using stencil.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rf_stencil.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Paste on RF board. Missed a spot on top left
    corner.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rf_paste.jpg" width="1600" height="893" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Paste on RF board after removing the stencil.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/rf_pop.jpg" width="1600" height="875" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Components soldered. Tabs on the sides are
    leftover from manufacturing of the PCBs, they could be snapped off but
    I haven't done it yet.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Shiny traces without solder mask are controlled impedance lines for high
frequency signals. Solder mask
would cause some additional loss and slightly change the impedance of the lines
so it's better to remove it, though effect isn't very big.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hforsten.com/img/fmcw/splitter.jpg"&gt;Last time&lt;/a&gt; the traces were quite rough which
probably had some effect on the matching. This time edges of the traces are
etched very smoothly.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/jump_wire.jpg" width="1600" height="861" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Jump wires and transistor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Somehow everything ended up working without any bigger mistakes.
One small mistake I made that required a jump wire was wiring the ramp complete
pulse from PLL to general input of microcontroller. I was thinking I could put
a pin-change interrupt on the pin, but this ended up not working. SGPIO buffers
the samples from ADC so that it's not possible to know the exact sample that
ramp was completed from interrupt. I had to connect also the ramp complete signal to SGPIO
input and sample it for every sample from ADC.&lt;/p&gt;
&lt;p&gt;This required adding a transistor to lengthen the pulse as input isn't anymore
edge triggered. Pin is connected to the drain of the transistor with integrated
pull-up resistor of the sourcing current and parasitic capacitance of the board
slowing the signal. Luckily I had added a test pad to one of the unused SGPIO
inputs, otherwise fixing it would have been impossible due to BGA package.&lt;/p&gt;
&lt;p&gt;Also visible in the picture above is glob of hot glue holding the boards
together. Mounting holes would have costed at least 10€ more in PCB costs so
I decided to leave them out.&lt;/p&gt;
&lt;h1 id="range-measurements"&gt;Range measurements&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/field_range.jpg" width="1800" height="1350" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Image of the football field.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Easy way to measure radar range is to have radar pointing at empty area and then
walk back and forth in its field of view. From the radar data it's then easy to
see how the signal-to-noise ratio changes as distance to radar increases.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/field_700mhz_2.png" width="1074" height="1022" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time-range plot of me walking down a football
    field. Bandwidth was 700 MHz.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a time-range plot of me walking back and forth a football field above.
Lines around 35m are reflections from a football goal. Beyond 80m is a fence, some
bushes and buildings which have large radar cross-section.&lt;/p&gt;
&lt;p&gt;For antennas I used the &lt;a href="http://hforsten.com/horn-antenna-for-radar.html"&gt;same horn antennas&lt;/a&gt; I made for the previous radar.&lt;/p&gt;
&lt;p&gt;Compared to &lt;a href="https://hforsten.com/img/fmcw-antenna/new_snr_clutter.png"&gt;same test&lt;/a&gt; from previous radar signal-to-noise and range resolution ratio are much better. Improved range resolution is caused by much larger bandwidth and more linear sweep. Increased bandwidth also increases signal-to-noise ratio as there is less clutter per range cell. More sensitive receiver has a big effect on the signal-to-noise ratio and because of range compensation both near and far targets are visible at the same time.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/field_700mhz_clutter_reduction.png" width="1074" height="1022" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Reducting clutter by substracting the previous
    sweep from the next one.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Subtracting previous sweep from the current one in time domain should remove all
the static targets since they should generate exactly same return. Small targets
are removed, but some of the biggest are removed only partly.&lt;/p&gt;
&lt;p&gt;When I'm close to the antenna, I'm shadowing the background which
changes its reflection and doesn't cause completely perfect subtraction.&lt;/p&gt;
&lt;p&gt;Wind can move the leaves a bit, even a millimeter of the movement will change
phase of the signal and not cause complete subtraction. That's why my track is
visible even though my movement between sweeps is only about one centimeter.&lt;/p&gt;
&lt;p&gt;Clock drift between PLL reference and ADC sampling clock will also cause the
subtraction to not be completely perfect. PLL reference and ADC clock are
separate, but should have been derived from the same source for best accuracy.
The small drift between them
will cause phase of the baseband signal to vary a little and subtraction is not
perfect.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sweep_time.png" width="807" height="475" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time plot of the baseband signal. Y-axis is raw
    ADC values.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the raw baseband signal from ADC. Due to high pass filter high
frequency returns from far away targets and low frequency returns from near have about
same amplitude.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sweep_dist.png" width="778" height="494" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Fourier transform to get distance.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Taking Fourier transform of the ADC output gives the distances. This is from the
beginning and reflection around 9 m is from me. Signal-to-noise ratio is about
60 dB.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sweep_dist_zoom.png" width="1149" height="1022" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Zoom around 9 m.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Resolution of radar depends on its bandwidth with higher bandwidth giving better
resolution. It should be noted that resolution only means being able to detect
nearby targets as separate. Accuracy of the distance depends only on accuracy of
clocks of the radar. Padding signal with zeroes smooths the FFT and allows to
locate the peak with much better accuracy. Zooming into the 9 m peak reveals
that my position at that moment was about 9.43 m. Unfortunately I don't have
means to verify this to required precision.&lt;/p&gt;
&lt;h1 id="synthetic-aperture-radar"&gt;Synthetic Aperture Radar&lt;/h1&gt;
&lt;p&gt;Besides measuring the distances, radar can also be used to generate
two-dimensional images of the scene. This can be done by measuring several
distance profiles while radar is moved on a straight line with antenna looking
sideways from the direction of travel. If the antenna radiation pattern is very
narrow, range profiles can be stacked to make image of the scene. Issue with
this method is that assumption about the narrow beam width can't often be
fulfilled. Antennas I'm using have beam width of about 40 degrees and resolution
in direction that radar was moved (cross-range or azimuth) would be horrible.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_diagram2.png" width="382" height="323" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SAR system diagram. As radar moves along the line
    targets inside the antenna radiation pattern change.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Synthetic_aperture_radar"&gt;Synthetic aperture
radar&lt;/a&gt; is a signal
processing method of achieving much better cross-range resolution from same
range profile measurements. It uses many range profile measurements
along a line to synthesize antenna that is as long as the line radar moved in.
Beam width of an antenna is related to its physical size and very long
synthesized antenna has very small beam width giving much greater cross-range
resolution.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_diagram.png" width="489" height="264" style="width: 40%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Airborne SAR system diagram.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Typically SAR imaging systems are mounted on either airplane or satellite so
that it can look at large area of ground from angle. Radar sends pulses to
ground which reflects them back and gives information about distance to the
scene being imaged.&lt;/p&gt;
&lt;p&gt;To get reflections from
the ground and avoid shadowing from big structures antenna of the radar needs to
be mounted in angle. If angle is too big and antenna points straight to ground
range resolution is poor since all of the reflections come from almost same
distance away. If angle is low and antenna points along the ground then tall
structures such as buildings and hills can hide other objects behind them and flat
objects like ground don't reflect signal back to the radar since reflection
angle is too shallow.&lt;/p&gt;
&lt;p&gt;Unfortunately I don't have a spare airplane so I mounted the
antenna on bicycle pointing in zero angle. This means that only tall vertical
structures like lamp posts, building walls and trees reflect the signal and
image looks like cross-section of the imaged area.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/bicycle_sar.jpg" width="3264" height="2448" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Radar mounted on bicycle for SAR imaging.
    Antennas have started to rust a little.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To properly focus the SAR image, platform (bicycle) needs to move at constant
speed in straight line, this is relatively easy to do even on bicycle if ground
is flat. Antenna should also point in the
same direction. Tilting the bicycle will cause it to point to ground or sky and
change the radar return. Tilt of few degrees shouldn't affect the image too much because of large antenna beam width.&lt;/p&gt;
&lt;p&gt;Hard part is that path needs to be very straight because phase of the return
signal is used for focusing the image. Phase of the baseband radar signal wraps
around when radar moves half wavelengths of the carrier frequency. In this case
carrier frequency is about 5.5 GHz making the wavelength 5.5 cm. To be properly
focused error in path linearity should be around less than tenth of
a wavelength, which is only 5 mm. It is very challenging to move in this
straight line for several meters and some amount of defocusing from movement is
expected.&lt;/p&gt;
&lt;p&gt;Cross-range size of the image depends on the length that platform traveled, but
going larger distance also means that motion errors are larger which increases
defocusing of the image. Proper SAR imaging systems use GPS and very accurate
and expensive inertial measurement units to measure the actual motion of the
platform and take it into account when focusing the image. Phone GPS doesn't
have high enough resolution and I don't have inertial measurement unit so I just
needed to try my best to keep constant speed and straight path.&lt;/p&gt;
&lt;p&gt;I decided to try taking a SAR image of the nearby football field where I 
tested the radar before. Right next to it is paved road that is very flat
minimizing the motion errors.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/field_raw_data.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/field_raw_data.png" width="1061" height="1003" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Raw unprocessed data.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/sar_visible_image.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_visible_image.jpg" width="1800" height="1103" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Camera image of the football field. Taken next
    day, bicycles weren't in the SAR image.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the raw range data from radar before SAR focusing. As expected the
method of just stacking range profile doesn't result in very high resolution
image. Format is the same as in stationary range-time plots before but now the
radar is moving. Y-axis is range in meters and X-axis is samples. Sweep time was
2 ms, track was about 30 m long and bicycle speed was approximately 1.4 m/s.
This works out to one sweep every 2.8 mm and for signal processing it can be
assumed that radar was still while sweeping. This is called stop-and-go
approximation and it simplifies the signal processing. Only every tenth sweep
was used for image focusing.&lt;/p&gt;
&lt;p&gt;SAR focusing algorithms are a big topic that you could write a book about.
In short there are several algorithms for focusing SAR images designed for different
imaging geometries. The one I'm using is omega-k algorithm, which is also
called range migration algorithm.&lt;/p&gt;
&lt;p&gt;When radar moves along the line targets on the radiation pattern of the antenna
stay visible for some distance on the line. Because radar measures direct
distance to the target, returns from the target shows as arcs due to changing
distance to it. These arcs are very clearly visible in the raw data image above
spanning wide area of the image due to wide antenna radiation pattern. Before
cross-range focusing can be done this range migration must be corrected.&lt;/p&gt;
&lt;p&gt;Omega-k algorithm corrects the range migration using process called Stolt
interpolation on the Fourier transformed raw data. As a result 
energy from same target is lined on a same range cell and inverse Fourier
transform can be used to focus the image. More detailed explanation and
mathematical derivation can be found in example from &lt;a href="http://www.mers.byu.edu/docs/thesis/msthesis_tolman.pdf"&gt;this thesis[PDF]&lt;/a&gt;&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/sar_field_uncorrected.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_field_uncorrected.png" width="1917" height="764" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;SAR image of the football field.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the resulting focused SAR image of the same raw data above. Resolution
is greatly increased and now some of the features of the scene can be seen.
Still lack of focus can be seen especially on the fence posts in the middle of
image. Each post is smeared in cross-range direction several meters due to
motion errors during the imaging.&lt;/p&gt;
&lt;p&gt;Camera picture above is taken at +8m cross range. Bigger fence post of green
metallic fence on the left in the camera image have high radar cross-section and
are very clearly visible in the SAR image. Bicycle racks are visible as lines.
Radar signal passes relatively easily through the trees on the foreground, but
metal fence at the end of the field at 120m reflects very strongly. Still some
of the signal goes through it and reflects from building across the street.&lt;/p&gt;
&lt;p&gt;To focus the image better, I wrote a &lt;a href="https://www.ll.mit.edu/asap/asap_06/pdf/Papers/27_Kragh_Pa.pdf"&gt;minimum entropy
autofocusing[PDF]&lt;/a&gt;
program. When image is in focus it is sharper and its entropy is lower.
Minimum entropy autofocusing algorithm picks a phase shift for each sweep that
minimizes the image entropy.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/sar_field.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_field.png" width="1917" height="764" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Autofocused SAR image. Click to enlarge.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Autofocused image is much sharper. Now the fence posts have much better
resolution, but there are still some places in the image that are not perfectly
focused.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw2/motion_error.png" width="831" height="500" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Motion error from autofocus.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Calculated motion error is few centimeters. Most of the remaining lack of focus
is probably caused by non-constant speed of the bicycle as autofocusing algorithm
can only correct motion error in range-direction.&lt;/p&gt;
&lt;p&gt;Note that autofocusing algorithm didn't enforce any continuity or other
structure on the phase error. Still the solved phase error is very physically
reasonable. Antenna couldn't have moved too much during the few milliseconds between the
sweeps and phase error difference between adjacent sweeps is small as expected.
I find it surprising that using optimization goal as simple as entropy
of the image works this well.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw2/sar_map.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw2/sar_map.jpg" width="1299" height="817" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Overlaid on aerial photo. Red line is the line of
    movement. Goals on field have moved since taking the picture.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When overlaid to image of the field from Google maps, SAR image agrees well with
it. Football goals have moved a little since map has been updated, but
everything else is where it should be.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Total cost was about 200€. PCBs were 28€, components were about 120€, battery
pack was 21€ from China and &lt;a href="http://hforsten.com/horn-antenna-for-radar.html"&gt;antennas were made from scrap
metal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All hardware design files, firmware and processing software is available at
&lt;a href="https://github.com/Ttl/fmcw2"&gt;github&lt;/a&gt;.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Horn antenna for radar</title><link href="https://hforsten.com/horn-antenna-for-radar.html" rel="alternate"></link><published>2015-02-22T00:00:00+02:00</published><updated>2015-02-22T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2015-02-22:/horn-antenna-for-radar.html</id><summary type="html">&lt;p&gt;Designing and fabricating better antennas for the radar&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;For background about the radar, read the article &lt;a href="http://hforsten.com/6-ghz-frequency-modulated-radar.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since the previous can antenna wasn't very good I made a better horn antennas.
Horn antenna is a good choice for a radar antenna for many reasons most
important being the very wide bandwidth and it's easy and cheap to make.&lt;/p&gt;
&lt;h1 id="cst-model"&gt;CST Model&lt;/h1&gt;
&lt;p&gt;I made few different antennas and I ended up with this small horn antenna.
Aperture is 100mm x 85mm and length of the antenna is 90mm. Bigger would have
been better, but would have needed more sheet metal and thus costed more.&lt;/p&gt;
&lt;p&gt;Simulated gain is 14.4 dB and 3 dB beam width is 35 degrees. For comparison old
cantennas had gain of 6 dB, beam width of 100 degrees and much higher sideband
levels, so these new antennas
should be significantly better increasing range and reducing clutter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_struct1.png" width="513" height="515" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Horn antenna model in CST.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_struct_feed.png" width="660" height="525" style="width: 50%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Horn antenna model in CST.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;

    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_farfield_structure.png" width="596" height="525" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated farfield radiation pattern.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_gain_2d.png" width="1394" height="504" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated gain.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_sparam.png" width="1533" height="496" style="width: 100%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="fabricated-antenna"&gt;Fabricated antenna&lt;/h1&gt;
&lt;p&gt;Antennas are made from 0.7mm sheet metal with pieces welded together. Quality
could have been better and there is more than 2mm tolerance at some places, it is
probably big enough to affect the performance. Base is
made from a piece of extruded aluminium that I picked from trash.&lt;/p&gt;
&lt;p&gt;I also put a piece of sheet metal between the antennas to reduce the
antenna to antenna coupling but it didn't seem to have much effect.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_front.jpg" width="1600" height="1208" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Fabricated horn antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_above2.jpg" width="2000" height="1500" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Fabricated horn antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/horn_feed.jpg" width="1600" height="1200" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Front view. Feedline can be seen in the middle.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="measurements"&gt;Measurements&lt;/h1&gt;
&lt;p&gt;I measured input reflection coefficient with a network analyzer.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/right_s11.png" width="1032" height="562" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Right side antenna S11.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/left_s11.png" width="1032" height="562" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Left side antenna S11.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Due to tolerances in manufacturing there are some differences in the input
reflection coefficient, but most importantly with both antennas S11 is below 10 dB for the whole usable frequency range of the radar (about 5.4 GHz to 6.1 GHz). When compared to the simulated
S11 above it's clear that the shape of the curve roughly agrees, but especially
at higher frequencies there are big differences with real antennas actually
working better than simulated.&lt;/p&gt;
&lt;h2 id="tests-with-radar"&gt;Tests with radar&lt;/h2&gt;
&lt;p&gt;I tested the antennas with the radar at the same football field as with the
previous antennas. The goal in the middle of the field 
that caused the targets near 50m with old antennas was removed but the surrounding trees and fence were still there.&lt;/p&gt;
&lt;p&gt;This time even without the clutter reduction (subtracting
previous pulse to get rid of stationary targets) results are
much better. Looking at the signal strength near the radar it's clear that new
antennas have much higher gain. From the lack of clutter it also seems that beam width is also now much narrower. The path were I walked is clear even before the clutter
reduction unlike with the previous antennas.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/new_snr_clutter.png" width="1704" height="1037" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Time-range plot with new antennas and no clutter
    reduction&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/football_field_range_clutter.png" width="1704" height="1037" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Same measurement with the old cantennas.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id="measuring-the-beam-width"&gt;Measuring the beam width&lt;/h2&gt;
&lt;p&gt;To know the exact beam width better I made a measurement where I walked
perpendicular to the radar beam. &lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/field.jpg" width="1600" height="1200" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Picture of the radar and surroundings with path I walked in red.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw-antenna/beam_clutter.png" width="1695" height="1037" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Beam size measurement results.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the measurement results and near 80 second mark small red blip can be
seen when I was inside the radar beam. I measured from that my walking speed is
about 1.4 m/s. Time I was inside the beam such that it could be differentiated
from the background was about 6.5 seconds. From the above plot we find that
the distance to radar is 11 m. From these numbers we can calculate that the beam
width of the radar is about 44 degrees. Since this is not 3 dB beam width but
more like 10 dB beam width it agrees well enough with the simulations. For
comparison beam width of the old cantennas was more than 100 degrees.&lt;/p&gt;
&lt;p&gt;In conclusion the radar works much better with proper antennas. Range is
increased, but the football field isn't long enough to test it. More importantly
smaller beam width decreases the amount of clutter picked up from the
surroundings and greatly increases the signal to noise ratio.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>6 GHz frequency modulated radar</title><link href="https://hforsten.com/6-ghz-frequency-modulated-radar.html" rel="alternate"></link><published>2014-12-02T00:00:00+02:00</published><updated>2014-12-02T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2014-12-02:/6-ghz-frequency-modulated-radar.html</id><summary type="html">&lt;p&gt;Making a cheap and simple radar with a range of about 100 meters.&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.ready.jpg" width="800" height="374" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finished radar board without antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I've for some time now wanted to do more RF design. Although I have taken some RF 
design courses, I haven't actually made a single RF design before. But you
can't learn without doing and inspired by the
&lt;a href="http://ocw.mit.edu/resources/res-ll-003-build-a-small-radar-system-capable-of-sensing-range-doppler-and-synthetic-aperture-radar-imaging-january-iap-2011/"&gt;MIT coffee can radar&lt;/a&gt; designed by Gregory Charvat, I figured that
building a radar should be a doable project
that would offer some challenge while also having some real world use.&lt;/p&gt;
&lt;p&gt;The simplest radar is a continuous wave Doppler radar, which continuously transmit
a constant frequency signal. This signal reflects from a moving target and
Doppler shift causes reflected signal to change frequency. This reflected signal
is then received and mixed with the transmitted signal. Mixer product
is the difference of the frequencies which is proportional to the speed of
the target. This kind of radar is very simple to make, in fact there are
even some &lt;a href="http://www.instructables.com/id/Radar-Gun-Hacked!/"&gt;children's toys&lt;/a&gt;.
Unfortunately it can't detect the range of the target and isn't that exiting.&lt;/p&gt;
&lt;p&gt;A little more sophisticated radar which can detect also the range can 
be made by modulating the frequency of the transmitted signal. This
kind of radar is called continuous wave frequency modulated radar (FMCW radar).&lt;/p&gt;
&lt;p&gt;Radar transmits a chirp, which increases linearly with the frequency. This chirp
is then radiated with the antenna, it reflects from the target and is 
received by the receiving antenna. On the reception side received and
undelayed copy transmitted chirps go to the mixer. Because
received chirp reflected from the target, but copy of the transmitted chirp is
undelayed there is delay between the signals. This causes mixer output
to have a low frequency signal which frequency depends on the distance to
the target. When there are several targets the output signal is sum
of different frequencies and the distances to the targets can be
recovered with Fourier transform.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/block.png"/&gt;
    &lt;p style="font-size:13px" &gt;FMCW radar block diagram&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Glossary of the terms in the block diagram:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MCU - Microcontroller&lt;/li&gt;
&lt;li&gt;VCO - Voltage controller oscillator&lt;/li&gt;
&lt;li&gt;PA - Power amplifier&lt;/li&gt;
&lt;li&gt;LNA - Low noise amplifier&lt;/li&gt;
&lt;li&gt;ADC - Analog to digital converter&lt;/li&gt;
&lt;li&gt;FFT - Fast Fourier transform&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Transmitted signal is: &lt;span class="math"&gt;\(f(t) = sin\left( 2\pi\left(\frac{f_0
+ f_1}{t_{\text{ramp}}}t+f_0\right)t\right)\)&lt;/span&gt; and the received signal
is a delayed copy of it, &lt;span class="math"&gt;\(f(t-t_d)\)&lt;/span&gt;. Mixer multiplies these signals, outputting
two frequencies which are the sum and difference of the multiplied signals. These can
be solved using the trigonometric identity: &lt;span class="math"&gt;\(\sin\phi \sin \theta
= \frac{1}{2}\left(\cos(\phi-\theta)-\cos(\phi+\theta)\right)\)&lt;/span&gt;.
Sum frequency is too high to be detected and is filtered out. Frequency
of the difference term gives: &lt;span class="math"&gt;\(f_{\text{diff}} = \frac{t_d
(f_1-f_0)}{t_{\text{ramp}}}\)&lt;/span&gt;. Since electromagnetic waves travel at the speed
of light delay of the received signal is twice the distance to the target divided by the speed of light.
Distance to the target in terms of the detector frequency
can be solved to be: &lt;span class="math"&gt;\(d = \frac{c f_{\text{diff}} t_{\text{ramp}}}{2(f_1-f_0)}\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;For example with &lt;span class="math"&gt;\(f_0\)&lt;/span&gt; = 6 GHz, &lt;span class="math"&gt;\(f_1\)&lt;/span&gt; = 6.1 GHz, &lt;span class="math"&gt;\(t_{\text{ramp}}\)&lt;/span&gt;
= 5 ms, &lt;span class="math"&gt;\(d\)&lt;/span&gt; = 100 m the output detector output frequency is
13.3 kHz, which is low enough to be detected with very cheap equipment.
Even a PC soundcard would have enough bandwidth and this is actually how the MIT coffee can
radar works.&lt;/p&gt;
&lt;p&gt;How about the Doppler shift? It can be found out in two ways. If the frequency
waveform is sawtooth wave, then we can take multiple consecutive range measurements
and then take another FFT over them. Because movement of the target also changes
the distance to it speed can be recovered and distance corrected.&lt;/p&gt;
&lt;p&gt;Another way is to modulate the frequency with triangle wave. Doppler shift
causes the mixer output frequency to be different depending on whether the
frequency is increasing or decreasing.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/fmcw_sch.pdf" target="_blank" width=100%/&gt;
    &lt;img src="https://hforsten.com/img/fmcw/fmcw_sch-0.png" width="1191" height="842" border="2" style="width: 100%; height: auto; "border:2px solid black;" title="Click for a pdf version""/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for a pdf version.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/layout.png" width="1429" height="689" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Board layout in Kicad without zonefills&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="designing-the-radar"&gt;Designing the radar&lt;/h1&gt;
&lt;p&gt;For radar applications high frequency is needed for smaller resolution.
High frequency also allows using smaller antennas because wavelength is smaller.
Unfortunately going to too high frequencies is going to get too expensive, component
selection starts to be very limited and the RF design starts to also get much more difficult.
2.4 GHz would be very easy and cheap to design
for because there are many different and cheap components for this frequency range due 
to it being used for many different purposes such as WLAN, microwave ovens,
radios and more.
Another frequency range where there also exists some more use and cheaper components is
5 – 6 GHz range which is used for 5 GHz WLAN. Going higher than that and
there isn't that much general applications and the components are going to
be very expensive. 6 GHz range is also still quite easy to design for and
parasitic losses aren't yet too high to demand using exotic substrates and
components.&lt;/p&gt;
&lt;p&gt;Normal FR4 board isn't very good for RF design, since impedance is not
controlled and there can be significant variations from board to board which
makes accurate designing impossible. Only cheap manufacturer that offers board
with controlled impedance is &lt;a href="https://oshpark.com/"&gt;OSH Parks&lt;/a&gt; four layer
process which uses FR408 substrate with a dielectric constant of 3.66.&lt;/p&gt;
&lt;p&gt;This board is not ideal for RF design, since spacing between the first and 
second layer is only 0.17 mm. This leads to very thin traces (50 ohm microstrip
is 0.35 mm wide) but they are still
within the manufacturing tolerances so there is no problem.&lt;/p&gt;
&lt;h1 id="microcontroller"&gt;Microcontroller&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/kinetis.png" width="1209" height="489" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Kinetis KL26Z128VFT4&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I wanted to have a microcontroller with a USB interface, so that it would be
easy to interface with the PC doing the heavy processing. Integrated ADC and DAC
would also be nice to save some board space and money.&lt;/p&gt;
&lt;p&gt;The cheapest microcontroller having all these was Freescale Kinetis KL26Z128VFT4.
It has an ARM Cortex-M0+ core that can be clocked at 48 MHz.
It costs 3.4€, has USB, 16-bit ADC and 12-bit DAC. Though, when you actually
read the datasheet ADC only haves about 13 effective bits without the hardware
averaging. It isn't a big downside, since even 13-bits is very good precision for this price.
I didn't notice it before ordering the board, but there is another catch with this
processor: it only supports USB 1.0. In theory full-speed USB 1.0 can transfer
at 12 Mbits/s which would be enough, but in practice this is not reached and only
about five sweeps of 1000 samples can be transferred in a second,
which gives a transfer speed of about 10 kB/s.&lt;/p&gt;
&lt;p&gt;Transfer speed can be almost doubled quite easily by compressing the transferred
signal. Since the signal is smooth, difference between two consecutive samples
is small.
This makes it possible to only transfer the difference to the last sample in one
byte. In some cases the difference doesn't fit in one byte, this can be worked
around by using a
special value (I used all ones) for byte, which is used to signal that two bytes long full sample value
follows. In practice this works really well and difference overflows only a few
times in full sweep and about ten 20 ms long sweeps of 1000 samples can be
transferred in a second. I believe that with more software optimization slightly
higher speeds could be reached, but 10 sweeps per seconds works well enough that
I haven't bothered yet.&lt;/p&gt;
&lt;p&gt;From the software perspective this was very interesting processor. Kinetis
offers a Processor Expert tool that can generate the driver code for the
peripherals automatically. This makes configuring clocks, ADC, I2C and all other
peripherals much easier and it works really well also in practice. There are
some things that it doesn't support, for example changing the timer speed at
runtime is not possible. Still, it's always possible to use the registers directly
to implement the missing functionality.&lt;/p&gt;
&lt;p&gt;I had much difficulties in programming this board, programming would succeed
only very rarely and most of the time programmer would either fail to
recognize the processor or fail to write to the flash. I even managed to brick one
microcontroller so that it couldn't be programmed anymore.
The cause turned out to be that this processor requires capacitance at reset
pin. I couldn't find a note about this on the datasheet, but without it
programming is not reliable. When looking at the reset line with the scope it
seems that processor will generate a sawtooth signal on it. It seems that without
the capacitor processor will reset itself too quickly for the programmer to connect.
After adding a 100 nF capacitor to the reset pin everything started to work reliably.&lt;/p&gt;
&lt;h1 id="rf-portion"&gt;RF portion&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/vco_sch.png" width="1080" height="536" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;VCO and PA input&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;RF signal is generated by the voltage controlled oscillator (VCO), which
tuning voltage is generated by the microcontroller. Output power 
of the VCO is 2 dBm, but because this power would be too much for the power
amplifier attenuator needs to be places before the PA to attenuate the input
signal so that it won't saturate the amplifier.&lt;/p&gt;
&lt;p&gt;Discrete microwave frequency VCOs don't seem to be very common components and
selection is very limited. VCO is the most expensive component in the whole radar costing 17€ a piece.&lt;/p&gt;
&lt;p&gt;In the picture you can also see a MOSFET that allows the PA to be switched off
by cutting of the bias voltage to it. 0 ohm resistor was put there to allow
replacing it with a bigger resistor to drop the bias voltage if needed.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/pa_sch.png" width="1423" height="626" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;PA and power divider&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The reason I chose to use 5 – 6 GHz band was because there is a band reserved
for radars around 5.8 GHz and this range overlaps with 5 GHz WLAN. Since
wifi components are very common, there are many different cheap choices for the
power amplifier. PA I chose to use is SST11LP12 which is a cheap WLAN PA costing
1.5€. There would haven been cheaper PAs, but their datasheets were so short on
the details that I didn't dare to use them.&lt;/p&gt;
&lt;p&gt;PA is the component using most of the power on the board. Supply current is
about 400mA from 3.3V. Since this is supposed to be a somewhat portable device
and I don't want to include big batteries the power and communication is done
over the USB. Maximum current draw of the USB device is 0.5A from 5V. This means
 that the PA uses about half of the available power. Since other components
 don't use nearly as much there shouldn't be a problem with USB powered
 operation.&lt;/p&gt;
&lt;p&gt;PA amplifies the -2 dBm signal from the VCO to 21 – 23 dBm (0.1 – 0.2 W) signal depending on
the frequency. Output signal then goes to the Wilkinson power divider, which divides the output
to the transmitting antenna and the mixers input port. Since full divider output
would be too much for the mixer a 7 dB attenuator is added before the mixer to
attenuate the input signal to more manageable level.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/mixer_sch.png" width="1560" height="660" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;LNA and mixer&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Radiated power is reflected from the target, captured by the receiving antenna,
amplified by the low noise amplifier and then mixed with the PA output signal.
LNA is also meant for the 5 GHz WLAN. It has a very low noise figure while also
being incredibly cheap costing only 1.1€ at small quantities.&lt;/p&gt;
&lt;p&gt;Amplified received signal is then passed to the mixers RF port, where it's mixer
multiplies the PA signal and the received signal. IF port of the mixer then
outputs two frequencies: difference of the input frequencies and the sum of the
input frequencies. Sum frequency is over 10 GHz which exceeds to mixer output frequency and doesn't need to be worried about.
Difference signal is the wanted result signal and it has a frequency of about 100 Hz to 10 kHz.&lt;/p&gt;
&lt;h1 id="baseband"&gt;Baseband&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/preamp_sch.png" width="416" height="616" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Variable gain amplifier&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After the mixer output signal has a very small amplitude in about microvolt range
and must be first amplified before filtering can be done. First amplifier uses
LT6230 low noise op-amp in a non-inverting amplifier connection. MCP4017 is a I2C controlled
variable resistor and varying it's resistance varies the gain of the amplifier.
Gain is &lt;span class="math"&gt;\(1+\frac{\text{R5}+\text{R(MCP4017)}}{\text{R9}}\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Input signal is has some small DC offset that depends on the antenna to antenna
coupling, but it should be around zero volts. Since I wanted to do this as cheap
as possible I chose not to use dual supplies for the amplifiers. This means that
signals DC offset needs to be rebalanced to 1.65V which is half of the op-amps supply voltage.
Rebalancing is done with the capacitor C17 that blocks the DC voltage and
resistor R3 which is connected to the 1.65V setting the signal DC level.&lt;/p&gt;
&lt;p&gt;This works, but is not a very good solution. Resistor attenuates the signal
and increases the noise at the input. In hindsight using two supplies would have
been a better solution without increasing the cost by more than few euros.&lt;/p&gt;
&lt;p&gt;DC balancing has also another issue: Since the balancing is done with a resistor
the DC voltage at the input won't be exactly 1.65V. Amplifier has a high gain
and it will also amplify the DC offset causing the output voltage DC offset to
be off.&lt;/p&gt;
&lt;p&gt;I noticed the issue before ordering the boards, but my solution of adding
another DC blocking capacitor after the filter is not really a good solution
since it doesn't fix the bad balance after the first amplifier.
A better solution is to make the gain frequency dependent by adding a series
connection of 
capacitor and resistor (R0 and C0 in the picture) in parallel with R9. DC won't flow through the capacitor
and the gain is set by the R9. AC will flow through the capacitor and gain is
set by the parallel combination of R9 and the resistor R0.
I found that setting R9 to 1.5k and adding 44 uF capacitor and 50 ohm resistor worked well.
This solution is not without downsides, R9 contributes most of the noise in the
amplifier and increasing it will also increase the noise. It doesn't matter much
though, since turns out that due to bad antennas gain can't be turned big enough
for it to reach the noise floor.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/filter_sch.png" width="882" height="457" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Antialiasing filter&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Because signal is going to be sampled by the ADC, filtering is needed to ensure
that highest frequency of the signal is less than half of the sampling
frequency, so that aliasing doesn't occur. Filter itself is the same as the one
used on the MIT coffee can radar. Cutoff frequency is 15 kHz, but it could have
been higher since the microcontroller can sample at least at 200 kHz. In
practice it doesn't matter since range of the radar is not enough to generate a 15
kHz signal.&lt;/p&gt;
&lt;p&gt;Filter is also not very good. It's a low pass filter, but since higher
frequencies are generated by the targets that are farther away filters gain
should increases with the frequency until the cutoff frequency. This way the
weak higher frequency signals are amplified more and the dynamic range of the
radar increases.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/single_to_diff_sch.png" width="1182" height="642" style="width: 90%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Single ended to differential conversion circuit&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Using a ADC with differential input gives a better resolution than using
a single ended input with the microcontroller that I'm using. The circuit above
is straight from the LTC6242 datasheet and it will convert the single ended
signal from the filter to differential signal for the ADC.&lt;/p&gt;
&lt;p&gt;This circuit also had some unexpected problems. LTC6242 is rail-to-rail op-amp,
but when I tested the circuit output saturated already at 2.6V even though
operating voltage is 3.3V. Cause for this turned out to be the common mode
voltage range of the inputs. 2.6V is the maximum voltage that should be applied
to inputs and higher voltage than that will saturate the inputs and the output
responds as if the input voltage was 2.6V.&lt;/p&gt;
&lt;p&gt;Fix for this isn't too complex. Some gain needs to be added to the circuit so
that the 2.6V input generates 3.3V output without any inputs voltage exceeding
2.6V. This is possible by adding a resistor Rx in parallel with C21. This
attenuated 
feedback signal to the first op-amp which causes its output to increase output voltage
so that the feedback voltage at negative input equals the input signal at
positive input. Full voltage of the first op-amps output won't be
applied to the second op-amp and the circuit will work correctly with full
output swing.&lt;/p&gt;
&lt;h2 id="power-splitter"&gt;Power splitter&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/splitter.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.splitter.jpg" width="800" height="807" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Wilkinson power splitter&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Power splitter is a &lt;a href="http://en.wikipedia.org/wiki/Wilkinson_power_divider"&gt;Wilkinson divider&lt;/a&gt;.
It's lossless divider with good isolation between the ports, but it is frequency
dependent. If the signal is too far off from the design frequency
isolation between the ports suffers. Frequency range that radar uses is not
too big for this to matter too much and it works fine. One disadvantage
in this application is the equal division of the power. We would like most
of the power to go to the transmission antenna and only a little bit to the
mixer. With equal division there needs to be an attenuator before the mixer to
make decrease the input power. This is a wasted power that should be used in
transmission. More complicated power splitter would add too much
board area to be worth it, even the simple Wilkinson splitter uses large amount
of the board compared to how many components could be fit into the space it uses.&lt;/p&gt;
&lt;p&gt;I would have liked to use smooth bends in the splitter since they are simple to
design, but Kicad doesn't
support them. So instead I had to use 90 degree mitered corners.
Mitered corner is just a bend with a cutout
on the corner which negates the impedance change from the bend. They are very
often used in microwave circuits since they don't reflect the signal and can be
fit into smaller space than smooth bends.&lt;/p&gt;
&lt;p&gt;Solder mask is also removed from the top of the traces so that it doesn't affect the
impedance. It also has a side effect of making the traces look much cooler 
since the beautiful gold plating is visible. Large number of vias are
required to connect top and bottom ground planes together. Wavelength at 6 GHz is
about 36 mm, distance between the vias needs to be much smaller than this so that
there isn't potential differences in the ground planes. A common rule of thumb
is that the distance between vias should be 1/20 of the wave length. Since
adding more vias doesn't cost anything I were generous in placing them and the distance between vias is
about 1mm around the RF traces.&lt;/p&gt;
&lt;p&gt;First version I designed used the usual design equations: Impedance of two divider
traces is
&lt;span class="math"&gt;\(\sqrt{2}\times 50 \approx 71~\Omega\)&lt;/span&gt; and the length is quarter of the
wavelength, which equals about 8mm.&lt;/p&gt;
&lt;p&gt;Since I had a access to &lt;a href="https://www.cst.com/Products/CSTMWS"&gt;CST microwave
studio&lt;/a&gt;, I decided to simulate the divider
with it. CST has a handy feature that allows importing the gerber files so the
simulation model dimensions are exactly as drawn in KiCad.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/cst_divider.png" width="1152" height="555" style="width: 75%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Power divider modeled in CST. Blue cylinder is
    100 ohm resistor and red planes are waveguide ports.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/divider_s_params_with_cut.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.divider_s_params_with_cut.png" width="800" height="302" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Simulated S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Divider was designed to have a center frequency of 5.5 GHz, but according to
the CST it's working best at 6 GHz. Adding 1.2 mm more length shifts the working
frequency to 5.5 GHz. I guess the difference is caused by the fact that
I calculated the trace length from the branch to the center of resistor pad, but I believe length
of the trace should be calculated from the branch to the center of the resistor.&lt;/p&gt;
&lt;p&gt;From the low S11, -35 dB at best, it can be seen that the mitered bends are 
working and only a minimal amount of power is reflected back to the input.
Though even with a straight 90 degree bends without miters reflections at
these frequencies are most likely going to be minimal since wavelength of the
signal is much bigger than the corners.&lt;/p&gt;
&lt;p&gt;S21, gain from PA to transmitting antenna is -3.3 dB. Ideally it would
be -3 dB, which equals even split of power with nothing being wasted,
but especially substrate losses consume some of the power. 0.3 dB extra
loss is still a very good figure.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/divider_s_param_copper_accurate.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.divider_s_param_copper_accurate.png" width="800" height="289" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;S-parameters after adjusting the length.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With the adjusted trace lengths port isolation is below -20 dB at all usable
frequencies (5.3 GHz to 6.3 GHz).&lt;/p&gt;
&lt;h1 id="sma-connector-interface"&gt;SMA connector interface&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/cst_sma.png" width="892" height="605" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;SMA connector modeled in CST&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With ordinary low frequency design something as simple as connector doesn't
really require any attention. However, things are different with RF
frequencies. To minimize the loss in microstrip to coaxial cable transition
I decided to simulate also this interface. The potential issue with the connector is that
the 1.3 mm center conductor is much bigger than the 0.35 mm microstrip that
should connect to it. Since connector pin needs few millimeter solder pad to
solder the pin, the pad itself works as a microstrip and since it's so wide
it has a very low impedance and there is a significant impedance discontinuity between the microstrip and
coaxial cable which reflects the power back.&lt;/p&gt;
&lt;p&gt;There are connectors that are more suitable for these track widths, but
these are the cheapest SMA connectors and even they are quite expensive costing 3€ a piece.
Better ones would have costed about 10€ a piece, which would have been 
a significant part of the total cost.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/sma_sparams.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.sma_sparams.png" width="800" height="298" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Simulated S-parameters.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Simulation confirms that the there is an issue. S11 is -3 dB at 6 GHz, which
means that half of the power goes to antenna and other half is reflected back.
But this doesn't mean that this connector cannot be used, it only requires 
some changes to interface. Issue is that the solder pad works as a microstrip
and it has a wrong impedance. Making it thinner would solve the problem,
but it's not possible or connector can't be soldered. The other solution
is to make a cutout to the ground plane. Because impedance of the microstrip
depends on the distance to the ground plane below it, making a cutout to the
ground plane below the pad will raise its impedance and eliminate the impedance
discontinuity.&lt;/p&gt;
&lt;p&gt;After few simulations turns out that the optimal cutout is 3.7x2.6 mm and
with it the S11 is -22 dB, so hardly any power (0.6%) is reflected back.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/sma_ground_cut.png" width="809" height="631" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Ground plane cutout.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/sma_cut_sparams.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.sma_cut_sparams.png" width="800" height="298" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;S-parameters with a cut ground plane.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="soldering-the-board"&gt;Soldering the board&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/1bare_board.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.1bare_board.jpg" width="800" height="440" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Bare PCBs from &lt;a href="https://oshpark.com/"&gt;OSH Park&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Beautiful purple PCBs from OSH Parks.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/2divider.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.2divider.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Close up of the PA and power divider.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Some roughness can be seen on the thin power divider 71 ohm trace edges. It's
not very good for performance since width of the trace affects its impedance.
Roughness is still low enough that the effect to the impedance should be minimal.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/3stencil.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.3stencil.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Spreading the solder paste with stencil from &lt;a href="https://www.oshstencils.com/"&gt;OSH stencils&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Paste for the QFNs can be applied by hand, but I'd rather avoid it if
possible.
Using stencil just makes applying the solder paste so much easier and saves
so much time that it makes sense to order a stencil even when making a single
board since they are so cheap.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/4paste.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.4paste.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;After applying the solder paste.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A very good spread of the paste. There was a slight misalignment of the stencil, but it isn't
big enough to matter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/5populated.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.5populated.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Placing the components manually.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I could really use microscope with some of the smallest components, but
I managed to place all the components even without one. 0402 passives are still
kind of pain to use, but they save board space and are required for RF reasons
in some places.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/7soldered_pa.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.7soldered_pa.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Close up of the soldered PA.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After five minutes in the oven paste has reflowed and components are soldered.
Soldering with the reflow oven is just so good that I wonder how I once managed
without one. Solder joint quality is just so
much better than manually soldered and oven gets the job done much faster.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/8backside.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.8backside.jpg" width="800" height="397" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Backside also had some components.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For the smallest board (and cheapest price) both sides of the board need to be used.
I planned on not ordering a stencil for the backside so there are only
few passive components which I applied the solder paste manually.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/labeled.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.labeled.jpg" width="800" height="600" border="2" style="width: 80%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Board with the components labeled.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Finally above is a picture with the different components labeled.
Unlabeled components on the center are: 3.3V regulator for analog components,
3.0V regulator for VCO and LNA and op-amp to for generating 1.65V virtual ground
for the baseband filter.&lt;/p&gt;
&lt;p&gt;VCO VTUNE amplifier is needed to amplify 3.3V DAC output signal from
microcontroller to 10V.&lt;/p&gt;
&lt;h1 id="testing-the-board"&gt;Testing the board&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/spectrum.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.spectrum.jpg" width="800" height="600" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Output spectrum at full sweep bandwidth. 20 dB
    attenuator at the output.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the spectrum analyzer output when radar is VCOs tuning voltage
is swept full range from 0 to 10V. Output frequency goes from 5.6 GHz to 6.3
GHz. Since there is a 20 dB attenuator at the output, real output power is 15 dBm. This
includes the loss in the 1m SMA cable which should be around 1 dB at these
frequencies.&lt;/p&gt;
&lt;p&gt;Output power is not quite even and drops actually 6 dB over the
range. This is expected and follows the specifications in the PA datasheet. Maximum gain
is achieved already at 4.0 GHz and it drops by about 10 dB at 6 GHz. This
amount of gain unevenness is not a problem since sweep bandwidth is around 100
MHz and drop over this bandwidth is low.&lt;/p&gt;
&lt;p&gt;Theoretically input power of the PA is -5 dBm and gain is about 27 dB at 6 GHz,
so the output power should be 22 dBm. Since the power divider has simulated loss of 3.3 dB the
output power should be 18.7 dBm minus the unknown loss in the cable. Measured
power is not quite the calculated one, but it's close enough to say that the PA
and divider are working as designed.&lt;/p&gt;
&lt;h1 id="antennas"&gt;Antennas&lt;/h1&gt;
&lt;p&gt;With the board done it's time to make the antennas. Usually radars use very
directional horn antennas. Directionality of the beam is important so that only
signal from the target is picked up instead of the surroundings. Besides the
directionality radar antenna should have low sidebands so that antenna to
antenna coupling is low and it doesn't pick up reflected signals from behind and
sides of
the radar. Also the antennas should not reflect the power back but instead do
their job and radiate it.&lt;/p&gt;
&lt;p&gt;Maybe one of the best antennas would be a pyramidal horn antenna. It can
fulfill all the specifications, except that manufacturing is expensive. I didn't want
to spend too much money in making a very good antenna when I wasn't sure if the radar board
would work. So instead I decided to first make cheap and crappy antennas to
test the board and maybe then manufacture proper antennas.&lt;/p&gt;
&lt;p&gt;So I decided to make antennas out of cans. Can antennas are more
commonly known as cantennas. Problem with most of the cans is that they are too
big for 6 GHz range. 33 cl soda can would be good but they are made out of aluminium
which I don't have equipment to solder. Most promising cans I found were two
quite small cans originally containing canned pears.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/antenna_model.png" width="793" height="586" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Model of the cantenna in CST.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/farfield_3d.png" width="689" height="552" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Simulated farfield directivity pattern.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The only free parameters with the cantenna design is the location of the SMA
connector and length and the diameter of the SMA connector center wire.
Even though I tried to optimize them as best I could, not surprisingly performance of the cantennas is really bad.
Beam width is 100 degrees, which is really wide for a radar antenna. Simulated
radiation pattern is also quite weird, there is a hole right in the center of
the main beam and no matter how I adjusted the feed position, feed line length
or its diameter I couldn't get rid of the hole without sacrificing radiation
efficiency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/farfield_theta_phi90.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.farfield_theta_phi90.png" width="800" height="297" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Farfield with constant phi of 90 degrees.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/farfield_theta_phi0.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.farfield_theta_phi0.png" width="800" height="299" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Farfield with constant phi of 0 degrees.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As can be seen from the above, the hole is pretty deep. It's a pretty bad antenna
but it should be enough to test if the radar works.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/cantenna_s11.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.cantenna_s11.png" width="800" height="289" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Simulated input reflection.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Input reflection is decent, anything below -10 dB should be fine.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/cantennas.jpg" width="1400" height="887" style="width: 70%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Finished cantennas.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/cantenna_sma.jpg" width="1200" height="835" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Closeup of the SMA connector.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Best way to solder the SMA connector is to first remove the teflon insulator and
just solder the shell first. It's easier to position if the teflon piece is
inserted from the wrong side so that it comes out to the inside of the can.
Then the teflon piece will center the shell into the hole, which makes soldering
it much easier. After soldering the shell feed wire needs to be soldered to the
center conductor of the SMA connector. This is also easier to solder with the
teflon piece removed. After soldering the feed the teflon piece can be pushed
back from the outside.&lt;/p&gt;
&lt;p&gt;I was first worried if the soldering iron would have enough power to heat the
can hot enough, but there weren't any difficulties in soldering the connector with a regular
soldering iron and a regular solder wire.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/cantenna_inside.jpg" width="1200" height="1197" style="width: 60%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Inside view showing the feed.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/cantenna_s11_vna.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/cantenna_s11_vna.jpg" width="1200" height="742" border="2" style="width: 60%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Measured input reflection. X-axis is frequency from 4 to
    6 GHz and y-axis is S11 with 10dB/div.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I also measured the S11 of the real antenna using network analyzer. It had
a maximum frequency of 6 GHz so it was just barely enough. Measured S11 is -15
dB at 6 GHz, which is actually better than a simulated value. This might be the first
time that something works better in real life than simulations. Both of the antennas have pretty much identical S11, so
manufacturing tolerances probably can't be blamed. Reason is probably the caused
by the simplistic model which didn't include the ripples in the can. Those would
have been too time consuming to make, so I just modeled the antenna with smooth
walls. Shape of the S11 still agrees closely to the simulated shape suggesting
that the simulations are not too inaccurate.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/radar_system.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/radar_system.jpg" width="1400" height="1168" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Radar with the antennas.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Finally I taped the antennas to a supporting plate and connected the radar board.&lt;/p&gt;
&lt;h1 id="outside-testing"&gt;Outside testing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/field.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.field.jpg" width="800" height="600" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Football field and the radar.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is a football field where I tested the radar. I used a carboard box to
raise the antennas from the ground. In the picture radar is behind the PC.&lt;/p&gt;
&lt;p&gt;Usually radars have very directional antennas so that they can be pointed
accurately at the target without too much reflections coming from the
surroundings.
Since the antennas that I'm using have a beam width of about 100 degrees,
radar is going to pick up a lot of reflected signals, also called clutter, from
the surroundings.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/field_otherside.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.field_otherside.jpg" width="800" height="707" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;View from the other side of the field. Cardboad
    box can be seen in the center with radar on top of it.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the view from the other side. Box under the radar can be seen at the
middle, but radar isn't really visible at this distance.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/football_field_range_clutter.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.football_field_range_clutter.png" width="800" height="487" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Range-time plot without clutter reduction&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is the processed signal plotted as time in seconds on y-axis and
range on x-axis. Colors are the power of the detected frequency referenced to
some arbitrary value.&lt;/p&gt;
&lt;p&gt;There's a quite lot of clutter in the foreground from the trees at the sides.
Some of the clutter is also caused by the reflections from sides and behind of
the radar via sidebands of the antennas.
Around 50 m clutter from the goals in the middle of the field can be seen clearly
and at around 130 m is clutter coming from the buildings at the background.
It's quite hard to see, but especially at the foreground a diagonal line of my
path can be seen.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/fmcw/football_field_range.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/fmcw/thumb.football_field_range.png" width="800" height="487" border="2" style="width: 70%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Range-time plot with clutter reduction&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After subtracting the previous pulse from the current one all the stationary
clutter is cancelled and suddenly things are much more clearer. Signal to noise
ratio is about 15 dB at 50 m and goes to zero near 90 m. There is still quite a lot
of noise due to clutter not being completely canceled. For example wind moving
trees and inaccurate sampling causes the consecutive pulses to not be completely
identical and there is some clutter left even after the subtraction.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/football_signal_peak.png" width="1032" height="562" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Capture of one sweep. ADC range is scaled from -0.5 to 0.5.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is one of the captured waveforms and it's clear that everything doesn't
seem to be right. There is a very big peak at the beginning of the sweep which
limits the maximum gain of the automatic gain controller to about one third of the maximum
gain. If the peak and the
low frequency signals wouldn't exist, the gain could be increased which would
also improve range of the radar.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/fmcw/mixer_output.png" width="894" height="500" style="width: 80%; height: auto;"/&gt;
    &lt;p style="font-size:13px" &gt;Mixer output captured with an oscilloscope.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Looking at the oscilloscope capture of the mixers output it's clear what the
cause is. Low frequency high amplitude signals caused by the reflections
close to the antenna cause very big discontinuity at the start of every
sweep. Since this signal is AC coupled to the baseband, it will cause a decaying
peak after passing through the capacitor. Fix would be to use better antennas.&lt;/p&gt;
&lt;p&gt;So in summary the radar works and the range is not too bad, though it
could be easily increased with better antennas and I thats what I'm going
to start working on next.&lt;/p&gt;
&lt;p&gt;All hardware design files, firmware and processing software is available at
&lt;a href="https://github.com/Ttl/fmcw"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (true) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Electronics"></category></entry><entry><title>Making embedded Linux computer</title><link href="https://hforsten.com/making-embedded-linux-computer.html" rel="alternate"></link><published>2014-07-10T00:00:00+03:00</published><updated>2014-07-10T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2014-07-10:/making-embedded-linux-computer.html</id><summary type="html">&lt;p&gt;Designing and soldering a small ARM Linux board with 217 ball BGA package and 0402 passive components&lt;/p&gt;</summary><content type="html">&lt;p&gt;All of the best integrated circuits today come in hard to solder BGA packages.
Because BGA packages have connections under the chip soldering is
harder and it needs to be done using a reflow oven or hot plate. Another
problem is with designing the PCB, vias and traces need to be small enough
to fit between the solder balls and there needs to be usually quite many layers in
the board to make room for all the closely packed traces.
This means that a cheap Chinese two layer board doesn't have enough room and more layers
are needed. Adding layers increases the cost of the board dramatically when ordering
only a few copies.&lt;/p&gt;
&lt;p&gt;I wanted to try designing a board with BGA chips in it to see how hard soldering them 
could be. So I decided to design a small ARM
embedded system that can run Linux. ARM processor that I decided to use was &lt;a href="http://www.digikey.fi/product-detail/en/AT91SAM9N12-CU/AT91SAM9N12-CU-ND/3128657"&gt;AT91SAM9N12&lt;/a&gt; in a 217 ball LFBGA package, just because it was the cheapest ARM processor with memory management unit which is required to
run Linux.
Originally I wanted to have only one BGA chip, but RAM in BGA package was significantly cheaper than in other packages
and I decided to also have DDR2 memory in a BGA package.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/diy-bga/via_positioning.png" width="650" height="680" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Positioning VIA for maximum amount of space.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Finding a manufacturer for the board turned out to be a slightly challenging task.
Two layers wouldn't be enough and at least four layers would be needed.
Ball diameter of the 217-LFBGA package is 0.4 mm and distance between adjacent balls is 0.8 mm.
To make a little bit more room for the vias ball land pattern can be made
slightly smaller than the solder ball. I used 0.36 mm pads.
Putting via between the four
balls maximises the available space. Manufacturer needs to be able
to make a via that fits in about 0.8 mm space. Almost any manufacturer can
make a via with this diameter, but the problem is that this distance includes
via drill diameter, two times the annular ring around the via and two times the 
minimum distance between via and trace. For example iTead's four layer board
has minimum via drill diameter of 0.3 mm, minimum annular ring width of 0.15 mm and minimum
0.15 mm between via and trace. This adds up to 0.9 mm which means that minimum
sized via can't fit between the BGA balls. The only reasonably priced manufacturer I found was 
&lt;a href="http://oshpark.com/"&gt;OSH parks&lt;/a&gt;. Their four layer board has smaller limits and
via can just barely fit between the BGA balls. As a bonus it's also cheaper than iTead
for small board.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/diy-bga/via_positioning2.png" width="695" height="691" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Minimum via with OSH park design rules, fits just barely&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Even though the via can fit between the BGA balls, there are still some problems:
There is not enough room for trace to go between two vias. This means that it's
not possible to route the BGA using standard escape routing where every
pad has one via. This means that board needs to have enough unconnected pads on the perimeter,
so that traces from the inside can be routed. Fortunately the processor has many general I/O-
pins that are left unconnected.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/diy-bga/trace_between_vias.png" width="951" height="802" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;... but trace doesn't fit between two vias without violating the design rules. CAS trace doesn't have enough room to fit between DQM0 and D15 vias.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Manufacturing concerns solved, it's time to start thinking what components should be on the board.
I don't really care about the usefulness of the board and this whole project is more
of a learning experience. To keep the costs down board size needs to be kept small.
This means that there won't be space for any extra interfaces like
ethernet, serial ports or SD-card.&lt;/p&gt;
&lt;p&gt;Besides the processor
and RAM other essential components are: mass memory, voltage regulators and supervisor circuit
for handling the reset of the chip. Processor can boot from the NAND, but I decided to also
have Dataflash for the bootloader just in case, though this ended up being left unpopulated.
For mass memory NAND flash is a good choice because it's
cheap with big capacity. Having it also in a BGA package would have been
cheaper, but I already have enough trouble with two BGA packages, so I decided to use 4Gb NAND in a
48 pin TSOP package.&lt;/p&gt;
&lt;p&gt;Connecting the components is explained well in the processor's datasheet, but
because the document is over 1000 pages long it might be hard to find all the details.
Atmel also publishes schematics of the evaluation boards that are very helpful when
designing a board.&lt;/p&gt;
&lt;p&gt;Some freedom needs to be taken with DDR2 traces. Normally traces should
be length matched, have controlled impedance and terminating or series resistors.
Reference design from the development board uses series resistors in all of the
DDR2's signals. I don't have enough room for them so I just decided to leave them out.
Impedance isn't 50 ohms either, because I had to use smaller traces to fit everything.
I hoped that because RAM is so close to the processor, lack of series resistors
and impedance mismatch wouldn't matter. All traces from CPU to RAM are about 25 mm long. 
Usual rule of thumb is that if trace length is over 10 % of wavelength of the signal
then transmission line effects should be taken into account. In this
case it would mean that frequency should be roughly above 1 GHz. RAM clock
frequency is only 133 MHz and even the first few harmonics are under the 1 GHz, which
suggests that this should work fine.
Just to be sure I matched the trace lengths within few millimeters, but
this might have been unnecessary.&lt;/p&gt;
&lt;p&gt;Voltage supplies are bit complicated. Processors core voltage is 1.0 V, 
RAM needs 1.8 V and NAND needs 3.3 V. Because the input voltage is 5 V from USB,
board needs to have three different voltage regulators. Normally it would be good
to reserve one layer on the board for power supplies and keep it free from signal traces
to lower power supply impedance, but board only has four layers and
one of them is used for ground plane. This would mean that there would
be only two layers left for signals which wasn't enough. So I didn't have separate
power supply layer and instead made several planes in different layers for different
power supplies.&lt;/p&gt;
&lt;p&gt;Losses with linear regulators
would have been too big for USB powered applications in the worst case, so I decided
to have 3.3 V regulator as a more efficient switching regulator. 1.0 V and 1.8 V regulators
are linear regulators that have 3.3 V as input voltage. Because linear regulators
losses depend on the difference between input and output voltage using 3.3 V as input
instead of 5 V increases the efficiency.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/schematic.pdf" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/schematic.png" width="1282" height="847" border="2" style="width: 100%; height: auto; "border:2px solid black;" title="Click for a pdf version""/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for a pdf version.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/board.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/board.png" width="1569" height="767" border="2" style="width: 85%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;PCB layout. Copper pours are not filled.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="soldering"&gt;Soldering&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/1boards.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/1boards.jpg" width="734" height="551" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Bare boards.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/2solder_paste.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/2solder_paste.jpg" width="486" height="365" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Solder paste and components applied to backside. Paste was
    applied manually with a toothpick. Components here are 1 mm (0.04 in) long. I put
    only the passives first to see how they would reflow and if the results weren't good
    I could use another board.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/3reflow_oven.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/3reflow_oven.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Reflowing with a toaster oven and custom controller.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/4backside.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/4backside.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;After reflowing. Three components were soldered in a wrong place.
    I just ended up taking them out, there are enough decoupling capacitors on the board
    and if few are missing there is not any negative effects. I also mistakenly placed
    a capacitor on the upper left footprint, but there should be a resistors instead.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/5stencil1.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/5stencil1.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;For the topside I got a stencil from OSH stencils so I wouldn't need to
    apply paste manually to BGA footprints. I secured the board and stencil by taping
    them to the table.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/6stencil2.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/6stencil2.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Stencil lines up very well.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/7paste.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/7paste.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;This might look excessive, but almost all of the solder paste is recovered. Some extra solder paste is needed to get even fill.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/8paste2.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/8paste2.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;After spreading the paste and removing the stencil.
    Much better than the backside paste I applied by hand.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/9components.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/9components.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;I started with non-BGA components. They are placed manually with tweezers and a steady hand.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/10at91sam9n12.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/10at91sam9n12.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;CPU and my fingertips. Ball spacing is 0.8 mm. Many new BGAs
    use even smaller 0.5 mm spacing.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/11bgas.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/11bgas.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;BGAs placed on the board. Component needs to be placed with less than 0.4 mm error or otherwise it might be soldered with one row offset and because
    solder balls are under the chip alignment cannot be checked.
    Without border draw
    on the silk screen it would have been almost impossible to place with required precision,
    with the silk screen it's easy to just line up the component border to the silk screen border.
    &lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/12reflowing.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/12reflowing.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Reflowing the top side. Geiger counter PCB are used to raise the PCB
    so that bottom side components don't touch anywhere. Solder surface tension will
    keep the bottom side from falling.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/13reflowed.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/13reflowed.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;After the oven. Solder joints look very nice and all components
    are still at their places.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/14nand_flood.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/14nand_flood.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Soldering the NAND Flash. My solder iron tip is bigger
    than the pins and soldering one pin at a time was too difficult. Easier way is to flood pins with solder and
    then take the extra out with solder wick.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/15nand_cleaned.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/15nand_cleaned.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;After removing the excess solder the solder joints
    are very high quality.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/16finished.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/16finished.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Finished board after adding the headers for power supply and
    debug serial port.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/board.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/board.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Final product with hand for scale.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/17backside.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/17backside.jpg" width="864" height="648" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;The other side. Empty footprint is for Dataflash.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;After plugging the USB cable to the USB device port nothing has exploded and 
I can see that a new
serial port &lt;em&gt;/dev/ttyACM0&lt;/em&gt; has appeared and opening it with SAM-BA program,
which is used to program the bootloader and kernel, everything seems to be working.
Many people say that soldering BGAs is hard but based on this experience I can't
agree. Maybe I just got lucky but I didn't have any problems with them.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/diy-bga/sam-ba.png" width="551" height="388" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;Opening SAM-BA. At91sam9n12ek is Atmel's development
    kit for this processor and its configuration also works for this board.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/diy-bga/sam-ba2.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/diy-bga/sam-ba2.png" width="1094" height="1040" border="2" style="width: 75%; height: auto; "border:2px solid black;""/&gt;
    &lt;p style="font-size:13px" &gt;DDR2 works, executing programs work and writing to NAND
    works. In other words everything works.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="software"&gt;Software&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/diy-bga/boot_flow.png"/&gt;
    &lt;p style="font-size:13px" &gt;Boot flow graph&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id="bootloader"&gt;Bootloader&lt;/h2&gt;
&lt;p&gt;Boot process start with internal ROM bootloader trying to find a valid program
in different memories. It probes SPI flash, SD card, NAND flash, second SPI flash
and I2C EEPROM for a valid program. If it finds one it starts it, otherwise it
enters into SAM-BA monitor, which is debugging mode where processor listens
serial and USB port for commands. This mode allows programming the bootloader.&lt;/p&gt;
&lt;p&gt;ROM boot can't boot Linux kernel directly so a second stage bootloader is needed.
It will initialize RAM and clocks and then load the Linux kernel.
&lt;a href="http://www.at91.com/linux4sam/bin/view/Linux4SAM/AT91Bootstrap"&gt;AT91 Bootstrap&lt;/a&gt; is 
a ready made bootloader that does all of this.
It will be stored at the beginning of the NAND flash it could also be placed to Dataflash
if I had populated its footprint.
Even though AT91 bootstrap could boot Linux directly it's more useful for debugging to have U-boot bootloader after it.
U-boot is its own mini operating system with command line and it can read
USB sticks, use ethernet, write and read from NAND and of course boot Linux.
Using U-boot makes it easier for example to erase NAND or change Linux
boot parameters.&lt;/p&gt;
&lt;p&gt;To compile the bootloader an ARM cross compiler is needed. I'm using
&lt;a href="http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/"&gt;Sourcery codebench lite edition&lt;/a&gt;,
because it's very easy to set up and works well. It's easiest to first load AT91SAM9N12EK development board configuration file.
Modifying this configuration file saves a lot of trouble compared to writing
new configuration file from scratch.&lt;/p&gt;
&lt;p&gt;To make it work for this custom board some changed are needed:
RAM size needs to be configured to 64MB, number of banks changed to 4 and some latencies tweaked (Evaluation board has 128MB of RAM with 8 banks).
NAND initialization function also needs to be modified,
this board has NAND flash connected to different place than the development board
and it's necessary to tell the bootloader about it.&lt;/p&gt;
&lt;h2 id="u-boot"&gt;U-boot&lt;/h2&gt;
&lt;p&gt;Configuring U-boot is very straightforward now that AT91 bootstrap has initialized the hardware.
It also has configuration file for at91sam9n12ek, but it's set up to boot from
SD-card by default. There aren't many other required changes to make,
because hardware is already configured. Some optional ones are enabling
UBIFS tools for creating and editing partitions on NAND flash and 
enabling support for reading ext4 formatted USB sticks. USB support makes
it possible to boot Linux kernel from USB stick, which makes easy to experiment
with different kernel configurations.&lt;/p&gt;
&lt;h2 id="linux-and-root-filesystem"&gt;Linux and root filesystem&lt;/h2&gt;
&lt;p&gt;Installing Linux isn't as easy as with a regular x86 PC.
Kernel needs to be configured to support all required devices and the root filesystem
image needs to be built. This could be done by hand, but it's easier to use
&lt;a href="http://buildroot.uclibc.org/"&gt;buildroot&lt;/a&gt; which is a set of makefiles for
building the root filesystem and kernel. Still the process can be a little
hard because of massive number of different options in kernel and buildroot.&lt;/p&gt;
&lt;p&gt;Buildroot doesn't have configuration file for at91sam9n12ek development board,
but it has a file for other Atmel board, at91sam9260ek. Using this configuration
as a base will makes the configuration little easier.
These settings can be loaded with "make at91sam9260ek_defconfig".&lt;/p&gt;
&lt;p&gt;First we want to have a relatively new kernel version, because there have been some
minor changes related to this processor. So let's use version 3.15.3, which
was released last week.&lt;/p&gt;
&lt;p&gt;Linux is configured in buildroot with "make linux-menuconfig" command. It will
open the usual Linux menu configuration window.
Most important configuration in the kernel is system type configuration menu.
We need to check AT91SAM9N12 support and "Atmel AT91SAM Evaluation Kits with device-tree support" option.
&lt;a href="http://en.wikipedia.org/wiki/Device_tree"&gt;Device-tree&lt;/a&gt; is an external binary file
that is loaded with the kernel which describes the hardware available on the board.
This makes it possible to use same kernel with different boards and device configuration
for different boards can be made using text files instead of writing slightly different C-files for
every board. Luckily there exists a device tree file for at91sam9n12ek which can be used as a base.
Required changes are basically removing the devices that are not available.
Path to this modified device tree file needs to be added to buildroot configuration
so that it knows to compile and build an image out of it.&lt;/p&gt;
&lt;p&gt;Other options worth enabling in the kernel are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;USB host support AT91_USB.&lt;/li&gt;
&lt;li&gt;NAND flash support and processor's internal NAND ECC controller (PMECC) support.&lt;/li&gt;
&lt;li&gt;Support for UBIFS, which is going to be used as a root filesystem.&lt;/li&gt;
&lt;li&gt;Ext4 support for reading USB stick.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In buildroot configuration we need to choose what programs we want to have
on root filesystem and the options for generating the root filesystem image.
This board has raw NAND memory without controller so usual desktop filesystems
such as ext4 can't be used. UBIFS is the usual choice and it's also fine in this case.&lt;/p&gt;
&lt;p&gt;UBIFS has several options that depend on the NAND flash type and if they
are wrong Linux can't read the resulting filesystem. These options
could be figured out from the NAND flash datasheet, but easier way
is to boot Linux from USB stick and create ubi partition from there.
Or alternatively use U-boot's "ubi info" command which will read the NAND
and output the required configuration values.&lt;/p&gt;
&lt;p&gt;After typing "make". Buildroot will download cross compiler, Linux kernel
and all of the other packages; build them and output kernel, device tree and
root filesystem images. These can be transferred to board with SAM-BA program.
Same program is needed to program NAND ECC controller parameters. Same parameters
should be also configured to AT91 bootstrap, U-boot and Linux kernel or otherwise
they will report that NAND is corrupted.
In this case NAND has 2048 byte pages with 512 byte sectors and ECC should be able to
correct 4 bits per sector.
NAND addresses to store all the images can be found in AT91 bootstrap and U-boot configuration
files.&lt;/p&gt;
&lt;p&gt;After programming the board and resetting, the ROM bootloader should find the
AT91 bootstrap on the NAND and start the boot process:&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=80% controls&gt;
  &lt;source src="https://hforsten.com/video/diy-bga/booting.mp4" type="video/mp4"&gt;
  &lt;source src="https://hforsten.com/video/diy-bga/booting.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;If you are interested in learning more,
hardware and software files are available at
&lt;a href="https://github.com/Ttl/sam_board"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE 2014-08-10&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;I ran memory test on the RAM and there were some errors. Rarely, about once
per loop on memtest, there was an error on 19th bit on random write test.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;
&lt;span class="normal"&gt;53&lt;/span&gt;
&lt;span class="normal"&gt;54&lt;/span&gt;
&lt;span class="normal"&gt;55&lt;/span&gt;
&lt;span class="normal"&gt;56&lt;/span&gt;
&lt;span class="normal"&gt;57&lt;/span&gt;
&lt;span class="normal"&gt;58&lt;/span&gt;
&lt;span class="normal"&gt;59&lt;/span&gt;
&lt;span class="normal"&gt;60&lt;/span&gt;
&lt;span class="normal"&gt;61&lt;/span&gt;
&lt;span class="normal"&gt;62&lt;/span&gt;
&lt;span class="normal"&gt;63&lt;/span&gt;
&lt;span class="normal"&gt;64&lt;/span&gt;
&lt;span class="normal"&gt;65&lt;/span&gt;
&lt;span class="normal"&gt;66&lt;/span&gt;
&lt;span class="normal"&gt;67&lt;/span&gt;
&lt;span class="normal"&gt;68&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;memtester&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;
&lt;span class="nv"&gt;memtester&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;.&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2001&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2012&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Charles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Cazabon&lt;/span&gt;.
&lt;span class="nv"&gt;Licensed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;under&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GNU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;General&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;License&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;only&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;.

&lt;span class="nv"&gt;pagesize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;
&lt;span class="nv"&gt;pagesizemask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;xfffff000&lt;/span&gt;
&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nv"&gt;MB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20971520&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;got&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nv"&gt;MB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20971520&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mlock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;...&lt;span class="nv"&gt;locked&lt;/span&gt;.
&lt;span class="k"&gt;Loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Stuck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Address&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Value&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XOR&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MUL&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DIV&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Increment&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Bits&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Checkerboard&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Spread&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Flip&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Ones&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Zeroes&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;

&lt;span class="k"&gt;Loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Stuck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Address&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Value&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XOR&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MUL&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DIV&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Increment&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Bits&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Checkerboard&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Spread&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Flip&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Ones&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Zeroes&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;FAILURE&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;xfdb30157&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;xfdbb0157&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x001892e4&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;

&lt;span class="k"&gt;Loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Stuck&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Address&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Value&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XOR&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SUB&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MUL&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DIV&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OR&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Compare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AND&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Increment&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Bits&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Sequential&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Checkerboard&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Spread&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Flip&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Ones&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Walking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Zeroes&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Writes&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;FAILURE&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x7df4005f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x7dfc005f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x000b7778&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This probably means that the error is with D3 trace. It's the longest data trace
and runs parallel to some address lines for short runs. Hard to say for sure
what the cause is, but it is related to signal integrity as this chip should
be able to run at 133MHz reliably. Despite the errors, this configuration
worked well enough that no kernel panics or anything related to bad memory
was observed during the normal operation. Issues seems to arise only when
memory is stressed.&lt;/p&gt;
&lt;p&gt;If clock speed is decreased to 100MHz memory seems to work fine. I have run
memtester for over 10 hours consecutively and no errors have been found.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>BSDL file to KiCad library converter</title><link href="https://hforsten.com/bsdl-file-to-kicad-library-converter.html" rel="alternate"></link><published>2014-04-27T00:00:00+03:00</published><updated>2014-04-27T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2014-04-27:/bsdl-file-to-kicad-library-converter.html</id><summary type="html">&lt;p&gt;I made a small tool to automatically convert a BSDL file to KiCad library.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Boundary Scan Description Language (BSDL) files are provided by many
electronic component manufacturer for testing the pins of the
microcontroller, FPGA or any other bigger device with JTAG.
Even though they are meant for JTAG testing they can be used to
generate device symbols for schematic designs, because these files list pins of
the device and their locations on the chip.&lt;/p&gt;
&lt;p&gt;Eagle has an ulp file that can generate the symbol from BSDL and
I have used that with Eagle, but I wasn't aware
of any similar tool for KiCad so I made my own. It's still a beta version
so report any bugs you find.&lt;/p&gt;
&lt;p&gt;Automatic generation is not perfect and some errors are common.
BSDL files are not meant for this
and often some unimportant pins for JTAG are left out, for example
input voltage and ground pins, because testing them with JTAG is not possible.
Also listed pin names are usually the first function of the pin,
but in many devices one pin can have many alternate functions.
Still BSDL files usually have most of the pins and user only needs to
check that everything is correct and add alternate functions to
pin names if needed. Creating a big BGA device symbol using BSDL files
is significantly faster than doing everything by hand even
if some manual checking is needed.&lt;/p&gt;
&lt;p&gt;You can access the tool &lt;a href="http://hforsten.com/bsdl"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/kicad-bsdl/sam9n12.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/kicad-bsdl/sam9n12.png" width="814" height="797" border="2" style="width: 50%; height: auto; "border:2px solid black;""/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Automatically generated AT91SAM9N12 microcontroller symbol.
    Input voltage and ground pins are missing because BSDL file didn't list them.
    Other than that pins are correct.&lt;/p&gt;
&lt;/div&gt;</content><category term="Electronics"></category></entry><entry><title>Simulating audio effects with SPICE</title><link href="https://hforsten.com/simulating-audio-effects-with-spice.html" rel="alternate"></link><published>2013-12-08T00:00:00+02:00</published><updated>2013-12-08T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2013-12-08:/simulating-audio-effects-with-spice.html</id><summary type="html">&lt;p&gt;While I was looking at some audio effect circuit schematics at the internet, I though that it would be nice to try to simulate them SPICE first and listen them, before building them to confirm if they sound good or not ...&lt;/p&gt;</summary><content type="html">&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;While I was looking at some audio effect circuit schematics at the
internet, I though that it would be nice to try to simulate them SPICE first
and listen them, before building them to confirm if they sound good or not.
But there aren't any electrical simulators that can read and output audio files,
so I wrote a program using Pythons &lt;a href="http://docs.python.org/2/library/wave.html"&gt;wave&lt;/a&gt; module,
which can read a wave file and output a list of time-voltage points. Ngspice's
file source device can read this big list of points and output a voltage waveform
that matches the audio signal. This servers as a input for the effect circuit.
To hear the output another program is needed to convert the output trace
to wave file, for this too I used the Pythons wave module.&lt;/p&gt;
&lt;h1 id="distortion-effect"&gt;Distortion effect&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/od250_schm.png" width="1090" height="562" border="2" style="width: 75%; height: auto; "border:2px solid black;" width=75%"/&gt;
    &lt;p style="font-size:13px" &gt;Overdrive 250 schematic.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This was one of the simpler distortion effects that I found. It's called 
Overdrive 250 and it's pretty popular guitar effect pedal. Schematic is 
from &lt;a href="http://www.montagar.com/~patj/gindex.htm"&gt;this site&lt;/a&gt;. Function is simple, first
there is an AC-coupling capacitor and biasing circuit, because voltage supply is single sided.
Op-amp is used as a non-inverting amplifier to amplify the weak input signal. R5 is really potentiometer that controls
the gain. At the output there's an another AC coupling capacitor and two diodes that
clip the signal. Bigger the signals amplitude is the more diodes clip and
distort it.&lt;/p&gt;
&lt;p&gt;Test audio signal is a clean guitar sound and it's from &lt;a href="http://www.freesound.org/people/Khoon/sounds/151009/"&gt;freesound.org&lt;/a&gt; made by user named Khoon and released under a very permissive Creative Commons 0 License, which basically says that the work is public domain.&lt;/p&gt;
&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/123713570&amp;amp;color=ff6600&amp;amp;auto_play=false&amp;amp;show_artwork=false"&gt;&lt;/iframe&gt;

&lt;p&gt;First the output with 500k R5, which gives a milder distortion. Input amplitude
matters very much, because it's multiplied by the gain of op-amp. In this
recording input amplitude is 10mV. These clips are quite loud (as can be seen from the waveform), so check your output volume before hitting the play.&lt;/p&gt;
&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/123717199&amp;amp;color=ff6600&amp;amp;auto_play=false&amp;amp;show_artwork=false"&gt;&lt;/iframe&gt;

&lt;p&gt;Decreasing R5 increases the gain and distortion:&lt;/p&gt;
&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/123717610&amp;amp;color=ff6600&amp;amp;auto_play=false&amp;amp;show_artwork=false"&gt;&lt;/iframe&gt;

&lt;p&gt;Frequency spectrum effects of the distortion, made with audacity:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/original_spectrum.png" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Spectrum of the clean signal.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/od250_harsh_spectrum.png" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Spectrum of the harshly distorted signal.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above are the frequency spectrums of the original clean signal and the harshly distorted signal. Difference is not that great. Some low frequency signals are filtered by the AC coupling
capacitors and distortion has created new higher frequency signals. Increase of the
power over the whole bandwidth is an artifact of the SPICE to wav conversion.
To avoid clipping, I chose the maximum amplitude of signal to have the maximum
sample value. This changes the volume of the signal.&lt;/p&gt;
&lt;p&gt;Real circuit would probably sound slightly different, but I think this is pretty
good for a simulation.&lt;/p&gt;
&lt;p&gt;Simulation is not real time. It takes about five seconds to simulate one second
of audio at 44100Hz sampling rate. For longer simulations to save memory it's good to
save only the output waveform using &lt;code&gt;save v(out)&lt;/code&gt; SPICE statement.&lt;/p&gt;
&lt;h1 id="fuzz-effect"&gt;Fuzz effect&lt;/h1&gt;
&lt;p&gt;Second circuit is fuzz circuit, which is very similar to the previous
distortion circuit, except op-amp is replaced by discrete transistors.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/fuzz_schm.png" width="1101" height="533" border="2" style="width: 80%; height: auto; "border:2px solid black;" width=80%"/&gt;
    &lt;p style="font-size:13px" &gt;Fuzz circuit schematic. Transistors model is BC108.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This circuits output is also greatly affected by the input voltages amplitude.&lt;/p&gt;
&lt;p&gt;This one is simulated with 1mV input amplitude:&lt;/p&gt;
&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/123730724&amp;amp;color=ff6600&amp;amp;auto_play=false&amp;amp;show_artwork=false"&gt;&lt;/iframe&gt;

&lt;p&gt;And this one is with 10mV input:&lt;/p&gt;
&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/123730984&amp;amp;color=ff6600&amp;amp;auto_play=false&amp;amp;show_artwork=false"&gt;&lt;/iframe&gt;

&lt;p&gt;Result is very similar to the distortion circuit, mostly because of the
similar diode clipping.&lt;/p&gt;
&lt;h1 id="diode-clipper"&gt;Diode clipper&lt;/h1&gt;
&lt;p&gt;Waveform of the diode clipper is much smoother than I excepted it to be.
Of course this also depends on the voltage of the signal and parameters of the
diodes. But even this fairly smoothly clipped signal has lots of high frequency
harmonics.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/clipped_sine_1N4148.png" width="983" height="1001" border="2" style="width: 75%; height: auto; "border:2px solid black;" width=75%"/&gt;
    &lt;p style="font-size:13px" &gt;Simulation of 1V 400Hz sine wave clipped with a pair of 1N4148 diodes.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/spice-audio/clipped_sine_1N4148_spectrum.png" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Spectrum of diode clipped sine wave.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="source-code"&gt;Source code&lt;/h1&gt;
&lt;p&gt;Source code for the both of the used programs is available &lt;a href="https://github.com/Ttl/spice-audio-tools"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Toaster oven reflow controller</title><link href="https://hforsten.com/toaster-oven-reflow-controller.html" rel="alternate"></link><published>2013-09-08T00:00:00+03:00</published><updated>2013-09-08T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2013-09-08:/toaster-oven-reflow-controller.html</id><summary type="html">&lt;p&gt;Nowadays many of the most "exiting" chips come only in leadless packages, such as &lt;a href="http://en.wikipedia.org/wiki/Ball_grid_array"&gt;BGA&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Quad-flat_no-leads_package"&gt;QFN&lt;/a&gt; which are hard or impossible to solder just by soldering iron, because leads are under the chip where they can't be reached. These kinds of chips are usually soldered using reflow soldering ...&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/board.jpg" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Almost finished controller board. This one was soldered by hand.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Nowadays many of the most "exiting" chips come only in leadless packages, such as &lt;a href="http://en.wikipedia.org/wiki/Ball_grid_array"&gt;BGA&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Quad-flat_no-leads_package"&gt;QFN&lt;/a&gt; which are hard or impossible to solder just by soldering iron,
because leads are under the chip where they can't be reached.
These kinds of chips are usually soldered using reflow soldering.
In reflow process solder paste is used instead of solder wire.
It contains very small balls of solder in flux, diameter of the balls is just few micrometers.
First this paste is put on the contact pads, then components
are placed on the pads and whole board is heated in reflow oven where solder balls
in the paste melt and attaches the components firmly in place.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/reflow_profile.png" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Temperature profile in reflow soldering. Source: &lt;a href="http://en.wikipedia.org/wiki/File:RSS_Components_of_a_Profile1.svg"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For the solder paste to work correctly temperature of the oven must
follow correct temperature profile accurately. If the temperature rises
too quickly it can damage the components and
if the temperature rises too slowly, too much flux can evaporate and the 
components aren't soldered reliably.&lt;/p&gt;
&lt;p&gt;Real reflow ovens are usually expensive, like many other manufacturing tools,
but ordinary toaster ovens can be used in reflow soldering with few modifications.
They are much cheaper than real reflow ovens, but temperature cannot be set programmatically
or accurately. External controller board can be used to control these ovens.&lt;/p&gt;
&lt;h1 id="reflow-oven-controller"&gt;Reflow oven controller&lt;/h1&gt;
&lt;p&gt;Toaster oven reflow controllers have been done before many times (for example &lt;a href="http://www.instructables.com/id/Hack-a-Toaster-Oven-for-Reflow-Soldering/?ALLSTEPS"&gt;1&lt;/a&gt; and &lt;a href="https://www.sparkfun.com/tutorials/60"&gt;2&lt;/a&gt;).
I wanted to make a simple and cheap controller that wouldn't use outdated parts.
Instead of buttons and dedicated display, it should use USB port 
for setting the temperature profile and monitoring the temperature if needed.
This would be cheaper to make and easy to use.
If the computer isn't connected, it should be able to be switched on by just connecting the power supply, load the temperature profile from non-volatile memory and run without user intervention.&lt;/p&gt;
&lt;p&gt;Because the oven gets very hot, it's not possible to add any electronics inside it.
Controlling is done by switching the power on and off rapidly with solid state relay
that is connected on extension cord. Temperature is measured using thermocouple inside
the oven. Microcontroller will read the temperature and output PWM signal for
the solid state relay which can be used to accurately control the temperature of the oven.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/oven.jpg" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;1380W Toaster oven with convection. Bought second hand for 20€.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I decided to use &lt;a href="http://www.atmel.com/devices/atmega8u2.aspx"&gt;ATmega8U2&lt;/a&gt; microcontroller,
because I'm familiar with AVRs and this is the cheapest AVR with hardware USB support.&lt;/p&gt;
&lt;p&gt;Because oven can get pretty hot I needed to use thermocouple for measuring the temperature,
instead of cheaper temperature sensors ICs.
I use &lt;a href="http://www.maximintegrated.com/datasheet/index.mvp/id/7273"&gt;MAX31855&lt;/a&gt;
as a thermocouple-to-digital converter. This is the most expensive component in the board
costing 6.21€ at Digikey, even the solid state relay from &lt;a href="http://dx.com/p/ssr-25da-25a-solid-state-relay-white-134494"&gt;dealextreme&lt;/a&gt; was cheaper costing only 5.08€.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/reflow-oven/schematic.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/schematic_thumb.png" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Schematic. Click for bigger view.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Unfortunately I made some mistakes with the PCB and wired the USB connector
pins in reverse order. One trace was also left partly unrouted, 
when I accidentally removed part of it in Eagle before generating the gerber files.
I didn't have tools to fix the USB connector traces, so I just cut them out and
soldered an USB cable on the board. Missing trace was replaced with enameled wire.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/relay.jpg" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Extension cord with solid state relay before adding tape.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/board2.jpg" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Board with USB cord and wire to fix the unrouted trace.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="firmware"&gt;Firmware&lt;/h1&gt;
&lt;p&gt;Program reads the current temperature of the oven from MAX31855 chip through SPI,
at the same time the chip also gives room temperature and signals if there is any
faults with the thermocouple. State machine is used to get the current reflow state
and target temperature. Every second current and target temperatures are outputted
through USB port to computer for logging if it's connected.&lt;/p&gt;
&lt;p&gt;On the computer side the microcontroller looks like a serial port and commands
can be sent and read with any serial port program. If computer isn't connected
microcontroller starts the reflow automatically with profile loaded from EEPROM.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/PID_controller"&gt;PID controller&lt;/a&gt; is used to adjust the relay PWM signal, but because the oven responds very slowly the derivate component of the PID
isn't very useful and the controller works really as a PI controller.
This microcontroller, like many other cheap microcontrollers, doesn't have native floating point unit, so all arithmetic is done without floating point numbers to save program space.&lt;/p&gt;
&lt;p&gt;Microcontroller has 8kB of program space and code uses 92% of it. Most of it is taken by 
USB library and printf function and the control code takes only about 10%.&lt;/p&gt;
&lt;h1 id="testing"&gt;Testing&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/graph.png" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Controller testing with a made-up temperature profile. Green is the target temperature and blue is the current temperature. Relay was fully on between 170 - 320 seconds, but oven couldn't heat fast enough to reach the target. Oven door was opened after the peak was reached to speed up the cooling.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Controller works like it should, but the oven doesn't heat fast enough
to accurately follow the proper reflow soldering profile. This should be fixable by
adding some insulation to the oven. Currently it has only small empty space between
outer case and oven chamber.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/reflow-oven/reflowed_resistor.jpg" border="2" style="border:2px solid black;" /&gt;
    &lt;p style="font-size:13px" &gt;Reflow soldered 0603 resistor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Reflow soldering works, but I have only tested with 0603 resistors right now.
Quality is good, much better than I'm able to solder by hand. Also if the
component isn't placed straight, surface tension
of the solder pulls the components almost perfectly straight when it's reflowed.&lt;/p&gt;
&lt;p&gt;Board design and microcontroller source code is available at &lt;a href="https://github.com/Ttl/reflow-controller"&gt;github&lt;/a&gt;.
The PCB on github has USB connector wired correctly and all the traces routed.&lt;/p&gt;
&lt;h1 id="bill-of-materials"&gt;Bill of materials&lt;/h1&gt;
&lt;table border="1"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Cost/€&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Oven, used&lt;/td&gt;
&lt;td&gt;20.00&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://imall.iteadstudio.com/open-pcb/pcb-prototyping/im120418001.html"&gt;PCBs, 10pcs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7.78&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://www.digikey.fi/product-detail/en/MAX31855KASA%2BT/MAX31855KASA%2BTCT-ND/2708805"&gt;MAX31855 thermocouple-to-digital IC&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6.21&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://dx.com/p/ssr-25da-25a-solid-state-relay-white-134494"&gt;Solid state relay&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5.08&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://www.digikey.fi/product-detail/en/ATMEGA8U2-AU/ATMEGA8U2-AU-ND/2238246"&gt;ATMega8U2 microcontroller&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3.05&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://dx.com/p/high-temperature-probe-sensor-350-c-max-68458"&gt;Thermocouple&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1.59&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="http://www.digikey.fi/product-detail/en/FT531JA/1219-1084-1-ND/3516036"&gt;3.3V regulator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.33&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Extension cord&lt;/td&gt;
&lt;td&gt;3.00&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Other components&lt;/td&gt;
&lt;td&gt;about 2.00&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Shipping+VAT&lt;/td&gt;
&lt;td&gt;9.02&lt;/td&gt;

&lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tfoot&gt;
    &lt;tr&gt;
      &lt;th&gt;Total&lt;/th&gt;
      &lt;th&gt;58.06&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/tfoot&gt;
&lt;/table&gt;</content><category term="Electronics"></category></entry><entry><title>I made a Geiger counter</title><link href="https://hforsten.com/i-made-a-geiger-counter.html" rel="alternate"></link><published>2013-05-01T00:00:00+03:00</published><updated>2013-05-01T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2013-05-01:/i-made-a-geiger-counter.html</id><summary type="html">&lt;p&gt;Ionizing radiation is something that almost anyone finds exciting (or scary) and I've also been for long wanted to build a Geiger counter&lt;/p&gt;</summary><content type="html">&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/populated1.jpg" border="2" style="border:2px solid black;"/&gt;
&lt;/div&gt;

&lt;p&gt;Ionizing radiation is something that almost anyone finds exciting (or scary) and I've also been for long wanted to build
a Geiger counter. Unfortunately Geiger tubes have usually been too expensive to seriously
consider buying them just for a hobby project. But I found out that &lt;a href="http://sovtube.com"&gt;sovtube&lt;/a&gt;
sold soviet cold war era Geiger tubes only for a couple dollars. I bought one
CI-22BG tube and one CI-3BG tube for total of 16€ including shipping from Ukraine to Finland.
The site itself didn't really convince me payment via Paypal failed because of invalid seller email address
and gmail warned me that order confirmation e-mail might not have come from the address it claimed. However, 
I got both of the tubes and they seemed to be okay.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/tubes_small.jpg" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;Two Geiger tubes. Longer one is CI-22BG and smaller one is CI-3BG.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Because I didn't have anything radioactive to test my Geiger counter, I also ordered a piece of radioactive &lt;a href="http://en.wikipedia.org/wiki/Fiesta_(dinnerware)#Radioactive_Glazes"&gt;Fiesta&lt;/a&gt; dinnerware
from ebay. Red Fiestware used to have uranium oxide in its glaze that was radioactive
enough to be detected with a Geiger counter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/fiesta.jpg" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;Two pieces of slightly radioactive fiestaware.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Geiger tube needs a high voltage, my tubes need 400V, to function. We need also a 
microcontroller to count the events and maybe a LCD to display the output.
Also no Geiger counter is complete without a buzzer. I decided to also have USB port
now that I included a microcontroller.&lt;/p&gt;
&lt;h1 id="schematic"&gt;Schematic&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/geiger/circuit.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/geiger/circuit.png" width="640" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Circuit schematic of the Geiger counter. Click for bigger view.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The device can be powered by USB or external voltage source. Circuit on the
upper left corner, which was copied from &lt;a href="http://electronics.stackexchange.com/a/21614"&gt;this&lt;/a&gt;
Stack Exchange answer, is responsible for choosing the right power supply.
It will use USB if it's connected, otherwise it'll connect the external power supply.
IC1 is step up converter that will increase supply voltage to 7V, or pass it through
if it's already over 7V. This will allow the device to work with even 2V external voltage.
IC3 is 5V low dropout regulator, that will regulate 7V to 5V for microcontroller.
At the center there's the high voltage supply that will output 400V from 7V input.
It runs in a closed loop with microcontroller adjusting the duty cycle to get a
stable output voltage.&lt;/p&gt;
&lt;p&gt;Normally Geiger tube will pass only a small leakage current, but
when ionizing radiation hits the Geiger tube some of the gas inside the tube
is ionized which will allow higher current through the tube, 4.7M anode resistor R10 
limits this current to a safe value. This current will
raise voltage at the base of T4 turning it on which pulls the detect net low. 
Then the microcontroller interrupts at the pin change and counts one event.&lt;/p&gt;
&lt;h1 id="high-voltage-supply"&gt;High voltage supply&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/hv_supply.png" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;High voltage supply schematic. R18, R19 and C13 are for feedback to microcontroller. R9 and C5 are used to filter the output and R10 is Geiger tubes anode resistor.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Because I wanted the device to work with USB the input voltage is 5V and this
needs to be raised to 400V. Many other Geiger counter high voltage supplies use some topology that
includes a transformer, but because transformers are big, heavy and expensive I didn't want to use them.
At first I wondered if ordinary &lt;a href="http://en.wikipedia.org/wiki/Boost_converter"&gt;boost DC/DC converter&lt;/a&gt; 
running in discontinuous mode would give high enough voltage gain, so I made some test on a breadboard and 
with right components I managed to get 444V from 5V input voltage, which is more than enough to drive
a Geiger tube. It could have gone to even higher voltages, but I was already over the SF16 diodes
400V breakdown voltage and 450V was upper limit of Geiger tubes working voltage.
With a better layout on the PCB output voltage can reach over 500V.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/hv_breadboard.jpg" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;Testing HV supply with 5V input voltage. This circuit uses
    SF16 diode, which should have a breakdown voltage of 400V, but looks like it can
    handle a little bit more. Mosfet is FQN1N50C.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I tested several different transistors and diodes. For mosfets, on resistance and 
switching speed were most important parameters. I didn't manage to get the only
high voltage BJT I had, MSP44, to work at all. Because boost converter works in 
discontinuous mode, reverse recovery time of diode is important to minimize leakage.
1N4007 diodes were garbage at this application, BA159 was okay, SF16 was even better,
but it's breakdown voltage was only 400V. Highest output voltage was with &lt;a href="http://www.cree.com/~/media/Files/Cree/Power/Data%20Sheets/CSD01060.pdf"&gt;Cree CSD01060&lt;/a&gt; 600V silicon-carbide schottky diode. Because it's schottky diode it has only neglible 
reverse recovery time. Difference between 1N4007 and CSD01060 was about 100V, which is quite significant.
With schottky diode output had hardly any noise and with 1N4007 noise was so bad
that Geiger tube event detection circuit triggered every time mosfet was switched.
I ended up using BA159, because of its high breakdown voltage and small footprint compared to CSD01060.
Schottky can also have bigger reverse leakage than ordinary silicon diode.
It's important in this application to keep it small, because available current at the
output is already very small.&lt;/p&gt;
&lt;p&gt;Switching transistor had an even bigger effect:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/ao.png" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;Voltage gain as a function of duty cycle with AOD3N50 mosfet. Input
    voltage was 1.2V for these tests.
    Voltage gain is low, because gate voltage was only 5V. Mosfets on-resistance at that gate voltage is too high to discharge the inductor fast enough,
    using higher voltage would have most probably raised the voltage gain.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/fqn.png" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;Same with FQN1N50C mosfet. Output voltage was very unstable at high duty cycles.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/msp44.png" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;I couldn't get MSP44 BJT to work at all.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="pcb"&gt;PCB&lt;/h1&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/geiger/pcbs.jpg" border="2" style="border:2px solid black;"/&gt;
    &lt;p style="font-size:13px" &gt;PCBs from iTead. All of them looked fine. LCD connection and Geiger tube mount
    limited the minimum size of the board, so I had plenty of room to place the components.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;PCB was designed with Cadsoft Eagle and manufactured in China by &lt;a href="http://imall.iteadstudio.com/open-pcb/pcb-prototyping.html"&gt;iTead&lt;/a&gt;,
 they were the cheapest PCB supplier I found.
I decided to use mostly SMD components wherever possible, resistors and capacitors are in 0603 package.
I was little sceptical if I would manage to solder them with my not so good tools,
but it in the end it wasn't hard at all. I might even consider using 0402 package
on my next projects.&lt;/p&gt;
&lt;p&gt;I made the high
voltage side using through hole components, because all of the SMD resistors I looked at
claimed a maximum working voltage of about 200V, when I needed at least 400V.&lt;/p&gt;
&lt;p&gt;Total cost was about 50€ including shipping costs. PCBs cost 21€ including shipping and for that price I got 12 of them.
Atmega8U2 was single most expensive component costing 3€, other components together cost about 15€ and the Geiger tubes were 16€.
Looking at the prices for Geiger counters at eBay, I don't think it was very expensive
and building at least 10 at the same time would about halve the price.&lt;/p&gt;
&lt;p&gt;Design is open source. Hardware design, PC software and microcontroller software 
are available at &lt;a href="https://github.com/Ttl/geiger"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/geiger/populated3.jpg" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/geiger/populated3.jpg" border="2" width="640px" style="border:2px solid black;"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;With components and 2€ coin for scale. I had to change the inductor on bottom right corner, because
    the inductor that I originally chose turned out to have so much coil noise,
    that it was almost louder than the buzzer.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="testing-the-geiger-counter"&gt;Testing the Geiger counter&lt;/h1&gt;
&lt;div id="centered" &gt;
&lt;video width=60% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/geiger/Geiger_counter_test.mp4" type="video/mp4"&gt;
&lt;source src="https://hforsten.com/video/geiger/Geiger_counter_test.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Fiestaware piece I have shows about 700 counts per minute, depending slightly on how close to the tube I hold it.
Background radiation level is about 10 CPM. I don't know how to convert CPM to
any SI units with this tube, but according to the eBay seller I bought the fiestaware
piece it should have radioactivity of about 20-30µSv/hr. Background radiation
level should be about 200-400nSv/hr. Ratio of these numbers is approximately 80, which is about 
the same as I measured.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Datapath of my PIC16F84 microprocessor clone</title><link href="https://hforsten.com/datapath-of-my-pic16f84-microprocessor-clone.html" rel="alternate"></link><published>2013-03-14T00:00:00+02:00</published><updated>2013-03-14T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2013-03-14:/datapath-of-my-pic16f84-microprocessor-clone.html</id><summary type="html">&lt;p&gt;PIC16F84 is old 8-bit microprocessor made by Microchip. It was once
very popular among the hobbyists, because of its low price. It was the first
microprocessor I started to use so I thought it would be fun to try remaking it
in VHDL.
The source code is open source and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;PIC16F84 is old 8-bit microprocessor made by Microchip. It was once
very popular among the hobbyists, because of its low price. It was the first
microprocessor I started to use so I thought it would be fun to try remaking it
in VHDL.
The source code is open source and can be found on my &lt;a href="https://github.com/Ttl/pic16f84"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I first started writing a single cycle design,
but converted it to two-staged pipeline, because the real pic16f84 uses this 
kind of architecture. Two stages are fetch/decode and read/execute/writeback.
For performance reasons it would be wise to have even more stages. With three stages separating
the writeback to is own stage would increase the maximum clock frequency the most,
but complicate the design and make some instruction take more time 
to complete in clock cycles than on the real version.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt="Datapath" src="https://hforsten.com/img/pic16f84/datapath.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:13px" &gt;Simplified datapath and control signals. Signals not related to the program flow are removed.&lt;/p&gt;
&lt;p&gt;&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;PIC16F84 has very simple accumulator based architecture. There is only one
accumulator register called W. Instructions are decoded to few control signals that control the multiplexers and
other datapath elements. I don't know exactly how the microchips real version works,
but I think it's very similar.&lt;/p&gt;
&lt;p&gt;Executing the instruction start with fetching the instruction pointed by the program
counter (PC) from the instruction RAM. Next it will be decoded to control signals
that are used to control the datapath. Control signals go through a flip-flop that
buffers them and allows decoding and executing two different instructions at the same
time, better known as pipelining. Then the datapath reads RAM, executes the instruction
and writes the result back if needed. This is the critical stage that limits the
clock rate of the whole microprocessor. It would be advantageous to divide this pipeline
stage to two or three seperate stages for higher clock frequency.&lt;/p&gt;
&lt;p&gt;Control unit is responsible for stalling the exeuction state when needed, 
computing the new program counter and handling the interrupts. On call instruction or interrupt the current
PC will be pushed on the stack and it will be popped from there on return instruction.&lt;/p&gt;
&lt;h1 id="control-signals"&gt;Control signals&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Signal&lt;/th&gt;
&lt;th align="left"&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;amux&lt;/td&gt;
&lt;td align="left"&gt;ALU B-port multiplexer control signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;bmux&lt;/td&gt;
&lt;td align="left"&gt;ALU A-port multiplexer control signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;writew&lt;/td&gt;
&lt;td align="left"&gt;W &amp;lt;= ALU result, when this is 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;writeram&lt;/td&gt;
&lt;td align="left"&gt;ALU result is written into memory location pointed by the current instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;aluop&lt;/td&gt;
&lt;td align="left"&gt;ALU operation, possible operations are: pass A-port, AND, OR, XOR, NOT A, A-B, A+B, A&amp;lt;&amp;lt;1, A&amp;gt;&amp;gt;1, set bit, test bit and swap nibbles of A (low and high 4 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;branch&lt;/td&gt;
&lt;td align="left"&gt;1 if decoded instruction is call or goto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;return&lt;/td&gt;
&lt;td align="left"&gt;1 if decoded instruction is return, retfie (return from interrupt) or retlw (return and set W to immediate)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With these control signals we can implement all of the instructions.
For example movlw instruction, that moves literal to W is implemented as writew &amp;lt;= '1',
aluop &amp;lt;= Pass A and others zero. This moves the literal from instruction to ALU result
and to the W register input.&lt;/p&gt;
&lt;p&gt;bmux signal might seem weird, but we need constant 1 available to this
port when incrementing or decrementing a memory location. This could
be replaced with two new ALU operations, increment and decrement, but it 
would really synthese into very similar logic.&lt;/p&gt;
&lt;p&gt;Constant 0 on the other ALU multiplexer is used for clear W and clear
memory location instructions. Also instruction that moves W to RAM is implemented as adding zero to W
and then writing the ALU result to RAM, this way we don't need an ALU operation
that passes the port B to ALU result.
This again reduces the number of ALU operations needed and makes the microprocessor
a little bit faster.&lt;/p&gt;
&lt;p&gt;Zero flag from ALU to control logic is used for conditional branches. This
signal is 1 if ALU operation is zero. When the conditional branch is taken we
need to discard the result of the next instruction that is currently on the decode stage.
This is done by asserting the not skip signal, which forces the writew and writeram signals to be zero for the next clock cycle.
The instruction is executed, but because the result is not written anywhere it doesn't
matter.&lt;/p&gt;
&lt;h1 id="lessons-learned"&gt;Lessons learned&lt;/h1&gt;
&lt;p&gt;Pipelining can easily have some subtle errors. Like when writing to program counter
 register in the RAM
 it functions as a branch. Writing can be actually done directly to the right RAM
 location or indirectly through special pointer RAM location (FSR). Both need to be checked
 at the control logic to stall the execution state.&lt;/p&gt;
&lt;p&gt;It is easier to use RAM that has asynchronous read, but block RAM in the FPGA
 has synchronous read. If asynchronous read RAM is required it must be implemented as distributed RAM.
 I converted my instruction RAM halfway through the design from asynchronous to synchronous,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;
&lt;span class="normal"&gt;9&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;we&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rising_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;p style="font-size:13px" &gt;RAM with synchronous read. Moving the "d1 &lt;= mem(to_integer(unsigned(a1)));
" one line down would make the read asynchronous.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I have still implemented the data RAM as asyncrhonous read and I don't
 think it's possible to make it synchronous without adding pipeline stage.
 Address to read is calculated in ALU so the data read would be delayed to the
 next clock cycle.&lt;/p&gt;
&lt;p&gt;Testing, you can never have too much of it. I started testing first by writing
 short programs and checking their output manually. This soon turned out to be too
 time consuming, because I would manually need to check outputs of the programs
 after making changes. Modifying programs to write results of the operations to
 output ports and using assert commands to test the correct values turned out to
 be much easier.&lt;/p&gt;
&lt;p&gt;I have heard that some people generate random programs and compare the output
 to reference implementation written in behavioral VHDL or other language.
 It would be a very effective way to find bugs, but writing the reference implementation
 would take too much time.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>UART designed to be used with FSM</title><link href="https://hforsten.com/uart-designed-to-be-used-with-fsm.html" rel="alternate"></link><published>2013-01-12T00:00:00+02:00</published><updated>2013-01-12T00:00:00+02:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2013-01-12:/uart-designed-to-be-used-with-fsm.html</id><summary type="html">&lt;p&gt;Some time ago I needed an UART for a project. Things it needed to do were very simple
and I didn't want to add a microprocessor to use UART and instead decided to just
code a simple finite state machine to use it.&lt;/p&gt;
&lt;p&gt;But I couldn't find a simple UART …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Some time ago I needed an UART for a project. Things it needed to do were very simple
and I didn't want to add a microprocessor to use UART and instead decided to just
code a simple finite state machine to use it.&lt;/p&gt;
&lt;p&gt;But I couldn't find a simple UART designed to be used with FSM. They add features
I didn't need and were hard to use without adding a microprocessor. One of the simpler
ones was &lt;a href="http://opencores.org/project,rs232_interface,overview"&gt;RS232 UART&lt;/a&gt; from opencores.
But it too was meant to be used with a microprocessor. Receive end signal and some
other signals were high for more than one clock cycle, which made design of the FSM more complex.
I could have added a rising edge detector, but I decided to change the UART implementation instead.&lt;/p&gt;
&lt;p&gt;So I made signals to be high only for one clock cycle, transmission to start immediately when
the transmission start signal was asserted and made baud rate generation more accurate.&lt;/p&gt;
&lt;p&gt;The code, readme and one example is uploaded to &lt;a href="https://github.com/Ttl/fsm_uart"&gt;github&lt;/a&gt;.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Generating normally distributed pseudorandom numbers on a FPGA</title><link href="https://hforsten.com/generating-normally-distributed-pseudorandom-numbers-on-a-fpga.html" rel="alternate"></link><published>2012-10-23T00:00:00+03:00</published><updated>2012-10-23T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-10-23:/generating-normally-distributed-pseudorandom-numbers-on-a-fpga.html</id><summary type="html">&lt;p&gt;The other day I was coding an FPGA design that needed a source of normally 
distributed random numbers. Uniformly distributed random numbers are 
fairly easy and cheap to generate with a &lt;a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register"&gt;linear feedback shift register&lt;/a&gt;, but generating normally distributed numbers
is a harder problem. There are &lt;a href="http://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution"&gt;several ways&lt;/a&gt; to generate …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The other day I was coding an FPGA design that needed a source of normally 
distributed random numbers. Uniformly distributed random numbers are 
fairly easy and cheap to generate with a &lt;a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register"&gt;linear feedback shift register&lt;/a&gt;, but generating normally distributed numbers
is a harder problem. There are &lt;a href="http://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution"&gt;several ways&lt;/a&gt; to generate a normally distributed number
using one or more uniformly distributed, some are more accurate than others.
For my purposes the numbers don't need to exactly follow the normal distribution,,
it's enough if it seems like for a normal human that the numbers are from the normal distribution.&lt;/p&gt;
&lt;p&gt;So I went for the easiest and cheapest implementation and use &lt;a href="http://en.wikipedia.org/wiki/Central_limit_theorem"&gt;the central limit theorem&lt;/a&gt;, which says that summing sufficiently many
independent random numbers result will be approximately normally distributed.
I went for the sum of four uniformly distributed random numbers generated by a 
linear feedback shift register. To avoid obvious period in the output I decided 
to have a rather large 31-bit registers. 31-bits because it's big enough and 
it needs only two taps when a 32-bit register would need four.&lt;/p&gt;
&lt;p&gt;Code for the linear feedback shift registers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;library&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE.STD_LOGIC_1164.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;others&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_uniform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;

&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;xor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rising_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Implementation of the linear feedback shift registers is very straight forward.
Seed value and output width can be set with generics making it also useful in many 
other places.&lt;/p&gt;
&lt;p&gt;Summing four of these uniform random numbers should generate number that is almost
normally distributed. Code for the normally distributed pseudorandom generator is 
below, it outputs 12-bit signed numbers every three clock cycles, but it should 
be easy to modify it to work in one clock cycle just by adding more adders.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;  1&lt;/span&gt;
&lt;span class="normal"&gt;  2&lt;/span&gt;
&lt;span class="normal"&gt;  3&lt;/span&gt;
&lt;span class="normal"&gt;  4&lt;/span&gt;
&lt;span class="normal"&gt;  5&lt;/span&gt;
&lt;span class="normal"&gt;  6&lt;/span&gt;
&lt;span class="normal"&gt;  7&lt;/span&gt;
&lt;span class="normal"&gt;  8&lt;/span&gt;
&lt;span class="normal"&gt;  9&lt;/span&gt;
&lt;span class="normal"&gt; 10&lt;/span&gt;
&lt;span class="normal"&gt; 11&lt;/span&gt;
&lt;span class="normal"&gt; 12&lt;/span&gt;
&lt;span class="normal"&gt; 13&lt;/span&gt;
&lt;span class="normal"&gt; 14&lt;/span&gt;
&lt;span class="normal"&gt; 15&lt;/span&gt;
&lt;span class="normal"&gt; 16&lt;/span&gt;
&lt;span class="normal"&gt; 17&lt;/span&gt;
&lt;span class="normal"&gt; 18&lt;/span&gt;
&lt;span class="normal"&gt; 19&lt;/span&gt;
&lt;span class="normal"&gt; 20&lt;/span&gt;
&lt;span class="normal"&gt; 21&lt;/span&gt;
&lt;span class="normal"&gt; 22&lt;/span&gt;
&lt;span class="normal"&gt; 23&lt;/span&gt;
&lt;span class="normal"&gt; 24&lt;/span&gt;
&lt;span class="normal"&gt; 25&lt;/span&gt;
&lt;span class="normal"&gt; 26&lt;/span&gt;
&lt;span class="normal"&gt; 27&lt;/span&gt;
&lt;span class="normal"&gt; 28&lt;/span&gt;
&lt;span class="normal"&gt; 29&lt;/span&gt;
&lt;span class="normal"&gt; 30&lt;/span&gt;
&lt;span class="normal"&gt; 31&lt;/span&gt;
&lt;span class="normal"&gt; 32&lt;/span&gt;
&lt;span class="normal"&gt; 33&lt;/span&gt;
&lt;span class="normal"&gt; 34&lt;/span&gt;
&lt;span class="normal"&gt; 35&lt;/span&gt;
&lt;span class="normal"&gt; 36&lt;/span&gt;
&lt;span class="normal"&gt; 37&lt;/span&gt;
&lt;span class="normal"&gt; 38&lt;/span&gt;
&lt;span class="normal"&gt; 39&lt;/span&gt;
&lt;span class="normal"&gt; 40&lt;/span&gt;
&lt;span class="normal"&gt; 41&lt;/span&gt;
&lt;span class="normal"&gt; 42&lt;/span&gt;
&lt;span class="normal"&gt; 43&lt;/span&gt;
&lt;span class="normal"&gt; 44&lt;/span&gt;
&lt;span class="normal"&gt; 45&lt;/span&gt;
&lt;span class="normal"&gt; 46&lt;/span&gt;
&lt;span class="normal"&gt; 47&lt;/span&gt;
&lt;span class="normal"&gt; 48&lt;/span&gt;
&lt;span class="normal"&gt; 49&lt;/span&gt;
&lt;span class="normal"&gt; 50&lt;/span&gt;
&lt;span class="normal"&gt; 51&lt;/span&gt;
&lt;span class="normal"&gt; 52&lt;/span&gt;
&lt;span class="normal"&gt; 53&lt;/span&gt;
&lt;span class="normal"&gt; 54&lt;/span&gt;
&lt;span class="normal"&gt; 55&lt;/span&gt;
&lt;span class="normal"&gt; 56&lt;/span&gt;
&lt;span class="normal"&gt; 57&lt;/span&gt;
&lt;span class="normal"&gt; 58&lt;/span&gt;
&lt;span class="normal"&gt; 59&lt;/span&gt;
&lt;span class="normal"&gt; 60&lt;/span&gt;
&lt;span class="normal"&gt; 61&lt;/span&gt;
&lt;span class="normal"&gt; 62&lt;/span&gt;
&lt;span class="normal"&gt; 63&lt;/span&gt;
&lt;span class="normal"&gt; 64&lt;/span&gt;
&lt;span class="normal"&gt; 65&lt;/span&gt;
&lt;span class="normal"&gt; 66&lt;/span&gt;
&lt;span class="normal"&gt; 67&lt;/span&gt;
&lt;span class="normal"&gt; 68&lt;/span&gt;
&lt;span class="normal"&gt; 69&lt;/span&gt;
&lt;span class="normal"&gt; 70&lt;/span&gt;
&lt;span class="normal"&gt; 71&lt;/span&gt;
&lt;span class="normal"&gt; 72&lt;/span&gt;
&lt;span class="normal"&gt; 73&lt;/span&gt;
&lt;span class="normal"&gt; 74&lt;/span&gt;
&lt;span class="normal"&gt; 75&lt;/span&gt;
&lt;span class="normal"&gt; 76&lt;/span&gt;
&lt;span class="normal"&gt; 77&lt;/span&gt;
&lt;span class="normal"&gt; 78&lt;/span&gt;
&lt;span class="normal"&gt; 79&lt;/span&gt;
&lt;span class="normal"&gt; 80&lt;/span&gt;
&lt;span class="normal"&gt; 81&lt;/span&gt;
&lt;span class="normal"&gt; 82&lt;/span&gt;
&lt;span class="normal"&gt; 83&lt;/span&gt;
&lt;span class="normal"&gt; 84&lt;/span&gt;
&lt;span class="normal"&gt; 85&lt;/span&gt;
&lt;span class="normal"&gt; 86&lt;/span&gt;
&lt;span class="normal"&gt; 87&lt;/span&gt;
&lt;span class="normal"&gt; 88&lt;/span&gt;
&lt;span class="normal"&gt; 89&lt;/span&gt;
&lt;span class="normal"&gt; 90&lt;/span&gt;
&lt;span class="normal"&gt; 91&lt;/span&gt;
&lt;span class="normal"&gt; 92&lt;/span&gt;
&lt;span class="normal"&gt; 93&lt;/span&gt;
&lt;span class="normal"&gt; 94&lt;/span&gt;
&lt;span class="normal"&gt; 95&lt;/span&gt;
&lt;span class="normal"&gt; 96&lt;/span&gt;
&lt;span class="normal"&gt; 97&lt;/span&gt;
&lt;span class="normal"&gt; 98&lt;/span&gt;
&lt;span class="normal"&gt; 99&lt;/span&gt;
&lt;span class="normal"&gt;100&lt;/span&gt;
&lt;span class="normal"&gt;101&lt;/span&gt;
&lt;span class="normal"&gt;102&lt;/span&gt;
&lt;span class="normal"&gt;103&lt;/span&gt;
&lt;span class="normal"&gt;104&lt;/span&gt;
&lt;span class="normal"&gt;105&lt;/span&gt;
&lt;span class="normal"&gt;106&lt;/span&gt;
&lt;span class="normal"&gt;107&lt;/span&gt;
&lt;span class="normal"&gt;108&lt;/span&gt;
&lt;span class="normal"&gt;109&lt;/span&gt;
&lt;span class="normal"&gt;110&lt;/span&gt;
&lt;span class="normal"&gt;111&lt;/span&gt;
&lt;span class="normal"&gt;112&lt;/span&gt;
&lt;span class="normal"&gt;113&lt;/span&gt;
&lt;span class="normal"&gt;114&lt;/span&gt;
&lt;span class="normal"&gt;115&lt;/span&gt;
&lt;span class="normal"&gt;116&lt;/span&gt;
&lt;span class="normal"&gt;117&lt;/span&gt;
&lt;span class="normal"&gt;118&lt;/span&gt;
&lt;span class="normal"&gt;119&lt;/span&gt;
&lt;span class="normal"&gt;120&lt;/span&gt;
&lt;span class="normal"&gt;121&lt;/span&gt;
&lt;span class="normal"&gt;122&lt;/span&gt;
&lt;span class="normal"&gt;123&lt;/span&gt;
&lt;span class="normal"&gt;124&lt;/span&gt;
&lt;span class="normal"&gt;125&lt;/span&gt;
&lt;span class="normal"&gt;126&lt;/span&gt;
&lt;span class="normal"&gt;127&lt;/span&gt;
&lt;span class="normal"&gt;128&lt;/span&gt;
&lt;span class="normal"&gt;129&lt;/span&gt;
&lt;span class="normal"&gt;130&lt;/span&gt;
&lt;span class="normal"&gt;131&lt;/span&gt;
&lt;span class="normal"&gt;132&lt;/span&gt;
&lt;span class="normal"&gt;133&lt;/span&gt;
&lt;span class="normal"&gt;134&lt;/span&gt;
&lt;span class="normal"&gt;135&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;library&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE.STD_LOGIC_1164.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE.NUMERIC_STD.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;

&lt;span class="k"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;adder_signed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;statetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;statetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="n"&gt;unif1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;697757461&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;unif2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1885540239&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;unif3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1505946904&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;unif4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random_uniform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="k"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_unsigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2693445&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;OUT_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;adder1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_signed&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;MAP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rising_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;others&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;-- Sign extend&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adder_r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;adder_b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;uniform4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And the code of the adder for adding two 12-bit signed numbers, it has a 
latency of one cycle:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;library&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE.STD_LOGIC_1164.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;IEEE.NUMERIC_STD.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;adder_signed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;STD_LOGIC_VECTOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;adder_signed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;adder_signed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;

&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r_next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;others&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rising_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r_next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;r_next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="kt"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behavioral&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I wrote a testbench to write output values to a file and a mathematica notebook
to test how close the numbers are to the normal distribution. Histograms were generated
with "Histogram[x, 200]" command and plots with "ListPlot[x, Joined-&amp;gt;True]", where
"x" is a list of values. &lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/gaussian-random-numbers/approx_time.png"&gt;
    &lt;p style="font-size:13px" &gt;500 output values of the generator.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/gaussian-random-numbers/normal_time.png"&gt;
    &lt;p style="font-size:13px" &gt;Same amount of samples from the normal distribution.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;You can see that the approximation is pretty close to the normal distribution.
It's maybe a little bit fatter, but it wouldn't be noticeable without comparing 
it to a true normal distribution.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/gaussian-random-numbers/histogram.png"&gt;
    &lt;p style="font-size:13px" &gt;Histogram of the generated numbers. Does it look 
like a normal distribution?&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/gaussian-random-numbers/histogram_normal.png"&gt;
    &lt;p style="font-size:13px" &gt;Histogram of the same amount of random numbers
generated from the normal distribution. Notice the different values on the axes.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As you can see from the histograms the approximation is a little bit flatter, 
but it comes amazingly close. Just by looking at the histogram without a 
normal distribution to compare it to, I would say that it is a normal distribution
and that's the only thing that was important.&lt;/p&gt;
&lt;p&gt;Appendix 1.&lt;/p&gt;
&lt;p&gt;VHDL test bench for writing the output values into a file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;
&lt;span class="normal"&gt;53&lt;/span&gt;
&lt;span class="normal"&gt;54&lt;/span&gt;
&lt;span class="normal"&gt;55&lt;/span&gt;
&lt;span class="normal"&gt;56&lt;/span&gt;
&lt;span class="normal"&gt;57&lt;/span&gt;
&lt;span class="normal"&gt;58&lt;/span&gt;
&lt;span class="normal"&gt;59&lt;/span&gt;
&lt;span class="normal"&gt;60&lt;/span&gt;
&lt;span class="normal"&gt;61&lt;/span&gt;
&lt;span class="normal"&gt;62&lt;/span&gt;
&lt;span class="normal"&gt;63&lt;/span&gt;
&lt;span class="normal"&gt;64&lt;/span&gt;
&lt;span class="normal"&gt;65&lt;/span&gt;
&lt;span class="normal"&gt;66&lt;/span&gt;
&lt;span class="normal"&gt;67&lt;/span&gt;
&lt;span class="normal"&gt;68&lt;/span&gt;
&lt;span class="normal"&gt;69&lt;/span&gt;
&lt;span class="normal"&gt;70&lt;/span&gt;
&lt;span class="normal"&gt;71&lt;/span&gt;
&lt;span class="normal"&gt;72&lt;/span&gt;
&lt;span class="normal"&gt;73&lt;/span&gt;
&lt;span class="normal"&gt;74&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;LIBRARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ieee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ieee.std_logic_1164.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;std.textio.&lt;/span&gt;&lt;span class="k"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ieee.std_logic_textio.&lt;/span&gt;&lt;span class="k"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ieee.numeric_std.&lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ENTITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian_test_bench&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian_test_bench&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ARCHITECTURE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;behavior&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian_test_bench&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;

&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;FILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test_out_data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WRITE_MODE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;output/gaussian.txt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Component Declaration for the Unit Under Test (UUT)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;COMPONENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;random_gaussian&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;std_logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;std_logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COMPONENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;--Inputs&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;--Outputs&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;std_logic_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;downto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- Clock period definitions&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;constant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;BEGIN&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Instantiate the Unit Under Test (UUT)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;uut&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random_gaussian&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;MAP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- Clock process definitions&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;clk_process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;clk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;-- Stimulus process&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nc"&gt;stim_proc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;L1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clk_period&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;L1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;writeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_out_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;L1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</content><category term="Programming"></category></entry><entry><title>Progress on evolutionary circuits</title><link href="https://hforsten.com/progress-on-evolutionary-circuits.html" rel="alternate"></link><published>2012-09-14T00:00:00+03:00</published><updated>2012-09-14T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-09-14:/progress-on-evolutionary-circuits.html</id><summary type="html">&lt;p&gt;If you haven't been reading my last entries, I have been working on a program 
that tries to design a circuits using genetic algorithm that fulfill the
requirements given by the programmer.
Read &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;this entry&lt;/a&gt; for an overview what the program does and how it works. In this post I …&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you haven't been reading my last entries, I have been working on a program 
that tries to design a circuits using genetic algorithm that fulfill the
requirements given by the programmer.
Read &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;this entry&lt;/a&gt; for an overview what the program does and how it works. In this post I will
tell about my findings about which algorithms and parameters work and which don't.&lt;/p&gt;
&lt;h1 id="population-size"&gt;Population size&lt;/h1&gt;
&lt;p&gt;Before I started writing this program I read some literature about similar experiments.
For example I read some &lt;a href="https://en.wikipedia.org/wiki/John_Koza"&gt;John R. Koza's&lt;/a&gt;
books about using genetic algorithms on designing electric circuits and he used 
some huge population sizes. In the book &lt;a href="http://www.amazon.com/Genetic-Programming-IV-Human-Competitive-Intelligence/dp/1402074468"&gt;Genetic Programming IV: Routine Human-Competitive Machine Intelligence&lt;/a&gt; population sizes that he uses are usually tens of thousands. Some of the runs
use population size of 100000 which is absolutely huge. This helps to explore the 
whole search space and guarantees a good solution. But I have found that population
sizes of this big are unnecessary and they slow down the simulation too much.
I have experimented and found that population sizes of 100-2000 are often good enough
and they convergence a lot of quicker to very similar solutions as the population sizes 
of 10000 to 20000. This is often a difference between a few hour simulation and a few 
days of simulation.&lt;/p&gt;
&lt;h1 id="crossover"&gt;Crossover&lt;/h1&gt;
&lt;p&gt;I have found that the way I currently do crossovers,
by cutting and pasting parts of the two circuits netlist to one new netlist often 
produces bad results and I have been thinking that the whole existence of crossovers
might be just waste and it would be a better use of resources to just disable them.
Currently on my latest files I have set the crossover probability to low value of
1% to 5%, compared to mutation probability of 70%.&lt;/p&gt;
&lt;p&gt;The problem with crossovers is that by applying it to two similar circuits we get 
one circuit that has many similar parallel devices. If this circuit with parallel
devices performs better than the previous circuit parallel devices will soon 
spread within the population and they decrease the number of devices available to
the rest of the circuit. Getting rid of the unnecessary parallel device, for example
a two resistor requires two lucky mutations: one of the resistors needs to be removed
and others value needs to be halved. Probability of this happening is small and instead
it would have been better if in the first place there would have been a mutation
in the value of the resistor that would have decreased it's value. This way it wouldn't 
have introduced an unnecessary device and it could have been used in a better way.&lt;/p&gt;
&lt;h1 id="circuit-encoding"&gt;Circuit encoding&lt;/h1&gt;
&lt;p&gt;I decided to encode the circuits as a simple SPICE netlists. The problem with them
is that crossovers often introduce duplicate parallel devices and seem harm more than help.
Also they lack clear input and output. It is equally likely that a new device will
connect straight from input to output or it will connect somewhere else. This usually
leads to circuits that are not very deep, meaning that input and output nodes are
very close together. This makes hard to evolve circuits that need to be deep to work
correctly, for example higher order filters are necessarily deep.&lt;/p&gt;
&lt;p&gt;Koza uses binary trees and set of functions to modify it. I have not tried them 
myself, but they don't suffer from same problems as netlists. Crossover is more efficient
because subtrees can be exchanged. It's also easier to evolve deeper circuits when
whole subtrees can be moved around.&lt;/p&gt;
&lt;p&gt;An alternatives to these two encodings would be a graph based encoding.
Graphs would work by having a starting node
and a list of instructions that insert devices, change the current node and instruction that
does both. This way duplicating a piece of genome makes a new series connected stage that
is connected to previous stage instead of duplicating devices of the previous stage.
I have not tried this approach yet. It's interesting, but it has some problems. Coding 
it is harder than simple netlists, it is more computationally intensive because
the circuit needs to be converted to netlist for simulation and handling devices with more than
two connections(e.g. Transistors) will be harder. Hopefully it will be worth
it and evolving deeper circuits will become easier.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/progress/graph_encoding.png"&gt;
    &lt;p style="font-size:15px" &gt;Lowpass filter encoded with graph encoding.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/progress/graph_encoding2.png"&gt;
    &lt;p style="font-size:15px" &gt;Same lowpass filter, but with a crossover that duplicated
    the instructions. Instead of parallel devices, the circuit now has a second stage.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id="scoring"&gt;Scoring&lt;/h1&gt;
&lt;p&gt;One of the most important things I have found that improve the quality of evolved circuits is
that instead of keeping the constraint functions scores constant I ramp up the scoring
weights over many generations and increase the strictness of constraints. 
For example when evolving an inverter I had a problem where my constraints for the
current use were so strict that the program decided that it's better to not make
anything to keep current use low instead of trying to make an inverter and use 
current. Ramping constraint scores solves this problem by allowing the program
first make circuits that use lots of current, but work like they should and after
some working circuits are found concentrate on fulfilling the constraints. In the 
inverter example this leads to very current efficient inverters that work like 
they should.&lt;/p&gt;
&lt;h1 id="summary"&gt;Summary&lt;/h1&gt;
&lt;p&gt;Since announcing this project I have made many improvements. Current and used power
can be measured and controlled. Better scoring and improvements in scoring algorithm.
New mutations that can change the output node and other mutation that changes a value
of a device only a little amount, probably other mutations that I have forgotten already.
Device can have an arbitrary cost(price, amount of area used...) that is added to the circuits
score. The biggest improvement might be the gradual ramping of constraint scores that 
generate higher quality solutions. I have also replaced the unmaintainable way of
writing the circuits straight to the programs source code with separate configuration
files that are easier to write and maintain.&lt;/p&gt;
&lt;p&gt;I have also added more options. For example it is now possible to not use the default
scoring functions, but instead to write your own function that takes the simulation
output and returns the score of the circuit.&lt;/p&gt;
&lt;p&gt;I have also since replaced Python with PyPy. It uses much less memory, usually two to
ten times less. With extremely big populations memory saving are even bigger. There 
might be something weird in the Python reference counting that keeps useless 
simulation results in memory instead of freeing them. I haven't investigated it much, because 
PyPy works so much better. Only problem with PyPy is that matplotlib isn't supported.
Currently I run matplotlib as a subprocess, but PyPy team is working on supporting
it and I hope that soon I can do the plotting also with PyPy.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>In-place sparse matrix conversion algorithm</title><link href="https://hforsten.com/in-place-sparse-matrix-conversion-algorithm.html" rel="alternate"></link><published>2012-08-15T00:00:00+03:00</published><updated>2012-08-15T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-08-15:/in-place-sparse-matrix-conversion-algorithm.html</id><summary type="html">&lt;p&gt;I have been writing a sparse matrix library for few weeks and for it I needed a 
algorithm for converting from &lt;a href="http://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29"&gt;coordinate list form&lt;/a&gt;(COO) matrix to &lt;a href="http://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_.28CSC_or_CCS.29"&gt;compressed sparse column form&lt;/a&gt;(CSC). All of the algorithms that I found allocated space for the CSC matrix and freed the COO matrix …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I have been writing a sparse matrix library for few weeks and for it I needed a 
algorithm for converting from &lt;a href="http://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29"&gt;coordinate list form&lt;/a&gt;(COO) matrix to &lt;a href="http://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_.28CSC_or_CCS.29"&gt;compressed sparse column form&lt;/a&gt;(CSC). All of the algorithms that I found allocated space for the CSC matrix and freed the COO matrix after building the CSC matrix. This is inefficient when I know that I don't need the coordinate list matrix after building the CSC matrix. So I decided to make an algorithm that can convert the matrix in-place.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/coo-to-csc/eq.png"&gt;
    &lt;p style="font-size:13px" &gt;Same matrix in COO and CSC formats. COO formats order is arbitrary. This time the Column array in CSC is the same length as number of non-zero entries, but in general its length is columns in matrix + 1, or one less if you leave the last optional element out.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Above is example of same matrix in COO and CSC formats. Note that order in COO format is arbitrary and 
it can have duplicate elements. Traditionally values of the duplicate entries are summed together, because this &lt;/p&gt;
&lt;p&gt;If you look at the above CSC and COO formats of the example matrix, it should be 
clear that CSC formats Value and Row arrays are just sorted COO formats Row and Value arrays. Only 
problem is the Column array but it can be built easily from the sorted Column array. We just need to compare current element to previous element and if they are not equal set the next unset element to be the current loop iteration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because we want to sort the arrays in-place first choice for the sorting algorithm is Quicksort.
I know that it really requires O(log n) of space in the stack because of the recursion, but
in reality it's less than 1kB even for a several gigabyte big matrices so we don't really need to 
worry about it. We need to sort both Row and Col arrays, such that Row is sorted first and then Col, but there is no need to make two passes as we can just define our comparison such that the Quicksort algorithm sorts both arrays at the same time. Below is the full algorithm and the same example matrix as above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;
&lt;span class="normal"&gt;53&lt;/span&gt;
&lt;span class="normal"&gt;54&lt;/span&gt;
&lt;span class="normal"&gt;55&lt;/span&gt;
&lt;span class="normal"&gt;56&lt;/span&gt;
&lt;span class="normal"&gt;57&lt;/span&gt;
&lt;span class="normal"&gt;58&lt;/span&gt;
&lt;span class="normal"&gt;59&lt;/span&gt;
&lt;span class="normal"&gt;60&lt;/span&gt;
&lt;span class="normal"&gt;61&lt;/span&gt;
&lt;span class="normal"&gt;62&lt;/span&gt;
&lt;span class="normal"&gt;63&lt;/span&gt;
&lt;span class="normal"&gt;64&lt;/span&gt;
&lt;span class="normal"&gt;65&lt;/span&gt;
&lt;span class="normal"&gt;66&lt;/span&gt;
&lt;span class="normal"&gt;67&lt;/span&gt;
&lt;span class="normal"&gt;68&lt;/span&gt;
&lt;span class="normal"&gt;69&lt;/span&gt;
&lt;span class="normal"&gt;70&lt;/span&gt;
&lt;span class="normal"&gt;71&lt;/span&gt;
&lt;span class="normal"&gt;72&lt;/span&gt;
&lt;span class="normal"&gt;73&lt;/span&gt;
&lt;span class="normal"&gt;74&lt;/span&gt;
&lt;span class="normal"&gt;75&lt;/span&gt;
&lt;span class="normal"&gt;76&lt;/span&gt;
&lt;span class="normal"&gt;77&lt;/span&gt;
&lt;span class="normal"&gt;78&lt;/span&gt;
&lt;span class="normal"&gt;79&lt;/span&gt;
&lt;span class="normal"&gt;80&lt;/span&gt;
&lt;span class="normal"&gt;81&lt;/span&gt;
&lt;span class="normal"&gt;82&lt;/span&gt;
&lt;span class="normal"&gt;83&lt;/span&gt;
&lt;span class="normal"&gt;84&lt;/span&gt;
&lt;span class="normal"&gt;85&lt;/span&gt;
&lt;span class="normal"&gt;86&lt;/span&gt;
&lt;span class="normal"&gt;87&lt;/span&gt;
&lt;span class="normal"&gt;88&lt;/span&gt;
&lt;span class="normal"&gt;89&lt;/span&gt;
&lt;span class="normal"&gt;90&lt;/span&gt;
&lt;span class="normal"&gt;91&lt;/span&gt;
&lt;span class="normal"&gt;92&lt;/span&gt;
&lt;span class="normal"&gt;93&lt;/span&gt;
&lt;span class="normal"&gt;94&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;#define comp(_a,_b,_x,_p) (_a[_x]!=_a[_p] ? _a[_x]-_a[_p] : _b[_x]-_b[_p])&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;matrix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;COOtoCSC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;q_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;COOtoCSC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/*  Sort by first Col and then Row */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;q_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/*  Remove duplicates */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/*  Build CSC column pointers in the Col array */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;q_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Sort smaller array first for less stack usage */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</content><category term="Programming"></category></entry><entry><title>Better evolved BJT inverter</title><link href="https://hforsten.com/better-evolved-bjt-inverter.html" rel="alternate"></link><published>2012-07-20T00:00:00+03:00</published><updated>2012-07-20T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-07-20:/better-evolved-bjt-inverter.html</id><summary type="html">&lt;p&gt;In my &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;first post about my evolutionary circuits project&lt;/a&gt; I wrote about an evolved BJT inverter. I have since that post written some new features in the program. Now it can measure the current taken from logic input and the power supply, so the last best solution where the transistors …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In my &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;first post about my evolutionary circuits project&lt;/a&gt; I wrote about an evolved BJT inverter. I have since that post written some new features in the program. Now it can measure the current taken from logic input and the power supply, so the last best solution where the transistors would be fired shouldn't be possible anymore. I also made possible for the program to decide where the transition from high to low input should be. Last feature that I just finished a few minutes ago was constraints on the measured values. If measurements don't fill the constraints the circuit gets ranked very poorly and has a low probability to be chosen to the next generation. Reason I added the constraints was because I tried to evolve a BJT inverter before adding them and the program got stuck on the minimum where the best inverters low to high transition was very fast, but other transition didn't work at all.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/dcsweep.png"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep of the broken inverter. Blue is the goal and green is the circuits output. With the 5V input output is 2.1V, which is above the transition voltage. If this inverter would drive another inverter the second inverter would output 1.5V which is the low voltage, but output should be 5V, same as input, because it was inverted twice.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/highlow.png"&gt;
    &lt;p style="font-size:13px" &gt;High to low transition is fast...&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/lowhigh.png"&gt;
    &lt;p style="font-size:13px" &gt;...But low to high transition doesn't work&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;So the constraints were necessary to guarantee that the program wouldn't get stuck on broken solutions. After implementing them I tried to evolve a BJT inverter again. Allowed components were: resistors, capacitors, 2N3904 and 2N3906 transistors. To avoid unreasonable big capacitors I limited the maximum capacitor value to 100pF. Picofarad capacitors were chosen because they are reasonable size for integrated circuits. Maximum number of components was 8 and population size was set to 4000 circuits. I also tried to model the finite impedance of the input with 50 series resistance and to avoid circuits that wouldn't work with load I fixed a 10kΩ load resistor to the output.&lt;/p&gt;
&lt;p&gt;This time I measured: current taken from logic input, current taken from the 5V power supply, input DC sweep from 0V to 5V, high to low and low to high transitions. Because measurements from SPICE are in basic SI units, the program will rank 1A average decrease in current usage same as 1V average decrease in transfer curve, to get the program appreciate current more the current scores were multiplied with one thousand so that 1mA decrease in current would equal 1V decrease in the transfer curve.&lt;/p&gt;
&lt;p&gt;Previously I arbitrarily chose the transition voltage to be 2.5V, but since then I
have improved the program and now it's possible for it to decide where the optimal transition is. Ordinary RTL or TTL inverters don't switch exactly at the half of power supply value either and it's very likely that the evolved inverter would perform better if it wasn't artificially limited to switch at 2.5V.&lt;/p&gt;
&lt;p&gt;Constraints were placed on the transient simulation values and DC current usage. After the transition the inverter was required to settle to 0.2V below or above the transition voltage depending on the which transition it was. This way the broken inverters like on the above hopefully wouldn't be possible anymore.&lt;/p&gt;
&lt;p&gt;This time I ran the simulation a lot longer than in my first post. I started it in the morning before going to the work and stopped it after coming home. Total running time was 12.5 hours. Below are the animated gifs of the measurements of the best circuits of each generation, currents aren't plotted because they were so close to zero that they all looked just like a straight line. But if you still want to look at some straight lines &lt;a href="https://hforsten.com/img/evolved-bjt-inverter/ivin0.gif"&gt;here's&lt;/a&gt; input current and &lt;a href="https://hforsten.com/img/evolved-bjt-inverter/ivc0.gif"&gt;here&lt;/a&gt; is current taken from the power supply.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/vn20.gif"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep. Blue line is the goal and green is the circuits output.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/vn21.gif"&gt;
    &lt;p style="font-size:13px" &gt;High to low transition. Scale of x-axis is 50ns/division.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/vn22.gif"&gt;
    &lt;p style="font-size:13px" &gt;Low to high transition.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/schematic.png"&gt;
    &lt;p style="font-size:13px" &gt;Schematic of the final circuit&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/static_current.png"&gt;
    &lt;p style="font-size:13px" &gt;Static current usage&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Voltage supply V2 is the logic input and V1 is 5V power supply. This is very neat looking circuit, looks almost like it could have been designed by a human. It doesn't have any unconnected or unneeded components, even the 27pF capacitor is needed(It improves the propagation delay). Especially the input stage is interesting with the 3 parallel PNP transistors. Connecting transistors in parallel improves their frequency response and current handling capacity. If this is built from discrete components connecting transistors in parallel might be problematic because the transistor might not be matched that well. But it shouldn't be a too much trouble because single transistors could handle all the current going through the three of them. If this was an integrated circuit the 3 individual transistors could be combined to one three times bigger transistor.&lt;/p&gt;
&lt;p&gt;If you look at the above transient responses it looks like it's rise and fall time is really fast. In SPICE simulation both rise and fall time are about 1.3ns. If you compare these to the ordinary &lt;a href="http://en.wikipedia.org/wiki/Resistor%E2%80%93transistor_logic"&gt;RTL&lt;/a&gt; inverter built with 2N3904 transistor and using 15k base resistors it has a fall time of 80ns and a rise time of almost 2µs(=2000ns). RTL inverters maximum static current usage is 1.5mA when input is at 5V. The evolved inverters maximum current use is just 660µA when the input is at 4.5V. Downside of course is that the evolved inverter uses more components than the RTL inverter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/rtl_schematic.png"&gt;
    &lt;p style="font-size:13px" &gt;Schematic of RTL inverter&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/ttl_schematic.png"&gt;
    &lt;p style="font-size:13px" &gt;Schematic of one TTL inverter. This is commonly called a TTL inverter with a totem pole output stage.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;But RTL is old technology and not in use anymore, but other bjt based logic family that is still used is &lt;a href="http://en.wikipedia.org/wiki/Transistor%E2%80%93transistor_logic"&gt;TTL&lt;/a&gt;. When built with 2N3904 transistors the TTl inverters maximum static current consumption is 36mA. TTl inverter is quicker than the RTL inverter. It has a fall time of 1.8ns and a rise time of 1.6µs. The reason both RTL and TTL inverters have slow rise time is because after being in saturation it takes time to remove the stored charge from the base and get transistor to the cutoff state. Evolved circuit takes care of this by dumping huge current spike through the base of Q5. When the input changes state from 5V to 0V, it creates a current through the capacitor. Part of the current goes to logic input and part of it goes through the 4 PNP transistors at the input to the base of Q5. Voltage at the base quickly decreases to -2V. Size of the current spike depends on the fall time of the logic input. Quicker it falls the bigger the spike and faster the outputs transition is. It should be noted that even tough the maximum current in the evolved circuit is bigger than in TTL inverter its duration is very short and the total amount of energy required to change output is smaller in the evolved circuit.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/switching_current.png"&gt;
    &lt;p style="font-size:13px" &gt;Current taken from the power supply. Input changes at 10ns. And it falls in 1ns. With 0.1ns fall time maximum current is 77mA. 2N3906 can handle 200mA of continuous current and even higher amounts for short time so this isn't enough to break it. 24mA of this goes through the Q5 and rest of it goes to logic input, which might be a problem for the circuit driving the inverter.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I also built the circuit to make sure that it works when built with real components. Unfortunately I have only single probe on my oscilloscope so I can't measure the rise and fall times and even if I had the second probe it would be a waste of time trying to measure this when built on a breadboard because breadboard has big parasitic capacitances which would mess up the capacitances near the 10pF capacitor. So this should be built on either IC or using the &lt;a href="http://en.wikipedia.org/wiki/Point-to-point_construction#.22Dead_bug.22_construction"&gt;Dead bug style&lt;/a&gt;. But I could measure the static current usage and the input DC sweep:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/built.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Built with real transistors.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/current_0V_in.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Current when the input is at 0V. 325µA. You can also see the LED at the output light up.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/current_5V_in.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Current when the input is at 5V. 276µA. 0V on the output as you can see from the LED.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/current_led_10k.png"&gt;
    &lt;p style="font-size:13px" &gt;Simulated with LED and 10k resistor at the output.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/dc_sweep.jpg"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep with triangle wave at the input. 0V is at the left and 5V at the right. Transition voltage is about 4.5V as simulated.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Current is near the simulated values but not quite there. 0V input current is very near the simulated value but the 5V input for some reason is much less. No idea whats going on here. Tolerances on the transistors or resistors?&lt;/p&gt;
&lt;p&gt;Could this circuit be used in a production? Maybe, even though it has some problems it's faster than the ordinary TTL inverter. It has a few problems with the big resistor values, small 27pF capacitor and current spikes in the logic input and the power supply. It also has a few volts of overshoot on the output voltage on high switching frequencies. Replacing the Megaohm resistors with smaller values helps but increases current usage.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolved-bjt-inverter/overshoot.png"&gt;
    &lt;p style="font-size:13px" &gt;Overshoot on the output. 33MHz switching frequency. Faster than the TTL inverter is capable of(when using 2N3904 transistors, it can go higher with faster transistors. Same with this circuit).&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;When fast rise and fall time is important this might be useful circuit, but before using it anywhere it might need some hand designing to fix the overshoot and current spikes. Overall it is a big increase in the quality compared to the previous inverter circuits.&lt;/p&gt;
&lt;p&gt;If you want to try it yourself download the code from &lt;a href="https://github.com/Ttl/evolutionary-circuits"&gt;github&lt;/a&gt; and run the "inverter.py" file. That version also has scoring for the transient current usage. The program works on Linux and might or might not work on Windows.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Metastable transistor circuit</title><link href="https://hforsten.com/metastable-transistor-circuit.html" rel="alternate"></link><published>2012-07-13T00:00:00+03:00</published><updated>2012-07-13T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-07-13:/metastable-transistor-circuit.html</id><summary type="html">&lt;p&gt;&lt;a href="http://www.hforsten.com/building-an-evolved-voltage-reference.html"&gt;In my previous post&lt;/a&gt;
I wrote about a circuit that would change it's output depending on what was the spice simulations DC sweep range.
Today I investigated the circuit a little and I was able to remove lots of components that didn't
affect the bug and this is the resulting …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://www.hforsten.com/building-an-evolved-voltage-reference.html"&gt;In my previous post&lt;/a&gt;
I wrote about a circuit that would change it's output depending on what was the spice simulations DC sweep range.
Today I investigated the circuit a little and I was able to remove lots of components that didn't
affect the bug and this is the resulting circuit:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/metastable-transistor-circuit/schematic.png"&gt;
    &lt;p style="font-size:13px" &gt;Schematic&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Spice simulation results of the changed circuit:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/metastable-transistor-circuit/dc_sweep_0v_10v.png"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep from 0V to 10V&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/metastable-transistor-circuit/dc_sweep_5v_10v.png"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep from 5V to 10V&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The next step was to build this circuit using real transistor and measure what the real output is.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/metastable-transistor-circuit/breadboard_labels.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Circuit built on the breadboard&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;First I tried to power the circuit from 9V battery and the measured output was 7.5V(The reason I use batteries is because I don't have a proper power supply, I have the components to make one using LM317 but I haven't found time yet). Spice simulated that output voltage would be 8.5V(or 50µV), but the 9V battery I'm using isn't exactly new and adding 300Ω resistor to series with the voltage source to model the internal resistance of the battery gives output of 7.5V at 9V input. I also tried to get the output to stay at the 0V and I found out that if I power the circuit with even emptier 9V battery that gives only 4.7V at the output and touch lightly with a grounded wire to the base of Q2, output voltage stays near 0V for few seconds and then rises quickly back to 4.7V. I couldn't really capture this in a picture so I took a video instead:&lt;/p&gt;
&lt;div id="centered" &gt;
&lt;video width=60% controls loop muted&gt;
&lt;source src="https://hforsten.com/video/metastable/metastable.webm" type="video/webm"&gt;
&lt;source src="https://hforsten.com/video/metastable/metastable.ogv" type="video/ogg"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;So in the end Spice was sort of correct. The circuit does have one stable state when the output is almost supply voltage and other metastable state where output is near 0V. The 0V state might be stable in the simulation, but small differences in the transistors or inaccurate models in the simulation might make this state only metastable.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Building an evolved voltage reference</title><link href="https://hforsten.com/building-an-evolved-voltage-reference.html" rel="alternate"></link><published>2012-07-12T00:00:00+03:00</published><updated>2012-07-12T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-07-12:/building-an-evolved-voltage-reference.html</id><summary type="html">&lt;p&gt;I've been playing with the &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;electronic circuit genetic algorithm design program&lt;/a&gt;. I have tried to make some stable bandgap voltage references, so far results have been promising, but not very good. None of the resulting circuits could be used in a production, except if you don't care much about the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been playing with the &lt;a href="http://www.hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html"&gt;electronic circuit genetic algorithm design program&lt;/a&gt;. I have tried to make some stable bandgap voltage references, so far results have been promising, but not very good. None of the resulting circuits could be used in a production, except if you don't care much about the temperature stability.&lt;/p&gt;
&lt;p&gt;One circuit that seemed good when it was evolved was this one(No circuit diagram because I'm lazy):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.model 2N3906  PNP(Is=455.9E-18 Xti=3 Eg=1.11 Vaf=33.6 Bf=204.7 Ise=7.558f
+               Ne=1.536 Ikf=.3287 Nk=.9957 Xtb=1.5 Var=100 Br=3.72
+               Isc=529.3E-18 Nc=15.51 Ikr=11.1 Rc=.8508 Cjc=10.13p Mjc=.6993
+               Vjc=1.006 Fc=.5 Cje=10.39p Mje=.6931 Vje=.9937 Tr=10n Tf=181.2p
+               Itf=4.881m Xtf=.7939 Vtf=10 Rb=10)
.model 2N3904   NPN(Is=6.734f Xti=3 Eg=1.11 Vaf=74.03 Bf=416.4 Ne=1.259
+               Ise=6.734f Ikf=66.78m Xtb=1.5 Br=.7371 Nc=2 Isc=0 Ikr=0 Rc=1
+               Cjc=3.638p Mjc=.3085 Vjc=.75 Fc=.5 Cje=4.493p Mje=.2593 Vje=.75
+               Tr=239.5n Tf=301.2p Itf=.4 Vtf=4 Xtf=2 Rb=10)
Vin n1 0
rload n2 0 100k
R39826856 n4 n11 1709.1717943
R52792856 n2 n10 2507.84316862
Q155540048 n0 n11 n5 2N3906
Q21597538600 n8 n3 n10 2N3904
R51398488 n0 n11 25282.9648097
R52736296 n10 n2 3272.12493658
R58783432 n7 n5 23812.5332944
R52713520 n4 n6 116372.392136
Q168167800 n1 n6 n3 2N3906
R57551328 n10 n3 821383.334394
Q158216032 n10 0 n10 2N3906
Q259752248 n2 n5 n1 2N3904
Q170112896 n10 n7 n2 2N3906
Q279097800 n3 n7 n11 2N3904
Q21325009232 0 n8 n4 2N3904
R39671064 n6 n11 30496.553302
R47819448 n1 n0 22223.2407018
R42659568 n1 n0 401.01058083
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When input is swept from 4V to 10V, with step of 0.1V. This is the output voltage:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/ngspice_bug_1.png"&gt;
    &lt;p style="font-size:13px" &gt;Spice command: dc Vin 4 10 0.1&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Looks good, but when output is swept from 0V to 10V this is the result:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/ngspice_bug_2.png"&gt;
    &lt;p style="font-size:13px" &gt;Spice command: dc Vin 0 10 0.1&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Note the y-axis values. After changing only the range of the simulation spice returns completely different results for the whole range. I investigated this a little and when the DC sweep starting value is lower than 3.8441V spice returns the almost zero output value. If starting value is 3.8442V this is the output:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/ngspice_bug_4.png"&gt;
    &lt;p style="font-size:13px" &gt;Spice command: dc Vin 3.8442 11 0.1&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;For the starting values 3.8443V and higher spice returns same results as in the first picture. I don't know which output is really correct and I don't really care that much as the circuit wasn't that good anyway, but if you decide to build this circuit let me know the results.&lt;/p&gt;
&lt;h1 id="new-bandgap-voltage-reference"&gt;New bandgap voltage reference&lt;/h1&gt;
&lt;p&gt;For the next simulation I changed evolution's goals to avoid spice bugs. I varied the dc sweep starting and ending values a small amount for every spice simulation. I also learned from my last post and added input current measurements, more measurements with different load resistances and measurements with different temperatures. I also changed goals so that I didn't check what value output voltage was, but only scored circuits for the stability of the output voltage and let the evolution to decide what is a easy to build voltage reference, but because permanent 0V on the output would also be stable I added a check that output voltage is higher than 1V. I also added a transient simulation that simulates a circuits response to 10V step in input voltage. Transient response isn't that important, but its reason is avoiding bugs. Transient simulation is harder than dc sweep and if spice doesn't know how to simulate a transient response it sure doesn't know to simulate the dc sweep. And if you're wondering why these changes aren't in the &lt;a href="https://github.com/Ttl/evolutionary-circuits"&gt;github&lt;/a&gt;, it's because I just hard coded everything and it can't be currently used to simulate anything else. I will be adding these changes too, but I need to make sure first that the program still works.&lt;/p&gt;
&lt;p&gt;I decided to abort the simulation after 30 minutes to avoid overfitting and because the resulting simulations looked good.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/schema.png"&gt;
    &lt;p style="font-size:13px" &gt;Generation 50. Schematic as the program designed it. Output is on the collector of Q3&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/schema_simplified.png"&gt;
    &lt;p style="font-size:13px" &gt;Unnecessary parts removed. I later realized that R3 can also be removed. R2 is the load resistor&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/schema_simple.png"&gt;
    &lt;p style="font-size:13px" &gt;Previous circuit redrawn and R3 removed. Labels changed.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Simulation results, output voltage is 1.4V, which is about 2*Vbe of a bjt transistor.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://hforsten.com/img/building-simple-evolved-125v-vref/ivin1.png" /&gt;
&lt;img alt="" src="https://hforsten.com/img/building-simple-evolved-125v-vref/vn20.png" /&gt;
&lt;img alt="" src="https://hforsten.com/img/building-simple-evolved-125v-vref/vn21.png" /&gt;
&lt;img alt="" src="https://hforsten.com/img/building-simple-evolved-125v-vref/vn22.png" /&gt;
&lt;img alt="" src="https://hforsten.com/img/building-simple-evolved-125v-vref/vn23.png" /&gt;&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/rload.png"&gt;
    &lt;p style="font-size:13px" &gt;Rload stepped from 1k to 10k with 1k step. Output begins to fall if load is less than 1k.&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/rload_zoomed.png"&gt;
    &lt;p style="font-size:13px" &gt;Zoomed view of previous plot&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The simulations look very promising, especially the constant voltage despite the load resistance, which I had problems with last time. Input current is also small and transistors should not break. Because the simulations looked so good I decided to build this circuit and test it's output.&lt;/p&gt;
&lt;h2 id="built-with-real-components"&gt;Built with real components:&lt;/h2&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/built_circuit.jpg"&gt;
    &lt;p style="font-size:13px" &gt;I changed the 800 Ohm resistors to 1k Ohms because I didn't have 800 Ohms resistors. Load resistor is 10k Ohms&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/output_room_temp.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Measured output: 1.362V at room temperature(20° C) when powered from 9V battery, Spice simulated 1.42V.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I measured the temperature coefficient by placing the circuit in the freezer for few hours.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/output_chilled_-20c_small.jpg"&gt;
    &lt;p style="font-size:13px" &gt;Measured output: 1.47V at about -20°C again powered from 9V battery. Taking pictures and using the multimeter same time is hard.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Temperature coefficient is about 3mV/°C, not very good as voltage reference but might be okay for some applications where temperature doesn't change that much.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/dc_sweep_output_0.5v_div_small.jpg"&gt;
    &lt;p style="font-size:13px" &gt;DC sweep, input is 0V to 10V triangle wave, 0.5V/div&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/dc_sweep_output_zoomed_0.2v_div_20us_div_small.JPG"&gt;
    &lt;p style="font-size:13px" &gt;Zoomed, 0.2V/div, there was some DC offset in the input triangle wave so the bottom isn't actually ground&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As you can see above the circuit really works as a voltage reference. I think it has a very good performance considering that it uses only three transistors, at least it's better than anything I could have designed using three transistors.&lt;/p&gt;
&lt;p&gt;Error due to the input voltage is about 20mV/V.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/building-simple-evolved-125v-vref/transient_response_2us_div_0.5v_div_10v_pulse_in_small.JPG"&gt;
    &lt;p style="font-size:13px" &gt;10V step input response, 2µs/div, 0.5V/div&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Transient behaviour is good. Output voltage rises very quickly, quicker than I can measure with my 60Mhz oscilloscope, but it falls slowly.&lt;/p&gt;
&lt;p&gt;Output voltage was little off from the simulated value, but transient response and dc sweep looked very much like in the simulation. I tried to change some of the transistors, but it didn't change output voltage much. Overall considering that the circuit uses only three transistors and the computer made it in 30 minutes it's behaviour is good.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Evolutionary algorithms and analog electronic circuits</title><link href="https://hforsten.com/evolutionary-algorithms-and-analog-electronic-circuits.html" rel="alternate"></link><published>2012-07-07T00:00:00+03:00</published><updated>2012-07-07T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-07-07:/evolutionary-algorithms-and-analog-electronic-circuits.html</id><summary type="html">&lt;p&gt;I've been working for a while on a program that automatically generates analog electronic circuits using evolutionary algorithms. It's still very much a work in progress, but I've used it to generate some useful circuits.
Current workings of the program is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Programmer decides a goal function, possible constraints …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;I've been working for a while on a program that automatically generates analog electronic circuits using evolutionary algorithms. It's still very much a work in progress, but I've used it to generate some useful circuits.
Current workings of the program is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Programmer decides a goal function, possible constraints, simulation type(AC, DC, Transient), number of nodes and types and values of the components available. Possibly tweaks genetic algorithm parameters, such as: population size, mutation probability etc...&lt;/li&gt;
&lt;li&gt;First generation of circuits is randomly generated and they are simulated in SPICE(I use &lt;a href="http://ngspice.sourceforge.net/"&gt;ngspice&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Outputs of the simulations are ranked against the goal function and good performers are mutated and added to the new generation of circuits.&lt;/li&gt;
&lt;li&gt;This is repeated until a good enough circuit is found.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example I have used it to find inverter circuits, bandgap references and rising edge detectors.&lt;/p&gt;
&lt;h1 id="bjt-inverter-with-4-components"&gt;BJT Inverter, with 4 components&lt;/h1&gt;
&lt;p&gt;First circuit that I evolved was a inverter using bjt transistors. Options were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maximum of 4 components.&lt;/li&gt;
&lt;li&gt;Allowed components: 2N3904 and 2N3906 transistors and resistors with values of 0.1 to 10M Ohms.&lt;/li&gt;
&lt;li&gt;I also provided one 5V power supply.&lt;/li&gt;
&lt;li&gt;SPICE simulation was DC sweep from 0 to 5V and the goal was transition on 2.5V. Rise and fall times were not tested.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is an animated gif of the responses of the best circuits:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/inverter_anim.gif"&gt;
    &lt;p style="font-size:13px" &gt; &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;As you can see the response improved quickly and last big breakthrough was in generation 78 and it took only 9 minutes to get there. Generation 2049 was reached 6.5 hours after starting.Currently the program isn't multithreaded and circuits are tested one at a time. Simulating more circuits at the same time should improve the simulation time linearly depending on number of available processor cores.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/generation70.png"&gt;
    &lt;p style="font-size:13px" &gt;The best circuit in generation 70&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/generation78.png"&gt;
    &lt;p style="font-size:13px" &gt;The best circuit in generation 78&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;If you compare the circuits in the generations 70 and 78 you can see that the breakthrough was in reconnecting Q2, it's base was connected to the base of Q3 and collector was connected to the input. Q4 was also removed but it didn't really do anything because it was permanently in the cutoff state. There's no more big changes and rest of the time was spent on trying to find a suitable fourth component. In the final circuit, generation 2049, fourth component was resistor and circuit greatly resembles a CMOS inverter.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/inverter4_schema.png"&gt;
    &lt;p style="font-size:13px" &gt;Circuit in generation 2049(If you are wondering what Q3 does, it doesn't really do anything.)&lt;/p&gt;

&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/cmos.png"&gt;
    &lt;p style="font-size:13px" &gt;CMOS inverter&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This is looks like it might work, but if we plot currents taken from input and current going to ground. We see that maximum current taken from the power supply is 200mA. This is horribly inefficient and it's probably more than the transistors can handle.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/evolution/inverter_current.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/evolution/inverter_current_thumb.png" width="840" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Currents at the collectors of the transistors&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;And if we look at the base current of Q1 maximum current is 210mA when input is at 5V. This is too much for 2N3904 transistor and real transistor would probably break.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/evolution/inverter_q1_basecurrent.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/evolution/inverter_q1_basecurrent_thumb.png" width="840" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Base current of Q1 as a function of input voltage&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;It makes sense that if power consumption of the circuit isn't tested it will evolve so that it doesn't care about power consumption. It's easier to use lots of power than to use a little of it.&lt;/p&gt;
&lt;p&gt;One problem is also that SPICE is happy to simulate that current through transistor is 100A, even if it clear that real component wouldn't be able to withstand that much current. To make this program useful it's probably necessary to add checks that power used by components is reasonable.&lt;/p&gt;
&lt;h1 id="bandgap-reference"&gt;Bandgap reference&lt;/h1&gt;
&lt;p&gt;Another circuit that I evolved is a bandgap reference. Goal was to generate stable 2.5V output voltage when the supply voltage varied from 4V to 10V. SPICE can also simulate temperature so added two input DC sweep with temperatures of 27 and 60 degrees Celsius. This time number of components was restricted to eight and components available were same as in the inverter evolution(2N3904 and 2N3906 transistors and resistors).&lt;/p&gt;
&lt;p&gt;Below are animated gifs of output voltages on different temperatures as a function of input voltage, I also added the current score to the plot:&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/bandgap27c.gif"&gt;
    &lt;img src="https://hforsten.com/img/evolution/bandgap60c.gif"&gt;
    &lt;p style="font-size:13px" &gt; &lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;img src="https://hforsten.com/img/evolution/bandgap_good_schema.png"&gt;
    &lt;p style="font-size:13px" &gt;The final bandgap circuit&lt;/p&gt;
&lt;/div&gt;

&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/evolution/bandgap_good.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/evolution/bandgap_good_thumb.png" width="840" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Output voltage as a function of input voltage, temperature: 27 degrees&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Because converting SPICE netlist to schematic must be done by hand and is time consuming I'm only giving a schematic of the final circuit.&lt;/p&gt;
&lt;p&gt;As you can see on the output sweep that the reference isn't that accurate and this circuit also has a power consumption problem. R2 is only 20 Ohms and it takes lots of current from the power supply, when supply voltage is high. If R2 is changed to a bigger value output voltage drops slightly. I exchanged it to a 10k resistor and output voltage drops to 1.3V and current taken from the supply is only 0.5mA when input voltage is 4V and 1.4mA when voltage is 10V. This starts to be a reasonable current considering that transistors used are discrete components. If this was an integrated circuit it would be still a too big current.&lt;/p&gt;
&lt;p&gt;Other problem that this circuit has is that it's output voltage drops when the output is loaded.&lt;/p&gt;
&lt;div id="centered" &gt;
    &lt;a href="https://hforsten.com/img/evolution/bandgap_good_rload_sweep_1kto100k_10kstep.png" target="_blank"&gt;
    &lt;img src="https://hforsten.com/img/evolution/bandgap_good_rload_sweep_1kto100k_10kstep_thumb.png" width="840" border="2" style="border:2px solid black;" title="Click for a bigger image"/&gt;
    &lt;/a&gt;
    &lt;p style="font-size:13px" &gt;Output voltage as a function of input voltage with different load resistances&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the above picture output voltage is swept and load resistance is stepped from 1k to 100k with a step of 10k. With 100k load 1.3V reference becomes 1.25V reference and output voltage decreases quickly when load resistance is decreased with a 1k load reference doesn't work correctly anymore. This plot is made with R2 changed to 10k.&lt;/p&gt;
&lt;p&gt;With 100k load output voltage is 1.39V when input voltage is 10V and 1.29V when input voltage is 4V. Output voltage rises about 1.6mV per 1V increase in input voltage.&lt;/p&gt;
&lt;p&gt;If temperature is 60 degrees Celsius. Output voltage is 1.18V when input is 4V and 1.29V when input is 10V. Temperature dependency is about -3mV per degree Celsius. Not very good as a reference but considering that this was generated automatically I think it's a success, even tough it's output voltage isn't 2.5V as was required. Allowing more components would have probably generated a more accurate reference.&lt;/p&gt;
&lt;p&gt;I would make this circuit using real components and test how it performs in real life, but I don't have enough transistors.&lt;/p&gt;
&lt;p&gt;If you are interested in the code it's available at my &lt;a href="https://github.com/Ttl/evolutionary-circuits"&gt;github account&lt;/a&gt;, but I must warn you that it's not easy to use and requires a Linux OS and &lt;a href="http://ngspice.sourceforge.net/"&gt;ngspice&lt;/a&gt; for simulating circuits, but I would still appreciate any contributions.&lt;/p&gt;
&lt;p&gt;I think this looks promising and if I can implement automatic check for used power and that currents and voltages are reasonable for real components, it might be actually useful.&lt;/p&gt;</content><category term="Electronics"></category></entry><entry><title>Redefining the number 2 in Python</title><link href="https://hforsten.com/redefining-the-number-2-in-python.html" rel="alternate"></link><published>2012-06-29T00:00:00+03:00</published><updated>2012-06-29T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-06-29:/redefining-the-number-2-in-python.html</id><summary type="html">&lt;p&gt;In Python it's possible to redefine some builtin values that shouldn't really be changed. For example True and False can be changed in Python versions 2.7 and lower. This is fixed in Python 3 and assignment to True or False raises: "SyntaxError: assigment to keyword" but it works on …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In Python it's possible to redefine some builtin values that shouldn't really be changed. For example True and False can be changed in Python versions 2.7 and lower. This is fixed in Python 3 and assignment to True or False raises: "SyntaxError: assigment to keyword" but it works on Python 2.7:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mf"&gt;2.7.3&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;2012&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GCC&lt;/span&gt; &lt;span class="mf"&gt;4.6.3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;linux2&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;#Exchange True and False&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;9071696&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;9071664&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#This returns the id of False, but interpreter printed True last line&lt;/span&gt;
&lt;span class="mi"&gt;9071664&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;#True doesn&amp;#39;t equal True&lt;/span&gt;
&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because of some internal Python workings only Trues and Falses that are written by the programmer are exchanged. Trues and Falses returned by Python are exchanged but are printed as not exchanged, as can be seen on the program above.&lt;/p&gt;
&lt;p&gt;True and False aren't the only builtin values that can be changed, because CPython caches small integers we can change values of the integers in the cache.&lt;/p&gt;
&lt;p&gt;In Python 2.7 and lower Python integers are represented in the C code as PyIntObject that has following definition:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;PyObject_HEAD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ob_ival&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PyIntObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PyObject_HEAD is a C macro that by default expands as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Py_ssize_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ob_refcnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;PyTypeObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ob_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ob_refcnt is the &lt;a href="http://en.wikipedia.org/wiki/Reference_counting"&gt;reference count&lt;/a&gt; and ob_type is a pointer to a structure that is used describe Python built-in types.&lt;/p&gt;
&lt;p&gt;Using the "ctypes" module we can change ob_ival field that stores the value of the integer. We can get the address of the integer with id() function and we know that because of the internal representation of the Python integer, ob_ivals offset from the start of the struct is &lt;code&gt;sizeof(size_t)+sizeof(void *)&lt;/code&gt;. Following code changes the ob_ival value of the cached integer 2 to 3:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ctypes&lt;/span&gt;

&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;ob_ival_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_voidp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ob_ival&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;ob_ival_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ob_ival&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because of the integer cache every time Python thinks that it's using the integer 2 it's actually using the value 3. In Python 2.7 this program prints 3 and crashes. Running this in the interactive shell works, but trying to use it afterwards might lead to interesting behavior if it needs the value 2.&lt;/p&gt;
&lt;p&gt;This code doesn't work in Python 3.0 or newer versions, because in Python 3.0 int and long types were combined in the one single type and this changed the PyIntObject struct.&lt;/p&gt;</content><category term="Programming"></category></entry><entry><title>First Post</title><link href="https://hforsten.com/first-post.html" rel="alternate"></link><published>2012-06-25T00:00:00+03:00</published><updated>2012-06-25T00:00:00+03:00</updated><author><name>Henrik Forstén</name></author><id>tag:hforsten.com,2012-06-25:/first-post.html</id><summary type="html">&lt;p&gt;This is a blog about electronics, computers and mathematics. I'm thinking about 
writing for example: VHDL, FPGAs, general electronics, LaTeX, programming, pitfalls in Python and lots of other stuff. Recently I have written a program that uses evolutionary algorithms to autonomously design analog electrical circuits. I have had some success …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is a blog about electronics, computers and mathematics. I'm thinking about 
writing for example: VHDL, FPGAs, general electronics, LaTeX, programming, pitfalls in Python and lots of other stuff. Recently I have written a program that uses evolutionary algorithms to autonomously design analog electrical circuits. I have had some success with it and I think I'm going to write a post or two about it when I have a time. Another recent project that I'm going to write about in the future is implementation of the &lt;a href="http://en.wikipedia.org/wiki/CORDIC"&gt;cordic algorithm&lt;/a&gt; on a FPGA.&lt;/p&gt;
&lt;p&gt;As you can see this blog is powered by &lt;a href="http://pelican.notmyidea.org/"&gt;Pelican&lt;/a&gt;. Theme is currently default theme of the Pelican. I'll tweak it when I have time, but I'm going to concentrate on the content at the moment.&lt;/p&gt;</content><category term="Main"></category></entry></feed>