Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ktauchathuranga/apt-encoder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

APT Encoder

A Rust command-line tool that encodes standard images (JPEG/PNG) into NOAA APT (Automatic Picture Transmission) audio signals. Supports dual-channel encoding (separate images for Channel A and B) and camera capture mode for encoding webcam frames into APT signals.

Rust License: MIT

Sample decoded APT signal
Sample APT signal decoded with SatDump

Features

  • File Mode: Encode images to WAV files for testing and offline use
  • Dual-Channel: Use separate images for Channel A (visible) and Channel B (infrared)
  • Camera Capture Mode: Capture frames from webcam(s) and encode to WAV or stream to stdout
  • Direct SDR Streaming: Pipe raw PCM directly to HackRF or other SDR tools with --stdout
  • Continuous Capture: Timelapse-style continuous frame capture mode
  • Graceful Shutdown: Ctrl+C saves the WAV file properly without corruption
  • Standards Compliant: Output compatible with WXtoImg, noaa-apt, and other APT decoders

Table of Contents


Quick Start

# Build with camera support
cargo build --release --features input-v4l

# Encode an image to WAV
./target/release/apt-encoder photo.jpg -o output.wav

# Capture from webcam to WAV (same camera for both channels)
./target/release/apt-encoder --capture --cam-b-idx 0 -o output.wav

# Stream webcam directly to HackRF at 433 MHz (requires gnuradio, gr-osmosdr)
./target/release/apt-encoder --capture --stdout --continuous --cam-b-idx 0 2>/dev/null | \
  python3 fm_stream.py --freq 433000000 --gain 0

What is APT?

APT (Automatic Picture Transmission) is an analog image transmission system developed in the 1960s for weather satellites. It allows the transmission of grayscale weather imagery as audio signals that can be received and decoded with relatively simple equipment.

APT is used by the NOAA POES (Polar Operational Environmental Satellites) series, specifically:

  • NOAA-15 (137.620 MHz) - Launched 1998
  • NOAA-18 (137.9125 MHz) - Launched 2005
  • NOAA-19 (137.100 MHz) - Launched 2009

These satellites orbit Earth in sun-synchronous polar orbits at approximately 850 km altitude, completing an orbit every ~102 minutes. During a typical pass overhead, you can receive 10-15 minutes of imagery.


History of APT

Year Milestone
1960 TIROS-1, first weather satellite, launched with primitive imaging
1963 APT system first proposed by NASA
1964 APT first demonstrated on TIROS-8
1966 ESSA-1 and ESSA-2 launched with operational APT
1970s NOAA satellite series begins with improved APT
1978 TIROS-N introduces the modern APT format still used today
1998 NOAA-15 launched, oldest currently operational APT satellite
2009 NOAA-19 launched, the last satellite with APT capability
2030s APT expected to be phased out as NOAA POES satellites reach end-of-life

Technical Deep Dive

Signal Structure

APT transmits two simultaneous image channels:

  • Channel A: Visible light (daytime) or 3.7 µm IR (nighttime)
  • Channel B: 10.8 µm thermal infrared (always)

The signal is transmitted at 137 MHz VHF using FM modulation with a 2400 Hz subcarrier that is amplitude modulated with the image data.

┌──────────────────────────────────────────────────────────────────────┐
│                    APT Signal Chain                                  │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Image Data ──► AM Modulation ──► 2400 Hz ──► FM Modulation ──► RF   │
│               (on subcarrier)   Subcarrier   (±17 kHz dev)    137MHz │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

APT Line Format

Each APT line takes exactly 0.5 seconds to transmit, resulting in 2 lines per second (2 LPS). Each line consists of 2080 "words" (pixels), giving a word rate of 4160 words per second.

◄──────────────────────────── 2080 words (0.5 seconds) ───────────────────────────────►

┌───────┬────────┬─────────────┬───────────┬───────┬────────┬─────────────┬───────────┐
│Sync A │Space A │  Image A    │Telemetry A│Sync B │Space B │  Image B    │Telemetry B│
│  39   │   47   │    909      │    45     │  39   │   47   │    909      │    45     │
└───────┴────────┴─────────────┴───────────┴───────┴────────┴─────────────┴───────────┘
│◄── Channel A (1040 words) ──►│           │◄── Channel B (1040 words) ──►│

