0
Bill of materials
Shopping list
Showing the recommended stack. Budget alternatives are noted inline. Full upgrade rationale and specs: Hardware guide →
■ Required
Raspberry Pi 4 or 5
Pi 5 (1 GB) $45 · Pi 4 (1 GB) $35
Pi 5 preferred — faster SatDump compilation. 1 GB RAM is sufficient for satpi. Prices are elevated due to the 2025–26 DRAM shortage.
■ Required — recommended
Airspy Mini SDR
$99 · airspy.us · wimo.com (EU)
12-bit ADC, 80+ dB dynamic range, no DC spike or IQ imbalance. The single biggest quality jump over the RTL-SDR. SatDump supports it natively — one line change in config.ini. Budget option: RTL-SDR Blog V3 (~$30) works fine as a starting point.
■ Required
QFH Antenna — 137 MHz
Buy ~$50–80 · DIY ~$15
Quadrifilar helix for RHCP hemispherical coverage. Diamond DP-KE137 is the commercial reference. DIY from copper pipe or RG-58 works equally well.
◆ Recommended
Uputronics 137.5 MHz Filtered Preamp
$80 · store.uputronics.com · airspy.us
LNA-first architecture (correct Friis order): 21 dB gain, 0.75 dB noise figure, MiniCircuits PSA4-5043+. Mount at the antenna feedpoint in an IP67 box. Power via bias-tee. Budget option: Nooelec SAWbird+ NOAA (~$30) works but has a higher noise figure due to filter-first design.
◆ Optional — Meshtastic ground node
MeshAdv-Pi v1.1 LoRa HAT
~$40–60 · FrequencyLabs (Etsy)
1 W SPI LoRa HAT for meshtasticd. Stacks on 40-pin header. Choose 868 MHz (EU) or 915 MHz (US/Americas). Confirmed working on Pi 4 and 5. No ham licence needed — Meshtastic uses ISM bands.
■ Required — Pi 5 recommended
NVMe SSD + M.2 HAT
$12–15 HAT + $35–95 SSD (volatile, see hardware guide) = ~$50–110 · Pi 5 only
SatDump writes 50–200 MB per pass continuously — SD cards fail under this workload. NVMe is 18× faster and built for sustained writes. Official M.2 HAT+ or Pimoroni NVMe Base. Avoid WD Green SN350. Pi 4 / budget: 64+ GB Samsung Pro Endurance or SanDisk Extreme SD card (~$12). Still needs the 27 W USB-C PSU for Pi 5.
1
Hardware preparation
Physical
1.1
Connect the RTL-SDR dongle
Plug the RTL-SDR into a USB 3.0 port (blue) on the Pi. If using an LNA, the chain is: antenna → LNA → coax → dongle. The RTL-SDR V3 bias-tee powers the LNA over the coax.
1.2
Mount the antenna
Install the QFH antenna outdoors with maximum clear sky view. Mount vertically, feedpoint down. Height and clearance from obstacles matters more than exact azimuth pointing — these satellites cross the full sky.
METEOR-M2 4 is strong enough to decode indoors through a window. M2-3 is weaker — an LNA helps significantly.
1.3
Fit the Meshtastic HAT (if using)
Power off the Pi before seating the MeshAdv-Pi HAT on the 40-pin GPIO header. Attach a LoRa antenna before applying power — operating the SX1262 without an antenna load will damage it.
⚠ Always connect the LoRa antenna before powering on.
2
OS installation
Software
2.1
Flash Raspberry Pi OS Lite 64-bit
Use Raspberry Pi Imager. Choose Raspberry Pi OS Lite (64-bit). In Advanced Settings configure hostname, SSH key, Wi-Fi, locale, and keyboard before writing.
2.2
Boot and update
sudo apt update && sudo apt full-upgrade -y && sudo reboot
2.3
Blacklist the DVB-T kernel driver
The kernel claims the RTL-SDR as a DVB-T device before librtlsdr can. Blacklist it first.
echo 'blacklist dvb_usb_rtl28xxu' | sudo tee /etc/modprobe.d/rtlsdr.conf
sudo rmmod dvb_usb_rtl28xxu 2>/dev/null; true
⚠ Skipping this is the most common cause of "device not found" errors in SatDump.
3
MeshSat installation
Core install
3.1
One-line guided install (recommended)
bash <(curl -fsSL https://raw.githubusercontent.com/IngeniiImperator/MeshSat/main/scripts/install.sh)
The install script handles everything in this guide interactively — SatDump build, config, all notification channels. You can stop here if using it.
3.2
Manual clone
sudo apt install -y git
git clone https://github.com/IngeniiImperator/MeshSat.git
cd MeshSat
3.3
Run the base install script
Installs Python deps, librtlsdr, rclone, msmtp, and builds SatDump from source. Takes 15–30 min on a Pi 4/5.
bash scripts/install_base.sh
4
Configuration
Config
4.1
Create config.ini
cp config/config.example.ini config/config.ini && nano config/config.ini
4.2
Set ground station coordinates
[station]
name = MySatPi
timezone = Europe/Zurich
[qth]
latitude = 47.3769 ; decimal degrees
longitude = 8.5417
altitude_m = 408
4.3
Configure hardware and paths
[hardware]
source_id = 00000001
gain = 40 ; lower if using LNA
bias_t = true ; enable for LNA power
[paths]
base_dir = /home/pi/MeshSat
5
rclone cloud upload
Cloud
5.1
Configure and test
rclone config
# then test:
rclone lsd dropbox:
rclone copy /tmp/test.txt dropbox:MeshSat/test/
When prompted for a remote name, use exactly the value in config.ini's rclone_remote setting.
6
Email notifications (msmtp)
Notify
6.1
Create ~/.msmtprc with Gmail App Password
defaults
auth on; tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
account gmail
host smtp.gmail.com; port 587
from you@gmail.com; user you@gmail.com
password YOUR_16_CHAR_APP_PASSWORD
account default : gmail
chmod 600 ~/.msmtprc
printf 'Subject: test\n\nOK\n' | /usr/bin/msmtp you@gmail.com
7
Meshtastic mesh notifications
LoRa RF · no licence
Meshtastic uses the ISM radio band (915 MHz in US, 868 MHz in EU) — no ham licence required. LoRa is low bandwidth (~1–21 kbps), designed for short text messages, not images. After each satellite pass decode, MeshSat broadcasts a short status message to every node in range. See the Hardware guide for node options including the $17 Heltec V3 and T-Deck Plus handheld. Check your local mesh coverage at meshmap.net ↗
7.1
Install meshtasticd (Pi ground station node)
echo 'deb http://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_13/ /' \
| sudo tee /etc/apt/sources.list.d/network:Meshtastic:beta.list
curl -fsSL https://download.opensuse.org/repositories/network:/Meshtastic:beta/Debian_13/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/network_Meshtastic_beta.gpg > /dev/null
sudo apt update && sudo apt install meshtasticd
This installs meshtasticd for the MeshAdv-Pi HAT. If you're running a standalone Heltec V3 or RAK WisBlock node instead, flash Meshtastic firmware directly from flasher.meshtastic.org ↗ — no Pi-side install needed.
7.2
Enable SPI and activate the MeshAdv-Pi HAT preset
sudo raspi-config nonint do_spi 0
sudo cp /etc/meshtasticd/available.d/lora-MeshAdv-900M30S.yaml /etc/meshtasticd/config.d/
sudo systemctl enable --now meshtasticd
Set your LoRa region at http://<pi-ip>:8080 before the radio will transmit. Choose US_915 for North America, EU_868 for Europe.
7.3
Install Python library and enable in config.ini
pip3 install meshtastic --break-system-packages
[mesh]
enabled = true
host = localhost
port = 4403
7.4
Add relay nodes to extend coverage (optional)
Any Meshtastic node within LoRa range of the Pi's MeshAdv-Pi HAT automatically joins the mesh and relays pass notifications further. For the drone integration, flash a Heltec V3 ($17–23) with Meshtastic firmware and velcro it to the airframe — at altitude it extends ground coverage to 60+ miles line-of-sight. For a fixed relay, a Heltec V3 or RAK WisBlock with a small solar panel makes a permanent outdoor node requiring no maintenance.
Flash relay nodes at flasher.meshtastic.org ↗ — set the same region and channel as the Pi node, and configure as Router mode to maximise relay efficiency and battery life.
7.5
Optional: add a BME280 weather sensor to a node
Meshtastic nodes can broadcast telemetry from a BME280/BME680 I²C sensor ($2–5) — temperature, humidity, and barometric pressure. Paired with MeshSat's satellite imagery, you get space-based weather from above and ground-truth sensor data at the same timestamp. Wire SDA/SCL to the node's I²C pins and enable the telemetry module in the Meshtastic app.
7.6
Test
python3 bin/notify_mesh.py "METEOR-M2 4" "65.3" "https://example.com"
Check meshmap.net ↗ to see your node appear on the global map once it's active.
8
TAK / Cursor-on-Target (CoT)
Situational
8.1
Enable in config.ini
No additional software install required — the CoT event is pure XML over UDP or TCP using the standard library only.
[tak]
enabled = true
host = 239.2.3.1 ; UDP multicast for ATAK on LAN
port = 6969
protocol = udp ; or tcp for FreeTAKServer
For FreeTAKServer (FTS), set protocol = tcp and host to your FTS IP with port 8087 (CoT streaming port).
For field operations beyond LAN range, consider adding a Wi-Fi HaLow MANET — a $106 Raspberry Pi 4 + OpenWRT mesh router that delivers 15 Mbps at 2,000+ feet over a private WPA3-encrypted IP network. TAK/CoT, MQTT, and ntfy all work over it automatically with no config changes. See Hardware guide → Wi-Fi HaLow for the full build.
8.2
Test
python3 bin/notify_tak.py "METEOR-M2 4" "72" "47.3769" "8.5417" "https://example.com"
Open ATAK or WinTAK on the same network. The satellite event should appear on the map pinned to your ground station coordinates.
8.3
FreeTAKServer (optional, self-hosted)
pip3 install FreeTAKServer --break-system-packages
python3 -m FreeTAKServer.controllers.services.FTS
Then set host to your Pi's LAN IP and protocol = tcp in [tak]. See FreeTAK docs ↗
9
ntfy push notifications
Push
9.1
Install requests and configure
pip3 install requests --break-system-packages
[ntfy]
enabled = true
server = https://ntfy.sh ; or your self-hosted instance
topic = my-meshsat-secret123 ; use a hard-to-guess topic
token = ; optional, for access-controlled servers
Install the ntfy app on your phone and subscribe to the same topic — notifications arrive instantly after each pass.
9.2
Test
python3 bin/notify_ntfy.py "METEOR-M2 4" "65" "https://example.com"
10
MQTT (Home Assistant / Node-RED)
IoT
10.1
Start Mosquitto and install paho-mqtt
sudo systemctl enable --now mosquitto
pip3 install paho-mqtt --break-system-packages
10.2
Enable in config.ini
[mqtt]
enabled = true
broker = localhost
port = 1883
topic = meshsat/pass/result
10.3
Test and subscribe
# terminal 1 — subscribe
mosquitto_sub -h localhost -t 'meshsat/pass/result'
# terminal 2 — trigger test
python3 bin/notify_mqtt.py "METEOR-M2 4" "65" "https://example.com"
11
Apprise (Telegram, Discord, Slack, etc.)
100+ services
11.1
Install and configure
pip3 install apprise --break-system-packages
[apprise]
enabled = true
urls = tgram://bottoken/chatid
discord://webhook_id/webhook_token
gotify://hostname/token
See Apprise URL schema docs ↗ for the full list of 100+ supported services.
11.2
Test
python3 bin/notify_apprise.py "METEOR-M2 4" "65" "https://example.com" "tgram://bottoken/chatid"
12
APRS (Baofeng UV-5R + Dire Wolf)
Ham licence
⚠ Requires Technician-class or higher ham licence. Baofeng UV-5R (~$23) + AIOC cable (~$30) + Dire Wolf. See Hardware guide → for full details. Build video: KM6LYW Radio on YouTube ↗ · AIOC deep-dive: Hackaday ↗
12.1
Install Dire Wolf and detect AIOC
sudo apt install -y direwolf
aplay -l | grep -i aioc # note the card number
The AIOC USB cable appears as a CM108 USB audio device. If aplay shows it on card 1, use plughw:1,0 below.
12.2
Configure Dire Wolf
sudo nano /etc/direwolf.conf
ADEVICE plughw:1,0 # your AIOC card number
ARATE 44100
CHANNEL 0
MYCALL YOURCALL-9
MODEM 1200
PTT CM108 # AIOC native PTT
AGWPORT 8000 # used by notify_aprs.py
KISSPORT 8001 # used by pat AX.25
sudo systemctl enable --now direwolf
12.3
Enable APRS in config.ini
[aprs]
enabled = true
callsign = YOURCALL-9
passcode = auto # computed from callsign
rf_enabled = true # transmit over UV-5R
aprsis_enabled = true # fallback to internet
agwpe_host = localhost
12.4
Test
python3 bin/notify_aprs.py "METEOR-M2 4" "72" "https://example.com"
Check aprs.fi for your callsign — the status packet should appear within a minute.
13
Winlink email over radio (pat)
Ham licence
⚠ Requires ham licence and a Winlink RMS gateway within VHF range. Check coverage: winlink.org/RMSChannels ↗
13.1
Install pat (Winlink client)
# Pi 5 (arm64):
wget https://github.com/la5nta/pat/releases/download/v0.15.0/pat_0.15.0_linux_armv8.deb
sudo dpkg -i pat_0.15.0_linux_armv8.deb
# Pi 4 (armhf):
wget https://github.com/la5nta/pat/releases/download/v0.15.0/pat_0.15.0_linux_armv6.deb
sudo dpkg -i pat_0.15.0_linux_armv6.deb
13.2
Configure pat with your callsign
pat --mycall YOURCALL --locator DN51 configure
Your grid square locator (e.g. DN51, EM38) sets your geographic location for RMS gateway auto-selection. Find yours at qrz.com/gridmapper ↗
13.3
Enable Winlink in config.ini
[winlink]
enabled = true
callsign = YOURCALL
to = YOURCALL@winlink.org # your own inbox
transport = ax25 # packet RF via Dire Wolf
gateway = # blank = auto-select nearest RMS
13.4
Test
python3 bin/notify_winlink.py "METEOR-M2 4" "72" "https://example.com"
Check your Winlink inbox at winlink.org ↗ — the message should arrive after pat completes the RMS handshake.
14
First manual run
Test
12.1
Run the full planning chain manually
python3 bin/update_tle.py
python3 bin/predict_passes.py
python3 bin/schedule_passes.py
python3 bin/generate_refresh_units.py
12.2
Verify timers are active
systemctl list-timers --all | grep satpi
You should see satpi-refresh.timer plus one timer per upcoming scheduled pass.
15
Verify and ongoing operation
Ongoing
13.1
Monitor a live pass
tail -f results/captures/<pass-dir>/satdump.log
13.2
Check decoded output and upload
ls results/captures/<pass-dir>/MSU-MR/
cat results/captures/<pass-dir>/upload.log
13.3
System is now fully autonomous
The refresh timer re-runs TLE update, pass prediction, and scheduling automatically. Every notification channel fires after each successful decode. No manual intervention needed.
Monthly: run
python3 bin/update_tle.py manually if decode quality drops — TLEs drift over weeks.