Comparing RF Demo Kits

10.09.2021 15:48

Earlier this year I was writing about the RF Demo Kit. It's a small circuit board with a sample of different radio frequency circuits that's sold as a learning tool. Among other things it includes a set of on-board short, open and load calibration standards. I was using these standards on the RF Demo Kit for VNA calibration when measuring antennas with U.FL connectors since I lack a better U.FL calibration kit. Having a better model of calibration standards leads to more accurate measurements and in the previous blog post I wrote about measuring some of the model parameters.

At the time I managed characterize the fringing capacitance C0 = 0.58 pF of the open standard on the RF Demo Kit. When I mentioned my measurements on the NanoVNA forum, it was pointed out to me that at least two versions of the RF Demo Kit exist and that the other version might have better characteristics due to the different PCB layout. Since I recently improved my home-made VNA, I decided to revisit this measurement and see if the second RF Demo Kit indeed shows different characteristics.

SOLT standards on the NWDZ RF Demo Kit.

Here is a close-up of the SOLT standards on the NWDZ Rev-01-10 RF Demo Kit. This is the board I measured in my earlier blog post. The load standard is a 0603 size SMT chip resistor. It measures 50.0 Ω at DC. Short and open standards are terminated directly at the U.FL connector, which means they have a different delay compared to the load standard. The substrate looks like a typical 1.6 mm thick FR-4.

SOLT standards on the Deepelec RF Demo Kit.

This is the other board mentioned in the NanoVNA forum, the Deepelec RF Demo Kit. Here the load standard is a smaller, 0402 size SMT chip resistor. It also measures 50.0 Ω at DC.

All three standards are terminated after an approximately 2 mm long trace. Compared to the NWDZ Demo Kit, this forms a more consistent calibration plane. The trace is covered in solder resist, which isn't ideal. The dimensions of the trace and gap to ground seem to be slightly different between the standards. The substrate looks identical to the other board, also most likely a 1.6 mm thick FR-4.

Compared to the NWDZ Demo Kit, U.FL connectors have thermal reliefs around their ground pads. Using thermal reliefs is usually discouraged in high frequency circuits.

My measurement setup for characterizing the RF Demo Kits.

I again used my home-made VNA for the measurements. I connected the RF Demo Kits to the VNA using a 20 cm SS405 coax and a reasonably expensive U.FL-to-SMA adapter. I measured at the frequency span from 500 to 3000 MHz. The VNA was calibrated at its port using a SMA calibration kit I wrote about previously. I applied port extension to null out the effects of the coax using the same method I described in the earlier blog post about RF Demo Kits.

In general, this setup yielded much more accurate results as before. The new version of my VNA has significantly better characteristics in terms of the dynamic range and phase noise. The SS405 coax with the U.FL-to-SMA adapter also performed much better and gives more consistent results than the 20 cm RG-316 pigtail I used previously. Compared to my earlier measurements the new ones look cleaner and with less random noise.

Smith chart for the NWDZ Demo Kit with port extension.

This is how the open, short and load standards measure on the NWDZ RF Demo Kit. Ideally, the standards should have 1, -1 and 0 reflectivity respectively and the plots should just be dots on the Smith chart. Due to various imperfections however the plots spread out from their ideal values as frequency increases.

Fitting a capacitance value to the measurement of the open standard gives a value of C0 = 0.57 pF. This is very close to the 0.58 pF figure I got in my previous measurement.

The load standard has more than 13.5 dB return loss up to 3 GHz. In my previous measurement I've seen return loss down to 12 dB.

Smith chart for the Deepelec Demo Kit with port extension.

The measurement of the Deepelec RF Demo Kit shows a similar picture, but differs in a few details. The calculated one-way time delay for the port extension was 30 ps longer compared to the NWDZ kit. 30 ps is equivalent to 9 mm in free space. This seems too large for the 2 mm line seen on the PCB, even when accounting for a reasonable velocity factor. There might be some other effects here or maybe it's just an error in my measurement.

Fitting a capacitor to the open standard yields C0 = 0.54 pF. The value is similar to the one I get for the NWDZ Demo Kit, however this fit is worse near 3 GHz. On the Smith chart, the trace for the NWDZ open increases in phase in a linear fashion with very little deviation from the ideal capacitor characteristic. On the other hand the trace for Deepelec open folds back at itself and makes a loop near 3 GHz.

Interestingly, the match of the load standard is slightly worse than with the NWDZ board. The worst return loss is 11.9 dB. I would expect the physically smaller 0402 resistor to behave better at higher frequencies.

Smith chart for the SMA calibration kit with port extension.

For comparison, this the same measurement done with the SMA calibration kit connected to the end of the same 20 cm SS405 cable (although with a SMA-to-SMA adapter instead of the U.FL-to-SMA). You can see that all 3 standards are much better defined compared to the two Demo Kits. The port extension comes out at exactly 1.00 ns, which is roughly consistent with the 20 cm length of the cable and the 0.7 velocity factor for SS405.

Setup for measuring the thru on Demo Kits.

I also measured these same three SMA standards by connecting them using a short U.FL pigtail and the thru on both Demo Kits. This way I wanted to get an estimate of the quality of the thrus on these boards since I can't measure them directly with my one-port VNA.

Measurements of the thru on the NWDZ Demo Kit.

Compare this measurement with the case above where the SMA calibration standards were connected directly to the 20 cm SS405 cable without the NWDZ thru in between. The three standards are much less well defined. This is the effect of the two U.FL connectors on the Demo Kit, the PCB trace between them and the U.FL pigtail. I can't say which one had a stronger effect.

Measurements of the thru on the Deepelec Demo Kit.

This is the same measurement, but with the thru on the Deepelec Demo Kit. Interestingly, it shows a much worse picture. I was so surprised that I repeated the measurement to make sure I was getting a consistent result. The thrus on both Demo Kits are effectively the same, with the only obvious difference being the dimensions of the PCB trace and the gaps to ground.

It seems unlikely this is an effect of the U.FL connectors. They seem identical on both boards and the connectors would also ruin the response of other standards. The most suspect is the 4 mm long trace between the connectors. Deepelec's trace does look a bit too narrow for 50 Ω on a typical two layer board. My guess is it's about 50 mil wide, with a 20 mil gap to ground. This makes the gap too small for a microstrip mode. A co-planar wave guide model with some typical PCB parameters gives a characteristic impedance of about 125 Ω, not accounting for the effect of the solder mask filling the gap.

