A polyphase channelizer implementation in VHDL for the AMSAT-UK FunCube+ Mode Dynamic Transponder (MDT), designed for spectrum monitoring and SIC (Successive Interference Cancellation) signal processing.
This project implements a polyphase channelizer that splits a wideband input signal into multiple narrowband frequency channels. The design supports two configurations:
| Configuration | Channels | Sample Rate | Target | Application |
|---|---|---|---|---|
| MDT | 4 | 40 ksps | iCE40 UltraPlus | FunCube+ transponder monitoring |
| Haifuraiya | 64 | 10 Msps | ZCU102 | Opulent Voice FDMA |
A polyphase channelizer efficiently splits a wideband signal into N frequency channels using:
- Polyphase Filterbank — N parallel FIR filters, each processing every Nth sample
- FFT — Converts filtered outputs to frequency domain
This is computationally efficient compared to running N separate bandpass filters, and is widely used in software-defined radio, satellite communications, and spectrum analyzers.
┌─────────────────────────────────────────────┐
│ Polyphase Channelizer │
│ │
Wideband ───────►│ ┌─────────────┐ ┌─────┐ │
Input │ │ Polyphase │ │ │ Channel 0 ──►│
│ │ Filterbank │─────►│ FFT │ Channel 1 ──►│
│ │ (N branches)│ │ │ Channel 2 ──►│
│ └─────────────┘ └─────┘ ... ──►│
│ │
└─────────────────────────────────────────────┘
The SIC (Successive Interference Cancellation) receiver splits processing between FPGA and MCU:
┌─────────────────────────────────────────────────────────────────────────┐
│ SIC Receiver System │
│ │
│ ┌─────────────────────────────────────────┐ ┌───────────────────┐ │
│ │ iCE40 FPGA │ │ STM32H7 │ │
│ │ │ │ │ │
│ │ I2S ──► Polyphase ──► FFT ──► SPI ─────────► Magnitude ──► SIC │ │
│ │ ADC Filterbank 4pt Slave │ │ Computation Algo │ │
│ │ │ │ │ │
│ └─────────────────────────────────────────┘ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
The FPGA handles real-time DSP (channelization), while the STM32 handles post-processing (magnitude, peak detection, SIC algorithm). This split keeps the FPGA within the 0.5W power budget for CubeSat deployment.
Mode-Dynamic-Transponder/
├── README.md # This file
├── docs/ # Documentation and reference models
│ ├── polyphase_channelizer.ipynb # Python reference implementation
│ └── mdt-model.ipynb # Store-and-forward simulation
├── rtl/ # Synthesizable VHDL
│ ├── README.md
│ ├── pkg/ # Packages
│ │ └── channelizer_pkg.vhd
│ ├── channelizer/ # Core modules
│ │ ├── coeff_rom.vhd
│ │ ├── delay_line.vhd
│ │ ├── mac.vhd
│ │ ├── fir_branch.vhd
│ │ ├── polyphase_filterbank.vhd
│ │ ├── fft_4pt.vhd
│ │ ├── fft_64pt.vhd
│ │ └── polyphase_channelizer_top.vhd
│ └── coeffs/ # Filter coefficients
│ ├── mdt_coeffs.hex
│ └── haifuraiya_coeffs.hex
├── sim/ # Simulation
│ ├── README.md
│ ├── run_tests.tcl
│ └── tb_*.vhd
├── syn/ # Synthesis
│ ├── ice40/ # iCE40 UltraPlus (Lattice Radiant)
│ │ ├── sic_top_ice40.vhd # iCE40 top wrapper (SPI, LEDs, synchronizers)
│ │ ├── TOOLCHAIN_SETUP.md # Radiant + MSYS2/openFPGALoader setup
│ │ ├── WIRING_GUIDE.md # Hardware connections (read this first!)
│ │ └── README.md
│ ├── radiant/ # Lattice Radiant project
│ │ └── sic_receiver/
│ │ ├── sic_receiver.rdf # Radiant project file
│ │ └── source/impl_1/sic_top.pdc # Pin constraints
│ └── zcu102/ # Xilinx ZCU102 (future)
│ └── README.md
└── firmware/ # MCU firmware
└── stm32/
├── sic_receiver/ # Complete STM32CubeIDE project
│ ├── sic_receiver.ioc # CubeMX config (SPI4, GPIO, clocks)
│ ├── Core/Src/ # User code (main.c, sic_fpga.c, spi.c)
│ ├── Core/Inc/ # Headers (sic_fpga.h)
│ └── Drivers/ # STM32H7 HAL (self-contained build)
├── HARDWARE_BRINGUP.md # Step-by-step bringup checklist
└── STM32_SETUP_GUIDE.md # STM32CubeIDE setup guide
| Item | Part Number | Cost |
|---|---|---|
| FPGA Board | Lattice iCE40UP5K-B-EVN | ~$50 |
| MCU Board | STM32 NUCLEO-H753ZI | ~$70 |
| Jumper wires | Female-Female Dupont | ~$5 |
See syn/ice40/WIRING_GUIDE.md for complete connection details.
The FPGA is programmed using Lattice Radiant for synthesis and openFPGALoader via MSYS2 for flashing. See syn/ice40/TOOLCHAIN_SETUP.md for full setup.
# In MSYS2 UCRT64 (after running Zadig to set WinUSB driver):
openFPGALoader -b ice40_generic -f --unprotect-flash \
/c/Mode-Dynamic-Transponder/syn/radiant/sic_receiver/impl_1/sic_receiver_impl_1.binThe firmware is a complete STM32CubeIDE project stored in firmware/stm32/sic_receiver/.
- Open STM32CubeIDE
- File → Import → General → Existing Projects into Workspace
- Browse to
firmware/stm32/sic_receiver/ - Uncheck "Copy projects into workspace"
- Build and flash
# In Vivado TCL console:
cd /path/to/Mode-Dynamic-Transponder
source sim/run_tests.tcl
create_sim_project channelizer_sim
run_all_testscd docs
jupyter notebook polyphase_channelizer.ipynb
# Run all cells — generates rtl/coeffs/*.hex filespolyphase_channelizer_top
├── coeff_rom # Filter coefficients (Block RAM)
├── polyphase_filterbank # N parallel FIR branches
│ └── fir_branch (×N)
│ ├── delay_line # Sample history (shift register)
│ └── mac # Multiply-accumulate
└── fft_4pt / fft_64pt # Frequency separation
The FPGA is a SPI slave (Mode 0). The STM32 reads 17 bytes per transaction:
TX: [CMD=0x01] [0x00] [0x00] ... (17 bytes)
RX: [0x00] [I0_H][I0_L][Q0_H][Q0_L][I1_H][I1_L][Q1_H][Q1_L]
[I2_H][I2_L][Q2_H][Q2_L][I3_H][I3_L][Q3_H][Q3_L]
| Resource | Used | Available | % |
|---|---|---|---|
| LUTs | ~4,850 | 5,280 | 92% |
| DSPs | 4 | 8 | 50% |
| Fmax | 39 MHz | — | target: 12 MHz ✓ |
Power Budget: The iCE40UP5K was chosen for its low power (~5mW typical). The signal processing subsystem must fit within 0.5W for 2U CubeSat deployment.
Magnitude Computation: Moved from FPGA to STM32 to save LUTs. The STM32H7's FPU computes sqrt(I² + Q²) faster than FPGA LUT-based approximations.
SPI Synchronizers: All SPI inputs use 2-stage flip-flop synchronizers before entering FPGA logic, preventing metastability at the 12 MHz / 1 MHz clock domain crossing.
Filter Design: Uses Daniel Estévez's pm-remez library for prototype filter design, following fred harris's 1/f stopband weighting recommendation.
- Python reference model with filter design (pm-remez)
- Coefficient generation and export
- All RTL modules (coeff_rom, delay_line, mac, fir_branch, polyphase_filterbank, fft_4pt, fft_64pt, top)
- Testbenches for all modules (7/7 passing)
- Vivado simulation scripts
- Synthesize for iCE40 UltraPlus — 92% LUT, 4 DSPs, 39 MHz Fmax
- Lattice Radiant project with PDC pin constraints
- STM32CubeIDE project with SPI4 driver (sic_fpga.c/h)
- Hardware bringup complete — SPI link verified, real channelizer data flowing
- Full I/Q SPI protocol (4 channels × I + Q, 17 bytes/transfer)
- 2-stage input synchronizers for metastability protection
- Documentation (WIRING_GUIDE, TOOLCHAIN_SETUP, HARDWARE_BRINGUP)
- Complex I/Q channelizer — polyphase_channelizer_top currently processes real part only (imaginary input hardwired to zero)
- Complex I/Q filterbank — process both sample_re and sample_im
- I2S ADC integration — replace test pattern with TLV320ADC6120 input
- SIC algorithm on STM32 — peak detection, reconstruction, subtraction
- Synthesize for ZCU102 (Haifuraiya / Opulent Voice)
- Integration with FunCube+ receiver
- Integration with Opulent Voice modem
| Document | Location | Description |
|---|---|---|
| Wiring Guide | syn/ice40/WIRING_GUIDE.md |
Hardware connections, pin mapping, programming |
| Toolchain Setup | syn/ice40/TOOLCHAIN_SETUP.md |
Radiant, MSYS2, openFPGALoader setup |
| Hardware Bringup | firmware/stm32/HARDWARE_BRINGUP.md |
Step-by-step bringup checklist |
| STM32 Setup | firmware/stm32/STM32_SETUP_GUIDE.md |
STM32CubeIDE project guide |
| RTL Reference | rtl/README.md |
Module descriptions and timing |
| Python Reference | docs/polyphase_channelizer.ipynb |
Filter design and visualization |
- Martin Ling's dynamic-transponder — Hardware reference design
- Opulent Voice Protocol — Digital voice protocol
- AMSAT-UK FunCube+
- ORI Haifuraiya
This is an Open Research Institute project. Contributions are welcome!
- Fork the repository
- Create a feature branch
- Submit a pull request
- Have fun!
For questions or discussion, join the ORI community channels
This project is open source, using CERN OHL 2.0 and other OSI approved licenses.
- Open Research Institute engineering team
- AMSAT-UK community for FunCube documentation, participation, and leadership
- David Bowman G0MRF for the MDT concept
- Martin Ling for hardware design reference
- Daniel Estévez EA4GPZ for the pm-remez library
- F5OEO for filter design feedback
Open Research Institute — Advancing open source digital radio for space and terrestrial use