Word Rate: 4160 words/second
Line Rate: 2 lines/second

Telemetry Wedges

Each frame (128 lines) contains calibration data in the telemetry sections:

Telemetry Frame Structure (16 wedges per frame):

Wedge  │ Lines  │ Function
───────┼────────┼──────────────────────────────
  1-8  │  1-64  │ Grayscale calibration (black to white)
  9-10 │ 65-80  │ Thermistor temperature
 11-12 │ 81-96  │ Patch temperature
 13-16 │ 97-128 │ Channel ID wedge

Installation

Prerequisites

  • Rust 1.70 or later
  • Cargo (comes with Rust)

Building from Source

# Clone the repository
git clone https://github.com/ktauchathuranga/apt-encoder.git
cd apt-encoder

# Build without camera support
cargo build --release

# Build with camera support (Linux only, requires V4L2)
cargo build --release --features input-v4l

# The binary will be at: ./target/release/apt-encoder

Running Tests

cargo test

Usage

File Mode - Encode Images to WAV

# Basic: encode single image (duplicated to both channels)
apt-encoder photo.jpg -o output.wav

# Dual-channel: separate images for Channel A and B
apt-encoder visible.png -o output.wav --input-b infrared.png

# Repeat the image 3 times for a longer transmission
apt-encoder photo.jpg -o output.wav --repeat 3

# With custom telemetry settings
apt-encoder photo.jpg -o output.wav \
  --thermistor-temp 0.3 \
  --patch-temp 0.7 \
  --channel-a-id 1 \
  --channel-b-id 4

Camera Capture Mode - Webcam to APT

Capture frames from webcam(s) and encode to APT. Each frame is encoded line-by-line (like real NOAA satellites), producing decodable images.

Note: Requires building with camera support: cargo build --release --features input-v4l

Capture to WAV File

# Single frame capture (same camera for both channels)
apt-encoder --capture --cam-b-idx 0 -o output.wav

# Single frame with separate cameras
apt-encoder --capture --cam-a-idx 0 --cam-b-idx 1 -o output.wav

# Continuous capture (timelapse) - press Ctrl+C to stop
apt-encoder --capture --continuous --cam-b-idx 0 -o output.wav

Stream to SDR (HackRF, etc.)

Use --stdout to output raw 16-bit signed PCM (48kHz) directly to stdout for piping to SDR tools.

Note: For proper FM transmission (like real NOAA satellites), use fm_stream.py which applies WBFM modulation before transmission.

Method 1: Using fm_stream.py (Recommended)

The included fm_stream.py script reads PCM audio, applies proper WBFM modulation (±17 kHz deviation), and transmits via HackRF:

# Install dependencies (if not already installed)
# Requires: gnuradio, gr-osmosdr

# Stream camera to HackRF at 433 MHz (minimum power, safe for testing)
apt-encoder --capture --stdout --continuous --cam-b-idx 0 2>/dev/null | \
  python3 fm_stream.py --freq 433000000 --gain 0

# Single frame transmission
apt-encoder --capture --stdout --cam-b-idx 0 2>/dev/null | \
  python3 fm_stream.py

# Custom frequency and gain
apt-encoder --capture --stdout --continuous --cam-b-idx 0 2>/dev/null | \
  python3 fm_stream.py --freq 433500000 --gain 20

fm_stream.py options:

Flag Description Default
--freq, -f Transmit frequency (Hz) 433000000
--gain, -g TX gain (0-47 dB) 0
--amp-on Enable 14dB amplifier off
--amp-off Disable amplifier default
Method 2: Using GNU Radio Companion

A pre-configured flowgraph is included in the repository for transmitting WAV files:

# Open the included GNU Radio Companion project
gnuradio-companion gnu-radio-companion/apt-encoder.grc

Or create your own flowgraph:

