• Stars
    star
    212
  • Rank 186,122 (Top 4 %)
  • Language
    Jupyter Notebook
  • License
    Apache License 2.0
  • Created almost 4 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Bluetooth PHY based on one-bit input and output

A Bluetooth Low Energy Radio using FPGA SERDES: No ADC, AGC, filters, mixers, or amplifiers required.

This is a proof-of-concept Bluetooth receiver that can receive bluetooth (advertising) packets using an FPGA and an antenna, read: straight RF into a SERDES port sampling at 5Ghz. It's written in the nmigen HDL targeting FPGAs.

image

...why?

I wanted to prove to myself that I knew enough about RF to interface with a device I actually own (i.e. my phone), using a stack I wrote from scratch. After considering various protocols such as WiFi, LoRa, etc, I concluded that Bluetooth (Low Energy) would be easiest because:

  1. There's generally one dominant modulation type used (GMSK) as compared to a bajillion for the various 802.11/WiFi standards.
  2. In the default case, there's no forward error correction, so I wouldn't have to write a viterbi decoder or anything like it in hardware.
  3. I have multiple devices that can easily transmit or receive bluetooth packets.

I could use one of the many SDRs I own, combined with GNURadio to write a packet parser but SDRs generally have built-in (configurable) direct conversion front-ends which take care of much of the "hard" parts of building a radio (read: the analog bits, which are quite finnicky). Assembling a circuit that had all the necessary mixers, amplifiers, oscillators seemed like a lot of work. After reading 30+ papers and PhD theses I hypothesized I could do without all of this. To my surprise, this hypothesis was correct!

How do I run this?

You'll first need to check out this code and install the dependencies (I recommend inside a python 3 virtual environment)

pip install numpy scipy matplotlib jupyterlab
pip install git+https://github.com/nmigen/nmigen
pip install git+https://github.com/newhouseb/serialcommander
pip install git+https://github.com/newhouseb/alldigitalradio

Next, clone this repository.

Running on simulated hardware

In order to runn the full thing at a reasonable speed, I use iverilog to compile the design to C++ which runs infinitely faster than the built-in nmigen simulation. Assuming you have iverilog installed, running is as simple as:

> python -m onebitbt.radio virtual data/bt1bit.txt
[...tons of compilation removed...]
Reading ../data/bt1bit.txt
Argon
Simulation Complete!

Once this is done you'll have a (huge, 1.3GB) VCD file at build/sim.vcd that you can inspect. This command also prints out any bytes sent by the UART ("Argon" in this case which is received the name of the device).

image

Running on real hardware

If you have the specific board I've been using (a TE0714 with a TEBB0714 carrier with a TE0790-03 programmer) then assuming you have vivado somewhere in your path, run from this repos root directory:

python -m onebitbt.radio te0714

Next, if you connect to the built-in serial port on the programmer (at 115200 baud) you'll see the names of nearby advertising devices printed out.

I LOVE MINDY
I LOVE MINDY
[TV] Samsung 6 Series (65)
I LOVE MINDY

If you don't have anything advertising and you have an iPhone you can download an app called "BLE Scanner" that has an "Advertiser" tab that allows your phone to advertise a bunch of random things.

If you'd like to transmit, you can instead run

python -m onebitbt.advertiser te0714

And then use the aforementioned app to look for a device named "I LOVE MINDY." I can usually pick up signal close to the device with no antenna attached, but if you plan to attach an antenna please add a bandpass filter to avoid cluttering up the RF spectrum.

How does this work?

The chief realization here is that there are a large class of commonly used wireless protocols that encode their data in the phase/frequency of a radio wave, and not the amplitude. Modulation types that fall into this include: BPSK, QPSK, FSK, GMSK and others which are used in things like (low-end) Wi-Fi, bluetooth, LoRa. If you don't need to measure the amplitude, then all you need to measure is when the radio waveform crosses zero.

Second, high-speed communication between ICs/chips often use differential signalling, where rather than representing a one or zero as a voltage above or below a set threshold, a bit is sent by controlling a pair of wires. If one voltage is greater than the other, that may be a one bit. If it's the opposite, it may be a zero bit. In order to build this, chips must integrate a high-speed comparator. By grounding one of these wires, we can then use the other wire to measure whether or not a signal is above or below zero (ground).

The final realization is that the industry has realized it's easier (up to a point) to build one very high speed pair rather than many parallel wires when communicating between chips (largely because parallel signals will have crosstalk). So rather than having 20 wires at 250 Mhz, it's better to just do 1 pair at 5 Ghz. Once we're sending bits at 5Ghz... this is above the required speed to sample 2.4Ghz WiFi/Bluetooth and most other consumer (non 5G mmWave or 5G WiFi) radio signals (using the standard Nyquist criterion -- which doesn't always hold, but this is a story for another time...).

