Vector measurements with the HackRF

27.03.2021 20:09

Over the last year I slowly built up a small, one-port vector network analyzer. The instrument consists of a rtl-sdr USB receiver dongle, the ERASynth Micro frequency synthesizer, an RF bridge, a custom time multiplex board I designed and, of course, a whole lot of software that glues everything together and does final signal processing in the digital domain. In the past months I've written pretty extensively about its development here on this blog and I've also used the system in practice, mainly to measure VSWR and matching networks of various multiband LTE antennas.

The original instrument could perform S11 measurements up to around 2 GHz. However the 2 GHz limit was only due to the E4000 tuner in the rtl-sdr. Since ERASynth Micro can generate signals up to 6.4 GHz and I designed the multiplexer with signals up to 8 GHz in mind, getting measurements at higher frequencies was only a matter of upgrading the receiver. I was tempted to do that since the popular 2.4 GHz ISM band was just out of reach of my measurements. I did a lot of searching around for a suitable SDR that would cover that frequency range. The top of my list was an USRP B200, but amid the lockdowns and the general chaos in international shipping last year I couldn't find a supplier. In the end I settled for a HackRF One.

My small, home-made vector network analyzer, upgraded with a HackRF.

On one hand, moving from the rtl-sdr to HackRF was pretty simple. After I got hackrf_tcp up and running most of my old code just worked. Getting things to work reasonably well took longer though. I lost a lot of time figuring out why the gain in the system varied a lot from one measurement to the other. I would optimize signal levels for best dynamic range, only to try it again next day and find that they have changed considerably. In the end, I found out that the USB hub that I was using could not supply the additional current required by the HackRF. Interestingly, nothing obvious broke with the USB bus voltage wildly out of spec, only the analog performance became erratic.

I wish HackRF came with some hardware settings (jumpers or something) that would allow me to semi-permanently disable some of its more dangerous features. As it is right now I need to use a DC block to protect my multiplex board from a DC bias in case the antenna port power gets enabled by a software bug. I also had to put in a 20 dB attenuator to protect the HackRF in case its pre-amplifier gets turned on, since that would get damaged by the signal levels I'm commonly using.

Error network terms when using Henrik's bridge with HackRF.

Here are the error network terms with the upgraded system. The faded lines are the measurements by Henrik Forstén from whom I copied the RF bridge design. Up to 2 GHz the error terms match pretty well with those I measured with the rtl-sdr. As I noted in my previous blog post, the error terms are a measure of the whole measurement system, not only the bridge. Hence my results differ from Henrik's quite significantly since apart from the bridge his system is quite different.

Unfortunately, my new system still isn't very well behaved beyond 2 GHz. I suspect this has to do with the construction that has a lot of connectors everywhere and RG-316 cables of questionable quality. Every contact introduces some impedance mismatch and in the end it's hard to say what is causing what. I also know that I made an error in calculating the dimensions of the coplanar waveguides on my multiplex board. I'm sure that is not helping things.

Estimated dynamic range of the system.

This is an estimated dynamic range of the vector measurement, using the method described in this post. Let's say it's better than 50 dB below 1.5 GHz and better than 30 dB below 3.5 GHz. It quickly gets worse after that. At around 5.5 GHz I suspect there's some kind of a resonance in the 10 dB attenuator I have on the multiplex board that's made out of 0603 resistors. Beyond that point the signal that passes through the attenuator is stronger than the un-attenuated signal and I can't measure anything anymore.

I really would like to improve the dynamic range in the future. Basically all the practical measurements I did with this system was with the device-under-test being behind a U.FL connector. The problem with that is that the connector introduces a significant mismatch before the device-under-test. You can calibrate this out, but this comes at a cost of the effective dynamic range. Hence it may be true that 30 dB of dynamic range is enough for practical measurements. However as soon as you start moving the measurement plane away from the port of the instrument, you really want to start with as much dynamic range as possible.

I believe most of the total noise in the system now actually comes from the phase noise. Currently the signal source and the receiver use independent clocks and each of those clocks has its own drift and various artifacts introduced by individual phase locked loops. I suspect things would improve significantly if I could run both ERASynth Micro and HackRF from a common clock source, ideally from the ERASynth Micro's low phase noise TCXO. Unfortunately they use incompatible interfaces for reference clock in/out. I need to make an adapter circuit before I can try this out.

In the end, this is all kind of an endless rabbit's hole. Every measurement I take seems like it could be done better and there appears to be a lot of room for improvement. I've already accumulated a wish list of changes for the multiplex circuit. At one point I will likely make a new versions of the bridge and the multiplexer PCBs using the experience from the past year of experimenting.

Posted by Tomaž | Categories: Analog | Comments »

Characterizing the RF Demo Kit

19.03.2021 18:47

The RF Demo Kit is a small printed circuit board with several simple RF circuits on it. There are several variants out there from various sellers. It's cheap and commonly sold together with NanoVNA as a learning tool since it is nicely labeled. Among random circuits like filters and attenuators, it also contains a set of short, open, load and thru (SOLT) standards that can be used for VNA calibration. These are all accessible over U.FL surface mount coaxial connectors.

The "RF Demo Kit" circuit board, NWDZ Rev-01-10.

I've been using the SOLT standards on the Demo Kit for calibrating my home-made vector network analyzer. I've been measuring some circuits with an U.FL connection and I lack a better U.FL calibration kit at the moment.

Of course, for this price it's unrealistic to expect the Demo Kit to come with any detailed characterization of the standards. Initially I just assumed the standards were ideal in my calculations. However I suspected this wasn't very accurate. The most telling sign was that I would often get an S11 measurement of a passive circuit that had the absolute value larger than 1. This means that the circuit reflected more power than it received and that wasn't possible. Hence the calibration must have given my instrument a wrong idea of what a perfect signal reflection looks like.

The "RF Demo Kit" connected to my home-made vector network analyzer.

I suspected that my measurements would be more accurate if I could estimate some stray components of the standards on the Demo Kit and take them into account in my calibration. I did the estimation using a method that is roughly described in the Calibrating Standards for In-Fixture Device Characterization white paper from Agilent.

I did my measurements in the frequency range from 600 MHz to 3 GHz. First I used my SMA cal-kit to calibrate the VNA with the measurement plane on its SMA port. I then measured the S11 of the Demo Kit short standard, using a SMA-to-U.FL coax cable:

S11 for the short standard before port extension.

I used this measurement to calculate the port extension parameters to move the measurement plane from the VNA port to the position of the short on the Demo Kit PCB. I verified that the calculated port extension made sense. Calculated time delay was approximately 0.95 ns. With a velocity factor of 0.7 for the RG-316 cable, this gives a length of 19.9 cm which matches the length of the 20 cm cable I used almost exactly.

S11 for the short standard after port extension.

Using the port extension "unwinds" the Smith chart for the short standard. Ideally the whole graph should be in one spot at Z = 0 (red dot). In reality, noise of the VNA and various other imperfections make it a fuzzy blob around that point.

I then applied the same port extension to the measurement of the open standard. Ideally, this graph should be in one point at Z = infinity (again, marked with a red dot). This graph is still very noisy, like the previous one. However one important difference is that it clearly shows a systematic, frequency dependent deviation, not just random noise around the ideal point. Some calculation shows that this deviation is equivalent to a stray capacitance of about 0.58 pF. The simulated response of an open with 0.58 pF stray capacitance is shown in orange:

S11 for the open standard after port extension.

The Agilent white paper goes further and also estimates the stray inductance of the load standard. This is how my measurement of the Demo Kit load standard looks like with the same port extension applied as before. Again, the ideal value is marked with the red dot:

S11 for the load standard after port extension.

It looks pretty messy, with maximum reflection loss of about -12 dB at frequencies up to 3 GHz. Note that the U.FL connectors themselves are only specified up to about -18 dB reflection loss, so a lot of this is probably due to connector contacts. The white paper describes using time gating to isolate the effect of the load from the effect of contacts, but the mathematics of doing that based on my measurements escape me for the moment. I also suspect that my setup lacks the necessary bandwidth.

I stopped here. I suspect estimating the stray load inductance wouldn't make much difference. Keep in mind that the graph is drawn with port extension in place, which removes the effect of the cable. Hence the circling of the graph must be due to phase delays in the load resistor and the U.FL connector. The phase delay also cannot be due to the short length of microstrip between the U.FL and the 0603 resistor on the RF Demo Kit PCB. That shouldn't account for more than about 15° of phase shift at these frequencies.

At the very least I'm somewhat satisfied that the Figure 7(b) in the white paper shows a somewhat similarly messy measurement for their load standard. I'm sure they were using a much higher quality standard, but they were also measuring up to 20 GHz:

S11 measurement of the load standard from the Agilent white paper (Fig 7b)

Image by Agilent Technologies, Inc.

In the end, here is the practical effect of taking into account the estimated stray capacitance of the open standard when measuring S11 of a device:

S11 measurements calibrated using ideal open or open with stray capacitance.

When the VNA was calibrated with the assumption of a ideal open standard, the log magnitude of the measured S11 went above 0 dB at around 1400 MHz. This messed up all sorts of things when I wanted to use the measurement in some circuit modeling. However, when I took the stray capacitance of the open standard into account, the measured S11 correctly stayed below 0 dB over the entire frequency range. Since I don't have a known-good measurement I can't be sure in general whether one or the other is more accurate. However the fact that the limits seem correct now suggests that taking the estimated stray capacitance into account is an improvement.

Posted by Tomaž | Categories: Analog | Comments »

hackrf_tcp, a rtl_tcp for HackRF

13.03.2021 19:03

rtl_tcp is a small utility that exposes the functionality of a rtl-sdr receiver over a TCP socket. It can be interfaced with a simple Python script. I find it convenient to use when I need to grab some IQ samples from the radio and I don't want to go into the complexity of interfacing with the GNU Radio or the C library. Using it is also much faster compared to repeatedly running the rtl-sdr command-line utility to grab samples into temporary files at different frequencies or gain settings.

I wanted to use something similar for the HackRF. Unfortunately the stock software that comes with it doesn't include anything comparable. After some searching however I did find an old fork of the HackRF software repository by Zefie on GitHub that included a file named hackrf_tcp.c. Upon closer inspection it seemed to be a direct port of rtl_tcp from librtlsdr to libhackrf. It didn't compile out of the box and merging the fork with the latest upstream produced some conflicts, but it did look promising.

I resolved the merge conflicts and fixed the code so that it now compiles cleanly with the latest libhackrf. I also added a few small improvements.

Just like with rtl_tcp, the protocol on the socket is very simple. Upon receiving the client's connection the server initializes the radio and starts sending raw IQ samples. The client optionally sends commands back to the server in the form of a simple structure:

struct command{
	unsigned char cmd;
	unsigned int param;
}

cmd is the command id and param is a parameter for the command. Commands are things like SET_FREQUENCY = 0x01 for setting frequency, SET_SAMPLERATE = 0x02 for setting ADC sample rate and so on.

The original code attempted to keep some backwards compatibility by mapping HackRF's functionality to existing rtl-sdr commands. This included things like using the frequency correction command to enable or disable the HackRF's RF preamplifier. I dropped most of that. Obviously I kept the things that were direct equivalents, like center frequency and sample rate setting. The rest seemed like a dangerous hack. Enabling HackRF's preamp by mistake can damage it if there's a strong signal present on the antenna input.

Instead, there is now a new set of commands starting at 0xb0 that is HackRF exclusive. Unsupported rtl-sdr commands are ignored.

Even with hacks backwards compatibility wasn't that good in the first place and I wasn't interested in keeping it. HackRF produces IQ samples as signed 8 bit values while rtl-sdr uses unsigned. The code makes no attempt to do the conversion. There is also a problem that the 32 bit unsigned parameter to the SET_FREQUENCY = 0x01 command can only be used for frequencies up to around 4.3 GHz, which is less than what HackRF can do. To work around that limitation I added a new command SET_FREQUENCY_HI = 0xb4 that sets the central frequency to parameter value plus 0x100000000.

My updated version of hackrf_tcp is in my hackrf fork on GitHub. It seems reasonably stable, but I've seen it hang occasionally when a client disconnects. I haven't looked into this yet. In that case it usually requires a kill -9 to stop it. In hindsight, separating hackrf_tcp out into its own repository instead of keeping it with the rest of the upstream tools might have been a better idea.

As it is right now, you need to compile the whole libhackrf and the rest of the host tools to get hackrf_tcp. The basic instructions in the README still apply. After installation you can just run hackrf_tcp from a shell without any arguments:

$ hackrf_tcp
Using HackRF HackRF One with firmware 2018.01.1
Tuned to 100000000 Hz.
listening...

You can also specify some initial radio settings and socket settings on the command-line. See what's listed with --help.

Posted by Tomaž | Categories: Code | Comments »