For 3 GHz, the 1/10 wavelength rule is between 5 and 10 mm, depending on the velocity factor. The trace for the thru is about 4 mm long so it's surprising to me that it would have this much of an effect, even if the trace has a wrong characteristic impedance. However a quick simulation of the measurement did in fact show very similar results if I used a 4 mm, 125 Ω transmission line segment as a thru:

Simulated effect of a 4 mm, 125 ohm transmission line segment.

In summary, the SOLT calibration standards on the NWDZ RF Demo Kit seem to be better than on the Deepelec one. The return loss of the load standard is slightly better and the open standard is more accurately described with the fringing capacitance model at high frequencies. The thru on the NWDZ is significantly better compared to Deepelec, which seems to have a significant impedance mismatch.

I need to stress that these Demo Kits sell for about 20 €, which is an order of magnitude less than similar, semi-professional kits. A cal-kit with a brand name will have at least one more figure in its price on top of that. They can't really compare. The RF Demo Kits I've tested are mostly meant to be used with the NanoVNA that works best up to a few hundred MHz and are perfectly fine as a learning tool.

Posted by Tomaž | Categories: Analog | Comments »

Inside a 3.5 mm plug from old Bose headphones

28.08.2021 10:16

Some time ago my dad told me about a problem he was having with a new ham radio transceiver. The transceiver has a RS-232 serial interface in a 3.5 mm stereo socket in the back panel. Connections that would normally be used for left and right audio channels are used for data RX and TX instead. Since the transceiver didn't come with a suitable cable my dad made his own: he found an old cable with a molded 3.5 mm plug on one end and soldered the other end to a DB-9 connector he could plug into his PC. However no matter what he tried, he could not get the digital interface working.

Surprisingly, we traced the problem to his adapter cable. Even though the multimeter showed that the cable had continuity on all cores and that no lines were shorted, the digital signals that passed through it came out distorted on the oscilloscope. I was curious what was going on, so I took the cable home with me for some more investigation. I commonly re-use the 3.5 mm plugs from old headphones as well and have never came across a problem like this before.

This is the 3.5 mm plug molded onto the end of the cable in question. It has nice gold plating on the contacts and a no-expense-spared molding with a combination of hard plastic and a soft rubber-like overmold. It probably once served some higher-end Bose headphones.

The 3.5 mm stereo plug on the end of the suspect adapter cable.

First thing I did was to measure the frequency response of the cable in the audio frequency range. I connected the stimulus signal to the 3.5 mm connector and measured the gain and phase of the signal coming out of the other end of the cable. This is the resulting Bode plot:

Measured frequency response of the adapter cable.

Obviously there is something more going on in this cable than just normal copper connections. At these frequencies I would expect to see practically flat, 0 dB gain and next to no phase shift across the cable. Instead, there seem to be a -10 dB band stop filter with a center frequency of around 2 kHz somewhere inside.

I found it unlikely that the actual cable was doing the filtering, so I focused on the molded handle around the actual 3.5 mm connector. After some application of unreasonable force it still refused to fully come apart. It did reveal what looked like tops of two MLCCs molded into the plastic. However it seemed that if there is some more electronics inside removing the rest of the plastic by force would also destroy the circuit.

3.5 mm plug with the partially removed molding.

Since I heard that acetone sometimes dissolves potting compounds I put the connector into a glass of acetone-base nail polish remover. I then forgot about it, so it soaked in the acetone for about 2 months. Still, this didn't have as much effect on the plastic as I thought it would. It did make it brittle enough so that I could chip it away until I revealed a small double-sided printed circuit board with a few passive SMD components:

Circuit board inside the 3.5 mm plug handle, bottom side.

Circuit board inside the 3.5 mm plug handle, top side.

The circuit board is marked "YODA" Ver A. Apparently someone working at Bose was a Star Wars fan. If I read the date code correctly this board was produced in 2005. The circuit is symmetrical and has two identical parts for the left and right channel. Each half consists of 2 multi-layer ceramic capacitors and two chip resistors. Tracing the circuit revealed this schematic:

Schematic of the filter circuit embedded in the 3.5 mm plug.

Note that the resistances in the circuit are low enough that a typical multimeter on continuity setting will see them as a short. This is what made debugging the initial problem so frustrating.

I couldn't get a reliable measurement of the capacitors, so 10 μF was a bit of a guess here. However simulating the response of this circuit in SPICE shows that it behaves similarly to what I measured before I destroyed the connector:

Simulated frequency response of the filter circuit compared to the measurement.

Why did Bose go into the trouble of embedding this circuit into the connector? I'm guessing they wanted to improve the frequency response of their headphones. Maybe the speaker has a mechanical resonance at 2 kHz and the filter circuit damps that electrically? I don't know much about hi-end audio engineering. This article mentions passive correction filters that are placed in-line with a headphone cable to equalize the speaker response. While the circuit mentioned is a bit more complicated than the one I found, the one Bode plot they show is very similar to what I measured.

One quick test I found that is capable of detecting this specific circuit without any elaborate setup is setting the multimeter for a capacitance measurement and connecting the probes between one of the channels and ground. On a normal audio cable the multimeter reads some low capacitance, but in the case of the embedded filter it shows around 20 μF due to the two capacitors in the circuit.

In conclusion, it seems that not all connectors are merely connectors. When all other options fail it's worth doubting the most basic assumptions, like that a cable you're using is not actually behaving like a cable.

Posted by Tomaž | Categories: Analog | Comments »

Factory contents of a USB flash drive

19.08.2021 20:33

I recently bought a new USB flash drive. By chance I noticed that it didn't appear to be empty. The drive came pre-formatted with a VFAT file system that indeed did not contain any files when I took the drive out of the box. However the raw contents of the drive seemed to contain some data that was apparently written to it in the factory. Since I'm used to seeing just zeros or 0xff on new devices I was curious what was on it and whether it contains any hints on how these are tested in the factory.

ADATA S102 Pro 32 GB flash drive.

This is the flash drive in question. It's an ADATA S102 Pro 32 GB flash drive.

