Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.
Closed
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
19 changes: 11 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ on:
branches: [ master ]

jobs:
build-ubuntu-22-04:
runs-on: ubuntu-22.04
build-ubuntu-24-04:
name: "Build on Ubuntu 24.04"
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4
- name: Install dependencies (apt)
run: sudo apt install python3-pip ninja-build libsndfile1-dev libliquid-dev
- name: Install meson (pip3)
run: pip3 install --user meson
run: sudo apt install meson libsndfile1-dev libliquid-dev
- name: meson setup
run: meson setup -Dwerror=true build
- name: meson compile
run: cd build && meson compile

build-ubuntu-20-04:
runs-on: ubuntu-20.04
build-ubuntu-22-04:
name: "Build on Ubuntu 22.04"
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4
Expand All @@ -37,8 +37,9 @@ jobs:
run: cd build && meson compile

build-debian-oldoldstable:
name: "Build on Debian 11 (Bullseye)"
runs-on: ubuntu-latest
container: debian:buster
container: debian:bullseye

steps:
- uses: actions/checkout@v4
Expand All @@ -52,6 +53,7 @@ jobs:
run: export PATH=$PATH:$HOME/.local/bin && cd build && meson compile

build-macos:
name: "Build on macOS"
runs-on: macos-latest

steps:
Expand All @@ -64,6 +66,7 @@ jobs:
run: cd build && meson compile

test:
name: "Build and test on Ubuntu 22.04"
runs-on: ubuntu-22.04

steps:
Expand Down
90 changes: 63 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,100 @@ deinvert requires liquid-dsp, libsndfile, and meson.

On Ubuntu, these can be installed like so:

sudo apt install libsndfile1-dev libliquid-dev meson
```bash
sudo apt install libsndfile1-dev libliquid-dev meson
```

On older Debians:

sudo apt-get install python3-pip ninja-build build-essential libsndfile1-dev libliquid-dev
pip3 install --user meson
sudo ldconfig
```bash
sudo apt-get install python3-pip ninja-build build-essential libsndfile1-dev libliquid-dev
pip3 install --user meson
sudo ldconfig
```