So if we can sample a waveform at 1-bit of precision at 5Ghz then we can do all the traditional radio bits (i.e. downconversion) digitally in an FPGA. Naturally, digitally mixing a signal at 5GHz sounds quite expensive, but when your signals have only 1 bit of precision, multiplication reduces down to basic boolean logic which can be quite efficient. We can use this to convert the signal down to baseband and then from there on out we're into pretty standard FPGA DSP territory.

But you don't have to read all this prose, to explain (some of this) I've written a number of python notebooks that walk through the "research"

  • Detection - (Covers) modulation and demodulation of the waveform to bits, as well as an evaluation of the demodulation performance versus more traditional methods.
  • Parsing - Dewhitening, parsing and checking the bits for correctness.

For non-Bluetooth specific radio building blocks, you can refer to the notebooks in the alldigitalradio repository:

FAQ

How good is this radio?

Honestly, it's not fantastic, but this is less because of the precision, and more because I'm demodulating GMSK as if it was FSK (because it was quicker/easier to implement).

image

  • Blue - Traditional demodulation (read: taking the derivative of the phase of the baseband signal)
  • Orange - Traditional demoduation of a signal rounded to -1 and 1
  • Green - FSK demodulation in simulated hardware
  • Red - FSK demodulation in numpy.

Once I implement CORDIC to estimate the phase, we should get performance closer to the more traditional implementations.

What about the transmitter?

I've built a transmitter as well, but the interest in the receive was far greater so I've started there. Will integrate the transmitter here in due time.

Can I run this on my hardware?

If you're using a Xilinx platform with a GTP SERDES block (read: Xilinx 7-Series Artix parts) then yes, without much pain. Other Xilinx parts should be doable, provided you initialize the SERDES transceiver using the right magic.

I wish so much that this would work on a Lattice platform but there's no easy way to get Lattice SERDES blocks to lock to a reference rather than the incoming data. By setting a register, you can theoretically tell it to lock to the clock reference, but realistically it just instead runs RX clocked by an unstable ring oscillator. Now that I've got this all working on Xilinx, perhaps I can get back to trying to get this to work on Lattice parts.

Why didn't you do the parsing in software?

That would be the long-term correct thing to do but I value a quick feedback loop and simplicity while debugging so it made sense to build a quick and dirty parser purely in gateware.

Something is incorrect!

Please let me know! Twitter (@newhouseb) or GitHub is fine. I've been engineering in a cave and have no professional experience in this space, so I'm sure there are errors in addition to random bugs.