usb 9-3.3: New USB device found, idVendor=125f, idProduct=312b, bcdDevice=11.00
usb 9-3.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 9-3.3: Product: ADATA USB Flash Drive
usb 9-3.3: Manufacturer: ADATA

This first thing I did was to dump an image of the drive in factory condition to a 32 GB file on my hard drive. I then used the binvis tool from Aldo Cortesi to visualize its contents.

The following picture shows just the first 32 MiB using the zig-zag curve. Beginning of the drive is in the top left corner. Offset increases from left to right and top to bottom:

binvis visualization of the flash drive contents.

There's mostly zeros in the first 16 MiB of the drive, with a few small isolated chunks of data and strings. At very the start there's the default partition table and the empty FAT. One of the other non-zero chunks has some recognizable strings referring to a burn-in test.

0101c000  41 44 41 54 41 20 55 46  44 20 20 08 00 00 00 00  |ADATA UFD  .....|
0101c010  00 00 00 00 00 00 56 5a  7e 33 00 00 00 00 00 00  |......VZ~3......|
0101c020  e5 73 00 74 00 20 00 66  00 69 00 0f 00 b5 6c 00  |.s.t. .f.i....l.|
0101c030  65 00 73 00 00 00 ff ff  ff ff 00 00 ff ff ff ff  |e.s.............|
0101c040  e5 42 00 75 00 72 00 6e  00 49 00 0f 00 b5 6e 00  |.B.u.r.n.I....n.|
0101c050  54 00 65 00 73 00 74 00  20 00 00 00 74 00 65 00  |T.e.s.t. ...t.e.|
0101c060  e5 55 52 4e 49 4e 7e 31  20 20 20 10 00 7b 83 be  |.URNIN~1   ..{..|
0101c070  74 52 74 52 00 00 84 be  74 52 03 00 00 40 00 00  |tRtR....tR...@..|
0101c080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Other than that I didn't find anything interesting. This seems to be just a remnant of some filesystem metadata. Chunks are too small to contain actual test code and I didn't expect that to be on the drive anyway.

Following the first 16 MiB is a pseudo-random pattern. It starts at offset 0x1024000 and repeats every 4 MiB (4194304 bytes). Extracting just a single repetition of the pattern and plotting it with binvis reveals that it has some internal structure to it. This plot is using the gradient color map and the same zig-zag curve as before:

Visualization of the 4 MiB test pattern on the flash drive.

I also played around with different ways of distributing the pattern onto a grid using matplotlib. This is how it looks like when drawn onto a 2048 by 2048 grid. The plot is zoomed into just one corner of the pattern:

A different view of the test pattern.

I don't know the reason behind this pattern. It might just be some pseudo-random algorithm they are using and the values themselves don't matter. I don't recognize any obvious bit test patterns in it. It seems that on every 2048 byte line the byte values in the pattern increase using a different step. Histogram of byte values shows that all 256 values are represented approximately equally.

Interestingly, the test pattern does not continue throughout the entire 32 GB of the drive. It stops at approximately 3 GB. The drive just contains zeros after that. Does this mean that only the first 3 GB has been factory tested? Or maybe there was a bug in the process and only this first chunk didn't get zeroed out after the test while the rest of the drive did?

Posted by Tomaž | Categories: Life | Comments »

Wio RP2040 review

12.08.2021 7:44

I've been following the development of the ecosystem around the new RP2040 microcontroller from the Raspberry Pi Foundation. I've found the microcontroller interesting in the combination with MicroPython since it appeared suitable for development in high-level language while still offering reasonably good real-time performance. For the common low-level bit banging microcontroller stuff I'm not sure if Python beats C/C++. However as soon as any kind of networking is involved, I think using a high-level language is significantly easier. With all sorts of necessary error handling and multi-tasking, networking code quickly becomes unreadable in C.

Hence I've been curiously waiting for RP2040 development boards to appear that would integrate some kind of a network interface. The two products that were most prominent on my radar were Arduino Nano RP2040 Connect and Seeed Wio RP2040. Both were announced earlier this year, but were more or less unobtainable. In June however Seeed reached out to me and offered to send me a free sample of their Wio RP2040 development board in return for a review. Two months later I've finally got one on my desk.

Seeed Wio RP2040 mini dev board on top of its box.

Wio RP2040 itself is a surface-mount module with castellated holes suitable for machine mounting on custom PCBs. It contains just the RP2040 microcontroller, the radio and an integrated inverted-F antenna for connecting to a Wi-Fi network. Voltage supply for the module is 5 V, the GPIO pins use 3.3 V levels. There is also a 3.3 V regulator output pin available on the module, however I could not find any information on how much current you can safely draw for your own use.

To make development easier, Seeed also sells the module already mounted on the Mini Dev Board, which is what I got in the box you see above. The Mini Dev Board adds two LEDs, two buttons, a USB C connector and breaks out all the module pins to two 14 pin 100 mil headers. Schematic and PCB layout files are available for the Mini Dev Board, but not for the module itself.

Also worth noting is the declaration of an EU representative on the box. This is most likely related to the requirements of the European Radio Equipment Directive.

Wio RP2040 compared to Raspberry Pi Pico.

Compared to Raspberry Pi Pico, Wio RP2040 Mini Dev Board is slightly shorter and wider. It has a USB C connector instead of USB Micro for programming and getting power from a PC. In addition to the Pico's BOOT button, the Mini Dev Board also has a RUN button for manually resetting the microcontroller. There's also a power LED that is hard-wired to the power supply line.

Same as on Pico, the Mini Dev Board has space for 100 mil headers on its edge. The headers themselves are not included in the box, so if you want to mount this on a breadboard you need to supply and solder them yourself. GPIO 20, 21 and 22 are not available on the Wio RP2040 headers. They are probably used for communication with the wireless chip inside the module.

Curiously, I found zero information on which 2.4 GHz 802.11 b/g/n radio is used in the module. I'm yet to peek under the RF shield can, but I strongly suspect it hides an ESP8285 from Espressif. ESP8285 is a variant of the popular ESP8266 with built-in flash memory. This guess comes from the fact that the host name the module uses when obtaining an IP address from a DHCP server is espressif. The official firmware image also has a number of strings that mention ESP:

$ strings firmware.uf2|grep -i esp
esp8285
o rp2040] %s | esp8285_ipconfig could'n get ip
[wio rp2040] %s | esp8285_ipconfig could'n get gateway
[wio rp2040] %s | esp8285_ipconfig could'n get netmask
[wio rp2040] %s | esp8285_config could'n get ip
couldn't init nic esp8285 ,try again please
esp8285 power off

