Uladzimir KarpenkaandClaude Opus 4.8 fd2ba84e7d emnr: linear-time aepf, drop idiv from ring walks, halve getKey's logs
Three separate costs, found with a sampling profile of the RX chain with
emnr forced on:

aepf() averaged mask[] over a window of N = 2*psi + 1 = 41 bins by walking
the window for every one of the 2049 bins, i.e. O(msize*N). Its three spans
are all symmetric windows clipped at the array ends, so take each from a
prefix sum instead: one subtraction per output, O(msize).

xemnr() advanced four ring indices with a '% size' per step. iasize is 4096
and oasize 1024 here, and neither is known to the compiler, so each step was
a real integer division -- ~8700 of them per frame. The indices step by one
and, since iasize >= fsize and oasize >= incr always hold, wrap at most once
per loop, so walk contiguous runs and wrap between them.

calc_gain() called getKey() twice per bin with the same gamma, so the gamma
row index and its log10 were computed twice. Split getKey into keyIndex() +
keyLerp() and locate gamma once. The remaining logs go through wdsp_log10()
(new fastmath.h), accurate to 2e-13 against libm and ~2.4x its throughput;
gamma and xi are bracketed against the table limits first, so the argument
is always positive and normal. Also clamp the row index so the second
bilinear corner cannot address the next row of the 241x241 table.

Measured in situ on an Apple M1 Pro, 512-sample buffers, cost of turning
emnr on, best of 5:

    baseline                       73661 ns
    + aepf, ring walks             48927 ns   1.51x
    + getKey                       37852 ns   1.95x

Output is not bit-identical, as the prefix sum and the reassociated logs
round differently. Over 300 buffers with emnr alone the worst deviation is
4.0e-09, an SNR of 196 dB; perturbing a single input sample of the unmodified
code by one ulp diverges it from itself by 1.4e-08 (186 dB), so this change
disturbs the chain less than the last bit of the input does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:08:20 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00
2026-06-01 15:58:45 +03:00

WDSP

DSP library for SDR (Software Defined Radio) applications.

Originally written for Windows by Warren Pratt, NR0V. Ported to Linux and Android by John Melton G0ORX/N6LYT.

Project structure

wdsp/
├── *.c / *.h                    — WDSP library sources
├── org_openhpsdr_dsp_Wdsp.c/.h  — JNI bridge
├── Makefile                     — host build (Linux / macOS)
├── Makefile.android             — Android cross-build
├── java/
│   └── org/openhpsdr/dsp/Wdsp.java
├── third_party/
│   ├── fftw/              — FFTW source (download manually, see below)
│   ├── rnnoise/           — RNNoise noise suppression
│   └── libspecbleach/     — spectral noise reduction
├── obj/                   — object files (generated)
└── lib/                   — built libraries (generated)

Host build (Linux / macOS)

Dependencies

Linux:

sudo apt install build-essential libfftw3-dev pkg-config default-jdk

macOS:

brew install fftw pkg-config

Build

# Full build: static + shared + JNI library + Java classes
make

# Individual targets
make static        # lib/libwdsp.a
make shared        # lib/libwdsp.so  (or .dylib on macOS)
make java          # lib/libwdspj.so + java/build/

Install

sudo make install                    # installs to /usr/local/lib and /usr/local/include
sudo make PREFIX=/opt/wdsp install   # custom prefix
sudo make install_java               # install libwdspj alongside libwdsp

Options

Variable Default Description
PREFIX /usr/local Install prefix
NR34LIB off Set to ON to skip bundled rnnoise/libspecbleach
CC gcc C compiler
CFLAGS -pthread -O3 ... Compiler flags
make NR34LIB=ON    # build without NR3/NR4 noise reduction
make CC=clang

Clean

make clean

Android build

Prerequisites

  1. Android NDK r23 or newer
  2. FFTW source — download and extract into third_party/fftw/:
wget https://www.fftw.org/fftw-3.3.10.tar.gz
tar xf fftw-3.3.10.tar.gz
mv fftw-3.3.10 third_party/fftw
  1. Java compiler — for building the .class file (Android Studio's JBR or any JDK)

Build

make android
# or directly
make -f Makefile.android

Output is placed into:

lib/android/
├── arm64-v8a/
│   ├── libfftw3.so
│   ├── libfftw3f.so
│   ├── libwdsp.so
│   └── libwdspj.so
├── armeabi-v7a/
│   └── ...
├── x86_64/
│   └── ...
└── java/
    └── org/openhpsdr/dsp/Wdsp.class

libwdsp.so has FFTW linked statically — no separate FFTW dependency at runtime on the device.

Options

Variable Default Description
ANDROID_NDK /home/vladimir/Android/Sdk/ndk/29.0.14206865 Path to Android NDK
ANDROID_API 24 Minimum API level
ANDROID_ABIS arm64-v8a armeabi-v7a x86_64 Target ABIs
ANDROID_HOST_TAG auto-detected Host platform (linux-x86_64, darwin-x86_64, etc.)
FFTW_SRC third_party/fftw Path to FFTW source
JAVAC /opt/android-studio/jbr/bin/javac Java compiler
make -f Makefile.android \
    ANDROID_NDK=~/Android/Sdk/ndk/26.3.11579264 \
    ANDROID_API=26 \
    ANDROID_ABIS=arm64-v8a

Clean

make -f Makefile.android clean
# removes obj/android/ and lib/android/

Using in an Android project

Copy lib/android/{abi}/ into your Android project's jniLibs:

app/src/main/jniLibs/
├── arm64-v8a/
│   ├── libwdsp.so
│   └── libwdspj.so
├── armeabi-v7a/
│   └── ...
└── x86_64/
    └── ...

Add Wdsp.java to your source tree and load the libraries at startup:

System.loadLibrary("wdsp");
System.loadLibrary("wdspj");

Or use the singleton:

Wdsp wdsp = Wdsp.getInstance();
S
Description
No description provided
Readme
63 MiB
Languages
C 99.9%
Java 0.1%