System Diagram

                x     x
                 x   x
                  x x
                   β”‚
                   β”‚  Antenna
                   β”‚
                   β”‚
                   β”‚
                   β”‚
           Open    β”‚
             β”‚     β”‚
        β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
        β”‚   RX_N  RX_P    β”‚
        β”‚                 β”‚
        β”‚    SERDES RX    β”‚
        β”‚    @ 5 GSPS     β”‚
        β”‚                 β”‚
        β”‚       OUT       β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚ 20bits wide at 250MHz
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚           β”‚        Summing Mixer @ 2.40175 GHz
     β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  β”‚        β”‚                                   β”‚
     β”‚  β”‚        └────────┬────────┐                 β”‚
     β”‚  β”‚                 β”‚        β”‚                 β”‚
     β”‚  β”‚                 β”‚        β”‚                 β”‚
     β”‚  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”      β”‚        β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”   β”‚
     β”‚  β”‚   β”‚      β”‚    β”Œβ”€β”΄β”      β”Œβ”΄β”€β”    β”‚      β”‚   β”‚
     β”‚  β”‚   β”‚  ~   β”œβ”€β”€β”€β”€β”€X β”‚      β”‚X β”œβ”€β”€β”€β”€β”€  ~   β”‚   β”‚
     β”‚  β”‚   β”‚      β”‚    β””β”€β”¬β”˜      β””β”¬β”€β”˜    β”‚      β”‚   β”‚
     β”‚  β”‚   β””β”€β”€β”€β”€β”€β”€β”˜      β”‚        β”‚      β””β”€β”€β”€β”€β”€β”€β”˜   β”‚
     β”‚  β”‚  One Bit        β”‚        β”‚     One Bit     β”‚
     β”‚  β”‚  Oscillator     β”‚        β”‚     Oscillator  β”‚
     β”‚  β”‚  0deg phase     β”‚I      Qβ”‚     90deg phase β”‚
     β”‚  β”‚            β”Œβ”€β”€β”€β”€β”˜        └─────┐           β”‚
     β”‚  β”‚      β”Œβ”€β”€β”€β”€β”€β”€                   β”œβ”€β”€β”€β”€β”€β”     β”‚
     β”‚  β”‚      β”‚ +   β”‚   Running adders  β”‚ +   β”‚     β”‚
     β”‚  β”‚      β”‚     │◄──of last 4    ──►│     β”‚     β”‚
     β”‚  β”‚      β””β”€β”€β”¬β”€β”€β”˜   samples         β””β”€β”€β”¬β”€β”€β”˜     β”‚
     β”‚  β”‚         β”‚                         β”‚        β”‚
     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚            β”‚                         β”‚
     β”‚            β”‚                         └─────────────┐
     β”‚            β”‚                                       β”‚
     β”‚            └─────────────────────────────────────┐ β”‚
     β”‚                                                  β”‚ β”‚
     └───────────┐                                      β”‚ β”‚
                 β”‚                                      β”‚ β”‚
                 β”‚        Summing Mixer @ 2.40225 GHz   β”‚ β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
        β”‚        β”‚                                   β”‚  β”‚ β”‚
        β”‚        └────────┬────────┐                 β”‚  β”‚ β”‚
        β”‚                 β”‚        β”‚                 β”‚  β”‚ β”‚
        β”‚                 β”‚        β”‚                 β”‚  β”‚ β”‚
        β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”      β”‚        β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚
        β”‚   β”‚      β”‚    β”Œβ”€β”΄β”      β”Œβ”΄β”€β”    β”‚      β”‚   β”‚  β”‚ β”‚
        β”‚   β”‚  ~   β”œβ”€β”€β”€β”€β”€X β”‚      β”‚X β”œβ”€β”€β”€β”€β”€  ~   β”‚   β”‚  β”‚ β”‚
        β”‚   β”‚      β”‚    β””β”€β”¬β”˜      β””β”¬β”€β”˜    β”‚      β”‚   β”‚  β”‚ β”‚
        β”‚   β””β”€β”€β”€β”€β”€β”€β”˜      β”‚        β”‚      β””β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚
        β”‚  One Bit        β”‚        β”‚     One Bit     β”‚  β”‚ β”‚
        β”‚  Oscillator     β”‚        β”‚     Oscillator  β”‚  β”‚ β”‚
        β”‚  0deg phase     β”‚I      Qβ”‚     90deg phase β”‚  β”‚ β”‚
        β”‚            β”Œβ”€β”€β”€β”€β”˜        └─────┐           β”‚  β”‚ β”‚
        β”‚      β”Œβ”€β”€β”€β”€β”€β”€                   β”œβ”€β”€β”€β”€β”€β”     β”‚  β”‚ β”‚
        β”‚      β”‚ +   β”‚   Running adders  β”‚ +   β”‚     β”‚  β”‚ β”‚
        β”‚      β”‚     │◄──of last 4    ──►│     β”‚     β”‚  β”‚ β”‚
        β”‚      β””β”€β”€β”¬β”€β”€β”˜   samples         β””β”€β”€β”¬β”€β”€β”˜     β”‚  β”‚ β”‚
        β”‚         β”‚                         β”‚        β”‚  β”‚ β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
                  β”‚                         β”‚           β”‚ β”‚
              β”Œβ”€β”€β”€β”˜   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚ β”‚
              β”‚       β”‚                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
              β”‚       β”‚                  β”‚                β”‚
              β”‚       |   @ 62.5MHz      β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚       β”‚                  β”‚      β”‚
           β”Œβ”€β”€β”΄β”€β”  β”Œβ”€β”€β”΄β”€β” 64 sample   β”Œβ”€β”€β”΄β”€β”  β”Œβ”€β”΄β”€β”€β”
           β”‚ +  β”‚  β”‚ +  β”‚ wide boxcar β”‚ +  β”‚  β”‚ +  β”‚
           β”‚    β”‚  β”‚    β”‚ filters     β”‚    β”‚  β”‚    β”‚
           β””β”€β”€β”¬β”€β”˜  β””β”€β”€β”¬β”€β”˜             β””β”€β”€β”¬β”€β”˜  β””β”€β”¬β”€β”€β”˜
              β”‚       β”‚                  β”‚      β”‚
              β”‚       β”‚                  β”‚      β”‚
              └┐    β”Œβ”€β”˜                  └┐    β”Œβ”˜
               β”‚    β”‚                     β”‚    β”‚
             β”Œβ”€β”΄β”€β”€β”€β”€β”΄β”€β”                 β”Œβ”€β”΄β”€β”€β”€β”€β”΄β”€β”
             β”‚        β”‚                 β”‚        β”‚
             β”‚        β”‚  Magnitude      β”‚        β”‚
             β”‚  |s|   β”‚  Approximators  β”‚   |s|  β”‚
             β”‚        β”‚                 β”‚        β”‚
             β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                 β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
                 └────────────┐  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚  β”‚
                            β”Œβ”€β”΄β”€β”€β”΄β”€β”
                            β”‚      β”‚
                            β”‚  <   β”‚  Comparator
                            β”‚      β”‚
                            β””β”€β”€β”¬β”€β”€β”€β”˜
                               β”‚
                               β”‚
                               β”‚    Packet State Machine
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚                   β”‚                 β”‚
           β”‚                β”Œβ”€β”€β”΄β”€β”€β”              β”‚
           β”‚    Preamble    β”‚     β”‚              β”‚
           β”‚    Matching &  β”‚  =  β”‚              β”‚
           β”‚    Symbol Sync β”‚     β”‚              β”‚
           β”‚                β””β”€β”€β”¬β”€β”€β”˜              β”‚
           β”‚                   β”‚@1MHz            β”‚
           β”‚                β”Œβ”€β”€β”΄β”€β”€β”              β”‚
           β”‚    Dewhitening β”‚     β”‚              β”‚
           β”‚    LFSR        β”‚  ^  β”‚              β”‚
           β”‚                β”‚     β”‚              β”‚
           β”‚                β””β”€β”€β”¬β”€β”€β”˜              β”‚
           β”‚       β”Œβ”€β”€β”€β”€β”€β”     β”‚    β”Œβ”€β”€β”€β”€β”€β”      β”‚
           β”‚       β”‚     β”œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€     β”‚      β”‚
           β”‚       β”‚CRC  β”‚          β”‚BRAM β”‚      β”‚
           β”‚       β”‚     β”‚          β”‚     β”‚      β”‚
           β”‚       β””β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”¬β”€β”€β”˜      β”‚
           β”‚                           β”‚         β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚
                                   β”Œβ”€β”€β”€β”΄β”€β”€β”€β”€β”
                                   β”‚        β”‚
                                   β”‚ UART   β”‚
                                   β”‚        β”‚
                                   β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
                                       β”‚
                                       β”‚

                                    Output

Credits & Citations

More Repositories

1

clownfish

Constrained Decoding for LLMs against JSON Schema
Python
317
star
2

alldigitalradio

All Digital Radio Platform written in nmigen targeting FPGAs (for now)
Jupyter Notebook
78
star
3

simpleotp

Simple (T)OTP Server for Nginx Auth
Python
44
star
4

potatogpt

Pure Typescript, dependency free, ridiculously slow implementation of GPT2 for educational purposes
TypeScript
42
star
5

nginxwebauthn

Easily add (single user) webauthn in front of an arbitrary nginx host.
Python
38
star
6

SCPD-Scraper

Downloads class videos, converts them to iphone format
Python
24
star
7

linuxholoplayjs

Holoplay.js compatible server for Linux (and probably other OSes)
Go
10
star
8

zhread

chinese epub reader
JavaScript
8
star
9

handcrafted

Hand Crafted Transformers
Jupyter Notebook
8
star
10

serialcommander

Handy utilities to expose data and toggle signals in an nmigen FPGA design.
Python
8
star
11

nls

Notational ls
Python
5
star
12

openvr-c

A C binding to OpenVR that actually works
C
4
star
13

MobileSafariFinder

Bookmarklet for Mobile Safari to enable finding text in a web page
JavaScript
3
star
14

cppwrap

Tool to generate C wrappers for C++ code
Python
3
star
15

MatTex

Inline Matlab in LaTeX Documents
Python
2
star
16

SWS

Simple Website Schema
PHP
2
star
17

newhouseb.github.com

THE WEBSITE
HTML
1
star
18

pocketbook

HTML
1
star
19

vickiandthecsounds

Awful Synthesized Music
Python
1
star
20

lgoven

Self-contained web-page designed to bake holograms for the Looking Glass from video pans.
HTML
1
star
21

Noid

Notify.io Client For Android
Java
1
star
22

river

Simple tornado based web service that watches a directory for images from skitch (for tracking designer progress) and presents a git commit hook (for tracking developer progress)
Python
1
star
23

turmeric

A python layer over spice that doesn't require fiddling with inscrutable GUIs or programming in FORTRAN-era raw spice.
Jupyter Notebook
1
star
24

talktoomuch

chrome extension/bookmarklet that generated stats about your gchat sessions (ie. tells you if you're out talking the other person)
JavaScript
1
star
25

screenbytes

Boilerplate C code for byte-level image manipulation (with libjpeg and X11)
C
1
star