Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ else(SRSRAN_FOUND AND NOT FORCE_SUBPROJECT_SRSRAN)
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/srsRAN-download" )
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/srsRAN-download" )
execute_process(
COMMAND "${CMAKE_COMMAND}" -DSRSRAN_SRC_DIR=${CMAKE_BINARY_DIR}/srsRAN-src
-P "${CMAKE_SOURCE_DIR}/external/cmake/PatchDownloadedSrsran.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)

include_directories("${CMAKE_BINARY_DIR}/srsRAN-build/lib/include")
include_directories("${CMAKE_BINARY_DIR}/srsRAN-src/lib/include")
Expand Down Expand Up @@ -318,14 +323,14 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
endif(HAVE_SSE)
endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")

if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON")
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^(arm|aarch64)")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -DIS_ARM -DHAVE_NEON")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -DIS_ARM -DHAVE_NEON")
message(STATUS "have ARM")
set(HAVE_NEON "True")
else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^(arm|aarch64)")
set(HAVE_NEON "False")
endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^(arm|aarch64)")
set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS})

if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD)
Expand Down
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ cd build
cmake ../
make -j 4 (use 4 threads)
```

### Build this PR branch from scratch

This repository also has a PR branch with raw IQ replay/capture support:

```bash
git clone --branch LTESniffer-pr-raw-io https://github.com/vk5zsn/LTESniffer.git
cd LTESniffer
git rev-parse --short HEAD
```

The tested clean-build commit is:

```bash
1925243
```

On aarch64 systems such as Raspberry Pi 5, this branch includes the fix for the old `no SIMD instructions found` configure failure. It also applies the required vendored `srsRAN` build patches automatically during configure on a fresh clone.

Build commands:

```bash
mkdir build
cd build
cmake ..
cmake --build . --target LTESniffer -j4
```

## Usage
LTESniffer has 3 main functions:
- Sniffing LTE downlink traffic from the base station
Expand Down Expand Up @@ -191,6 +219,40 @@ example: sudo ./src/LTESniffer -A 2 -W 4 -f 1840e6 -u 1745e6 -I 379 -p 100 -m 1
```
The debug mode can be enabled by using option ``-d``. In this case, the debug messages will be printed on the terminal.

### Raw IQ replay and capture

The `LTESniffer-pr-raw-io` branch adds:

- `-Q` live raw IQ capture to `cf32`
- `-j` generic raw IQ replay with synchronization
- `-J` raw replay input is interleaved `sc16`
- `-G` minimum DL DCI search SNR for offline replay

Verified live capture example:

```bash
./<build-dir>/src/LTESniffer -A 1 -W 4 -f 763e6 -C -m 0 -a "num_recv_frames=512" -n 500 -Q iq_763M_cf32.bin
```

Verified raw replay example:

```bash
./<build-dir>/src/LTESniffer -i iq_763M_cf32.bin -j -c 405 -p 50 -A 1 -W 4 -m 0 -n 500
```

For raw replay, provide:

- `-c <PCI>`
- `-p <PRB>`

Verified Gqrx raw `fc32` replay example at `11.52 Msps`:

```bash
./<build-dir>/src/LTESniffer -i gqrx_20260413_233101_763000000_11520000_fc.raw -j -c 405 -p 50 -A 1 -W 4 -m 0 -n 500
```

If the raw file is interleaved `sc16`, add `-J`.

### Output of LTESniffer

LTESniffer provides pcap files in the output. The pcap file can be opened by WireShark for further analysis and packet trace.
Expand Down Expand Up @@ -287,4 +349,4 @@ To sniff the uplink traffic, LTESniffer requires USRP X310 with 2 daughterboards
[capture-readme]: https://github.com/SysSec-KAIST/LTESniffer/tree/LTESniffer-record-subframe
[cellular77]: https://github.com/cellular777
[Cemaxecuter]: https://www.youtube.com/@cemaxecuter7783
[Ksk190809]: https://github.com/Ksk190809
[Ksk190809]: https://github.com/Ksk190809
95 changes: 95 additions & 0 deletions docs/fresh_branch_verification_2026-04-13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Fresh Branch Verification

Date: `2026-04-13`
Worktree: `/home/vk2tlq/LTESniffer-pr-raw-io`
Binary: `/home/vk2tlq/LTESniffer-pr-raw-io/build/src/LTESniffer`

This note captures the verification runs performed from the clean PR branch itself.

## CLI surface

`-h` shows the new options:

- `-G minimum DL DCI search SNR in dB for offline replay [Default 6.0]`
- `-j treat -i as generic raw IQ and synchronize it before decoding`
- `-J raw -i/-j input is interleaved sc16 instead of cf32`
- `-Q output live raw IQ capture to cf32 file while decoding`

## Record-subframe replay

Command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
/home/vk2tlq/LTESniffer-pr-raw-io/build/src/LTESniffer \
-A 1 -W 1 \
-i /home/vk2tlq/subframe_iq_sample.bin \
-P 2 -c 405 -p 50 -m 0 -n 200
```

Observed result:

- MIB decoded: `PCI 405 / PRB 50 / ports 2 / PHICH Resources 1`
- final stats:
- `nof_decoded_locations=1103`
- `nof_cce=564`
- `nof_missed_cce=1`
- `nof_subframes=20`
- `nof_locations=1040`

## Direct raw `cf32` replay

Command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
/home/vk2tlq/LTESniffer-pr-raw-io/build/src/LTESniffer \
-A 1 -W 4 \
-i /home/vk2tlq/live_gold_763M_11p52M_cf32.bin \
-j \
-P 2 -c 405 -p 50 -G 1.0 -m 0 -n 2000
```

Observed result:

- lock point: `raw_samples=172040`
- MIB decoded: `PCI 405 / PRB 50 / ports 2 / PHICH Resources 1`
- final stats:
- `nof_decoded_locations=72506`
- `nof_cce=56383`
- `nof_missed_cce=1541`
- `nof_subframes=1954`
- `nof_locations=104221`

## Direct raw `sc16` replay

Command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
/home/vk2tlq/LTESniffer-pr-raw-io/build/src/LTESniffer \
-A 1 -W 4 \
-i /dev/shm/iq_763M_11p52M_sc16.bin \
-j -J \
-P 2 -c 405 -p 50 -G 1.0 -m 0 -n 1000
```

Observed result:

- lock point: `raw_samples=81783`
- MIB decoded: `PCI 405 / PRB 50 / ports 2 / PHICH Resources 1`
- final stats:
- `nof_decoded_locations=9613`
- `nof_cce=11984`
- `nof_missed_cce=24`
- `nof_subframes=936`
- `nof_locations=22239`

## Build notes

Two local dependency-tree fixes were needed in the generated `srsRAN` build directory on this Pi5/GCC 12 host:

- add `<array>` to `build/srsRAN-src/lib/include/srsran/srslog/bundled/fmt/core.h`
- initialize `cc_list` in `build/srsRAN-src/srsenb/src/stack/mac/nr/ue_nr.cc`

These are not branch source changes and are not intended for the pull request diff.
42 changes: 42 additions & 0 deletions docs/gold_standard_manifest_2026-04-13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Gold Standard Manifest

Date: `2026-04-13`
Host: `vk2tlq@piSDR`

This branch does not commit the raw capture binaries. The files below remain local to the host and are referenced by path and checksum for reproducibility.

## Local Files

### Live raw `cf32` gold capture

- Path: `/home/vk2tlq/live_gold_763M_11p52M_cf32.bin`
- Format: `cf32`
- Sample rate: `11.52 Msps`
- Center frequency: `763 MHz`
- Size: `922148560` bytes
- SHA256: `95cacdeb4c669bbbc7299b4fbb933f15b202100cc63e349780664081958ef70d`

### Record-subframe branch sample

- Path: `/home/vk2tlq/subframe_iq_sample.bin`
- Format: synchronized replay file emitted by `LTESniffer-record-subframe`
- Size: `17418240` bytes
- SHA256: `b33801ffee329244c0559f707b8c1864bcc24c55134eb7dcc07ebf3301d638a3`

### Robust raw `sc16` capture

- Path: `/dev/shm/iq_763M_11p52M_sc16.bin`
- Format: `sc16`
- Sample rate: `11.52 Msps`
- Center frequency: `763 MHz`
- Size: `458506240` bytes
- SHA256: `76b911f74d2f6499ff92577c6d6fc23e387e4b8a815eb30879303c6cdfb656f7`

## Why These Files Matter

- `live_gold_763M_11p52M_cf32.bin`
Gold reference generated from the same live `LTESniffer` receive path via `-Q`.
- `subframe_iq_sample.bin`
Confirms compatibility with the synchronized file format used by the `LTESniffer-record-subframe` branch.
- `iq_763M_11p52M_sc16.bin`
Robust raw capture produced in the cleaned Pi5/B210 setup and replayed through `-j -J`.
138 changes: 138 additions & 0 deletions docs/gold_standard_results_2026-04-13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Gold Standard Results

Date: `2026-04-13`
Host: `vk2tlq@piSDR`

## Live capture with `-Q`

Command:

```bash
sudo env LD_LIBRARY_PATH=/opt/install/uhd/lib \
./LTESniffer \
-A 1 -W 4 -f 763e6 -C -m 0 -n 10000 \
-a "num_recv_frames=512" \
-Q /home/vk2tlq/live_gold_763M_11p52M_cf32.bin
```

Key results:

- UHD: `4.9.0.0-0-g006d7f76`
- MIB: `PCI 405`, `PRB 50`, `ports 2`, `PHICH Resources 1`
- `nof_decoded_locations=162350`
- `nof_cce=221312`
- `nof_missed_cce=2773`
- `nof_subframes=9991`
- `nof_locations=408723`
- raw sink summary:
- `overflows=0`
- `late_rx=0`
- `rx_errors=0`
- `other_errors=0`
- `timestamp_gap_events=0`
- `timestamp_gap_samples_accum=0`
- `timestamp_gap_samples_max_abs=0`
- `file_write_failed=0`

## Offline replay of the live gold `cf32` file

Command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
./LTESniffer \
-A 1 -W 4 \
-i /home/vk2tlq/live_gold_763M_11p52M_cf32.bin \
-j \
-P 2 -c 405 -p 50 -G 1.0 -m 0 -n 10000 -d
```