[Wav File Source] ► [WBFM Transmit] ► [Rational Resampler] ► [osmocom Sink]

Block settings:

  • Wav File Source: Your output.wav, Repeat: Yes
  • WBFM Transmit: Audio Rate=48000, Quad Rate=480000, Max Dev=17000, Tau=75e-6
  • Rational Resampler: Type=Complex (Real Taps), Interpolation=25, Decimation=6
  • osmocom Sink: Device=hackrf=0, Sample Rate=2000000, Frequency=433000000

Tip: Edit the WAV file path in the flowgraph to point to your generated output.wav file.

Camera Index

  • --cam-a-idx: Camera index for Channel A (default: 0)
  • --cam-b-idx: Camera index for Channel B (default: 1)
  • Same index for both = shared mode (one camera, same image for both channels)
  • Different indices = dual camera mode (separate cameras)

Find your camera indices with:

ls /dev/video*
# or
v4l2-ctl --list-devices

Command-Line Reference

USAGE:
    apt-encoder [OPTIONS] [INPUT_IMAGE]

ARGUMENTS:
    [INPUT_IMAGE]    Input image file for Channel A (JPEG/PNG) - required for file mode

OPTIONS:
    Output:
        -o, --output <FILE>       Output WAV file

    Channel Options:
        --input-b <FILE>          Secondary image for Channel B (IR channel)
                                  If not provided, Channel A is duplicated

    Camera Capture Mode:
        --capture                 Enable camera capture mode
        --stdout                  Output raw PCM to stdout (for piping to SDR)
        --continuous              Continuously capture frames (Ctrl+C to stop)
        --cam-a-idx <N>           Camera index for Channel A [default: 0]
        --cam-b-idx <N>           Camera index for Channel B [default: 1]

    Telemetry Options:
        --thermistor-temp <F>     Thermistor temperature (0.0-1.0) [default: 0.5]
        --patch-temp <F>          Patch temperature (0.0-1.0) [default: 0.5]
        --channel-a-id <N>        Channel A identifier (1-6) [default: 2]
        --channel-b-id <N>        Channel B identifier (1-6) [default: 4]

    General Options:
    -r, --repeat <N>              Number of times to repeat the image [default: 1]
    -h, --help                    Print help information
    -V, --version                 Print version information

Example Commands Summary

Use Case Command
Image to WAV apt-encoder photo.jpg -o output.wav
Dual-channel apt-encoder a.jpg -o out.wav --input-b b.jpg
Camera to WAV (single) apt-encoder --capture --cam-b-idx 0 -o out.wav
Camera to WAV (continuous) apt-encoder --capture --continuous --cam-b-idx 0 -o out.wav
Camera to HackRF apt-encoder --capture --stdout --continuous --cam-b-idx 0 2>/dev/null | python3 fm_stream.py

Signal Specifications

Audio Output Format

Parameter Value
Sample Rate 48,000 Hz
Bit Depth 16-bit
Channels 1 (Mono)
Format PCM WAV or raw PCM (with --stdout)

APT Signal Parameters

Parameter Value
Subcarrier Frequency 2400 Hz
Line Rate 2 lines/second
Word Rate 4160 words/second
Samples per Line 24,000
Sync A Frequency 1040 Hz
Sync B Frequency 832 Hz
Modulation Depth 87% (0.05 - 0.92)

Decoding the Output

Using noaa-apt (Recommended)

noaa-apt is an open-source APT decoder.

noaa-apt output.wav -o decoded.png

Using WXtoImg

WXtoImg is a classic APT decoder (Windows/Linux/macOS).

  1. Open WXtoImg
  2. File ► Open Audio File
  3. Select your generated WAV file
  4. The image should decode automatically

Using SatDump

SatDump is a modern satellite data processor.


See Also

  • noaa-apt - Open-source APT decoder in Rust
  • rtl-sdr - Software-defined radio for receiving real APT
  • SatDump - General purpose satellite data processing

Bringing 1960s space technology to your command line

About

A tool to encode images/webcam into NOAA APT satellite signals with HackRF transmission support

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors