Paul Tagliamonte: Mode A (Part 9/12)
If you're looking for it, the intro to the PHY (layer 1) that this belongs at a high level and listing of the parts that make it up (including this one) is on the phy post.
| Attribute | Value | Description |
| Rate | 2.5 MHz | Sampling Rate / Bandwidth |
| LCG | 3149721335 | LCG "RNG" whitening constant (randomly selected) |
| Preamble | seq=16, order=4, count=2 | (this is as-written in the preamble post) |
| Modulation | QPSK/QAM-4 | 2 bits per data subcarrier |
| FFT Size | 64 | |
| Cyc Len | 16 | Cyclic Prefix length (16 IQ samples) |
| Symbols | 168 | Number of OFDM Symbols |
| LDPC Table | 802.3an | (this is as-written in the ldpc post) |
| Raw Bits | 14448 | 1806 bytes (168 symbols, 86 data bits per symbol) |
| LDPC Count | 7 | Number of packed LDPC encoded messages |
| Data Bits | 12061 | 1507 bytes |
Guard Bins
First up, let s place our guard bins.
As we ve already gone over, we re looking to clear some space right up against
the high and low end of the frequency range, so let s go ahead and do that:
I gave up the center bin (0 Hz) and 8 of the 64 bits on each side (1/4 of the
signal!) to give myself a bit of elbow room. This is perhaps definitely a bit
overkill, but it s been an extremely robust choice. If you multiply that
through, this accounts for 312.5 kHz of frequency domain padding at the high
and low end of the bandwidth, or 625.0 kHz of bandwidth which is not to be used.
Pilot Bins
Next up was the pilot bins. We ve already gone
over the purpose (and
use) of our pilots, but I ve found there to be an art
to the placement of the pilots. Interpolation between pilot bins has turned out
to be very reliable, but extrapolation, on the other hand, has been a major
pain, for reasons I don t fully understand yet.
My intent in placement was to pick out roughly even stretches of data bins
bracketed between pilots, with as few data subcarriers as practical outside
of a pilot (using extrapolation). I ve played a bit with my AGWN simulator(s),
as well as logging errors between two SDRs, and the configuration I have this
in has been fairly resillant (for whatever reason), and withstood a few rounds
of tweaking.
Data Bins
Almost as an afterthought all of the remaining bins become
data bins.
This puts the total number of data bins at 43, which, since Mode A carries data
in QPSK/QAM-4 (two bits per data subcarrier), means we can carry 86 bits of
data per OFDM symbol. That fairly modest capacity is largely due to the
modulation scheme (or fft size, but increasing that has been fraught) we re
using for Mode A but I ve made up for it by including 168 OFDM symbols
in a single burst in order to have enough data to carry IP traffic without
splitting the packet into two bursts.
With all that designed and on paper, we re ready to start to tackle the next
layer up our Layer 2, named, creatively, link .
Let s send some link layer data
Following along at home? Nice! As promised, I've put the full plan
copy-pasted from the pigeon source tree below so that no one has to feel
the need to transcribe this from the images above. Unlike most of the things
I've "left to the reader", manual transcription from images is not a
particularly useful task for anyone to do.
The following table is Mode A s OFDM Subcarrier Plan. This is in negative
first ordering (meaning the 0th member is the most negative fft bin,
and the Nth is the highest frequency fft bin).
SubcarrierPlan([
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Data,
Data,
Data,
Pilot(iq!(-1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Guard,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, -1.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, 1.0)),
Data,
Data,
Data,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
])
Still following along at home? Nicer! I've put the
preamble sequence
promised previously
copy-pasted from the pigeon source tree below in case that's actually
important (I don't think it is, but...).
The following table is Mode A s frequency-domain preamble. This is, as above,
in negative first ordering. I don t actually think these values matter much
(at all)? but in case they do, here s what I have. I muck with these a lot
and haven t found many changes in quality of detection or frequency correction
yet.
[
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
IQ::polar((TAU / 13.0) * 2.0, 1.0),
IQ::polar((TAU / 13.0) * 3.0, 1.0),
IQ::polar((TAU / 13.0) * 4.0, 1.0),
IQ::polar((TAU / 13.0) * 5.0, 1.0),
IQ::polar((TAU / 13.0) * 6.0, 1.0),
IQ::polar((TAU / 13.0) * 7.0, 1.0),
IQ::polar((TAU / 13.0) * 8.0, 1.0),
IQ::polar((TAU / 13.0) * 9.0, 1.0),
IQ::polar((TAU / 13.0) * 10.0, 1.0),
IQ::polar((TAU / 13.0) * 11.0, 1.0),
IQ::polar((TAU / 13.0) * 12.0, 1.0),
IQ::polar((TAU / 13.0) * 13.0, 1.0),
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
]
Let s send some link layer data
My intent in placement was to pick out roughly even stretches of data bins
bracketed between pilots, with as few data subcarriers as practical outside
of a pilot (using extrapolation). I ve played a bit with my AGWN simulator(s),
as well as logging errors between two SDRs, and the configuration I have this
in has been fairly resillant (for whatever reason), and withstood a few rounds
of tweaking.
Data Bins
Almost as an afterthought all of the remaining bins become
data bins.
This puts the total number of data bins at 43, which, since Mode A carries data
in QPSK/QAM-4 (two bits per data subcarrier), means we can carry 86 bits of
data per OFDM symbol. That fairly modest capacity is largely due to the
modulation scheme (or fft size, but increasing that has been fraught) we re
using for Mode A but I ve made up for it by including 168 OFDM symbols
in a single burst in order to have enough data to carry IP traffic without
splitting the packet into two bursts.
With all that designed and on paper, we re ready to start to tackle the next
layer up our Layer 2, named, creatively, link .
Let s send some link layer data
Following along at home? Nice! As promised, I've put the full plan
copy-pasted from the pigeon source tree below so that no one has to feel
the need to transcribe this from the images above. Unlike most of the things
I've "left to the reader", manual transcription from images is not a
particularly useful task for anyone to do.
The following table is Mode A s OFDM Subcarrier Plan. This is in negative
first ordering (meaning the 0th member is the most negative fft bin,
and the Nth is the highest frequency fft bin).
SubcarrierPlan([
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Data,
Data,
Data,
Pilot(iq!(-1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Guard,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, -1.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, 1.0)),
Data,
Data,
Data,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
])
Still following along at home? Nicer! I've put the
preamble sequence
promised previously
copy-pasted from the pigeon source tree below in case that's actually
important (I don't think it is, but...).
The following table is Mode A s frequency-domain preamble. This is, as above,
in negative first ordering. I don t actually think these values matter much
(at all)? but in case they do, here s what I have. I muck with these a lot
and haven t found many changes in quality of detection or frequency correction
yet.
[
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
IQ::polar((TAU / 13.0) * 2.0, 1.0),
IQ::polar((TAU / 13.0) * 3.0, 1.0),
IQ::polar((TAU / 13.0) * 4.0, 1.0),
IQ::polar((TAU / 13.0) * 5.0, 1.0),
IQ::polar((TAU / 13.0) * 6.0, 1.0),
IQ::polar((TAU / 13.0) * 7.0, 1.0),
IQ::polar((TAU / 13.0) * 8.0, 1.0),
IQ::polar((TAU / 13.0) * 9.0, 1.0),
IQ::polar((TAU / 13.0) * 10.0, 1.0),
IQ::polar((TAU / 13.0) * 11.0, 1.0),
IQ::polar((TAU / 13.0) * 12.0, 1.0),
IQ::polar((TAU / 13.0) * 13.0, 1.0),
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
]
Let s send some link layer data
SubcarrierPlan([
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Data,
Data,
Data,
Pilot(iq!(-1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(1.0, 0.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Guard,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, -1.0)),
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Data,
Pilot(iq!(0.0, 1.0)),
Data,
Data,
Data,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
Guard,
])
[
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
IQ::polar((TAU / 13.0) * 2.0, 1.0),
IQ::polar((TAU / 13.0) * 3.0, 1.0),
IQ::polar((TAU / 13.0) * 4.0, 1.0),
IQ::polar((TAU / 13.0) * 5.0, 1.0),
IQ::polar((TAU / 13.0) * 6.0, 1.0),
IQ::polar((TAU / 13.0) * 7.0, 1.0),
IQ::polar((TAU / 13.0) * 8.0, 1.0),
IQ::polar((TAU / 13.0) * 9.0, 1.0),
IQ::polar((TAU / 13.0) * 10.0, 1.0),
IQ::polar((TAU / 13.0) * 11.0, 1.0),
IQ::polar((TAU / 13.0) * 12.0, 1.0),
IQ::polar((TAU / 13.0) * 13.0, 1.0),
IQ::new(0.0, 0.0),
IQ::new(0.0, 0.0),
]
While the process here can look like a lot, it s really not that bad.
We begin by taking the incoming bytes, converting the bytes into bits, and chunk
those bits into parts which are sized to fit completely within an LDPC message. We will then
Here, we start with an incoming stream of IQ, where we will process
Not shockingly, the code looks like this:
As an aside, I likely should have called this a
The following contributor got their Debian Developer account in the last two
months:
My Debian contributions this month were all
The 


One of those arguments became its own fix. My wording implied symbols were
unpacked inside the isolated sbuild environment (the consequence was I was
handed a
Every merge request above landed with unit tests, but those only tell you
that the pieces behave correctly. What Colin and I wanted was a real gdb
fetching real symbols from a real instance, so
Another minor maintenance release of the
Sharing source code from host to guest VM
Finally we need to make sure we have access in the dev-vm to our source code repositories.
For this I will share the directory
Finally we mount the shared directory, and enable the mount on each boot.