Speaking of firmware, Seeed provides a firmware.uf2 file that contains a customized MicroPython interpreter with some added modules related to networking. Unfortunately, it's not clear at the moment what is the source used for building this file. Another problem is that the file linked from the Wiki seems to silently change without notice. Since July I've seen at least two files being distributed with the same name and URL but different contents.

The procedure for loading the firmware is the same as with Pico. Power up the module with BOOT button depressed and then copy the firmware image into the emulated USB storage device. Using rshell, this is how the module presents itself, running the firmware.uf2 downloaded on August 5:

$ rshell
Connecting to /dev/ttyACM0 (buffer-size 512)...
Trying to connect to REPL  connected
Testing if sys.stdin.buffer exists ... Y
Retrieving root directories ... 
Setting time ... Aug 05, 2021 07:43:39
Evaluating board_name ... pyboard
Retrieving time epoch ... Jan 01, 1970
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.
> ls /pyboard
> repl
Entering REPL. Use Control-X to exit.
>
MicroPython v1.15 on 2021-07-06; Seeed Wio with RP2040
Type "help()" for more information.
>>> help('modules')
__main__          machine           uasyncio/funcs    urandom
_boot             math              uasyncio/lock     ure
_onewire          micropython       uasyncio/stream   uselect
_rp2              mqtt              ubinascii         usocket
_thread           network           ucollections      ustruct
_uasyncio         onewire           uctypes           usys
builtins          rp2               uerrno            utime
cmath             uarray            uhashlib          uzlib
ds18x20           uasyncio/__init__ uio
framebuf          uasyncio/core     ujson
gc                uasyncio/event    uos

The embedded flash filesystem is empty by default, however there are some extra importable modules available in the interpreter: network, mqtt and a few others. Again, unfortunately there is very little information on these, apart from a few examples in the Wiki. No source available as far as I can tell either. MQTT module seems similar to umqtt.simple described here with some differences - there is no check_msg() method, for example.

I didn't have much luck with using these networking Python modules. Some examples in the wiki are apparently outdated and I didn't manage to get any of them to a usable state.

Specifically, the firmware I was using seemed to have problems receiving data from the network. I could connect to the Wi-Fi network and successfully open a usocket to another host. Sending data using usocket.send() worked. However as soon as the socket received anything from the other end, the MicroPython interpreter would apparently crash and I could never get anything back using usocket.recv(). The program stopped running and the REPL would not respond. I couldn't connect to the board over USB anymore until I reset the processor using the RUN button.

I had similar problems with Seeed's MQTT example code. After fixing it to account for the fact that WLAN_UART class is not defined, Wio RP2040 connects to my MQTT broker. I can successfully publish messages and subscribe to topics from MicroPython. However as soon as some other client sends a message to the topic that the Wio RP2040 is subscribed to, the interpreter crashes. There's definitely something still alive running on the MCU because the broker keeps getting periodic MQTT pings from Wio RP2040. The Python code doesn't seem to be executing though and neither Thonny nor rshell will connect to it.

I tried to find the problem, but without the source and any kind of debug info I was pretty much stuck. I also asked my Seeed contact about it and after a week I have yet to receive a reply.

Update: On 17 August I received a MicroPython firmware image from Seeed that fixes the interpreter crashes related to the networking I describe above. They say that they will fix the image linked from the wiki at a later date.

Screenshot of Thonny with Wio RP2040 MQTT example.

It's obviously very early in the product cycle. I actually don't know if these modules have shipped in any quantity so far. Each time I check, they are out of stock and the banner on Seeed website currently says they will start shipping in September. Still, I was disappointed to see that networking, the main feature of this module, doesn't seem to be functional at the moment. It seems Seeed's customized MicroPython port still needs some work. There's also support for programming the module in C/C++ using Arduino IDE. I have not tried that, but it seems other people are not having much success with that either.

Apart from fixing the software, I hope Seeed also adds some more documentation in the future. Having examples is great, but the custom Python modules should come with a reference. If the firmware image is open source, instructions on building one would be welcome as well. I'm also missing a proper hardware datasheet with some electrical specifications for the module.

The problems I encountered are even more puzzling since Wio RP2040 seems to be focused on being a base for a product than a development board for one-off projects. Its bare-bones design doesn't include any extra sensors that Arduino is shipping on their RP2040 boards. This makes it less inviting for playing around compared to the kitchen-sink-included approach of the Arduino. On the other hand, that's obviously a feature when you're designing a custom board with only the peripherals you need. Seeed is also running a promotional campaign and gives you some free modules when using their assembly services.

Another thing worth noting is that with recently introduced EU import regulations, getting these modules shipped in small quantities from China is quite troublesome and expensive. Even when receiving this free sample I had to deal with import customs paperwork and pay approximately 20 EUR in VAT and processing fees. Add shipping costs and the 13 USD base price shown in the Seeed store effectively becomes around 50 EUR. On the other hand, I have noticed that the modules are listed on Mouser, so this might improve in the future.

In summary, this module promises to be a cheap and simple basis for small network-connected sensors and actuators. I like the simplicity of connecting to MQTT using a few lines of Python. Unfortunately, current software does no deliver on that promise and I can only recommend waiting until the quality improves.

Posted by Tomaž | Categories: Digital | Comments »

HackRF clock converter, 3

26.06.2021 10:48

I modified my HackRF with a small board based around the LPC6957 clock buffer. This allows me to connect a wider range of clock sources to its CLKIN input for a 10 MHz reference clock. Among other things, I can now synchronize HackRF to the ERASynth Micro I use in my vector network analyzer. In my last blog post I said I will share some more measurements on how the modified HackRF performs, so here are a few initial observations.

HackRF connected to the ERASynth Micro.

