Tetra BlueStation : Mini cellule TETRA à domicile

Cet article recense les étapes à suivre, valables au moment de la rédaction, pour installer et mettre en service une cellule radio TETRA faible puissance.

Avant toute chose, assurez-vous d’avoir l’autorisation d’émettre sur les fréquences que vous allez utiliser :

  • Réseau pro avec autorisation d’utilisation de fréquence valide obtenue auprès de l’ANFR, et respect des puissances + localisation des émetteurs tel que déclarés ;
  • Réseau radio-amateur sur les fréquences allouées.

Prérequis

– Raspberry Pi 4 ;
– Carte MicroSD classe 10 ;
– Transceiver SDR ADALM-PLUTO (révision B dans mon cas) ;
– Une antenne accordée pour la partie TX à minima.

Procédure

Installer le système d’exploitation sur la carte microSD en utilisant Raspberry Pi Imager. Dans mon cas, il s’agit de Raspberry Pi OS Lite (sans environnement graphique) Trixie 64-bit datée du 04/12/2025. Patienter jusqu’à la fin de l’écriture des données sur la carte microSD.
Linux raspberrypi 6.12.75+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.12.75-1+rpt1 (2026-03-11) aarch64 GNU/Linux

Si vous utilisez une ancienne version de Windows qui n’accepte pas les mises à jour récentes de l’outil Raspberry Pi Imager, vous devrez créer vous-même le fichier contenant les identifiants utilisateur pour ouvrir une session sur la console :

  1. Créer un fichier texte nommé userconf.txt sur la racine de la carte microSD ;
  2. Ouvrir ce fichier en édition et coller les données suivantes :

    pi:$6$c70VpvPsVNCG0YR5$l5vWWLsLko9Kj65gcQ8qvMkuOoRkEagI90qi3F/Y7rm8eNYZHW8CY6BOIKwMH7a3YYzZYL90zf304cAHLFaZE0
  3. Enregistrez les modifications et fermez le fichier. Ceci va créer le compte utilisateur pi avec le mot de passe raspberry, car il n’est plus défini par défaut sur les versions récentes du système d’exploitation Raspberry Pi OS.

Tant que vous êtes sur la racine de la carte microSD, créez un fichier texte vide nommé ssh. Il ne faut pas ajouter d’extension, juste nommer le fichier ssh.
Cela permettra d’activer l’accès ssh à la console.

Maintenant que les deux fichiers sont créés, vous pouvez extraire proprement la carte mémoire de son lecteur sur le PC avec la fonction Éjecter et l’insérer dans celui de la Raspberry Pi, puis la mettre sous tension.

Pour les premières étapes d’initialisation, j’utilise une interface RS232 à câbler sur la connectique 40 contacts de la Raspberry Pi. Cela permet de s’assurer que tout se déroule comme prévu, en attendant que la console devienne accessible pour continuer avec l’installation des logiciels.

Si vous avez un écran HDMI que vous pouvez connecter sur la Raspberry Pi, ça fait aussi l’affaire.

Comme toujours, on commence par mettre à jour le système :


sudo apt update
sudo apt upgrade

Ensuite, on s’appuie fortement sur la documentation officielle : https://github.com/MidnightBlueLabs/tetra-bluestation-docs/blob/main/wiki/02-Dependencies-and-Building.md.
Je vous laisse consulter la doc officielle pour la description de chaque étape.


sudo apt install -y --no-install-recommends \
git make g++ cmake \
libsoapysdr-dev \
soapysdr-tools \
libasound2-dev \
clang llvm-dev libclang-dev


curl https://sh.rustup.rs -sSf | sh

L’installation automatisée de Rust pose une question : appuyez sur la touche sur entrée pour procéder à l’installation standard.


sudo apt-get install build-essential libxml2-dev bison flex libcdk5-dev cmake libaio-dev libusb-1.0-0-dev libserialport-dev libavahi-client-dev doxygen graphviz python3 python3-pip python3-setuptools -y

git clone -b libiio-v0 https://github.com/analogdevicesinc/libiio.git
cd libiio
mkdir build
cd build
cmake ../ -DCPP_BINDINGS=ON -DPYTHON_BINDINGS=ON
make -j$(nproc)
sudo make install

On observe quelques messages d’avertissement SetuptoolsDeprecationWarning. Il n’y a pas de raison de s’en inquiéter.

Dans mon cas, le partie radio sera assuré par un module SDR ADALM-PLUTO. Je vais donc m’intéresser à ce cas dans les étapes qui le concernent ci-dessous. Si vous utilisez un autre équipement, référez-vous à la documentation officielle : https://github.com/MidnightBlueLabs/tetra-bluestation-docs/blob/main/wiki/02-Dependencies-and-Building.md.

Aller sur le dépôt Git suivant et télécharger le premier fichier zip dans la liste : https://github.com/pgreenland/plutosdr-fw/releases/tag/v0.38_with_timestamping.

Connecter l’ADALM-PLUTO au port USB du PC et extraire tous les fichiers contenus dans l’archive zip téléchargée dans le disque amovible créé par l’ADALM-PLUTO :

Une fois cette opération effectuée, éjectez proprement le périphérique de stockage Pluto SDR :

La LED nommée LED1 se met alors a clignoter furieusement. Il ne faut absolument pas déconnecter l’ADALM-PLUTO tant que cette LED ne clignote pas à nouveau lentement.
À l’issue de sa mise à jour, l’ADALM-PLUTO redémarre avec son nouveau firmware.

L’ADALM-PLUTO peut maintenant être connecté à un port USB de la Raspberry Pi.


cd ~
git clone https://github.com/pgreenland/SoapyPlutoSDR.git -b sdr_gadget_timestamping
cd SoapyPlutoSDR
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig

On observe à nouveau des avertissements sans effet négatif sur le reste des opérations.

Pour valider l’installation correcte, on utilise les commandes suivantes :


Elles doivent retourner respectivement quelque chose de ce type :

Si vous observez des lignes contenant (missing), cela signifie une erreur lors de la compilation et/ou l’installation de Soapy SDR :

Le cas échéant, recommencez les étapes précédentes concernant SoapyPlutoSDR. Il est inutile d’aller plus loin car l’exécution de TETRA BlueStation échouera :

Une fois que les informations retournées par SoapySDRUtil ne contiennent plus d’erreur, on peut passer à la suite :

echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0456", ATTR{idProduct}=="b673", MODE="666"' | sudo tee /etc/udev/rules.d/90-libiio_pluto.rules
sudo udevadm control --reload-rules && sudo udevadm trigger

Ouvrir le fichier limits.conf :

sudo nano /etc/security/limits.conf

Copier/coller le texte suivant juste avant la fin du fichier :

Résultat attendu :

Une fois effectué, quitter l’éditeur avec Ctrl+X suivi de Y pour valider l’enregistrement.

cd ~
git clone https://github.com/MidnightBlueLabs/tetra-bluestation
cd tetra-bluestation
. "$HOME/.cargo/env"
cargo build --release

J’ai constaté des plantages aléatoires pendant la compilation, avec ce type de message : error: rustc interrupted by SIGILL et error: rustc interrupted by SIGSEGV.
Si ça vous arrive également, exécutez à nouveau la commande de compilation pour reprendre là où ça a planté :

Dès cette étape terminée, on peut enfin s’attaquer au paramétrage de TETRA BlueStation!

cd ~/tetra-bluestation
cp example_config/config.toml config.toml
nano config.toml

Là, ça devient sérieux car vous ne pouvez plus vous contenter de faire des copier/coller.
Je vais vous donner tous les éléments qui m’ont permis d’aboutir à un fonctionnement complet dans mes conditions de test : deux portatifs Airbus TH9.

Voici le contenu de mon fichier de configuration, dans lequel j’ai juste remplacé le MNC par 999 pour des raisons de sécurité (la mienne!) :

# TETRA BlueStation example configuration file
# This is an example configuration file for the TETRA base station stack
# DO NOT RUN without editing to stay within legal limits of your jurisdiction

config_version = "0.6"

# Stack operation mode: "Bs" (Base Station), "Ms" (Mobile Station), or "Mon" (Monitor)
stack_mode = "Bs"

# Uncomment to record debug log. Files get large quickly and generate additional system load
#debug_log = "./verbose_log.txt"

###############################################################################

# PHY layer i/o configuration

[phy_io]

# Input type: set to SoapySdr. May allow for future non-soapy RF backends.
backend = "SoapySdr"

# DEBUG/TESTING code. Capture files get large quickly.
# dl_tx_file = "./dl_output.bin" # Debugging; uncomment to save generated DL RF samples to file
# ul_rx_file = "./ul_output.bin" # Debugging; uncomment to save received UL RF samples to file

[phy_io.soapysdr]
# Transmit tx(dl) and rx(ul) frequencies in Hz
# !!! Make sure to also edit all related fields in the cell_info section to fit this frequency.
tx_freq = 424812500
rx_freq = 414812500 # 10MHz duplex spacing
#rx_freq = 423025000

# Adjust if your SDR has a non-negligible tuning error
# ppm_err = 0.0

################################################################################################
## OPTIONAL: specific device, antenna, gain selection ##
## Make sure to check your device for valid settings using SoapySDRUtil --probe ##
## or check: https://github.com/MidnightBlueLabs/tetra-bluestation-docs/wiki/03-Configuration ##
################################################################################################

# Optional device selection arguments.
# If not specified, the first supported device found is selected automatically.
# You may need to specify this if you have multiple supported device connected,
# or if your device cannot be automatically detected by enumeration.
# For example, to use a Pluto+ with a given IP address:
# device = "driver=plutosdr,uri=ip:192.168.42.42"
# To select a LimeSDR with a given serial number (check with SoapySDRUtil --find):
# device = "driver=lime,serial=123456789"
#device = "driver=plutosdr,uri=ip:192.168.3.51"

# Optional antenna selection to override device-specific defaults
# Check antenna names with SoapySDRUtil --probe
# rx_antenna = "LNAW"
# tx_antenna = "BAND2"
rx_antenna = "A_BALANCED"
tx_antenna = "A"

# Optional gain values to override device-specific defaults.
# Check valid gain names with SoapySDRUtil --probe
# For example, to increase transmit power on a LimeSDR:
# tx_gain_pad = 50.0
# To adjust LNA gain to optimize RX performance on a LimeSDR or SXceiver:
# rx_gain_lna = 30.0
rx_gain_pga = 10.0
tx_gain_pga = 89.0

###############################################################################

# Network Information
[net_info]
# Mobile Country Code (MCC) - identifies your country
#mcc = 204 # Netherlands
mcc = 208 # Frankrijk

# Mobile Network Code (MNC) - identifies the TETRA network
# MAKE SURE MNC MAY BE USED, CONSULT RELEVANT AUTHORITIES IF NEEDED
#mnc = 1337
mnc = 999 # dummy one

# Cell Information
[cell_info]

# These parameters specify the DL and UL frequencies
# Read the spec if in doubt. Make sure these match the tx/rx freqs as given above
# See ETSI TS 100 392-15 V1.5.1 Clause 6: Duplex spacing
freq_band = 4 # 400MHz band
#main_carrier = 1521 # + 1521 * 25kHz = 38250kHz
main_carrier = 992 #
#duplex_spacing = 4 # Use 5MHz spacing between ul and dl (for 400 MHz band)
duplex_spacing = 0 # set to 7 to use custom duplex spacing below
#custom_duplex_spacing = 10000000 # Don't uncomment unless you have programmed a custom duplex spacing entry in your radios
freq_offset = 12500 # Offset from carrier. Usually 0. Options: 0, 6250, -6250, 12500.
reverse_operation = false # False: UL below DL. True: UL above DL.

# Location Area identifier
location_area = 2

# Colour code (0-63), helps distinguish between adjacent cells on the same frequency
colour_code = 1

###############################################################################

# OPTIONAL: Additional cell parameters
# WIP; uncomment and configure as needed, many are not currently implemented,
# defaults should be fine

# Neighbor cell broadcast settings
# neighbor_cell_broadcast = 0

# Cell load (Channel Allocation) - current load
# cell_load_ca = 0

# Late entry support - allows joining ongoing group calls
# late_entry_supported = true

# Subscriber class - defines which MS classes can access this cell
subscriber_class = 0xFFFF

# Registration and mobility features
registration = true
deregistration = true
# priority_cell = false
# no_minimum_mode = false
# migration = false

# Service availability flags
system_wide_services = true # If false, radios will operate in fallback mode (ignored when Brew enabled)
voice_service = true
# circuit_mode_data_service = true
# sndcp_service = true
# aie_service = false
# advanced_link = false