On macOS I recommend using [homebrew](https://brew.sh/):
On macOS we recommend using [homebrew](https://brew.sh/):

xcode-select --install
brew install libsndfile liquid-dsp meson
```bash
xcode-select --install
brew install libsndfile liquid-dsp meson
```

## Compiling

meson setup build
cd build
meson compile
```bash
meson setup build
cd build
meson compile
```

If you wish to install it system-wide (/usr/local by default):
If you wish to install it system-wide (`/usr/local` by default):

meson install
```bash
meson install
```

## Usage
If you don't install it, the binary will be available in the `build`
directory.

Note that since scrambling and descrambling are the same operation this
tool also works as a scrambler.
## Usage

If no input and output file is given, deinvert reads raw 16-bit PCM via stdin
and outputs in the same format via stdout. The inversion carrier defaults to
2632 Hz.
* If no filename is given as input or output, deinvert will use the standard
streams for raw PCM (16-bit signed-integer mono sound).
* The inversion carrier defaults to 2632 Hz.
* The input signal should be single-channel. Multi-channel input works from
a file, but the output will be mono.
* Note that since scrambling and descrambling are the same operation this
tool also works as a scrambler!

### Simple inversion, WAV input

(De)scrambling a WAV file with setting 4:

./build/deinvert -i input.wav -o output.wav -p 4
```bash
deinvert -i input.wav -o output.wav -p 4
```

### Split-band inversion

(De)scrambling split-band inversion with a bandwidth of 3500 Hz, split at 1200 Hz:

./build/deinvert -i input.wav -o output.wav -f 3500 -s 1200
```bash
deinvert -i input.wav -o output.wav -f 3500 -s 1200
```

### Invert a live signal from RTL-SDR

Descrambling a live FM channel at 27 Megahertz from an RTL-SDR, setting 4:
Descrambling a live FM channel at 27 Megahertz from an RTL-SDR, setting 4. Here
we also use `rtl_fm` from the RTL-SDR distribution and the `play` command from `sox`
as examples.

rtl_fm -M fm -f 27.0M -s 12k -g 50 -l 70 | ./build/deinvert -r 12000 -p 4 |\
play -r 12k -c 1 -t .s16 -
```bash
rtl_fm -M fm -f 27.0M -s 12k -g 50 -l 70 |\
deinvert -r 12000 -p 4 |\
play -r 12k -c 1 -t .s16 -
```

### Invert a live signal from Gqrx (requires netcat)

You can listen to descrambled audio while receiving a radio channel in Gqrx
at the same time:

1. Set Gqrx to demodulate the audio (for example, narrow FM).
2. Go to the Audio window and click on the three dots button "...".
3. Go to Network and set host to localhost and port to e.g. 12345.
4. In the Audio window, enable UDP.
5. Run this command in a terminal window:
5. Run this command in a terminal window: (replace `play` with the audio
player of your choice):

nc -u -l localhost 12345 | ./build/deinvert -r 48000 | play -r 48k -c 1 -t .s16 -
```bash
nc -u -l localhost 12345 | deinvert -r 48000 | play -r 48k -c 1 -t .s16 -
```


### Full options

./build/deinvert [OPTIONS]
deinvert [OPTIONS]

-f, --frequency FREQ Frequency of the inversion carrier, in Hertz.

Expand Down Expand Up @@ -108,6 +137,9 @@ Descrambling a live FM channel at 27 Megahertz from an RTL-SDR, setting 4:

## Inversion carrier presets

We provide preset frequencies used by Selectone ST-20B as a convenience
(use the `-p` option):

| No | Frequency |
| -- | --------- |
| 1 | 2632 Hz |
Expand All @@ -119,6 +151,8 @@ Descrambling a live FM channel at 27 Megahertz from an RTL-SDR, setting 4:
| 7 | 3495 Hz |
| 8 | 3729 Hz |

You can also provide any other frequency using the `-f` option.

## Troubleshooting

### I can't understand the speech even after deinverting
Expand All @@ -135,7 +169,9 @@ frequency should be around 250 Hz less than the inversion carrier frequency. For
example, if the inversion carrier is 3023 Hz, the `play` command could be
changed to:

play -r 12000 -c 1 -t .s16 - sinc -2800
```bash
play -r 12000 -c 1 -t .s16 - sinc -2800
```

## Licensing

Expand Down
1 change: 0 additions & 1 deletion src/deinvert.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <vector>

#include "src/io.h"
Expand Down
5 changes: 0 additions & 5 deletions src/deinvert.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@
*/
#pragma once

#include <array>
#include <cstdint>
#include <string>
#include <vector>

#include "config.h"
#include "src/io.h"
#include "src/liquid_wrappers.h"

namespace deinvert {
Expand Down
2 changes: 0 additions & 2 deletions src/liquid_wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
#include "src/liquid_wrappers.h"

#include "config.h"

#include <cassert>
#include <complex>

Expand Down
2 changes: 0 additions & 2 deletions src/liquid_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
#pragma once

#include "config.h"

#include <complex>

#pragma clang diagnostic push
Expand Down
7 changes: 3 additions & 4 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,13 @@ inline Options GetOptions(int argc, char **argv) {

if (options.input_type == InputType::sndfile && samplerate_set)
throw std::runtime_error(
"don't specify sample rate (-r) with -i; I want to read it from the sound file");
"conflicting options; don't specify a sample rate when using an input audio file");

if (options.is_split_band && options.frequency_lo >= options.frequency_hi)
throw std::runtime_error("split point must be below the inversion carrier");
throw std::runtime_error("split point frequency must be below the inversion carrier");

if (options.samplerate < options.frequency_hi * 2.0f)
throw std::runtime_error(
"sample rate must be at least twice the inversion frequency (see Nyquist-Shannon theorem)");
throw std::runtime_error("sample rate must be at least twice the inversion frequency");

return options;
}
Expand Down
Loading