The goal of this project is to build a system that can listen to the radio frequency spectrum in real time and automatically identify what type of signal it is hearing,without any human intervention. Given a raw stream of IQ samples from a software-defined radio dongle, the system should classify the signal into one of several known categories with high confidence.
This sits at the intersection of signal processing and deep learning. Rather than hand-crafting features from the RF spectrum, we let convolutional neural networks learn directly from the data, either from the raw time-domain waveform or from a visual time-frequency representation of it.
The system supports both offline inference on pre-recorded .npy captures and live classification using an SDR.
The project includes two CNN-based approaches:
- 1D SignalCNN: learns directly from raw IQ waveforms.
- 2D SpectrogramCNN: converts IQ windows into spectrograms and learns time-frequency patterns.
The overall workflow of the project is illustrated below:
The overall pipeline is:
- An RTL-SDR captures raw IQ samples at a selected center frequency.
- The IQ samples are normalized and divided into windows.
- The samples are converted into the representation required by the selected model.
- A CNN predicts the signal class.
- The prediction is returned with a confidence score.
The models can also be used with previously captured .npy files, so an SDR is not required for offline inference.
| Class | Description |
|---|---|
| ADS_B | Aircraft transponder signals around 1090 MHz |
| FM_broadcast | Commercial FM radio signals |
| ISM_sensors | Signals from ISM-band devices, including 433 MHz systems |
| noise | Background RF noise / no target signal |
The dataset utlized was the TrevTron/rtl-ml-dataset along with simulations generated on Simulink and signals collected using an RTL-SDR dongle across multiple capture sessions. Each recording is stored as a .npy file containing a dict with the raw complex IQ samples and capture metadata (center frequency, sample rate, timestamp, label).
data/datasets_validated/
├── ADS_B/ — 160 recordings
├── FM_broadcast/ — 160 recordings (multiple FM stations: 91.1, 93.5, 95.0, 98.3, 104.8, 106.4 MHz)
├── ISM_sensors/ — 160 recordings
└── noise/ — 160 recordings (captured across 50, 300, 470, 800, 1200 MHz)
All recordings are split stratified per class: 70% train / 10% validation / 20% test. The split is done at the recording level so windows from the same capture never appear in both train and test.
The 1D model operates directly on the raw IQ signal.
Each 2048-sample window is represented using two channels:
- I: in-phase component
- Q: quadrature component
Input shape:
[B, 2, 2048]
The model uses a series of Conv1d blocks with BatchNorm, ReLU, and MaxPool layers, followed by adaptive average pooling, dropout, and a linear classifier.
The 2D model converts each 2048-sample IQ window into a 128 × 128 spectrogram.
Input shape:
[B, 1, 128, 128]
The spectrogram is generated using an STFT with:
- 256-sample Hann window
- 50% overlap
- log compression
- min-max normalization
- resizing to
128 × 128
The CNN uses multiple Conv2d blocks with BatchNorm, ReLU, and MaxPool layers, followed by adaptive average pooling, dropout, and a linear classifier.
Spectrograms are generated during data loading rather than storing the entire spectrogram dataset as .npy files. This keeps disk and RAM usage much lower.
A spectrogram represents the signal in the time-frequency domain:
- X-axis: time
- Y-axis: frequency
- Brightness: signal magnitude/power
Typical signal patterns include:
- ADS-B: short bursts
- FM broadcast: continuous wide-band structure
- ISM sensors: narrow/intermittent transmissions
- Noise: diffuse and less structured RF energy
Example spectrograms can be generated locally using the project's spectrogram utilities.
| ADS_B | FM_broadcast | ISM_sensors | noise |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
The SDR used for this project was:
Nooelec NESDR SMArt v5 SDR - HF/VHF/UHF (100 kHz–1.75 GHz) RTL-SDR, RTL2832U & R820T2-Based Software Defined Radio
The SDR is used to capture raw IQ samples from the RF environment.
The capture pipeline supports:
- configurable center frequency
- configurable sample count
- configurable sample rate
- automatic gain
- saving captures as
.npyfiles
The default sample rate used in the project is:
1,024,000 samples/second
The RTL-SDR dongle is the hardware interface between the physical RF environment and the software pipeline. It is a low-cost USB receiver that can tune across roughly 24 MHz to 1.7 GHz and stream raw IQ samples to the host machine.
live_capture.py handles all SDR interaction:
- Configures center frequency, sample rate (default 1,024,000 Hz), and gain
- Reads IQ samples in chunks to avoid USB buffer overflows
- Wraps captures in a metadata dict and saves as
.npy— the exact same format as the training dataset
You can use your own SDR recordings to test the trained models.
The project also includes a live_capture.py script that allows users with a compatible RTL-SDR to capture raw IQ samples directly from the RF environment.
The workflow is:
- Connect the RTL-SDR to the system.
- Specify the center frequency and number of samples to capture.
- Capture raw IQ samples from the selected frequency.
- Save the capture as a
.npyfile for later use. - Create a
predict_samplesdirectory in your local project setup - Place the captured file in the
predict_samples/directory to run offline inference using either the 1D or 2D model. - Alternatively, use the live classifier to perform classification directly from the SDR.
For example:
python src/live_capture.py 1090000000 --output predict_samples/live.npyIf you have a compatible RTL-SDR, you can also use the live classifier.
The live workflow is:
SDR → IQ samples → preprocessing → trained CNN → predicted class + confidence
The live classifier requires:
- a connected RTL-SDR
- a center frequency
- a trained model checkpoint
For example, the classifier can be run by providing the center frequency and model to the live-classification script.
Refer to the command-line arguments in the Run live SDR classification section below.
The 1D model was evaluated on the four-class test set.
| Metric | Score |
|---|---|
| Test Accuracy | 99.95% |
| Macro Precision | 99.95% |
| Macro Recall | 99.95% |
| Macro F1 | 99.95% |
| Test Loss | 0.0029 |
precision recall f1-score support
ADS_B 1.00 1.00 1.00 2111
FM_broadcast 1.00 1.00 1.00 2000
ISM_sensors 1.00 1.00 1.00 2298
noise 1.00 1.00 1.00 1848
accuracy 1.00 8257
The 2D model was evaluated on 31,443 test windows.
| Metric | Score |
|---|---|
| Test Accuracy | 93.2% |
| Precision | 93.7% |
| Recall | 93.1% |
| F1-score | 93.1% |
| Test Loss | 0.16 |
precision recall f1-score support
ADS_B 0.83 0.95 0.89 8000
FM_broadcast 0.98 1.00 0.99 7598
ISM_sensors 0.99 1.00 0.99 8149
noise 0.95 0.78 0.86 7696
accuracy 0.93 31443
macro avg 0.94 0.93 0.93 31443
weighted avg 0.94 0.93 0.93 31443
| 1D SignalCNN | 2D SpectrogramCNN | |
|---|---|---|
| Representation | Raw IQ waveform | STFT spectrogram |
| Input | [B, 2, 2048] |
[B, 1, 128, 128] |
| Test Accuracy | 99.95% | 93.2% |
| Macro F1 | 99.95% | 93.1% |
| Test Loss | 0.0029 | 0.16 |
| Main advantage | High classification accuracy | Time-frequency representation |
| Main trade-off | Raw waveform representation | Higher preprocessing cost |
The 1D model achieved higher test accuracy on its four-class problem. The 2D model provides a time-frequency representation that makes signal structure easier to visualize and interpret.
| Component | Library / Tool |
|---|---|
| Deep learning | PyTorch |
| Signal processing | SciPy |
| Numerical computing | NumPy |
| Evaluation | scikit-learn |
| Visualization | Matplotlib, Seaborn |
| SDR interface | pyrtlsdr |
| Notebooks | Jupyter / ipykernel |
| Progress bars | tqdm |
| Python | 3.11+ |
| SDR | Nooelec NESDR SMArt v5 |
git clone https://github.com/Ishita-190/RF-Signal-Classification-using-Deep-Learning.git
cd RF-Signal-Classification-using-Deep-LearningWindows:
python -m venv .venv
.venv\Scripts\activateLinux/macOS:
python3 -m venv .venv
source .venv/bin/activatepip install -r requirements.txtPlace the required dataset files in the expected local dataset directory.
The repository intentionally does not include the full dataset or trained model checkpoints.
The project includes two Jupyter notebooks, one for each model: experiements_1d.ipynb and experiements_2d.ipynb. Each notebook runs the complete pipeline from the raw dataset to model evaluation.
The overall process in each notebook is:
-
Load the dataset and map the RF recordings to their corresponding signal classes.
-
Preprocess the raw IQ samples according to the requirements of the selected model.
-
Split the dataset into stratified training, validation, and test sets.
-
Prepare the model inputs:
- 1D model: normalized I/Q windows.
- 2D model: 128×128 spectrograms generated from the IQ windows using STFT.
-
Initialize and train the CNN, while tracking training and validation loss and accuracy.
-
Evaluate the trained model on the unseen test set.
-
Calculate evaluation metrics, including accuracy, precision, recall, F1-score, and test loss.
-
Generate visualizations and results, such as confusion matrices, training curves, and example spectrograms for the 2D model.
Users can simply run the cells in the respective notebook in order to reproduce the complete pipeline without having to manually execute each individual preprocessing, training, or evaluation script.
Place your captured .npy files in:
predict_samples/
Then run the appropriate prediction module:
cd src
python -m model_1d.predictor:
cd src
python -m model_2d.predict_2dConnect the Nooelec NESDR SMArt v5 or another compatible RTL-SDR and provide the required center frequency and trained model to the live classifier.
To capture a sample and run inference:
# Capture 512k samples at 1090 MHz (ADS-B)
python src/live_capture.py 1090000000 --num-samples 512000 --output predict_samples/live.npy
# Run 1D model inference
cd src && python -m model_1d.predict
# Run 2D model inference
cd src && python -m model_2d.predict_2dOr, you can connect the SDR to your laptop, tune it to the required frequency using SDR++ and run the following commands
# run the 1D CNN model for any of the 4 signal types (ex - noise)
python live_classifier.py 920000000 --model 1d
# run the 2D CNN model for any of the 4 signal types (ex - FM broadcast)
python live_classifier.py 93500000 --model 2dYou will obtain live predictions in this manner:













