Let's Get Physical (Part 1/12) 🕊️
Paul Tagliamonte 2026-09-08 diyThe “PHY” (sometimes, “Layer 1”) is the way in which data is sent over some
physical medium. For things like
Ethernet, the Layer 1
is the way in which voltage is changed over twisted pair – things like
10BASE-T, 100BASE-TX, 1000BASE-T and 10GBASE-T1 are all each their
own Layer 1 schemes. For things like
Pigeon or WiFi’s many
versions (from 802.11a to 802.11ax), this specifies how radio waves are
generated.

While I knew that my old BPSK protocol was incredibly antiquated, when I started looking at what I should do next, I didn’t really know a ton about what was interesting. I wound up looking into a bunch of schemes (like LoRA’s chirps, as well as a deep dive to see if anyone has cooked up something interesting with wavelets), but wound up revisiting one concept over and over – OFDM (or: Orthogonal Frequency Division Multiplexing).
A few weeks in I was sold. While it’s not new or cutting edge or anything, OFDM does appear to still be the way most folks build Layer 1 stacks these days, and that’s good enough for me.
So, the only normal, well-adjusted and rational solution is to make a new Layer 1 and Layer 2, but this time, make it OFDM, since that would look like something that was made this century – or at least something that could be experimented with in the direction of something modern. So, that’s what I started to do (back in 2022). I’ve had a few major life events since then, so progress took a bit of a hiatus, as well as an, at time, unending number of rewrites in an effort to isolate and fix bugs that would crop up (without a clear culprit).
The resulting PHY (“pigeon”, as you may have guessed) is designed to transport data between hosts using Software Defined Radios, which are fucking awesome. The specific hardware used is not super important, but if anyone’s curious, I mostly developed this stack using two Ettus USRP radios, (a B210 and a B210mini; pictured), but this runs on all my radios, including successful data exchanges using a HackRF One, PlutoSDR, and rtl-sdr.

Since pigeon is designed to be a protocol to enable experimentation and
learning, I opted to make some fairly conservative and well-understood
tradeoffs in its design. I didn’t want to have to develop new techniques
in my Layer 1 while I was trying to learn about building one – rather,
I wanted to spend my time building the PHY so I can eventually experiment
on top of a working baseline.
In particular, one questionable decision that I made was to have pigeon’s PHY operate around fixed-sized blocks of data over the air. This fixed-sized block is what I call a “burst”. Each burst contains a fixed amount space for of user-controlled data, and some amount of overhead for forward error correction, even if there is only a single bit being sent over the air within the layer 2.

As for what’s inside the burst – we have two main sections. A preamble, and the actual data. Since we’re solving for as few complexities as possible (but no less!), I’ve taken a well-trodden strategy from existing protocols like WiFi, LTE, and WiMAX for generating a preamble. We’ll get more into the specifics later, but at a high level, pigeon uses the preamble for both burst detection and frequency correction. As for the rest of the burst – that’s our actual OFDM data.

The data section of the burst is where OFDM really kicks in, and since our
burst is always a fixed size, it is, similarly, made up of a fixed number of
OFDM “symbols”, which, in turn, carry data by
modulating data across subcarriers of many different
frequencies in parallel. Each OFDM data subcarrier is encoded with a defined
modulation scheme, such as
QPSK or
QAM-16.

Now, there’s a bit of additional work to make some of the above successfully exchange data (in practice), but, structurally, that’s really about it – It’s possibly even easier to generate OFDM data with an SDR than a conceptually simple encoding like PACKRAT. It’s worth planting the idea right now that everything else that we’re doing beyond sending raw symbols one after another is only there to help receivers correctly find or decode our data given an imperfect world.
If this all makes sense – great! – we’ll get into the tactical aspects of actually implementing this scheme through the rest of the posts. If it’s still confusing – that’s OK too! It does take a bit of time to conceptually round trip all the moving parts and structures involved. It may be worth re-reading the above a few times and trying to think about what you would do if you were implementing this yourself. If it’s any help, we’ll get more into the theory and mechanics of how to create and decode OFDM symbols in subsequent posts, but this is all a level-setting exercise to get everyone on the same high level mental model to maintain throughout this post.
I wrote up three "background" posts in an effort to explain some of the underlying concepts. If any of these sound like they're a thing that is still confusing, I'd love to invite a short diversion right around here.
Throughout this series -- at any time -- if you feel lost and that what I've got down in the post isn't quite enough, I found Dr. Marc Lichtman's PySDR tutorial to be very instructive. Pigeon has a number of implementation, design and conceptual differences with the PySDR approach, but I've found it to be incredibly complementary knowledge to work through pigeon's internals, and exceptionally well written. It's an outstanding resource.
One specific architectural note that bears repeating from above is that a pigeon burst is a fixed size. The number of symbols (and therefore: data subcarriers) carried by a burst do not depend on the amount of user-controlled data carried in the burst. The number of bits will always be the same, and the Layer 2 must (and does) include a mechanism to deal with fixed-length nature of the burst.
To avoid overwhelming everyone with mega-long posts, I’ve broken up my writeups of the PHY into a few posts due to the sprawling subject area. I would invite readers to follow the rough order listed below but i’m not your boss, do what you want.
If you are interested in taking my advice, the next post is:
Let’s talk about how to modulate data →
If you, like many of my radios, value nonlinearity, the full list of posts
about the PHY are listed below.