The measurements I talk about below were done with the HackRF antenna input connected to the RF output of the ERASynth Micro through a short piece of a RG-316 coaxial cable and a 20 dB attenuator. ERASynth Micro was set up to output a CW signal at various frequencies at -20 dBm level. I also had the REF OUT from the ERASynth Micro connected to CLKIN on the HackRF. For measurements where I didn't want ERASynth Micro and HackRF running from the same clock source I left the cable attached to CLKIN and disabled the CLKIN input using hackrf_clock --clkin 0.

The first thing I noticed when testing the clock converter modification was the fact that at some frequencies the phase noise appears higher at around 100 kHz offset when HackRF is running from an external clock. As I mentioned in my last post this was already noticeable in the waterfall plot of the spectrum analyzer application. Difference is even more obvious in the following plot of the apparent phase noise of the signal at 2420 MHz.

Apparent phase noise in digital baseband at 2420 MHz.

The plot shows spectral density calculated using the Welch's method from a 10 s long recording of digital I/Q samples from the HackRF at 8 MHz sampling frequency. This plot does not show phase noise of the actual signal on the wire. I have no instruments available to directly measure that (however the spec for ERASynth Micro phase noise is much lower than what I measured - I show the comparison in this post). The plot shows the apparent phase noise of the sine wave in the digital domain, including the contributions of both HackRF and ERASynth Micro.

A signal at 1000 MHz doesn't show a significant increase when CLKIN is enabled, however the interesting part at around 100 kHz offset it is obscured by some spurs:

Apparent phase noise in digital baseband at 1000 MHz.

My understanding is that at these offset frequencies the phase noise is largely defined by the various PLLs in HackRF. The synchronization itself shouldn't matter. As I said last time, I suspect the difference is because of different PLL settings in HackRF. When CLKIN is disabled, HackRF derives all internal clocks from a 25 MHz quartz oscillator. When CLKIN is enabled, it uses the 10 MHz reference, hence requiring a different multiplier in the first stage PLL that converts the reference to a 800 MHz clock.


For my specific application in the vector network analyzer the far-off phase noise is less important than the stability of the signal over periods of time in the range of 1 to 10 ms. This is because I use a time multiplex to compare the phase of the reference and measured signals. The assumption in this type of measurement is that the reference signal has a stable phase over one period of the time multiplex.

On the phase noise plots above, stability over this range of time intervals is shown beyond the left edge of the graph. However it's difficult to show this in the frequency domain since it requires Fourier transforms over a very large number of samples and at least my naive approaches ran out of computer memory. Hence I rather explored this in the time domain.

Setup for measuring phase stability.

This is the block diagram of the setup. The 10 MHz TCXO in the ERASynth Micro is the single reference frequency source. Two PLLs in the ERASynth Micro convert this reference into the 2420 MHz RF signal on the coax. HackRF then uses a complicated circuit that involves multiple PLLs, frequency conversions and an analog-to-digital conversion to convert the RF signal to a 2 MHz digital intermediate frequency. I then use a digital LO on the computer to convert the signal to DC and measure its phase angle.

A typical plot of the detected phase angle in degrees over a course of 100 ms looks like this. The plot is similar for other RF frequencies:

Phase stability of the received CW signal.

I was somewhat surprised that I still get this kind of random walk in signal phase, even when everything is running from a single clock source. I've seen it sometimes drift up to ±30 degrees. My understanding was that at these time scales the PLLs should largely track their reference clock and not contribute to the stability of the signal, so I'm not sure where this is coming from.

On the other hand, the whole system is very complicated and I find it hard to understand all the parts. Especially HackRF is internally much more complicated than I initially thought. It includes many nested layers of PLLs distributed through different chips and so far I failed to get a good high-level picture of how various parts affect could phase stability.

In conclusion, the clock converter board seems to work, but it has some side effects I didn't anticipate, like the unusual increase in phase noise at 100 kHz offset. The clock synchronization itself also didn't help as much as I thought it would in improving the accuracy of my vector measurements. However it did lead me to better explore the properties of the whole system and I found some other improvements I can make.

Posted by Tomaž | Categories: Analog | Comments »

HackRF clock converter, 2

18.06.2021 20:18

Last time I was writing about making a small modification for the HackRF to expand the range of signals that can be fed into the external 10 MHz reference input. My initial motivation for it was to sync the ERASynth Micro frequency synthesizer and HackRF in my home-made vector network analyzer. However I thought it might be more broadly useful, so I designed the PCB to fit nicely into off-the-shelf HackRF enclosures. I've now assembled a prototype, verified that it works and written the necessary HackRF firmware and host tools code to support the clock converter circuit.

Clock converter board mounted onto the HackRF.

I installed the clock converter into my HackRF as I described in my previous post. I cut the PCB trace on the HackRF that connects the center pin of the CLKIN SMA connector and the pin 2 of the P22 header. I then soldered three thin wires between the SMA connector and the input on the clock converter board. Two outer wires are ground and the center wire carries the 10 MHz signal. They are quite short. I could use a short coax for this, but wires were simpler and I think that the impedance mismatch of this length won't matter much at 10 MHz.

HackRF with the clock converter modification installed.

I've put a footprint for an extra edge-mount SMA connector on the clock converter board. This way it can be used without any destructive modifications to the HackRF. However cutting the trace makes it possible to use the existing connector for connecting the HackRF to an external reference, same as before the modification. This way the modified HackRF fits into cheap off-the-shelf enclosures that provide some extra vertical space above the base PCB. The original molded plastic enclosure is too low unfortunately.

HackRF mounted in a metal enclosure.

The LTC6957 chip on the clock converter is turned on and configured through spare GPIOs on the HackRF's ARM CPU. It is disabled by default using some pull ups. Hence the HackRF should work as before if the converter board is plugged in but the firmware doesn't know about it. To actually use it, a patched firmware must be uploaded to the HackRF's MCU.

The firmware modifications are largely just boiler plate code that is needed to toggle GPIO pins based on requests over USB. Most of the new code is in clock_conv.c file.

The original README has instructions on how to build and upload the firmware. I didn't have any problems with that on a stock Debian Buster system. Remember to reset the MCU after uploading new firmware using hackrf_spiflash -R.

The only thing that was slightly confusing was the firmware version string that is reported by hackrf_info. The version string is made automatically from the current git tag, or commit SHA1 if tag doesn't exist. However, it only seems to get refreshed when making a new build directory with cmake, not when merely running the build with make.