Key results:

- lock point: `raw_samples=172040`
- MIB: `PCI 405`, `PRB 50`, `ports 2`, `PHICH Resources 1`
- `nof_decoded_locations=159681`
- `nof_cce=219694`
- `nof_missed_cce=2780`
- `nof_subframes=9938`
- `nof_locations=405726`

Live vs replay deltas:

- `nof_decoded_locations`: `-1.64%`
- `nof_cce`: `-0.73%`
- `nof_subframes`: `-0.53%`
- `nof_locations`: `-0.73%`

Interpretation:

- direct raw replay is close to live when the file comes from the internal `-Q` sink
- this validates the raw ingest path independent of third-party capture tools

## Record-subframe compatibility

Command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
./LTESniffer \
-A 1 -W 1 \
-i /home/vk2tlq/subframe_iq_sample.bin \
-P 2 -c 405 -p 50 -m 0 -n 200 -d
```

Key results:

- valid MIB decode for `PCI 405 / PRB 50 / ports 2 / PHICH Resources 1`
- nonzero offline decode stats
- multiple RNTIs decoded successfully

Interpretation:

- the offline PHICH resource preset change keeps replay compatible with files produced by the `LTESniffer-record-subframe` branch

## Robust raw `sc16` replay

Capture command:

```bash
sudo env LD_LIBRARY_PATH=/opt/install/uhd/lib \
/home/vk2tlq/LTESniffer/build/src/UhdCaptureRobust \
--output /dev/shm/iq_763M_11p52M_sc16.bin \
--args "type=b200,num_recv_frames=512,master_clock_rate=23.04e6" \
--freq 763e6 \
--rate 11.52e6 \
--gain 50 \
--antenna RX2 \
--duration 10 \
--format sc16 \
--wirefmt sc16 \
--metadata
```

Replay command:

```bash
env LD_LIBRARY_PATH=/opt/install/uhd/lib \
./LTESniffer \
-A 1 -W 4 \
-i /dev/shm/iq_763M_11p52M_sc16.bin \
-j -J \
-P 2 -c 405 -p 50 -G 1.0 -m 0 -n 9950 -d
```

Key results:

- capture: `overflows=0 timeouts=0 late=0 other_errors=0`
- replay lock: `raw_samples=81783`
- replay MIB: `PCI 405 / PRB 50 / ports 2 / PHICH Resources 1`
- `nof_decoded_locations=63229`
- `nof_cce=165327`
- `nof_missed_cce=308`
- `nof_subframes=9865`
- `nof_locations=305978`

Interpretation:

- the raw replay path works for both `cf32` and `sc16`
- the cleaned Pi5/B210 setup is stable enough to use as a baseline capture environment
Loading