# System code (0-15) - identifies the TETRA system version
system_code = 0

# Sharing mode for the main control channel
# sharing_mode = 0

# Reserved frames for time slot allocation
# ts_reserved_frames = 0

# Discontinuous Transmission (DTX) on user plane
# u_plane_dtx = false

# Frame 18 extension support
# frame_18_ext = false

# IANA timezone for D-NWRK-BROADCAST time broadcasting. When set, the BS will
# broadcast UTC time and local time offset once per hyperframe (~61s) so MSs
# can synchronize their clocks. Handles DST automatically.
# timezone = "Europe/Amsterdam"
timezone = "Europe/Paris"

# Local SSI ranges for this cell. Traffic for these ranges will not leave the cell
# This overrides any settings in brew section, if enabled.
# Also, incoming brew traffic on those rangges will be dropped
# Range end is exclusive, so the range end number is NOT inside the ranges
# local_ssi_ranges = [
# [0, 91],
# ]
local_ssi_ranges = [
[1000, 3000],
]

###############################################################################

# Brew protocol: Connect to TetraPack/BrandMeister server via TETRA Homebrew Protocol.
# All groups that radios attach to are forwarded to Brew as affiliations.
# Uncomment this section to automatically load and use Brew entity
# See: https://wiki.tetrapack.online/books/tetra/page/brew

# [brew]

# Server address
# host = "core.tetrapack.online"
# port = 443

# Use TLS (wss:// / https://)
# tls = true

# HTTP Digest auth credentials, as registered in BrandMeister
# Username should be your SSID (DMR ID + 2-digit suffix).
# Password is your BrandMeister hotspot password.
# username = 123456700
# password = "012345"

# Reconnection delay (seconds)
# reconnect_delay_secs = 15

# Optional: additional initial latency compensation in frames for inbound Brew jitter playout.
# Adaptive jitter buffering is always enabled; this adds fixed startup delay if needed.
# jitter_initial_latency_frames = 0

# Enable SDS forwarding between local and Brew clients. Enabled by default.
# feature_sds_enabled = true

# Uncomment to allow only calls for select SSIs to be transmitted over Brew
# SDS works for all SSIs, currently, but the SDS over Brew feature may be fully disabled.
# If left commented, all (outside of local_ssi_ranges) calls are allowed over Brew
# whitelisted_ssis = [91]

Ce qu’il y a à savoir :

  • duplex_spacing est défini à l’index 0 car j’utilisais des portatifs paeramétrés sur un réseau pro avec un écart duplex de 10MHz. La valeur de cet index doit être aussi égale à 10MHz dans le paramétrage du poste TETRA ;
  • freq_offset dépend du paramétrage d’offset défini dans le paramétrage du poste TETRA. Dans mon cas, l’offset est de 12.5kHz soit 12500Hz ;
  • tx_freq doit être strictement identique au paramétrage défini dans main_carrier et freq_offset, en suivant la formule de calcul suivante : tx_freq = (freq_band * 100) + (main_carrier * 25) + (freq_offset / 100). Concrètement, dans l’exemple que je vous fournis, cela donne tx_freq = (400000) + (992 * 25) + 12.5 = 400000 + 24800 + 12.5 = 424812,5 MHz, soit 424812500 Hz ;
  • rx_freq doit être strictement identique à : tx_freq – valeur réelle du duplex_spacing, soit 424812500 – 10000000 = 414812500.
  • J’ai dû définir explicitement le paramétrage de rx_gain_pga, tx_gain_pga, rx_antenna et tx_antenna pour que la gestion de la porteuse TETRA ne plante pas avec des dizaines d’erreurs par secondes affichées à l’écran dès l’inscription des postes TETRA sur le réseau ;
  • J’ai décommenté les fonctions registration, deregistration et subscriber_class.

Une fois le fichier édité et les modifications enregistrées, on peut lancer TETRA BlueStation avec la commande suivante :

chrt -f 73 ./target/release/bluestation-bs ./config.toml

Ici on observe l’activation de la cellule et l’enregistrement d’un poste sur le réseau.
Les appels phonie et les envois de message texte fonctionnent. C’est un succès!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *


La période de vérification reCAPTCHA a expiré. Veuillez recharger la page.