For the host tools side of things, I patched the new hackrf_clock tool. I added two new command-line arguments: --clkin can be used to enable or disable the LTC6957 and hence the CLKIN input. --clkin-filt can be used to adjust the LTC6957 input filter bandwidth.

You can verify that the HackRF's PLL has locked onto the external reference using hackrf_debug as described in the wiki:

external reference disabled
$ hackrf_clock --clkin 0
$ hackrf_debug --si5351c -n 0 -r
[ 0] -> 0x51

external reference enabled
$ hackrf_clock --clkin 1
$ hackrf_debug --si5351c -n 0 -r
[ 0] -> 0x01

I will post some more detailed measurements of the performance of the modified HackRF later. For now, the simplest way to see the effect of the external clock is to check the frequency offset between HackRF and another device. Here are two screenshots of HackRF Spectrum Analyzer. In both cases I had the antenna input of the HackRF connected to ERASynth Micro via a coaxial cable and some attenuators. ERASynth Micro was set to output a 2420.000 MHz signal. Also, the REF OUT of ERASynth Micro was connected to CLKIN on the HackRF:

Spectrum of a 2.420 GHz signal with CLKIN disabled.

This is with the CLKIN disabled (--clkin 0). The signal appears on the spectrum display with an approximately 22 kHz offset, since the ERASynth Micro and the HackRF use their internal quartz references which have slightly different frequency offsets.

Spectrum of a 2.420 GHz signal with CLKIN enabled.

This is with the CLKIN enabled (--clkin 1). Now the signal appears exactly at 2420.000 MHz since both devices are synchronized to the common 10 MHz reference (in this case, the TCXO in the ERASynth Micro). Of course, that doesn't mean that the signal is really exactly at 2420.000 MHz, just that both devices now exactly agree on what 2420.000 MHz is.

One interesting thing to note is that the lower screenshot also shows a slightly increased level of phase noise around the signal peak. As far as I can see, this is not due to the clock converter board. Even when CLKIN is used on an unmodified HackRF, received signals seem to exhibit slightly increased phase noise compared to when the internal quartz oscillator is used. I also tried this with a different 10 MHz source, so it's not due to ERASynth Micro either.

I didn't investigate this further. It might be that all my 10 MHz sources are noisy. Another possible cause could be different settings in HackRF's SI5351C. The SI5351C uses a PLL to convert either 25 MHz from the internal quartz or 10 MHz from the CLKIN into a 800 MHz clock. This 800 MHz signal is then used to generate all other clock signals in the HackRF. It might be that the higher PLL divider value (80 versus 32) contributes to this effect.

If you want to modify your HackRF like this, you can find the hardware design files in my hackrf-clock-conv GitHub repository. The modified firmware can be found in my fork of the HackRF repository. If you don't want to bother with making and soldering the PCB yourself, I'm also still collecting interest for a small production run of these boards. Send me an email if you are interested.

Posted by Tomaž | Categories: Analog | Comments »

HackRF clock converter

06.06.2021 10:24

HackRF can use an external 10 MHz reference clock instead of the built-in crystal oscillator. The CLKIN input accepts a DC coupled, CMOS-level, 3.3V square wave signal since it's connected directly to the digital input pin on the SI5351C PLL chip. I want to run my HackRF from the 10 MHz reference signal generated by my ERASynth Micro. Unfortunately, the TCXO output from the ERASynth Micro is an AC coupled, sinewave-ish signal and hence not directly compatible with the HackRF's CLKIN. While I've seen reports that sine wave signals on CLKIN also tend to work, I wanted to make a proper interface that didn't drive the SI5351 input outside of its rated signal levels.

HackRF CLKIN input line highlighted on the schematic.

In the future I might also want to synchronize the HackRF to other clock sources and I think a DC coupled, CMOS-level output is quite rare on instruments. Hence modifying the HackRF to accept a wider range of signals on the CLKIN connector seems useful to me.

I very much copied the idea for the circuit design from the Osmocom project's osmo-clock-conv. osmo-clock-conv is a stand-alone board that uses an Analog Devices LTC6957 clock buffer to convert a wide range of clock signals into a CMOS-level square wave. The LTC6957 is a specialized chip for this purpose that introduces very little additional phase noise and jitter into the signal during conversion. It should perform much better than, for example, a diode and a Schmitt trigger "self-biasing clock squarer" circuit with a similar function in the osmo-clock-gen.

I could have just used osmo-clock-conv board directly, or in fact I could just order the LTC6957 evaluation board and connect it via a coax to CLKIN. However I felt like making a more elegant solution that would be more tightly integrated with the HackRF. HackRF offers quite a lot of possibilities through various extension headers on its circuit board. The header P22 is connected to CLKIN and can be used to add add a custom circuit that supplies the reference clock signal. Adding a small TCXO board to P22 is quite popular and there are HackRF enclosures readily available that leave enough space for the TCXO mod. Hence adding a small clock converter circuit in place of the TCXO should be relatively straightforward and I could get a nice enclosure off-the-shelf that would nicely fit my modified HackRF.

3D render of the HackRF clock converter circuit board.

The circuit required to support the LTC6957 is quite minimal, so it wasn't hard to cram it all into a small two-layer board that will sit in the corner between P22 and P20 headers. Compared to the typical TCXO mod that only mounts onto the P22 I decided to also use the P20 header. This both makes it a bit more mechanically stable as well as gives me access to some unused GPIO lines on the HackRF's LPC4320 CPU.

I designed the input circuit to be 50 Ω terminated and hence work best with 50 Ω sources. The input is AC coupled and should work with AC or DC coupled sources. The converter should work with square wave signals with amplitudes between 0.8 V and 8 V peak-to-peak (when measured without a 50 Ω load) and sine wave signals with levels between -4 dBm and +16 dBm.

The LTC6957 has some digital inputs that affect its operation. This includes setting filter bandwidth (useful for adjusting for a sine wave or a square wave input) and turning the clock conversion on and off. osmo-clock-conv uses jumpers to configure those, but since I had GPIO lines available I simply used those. This makes the LTC6957 configurable from software. I also wanted to make sure I can power down the LTC6957 on request - LTC6957 with a floating input will likely produce a random clock signal and I don't want the SI5351 to lock onto that if I leave CLKIN unconnected. With the LTC6957 output disabled, the SI5351 should automatically switch back to its own crystal oscillator.

The LTC6957 has two identical outputs. The second one isn't used on the board, but I wired it to an AUX header in case it later turns out to be useful.

Position of the clock converter board on the HackRF.

The only hairy part of this design is the fact that the HackRF offers no clean way for an extension board to sit between CLKIN and the SI5351 clock input. The P22 header only allows a board to be connected in parallel to the clock line (see the schematic at the top of the post). There is also no series element on the clock line that can be desoldered to isolate the CLKIN from the SI5351.

What I plan to do is cut the trace on the HackRF PCB going from the CLKIN connector to the SI5351 right before it connects to the P22 header. I then plan to use a short piece of coax, or simply a pair of thin wires, to connect the original CLKIN SMA connector to the input of my clock converter board. This way the external clock signal will enter through the original CLKIN connector, go through a wire jumper to the clock converter board. After conversion the signal will then go back onto the HackRF board through P22.

I also left a footprint for an edge-mount SMA connector on the clock converter board. This makes it possible to use it without modifying the HackRF PCB by having a separate SMA connector for the clock converter input. I probably won't be using that since the additional connector will not fit in existing HackRF enclosures.

I'm currently waiting for the PCBs, which should arrive any day now. I was lucky to get what appears to be the last two LTC6957-3 chips on the market, so I should be able to assemble the board and test its design shortly. I also still have to write the software. Unfortunately, the HackRF firmware doesn't provide a general way of controlling the spare GPIOs so I will have to modify and recompile it. I did some quick tests and I don't think that will be much of a problem. The latest firmware release also introduces a new hackrf_clock utility and I'm hoping I can integrate with that.

I'll be publishing the designs and the firmware patch after I verify that it works as intended. If you're also interested in modifying your HackRF like this, please drop me a mail. I might do a small production run of the clock converter board after the current component shortage passes if I see enough interest.

Posted by Tomaž | Categories: Analog | Comments »

Trace phase noise in NanoVNA

20.05.2021 10:10

A quick follow-up to my previous blog post where I was exploring the phase noise in my home-made vector network analyzer. One of the things I did last time was to estimate how much the final vector measurements are jumping around on the phase axis. For my system I got a result of approximately 1.4 degrees RMS at 1 GHz, which is quite bad. Commercial vector network analyzers have this trace phase noise error typically between 0.1 and 0.01 degrees RMS.

Since I had a Jupyter notebook with all the calculations already prepared I quickly ran a similar test on a NanoVNA-H for comparison. I disabled error network correction (i.e. no calibration, disabled CORRECTION in the CAL menu). I then took 200 measurements at 1 GHz with nothing connected to the CH0 connector. Here are the results in a polar plot with the calculated spread:

Trace phase noise for NanoVNA-H at 1 GHz.

I thought it would also be interesting to check how the phase noise varies with frequency:

Trace phase noise for NanoVNA-H versus frequency.

At the base frequency range up to 300 MHz, it seems NanoVNA-H is pretty much on par with professional instruments as far as this metric is concerned. At higher frequencies where it uses harmonic mode the trace phase noise gets worse, but it's still quite good. It stays below 0.15 degrees RMS up to 900 MHz and below 0.35 degrees RMS up to 1500 MHz.

Posted by Tomaž | Categories: Analog | Comments »

Vector measurements with the HackRF, 2

13.05.2021 20:10

Over the past year I've been slowly building up a small, one-port vector network analyzer. The last improvement I made to it was replacing the rtl-sdr receiver with the HackRF One. This increased the frequency range, but the dynamic range of the measurement was still quite low at higher frequencies. I suspected that a significant source of noise in the system was phase noise. In this post I describe some measurements I performed to get a better idea of what is going on in regard to phase. I also wanted to have a base reference to compare with when I change things in the future. This way I will be able to see whether I improved the instrument or made it worse.

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

First thing I measured was the apparent phase noise of the stimulus signal in the digital baseband. I manually set my instrument so that the signal coming from the ERASynth Micro synthesizer was routed directly to the HackRF receiver. I then recorded an IQ signal trace from the HackRF and calculated the apparent phase noise of the sine wave. The ERASynth Micro output frequency was set to 1 GHz.

Apparent phase noise of the stimulus signal in the digital baseband.

This is the resulting plot of the phase noise versus frequency offset. It is based on the FFT of the recorded digital baseband signal with 128k points and the Hann window. I verified that the measured noise level is above the spectral leakage due to FFT windowing (for Hann window the leakage falls off by 60 dB per decade). For reference I also plotted the phase noise specification of the ERASynth Micro from its datasheet. That would be the ideal result if HackRF was perfect and didn't contribute any additional noise. In reality, HackRF's internal oscillator is probably much noisier than the ERASynth Micro.

Currently the ERASynth Micro and HackRF are both running free from their own internal oscillators. They are not synchronized to a common reference, hence this graph is the combination of all sorts of effects in both devices: phase noise in both oscillators, jitter from various phase locked loops and probably other effects as well. The noise shown on the plot is not present in any real analog signal anywhere. It shows up on the digital data that comes out of the HackRF's ADC. Since that is the input to all further processing it's the thing I'm most interested in.

Trace phase noise when measuring the open standard.

Another thing I was interested in was the final noise level in the vector measurement. This is the trace phase noise that's usually specified for commercial vector network analyzers in degrees root-mean-square. It's the effective error on the phase coordinate that shows up in the final measurement result, after all the processing has been done. To estimate this for my system I did a zero-span vector measurement of the open calibration standard. I recorded 200 points at 1 GHz. The plot above shows the result on a linear-scale polar plot. The estimated error of the measurement was 1.39 degrees RMS.

Its apparent from the plot that my measurements are smeared more along the phase than the amplitude axis. This is where my initial assumption came from that the phase noise is currently more problematic in my system than inaccuracies in measuring the amplitude of the signal. Just for comparison I looked up some datasheets for commercial network analyzers. It seems a typical value for this would be in the range of 0.1 to 0.01 degrees RMS. Not that I ever expect to reach that level of accuracy with my home-brew instrument, but it's interesting to see how it compares.

Next step for this project is definitely to try to run the HackRF from the 10 MHz TCXO in ERASynth Micro and see how much this improves the metrics I described above. After some research it seems that I need to be careful with how I approach this. HackRF needs a 3.3V CMOS digital signal as a reference while Ref out on ERASynth Micro is a sine wave. I need to design a board that will convert the waveform, however a sloppy conversion can introduce additional jitter. I've been looking at some previous work published by the amazing Osmocom project and I will likely take their osmo-clock-gen and/or osmo-clock-conv designs as a starting point.

Posted by Tomaž | Categories: Analog | Comments »

Measuring interrupt response times, part 3

01.05.2021 18:17

Around five years ago I performed some measurements of interrupt response times in a Raspberry Pi Zero and an Arduino. My goal was to get some rough estimates of what kind of real-time performance you can expect from these systems. I was not interested in pushing them to their limits. I wanted to compare the most straightforward approaches - code you would find in documentation or in examples that pop up on top of web searches. This year the Raspberry Pi Pico was released and it promises to become just as popular. It brings some interesting new features that I wanted to explore, like MicroPython and the programmable I/O (PIO). I thought it would be interesting to repeat my old measurements and see how well it compares to the other two systems.

I only briefly summarize my previous results here. Read my original blog post for a longer introduction, description of the test setup and more in-depth discussion of the first batch of measurements. In the follow up post I also dug a little deeper into the reasons behind some of the more unusual results I got with Arduino and Raspberry Pi Zero.

Raspberry Pi Pico connected to the test setup.

For the purpose of this test, the interrupt response time is the time the system takes to change a state of an output GPIO pin in response to the change in an input GPIO pin. In real applications there is usually some kind of processing involved, so this value represents only the best-case scenario of how fast the software can respond to external events.

This response time was measured using a signal generator and an oscilloscope. A square wave generated by the signal generator was connected to the input pin. The two-channel oscilloscope was connected to both the input pin and the output pin. It was setup to measure the interval between the two state changes. The measurement was automated and repeated 500 times for each setup. Exact settings used are noted here.

To perform the test with the RP2040 processor on the Raspberry Pi Pico I installed a MicroPython firmware, as described in the Getting Started guide. I tried two implementations: A pure Python implementation was using the machine.Pin built-in class to configure a Python function as an interrupt handler. The PIO implementation used the rp2.asm_pio decorator to program the PIO state machine from Python code (see Section 3.9 in the Python SDK manual). After the state machine was programmed, the input was handled purely inside the PIO and the Python interpreter was not involved. You can find exact code I used in the GitHub repo.

Here is how the new measurements with the RP2040 compare with Arduino and the Raspberry Pi Zero:

Histogram of interrupt response time measurements.

The MicroPython implementation on the RP2040 (yellow) has the average response time of around 60 μs. This is around 3.5 times faster than using a CPython implementation on the Zero (cyan) which averages at around 210 μs. It is also more consistent, with less spread between minimum and maximum response times. A surprising result at the first glance, since Zero has a much more capable CPU running at up to 1000 MHz while the ARM core in the Pico only runs at 125 MHz.

The difference is very likely due to all the Linux kernel housekeeping and context switching that happens before the interrupt is propagated from the hardware to the Python process. MicroPython, while quite complex, is still a lightweight interpreter compared to the full CPython on the Zero. This is consistent with the fact that a C implementation that runs in the kernel on the Zero (blue) is much faster than MicroPython on the RP2040.

The following figure zooms in on the left end of the histogram:

Zoomed view of the left end of the response time histogram.

Here you can see that the PIO implementation is amazingly fast compared to all previously tested configurations. With the average response time of 0.043 μs it beats both the polling and the interrupt-driven C++ implementation on the Arduino by two orders of magnitude.

This comparison is a bit unfair though. The specialized PIO state machines on the RP2040 are indeed very fast, with only 8 ns per instruction and an instruction set that is optimized for responding to input events. However, the amount of processing you can do with them is very limited compared to all other approaches I've tested. Each PIO can only process 32 instructions. Most real-life applications beyond interfacing with a simple bus protocol will need a round-trip to MicroPython. This puts the response time back into the hundred-microsecond range.

Still, investigating PIO performance is interesting. Here is another level of zoom to show only the distribution of response times by the PIO implementation:

Histogram of response times for the RP2040 PIO implementation.

The response times should be in the range of 4 to 5 instruction cycles - 2 cycles for the input synchronizer (see 3.5.6.3 in the RP2040 Datasheet), between 1 or 2 cycles for WAIT and 1 cycle for SET. I did not use any clock dividers and used the default 125 MHz system clock, so each instruction takes 8 ns. This gives the range of response times between 32 to 40 ns.

I measured between 38 and 48 ns. Very likely this is a measurement error. Unfortunately my signal generator has a rise-time of around 10 ns. This means that in the nanosecond range the transition between low and high logic level is not well defined and this introduces an error into my measurement. I verified by other means that one PIO instruction indeed takes exactly 8 ns in my setup. It is also possible that I missed something and there is an additional PIO cycle (or two) needed somewhere before the response propagates to the GPIO pin.

On the oscilloscope screenshot below, the blue trace is the stimulus signal from the signal generator and the yellow trace is the response generated by the PIO on the output pin. You can see that the rise times are not insignificant compared to the measured time interval.

Signals on the input and output pins on the RP2040.

In the end this was an interesting exercise. I was surprised by the performance of MicroPython on the Raspberry Pi Pico and how quick the development setup is. I honestly expected Python code to run slower and I was again reminded that my intuition can be wrong sometimes. Unfortunately I didn't have time to setup the C SDK to also try out a native implementation of the same test on the RP2040. Perhaps some other day.

Programmable I/O is certainly the most interesting part of the RP2040. It took me a while to understand the unusual instruction set and how the FIFO buffers work. I like how the integration of the assembler into MicroPython makes it easily accessible for experimentation. I was impressed by the performance and quick response times. On the other hand, I was also surprised by how limited PIOs are in terms of the program size and the choice of instructions. I was expecting something similar to PRUs on the Sitara SoC. PIOs seem indeed very specialized devices for interfacing with digital buses and can't do much more in terms of algorithmic complexity.

Posted by Tomaž | Categories: Digital | Comments »