Several people asked me how to build a Tetra sniffer and because it was complicated and tricky, I decided to consolidate the software into several git repositories and write this howto.
We will build a sniffer for an unencrypted network. It will save voice traffic and SDS messages.
This is how it will look like:
HW:
SW from your distribution repository:
SW that is probably not in your repository:
Install jcf from blobutils to your $PATH and run build.sh to get gr-pack and gr-unpack; get them to your $PATH too.
Install libosmocore-dev package (Ubuntu 16.10+, Debian 9+) or build libosmocore by hand (autoreconf -i && ./configure –disable-pcsc && make && make install).
Build tetra-listener (instructions are in README).
Build fcl.
Try running sds-parser.py from tetra-multiframe-sds/. If it logs “tshark died!”, you have new tshark that has renamed some fields. Change it in config.py.
tetra-listener/sniff-utils/tetra-run.sh and radio-tetra/tetra.sh (path to tetra?.py) contain paths to other executables that need to be reachable.
We use an ugly hack with two demodulators and two rtl-sdrs because our network is so wide one rtl-sdr is not enough. The two demodulators are tetra1.py and tetra2.py and they differ only in some paths (diff it). If you want to use only one radio, you can just use tetra.py from examples/ in fcl distribution.
Recommended:
ip6tables -A INPUT -p UDP --dport 4729 -j DROP iptables -A INPUT -p UDP --dport 4729 -j DROP
Calibrate your SDR and edit PPMs in tetra-run.sh.
Use your favorite SDR tool to look for Tetra BTSs and determine the center frequencies you want to listen on.
If you have different samplerate than 1.8M, edit -n and -s parameters of FCL and of course the samplerate parameter of fir.py.
If samplerate of your SDR is not an integer multiple of 36000 (2*channel symbol rate), use the nearest lower step. If the difference is too big (where “too big” probably means something like 100 Hz), it won't work. Tweaking the Omega parameter of MPSK demodulator helps. E.g. when using airspy with 2.5MHz rate, set step to 69 and omega to (2500000)/69/18000 = 2.0129.
If you have relatively slow CPU, you may want to run FCL in multiple threads. Edit the -t parameter.
Run it and use gethisto FCL command (echo gethisto | nc localhost 3333) to get sane gain settings.
You can use this oneliner to get the list of 20 strongest channels:
echo getpwr 3 | nc localhost 3333 | while read line; do n=`echo $line | cut -d " " -f 1`; if [ $(( ( $n - 1 ) % 3 )) -eq 0 ]; then echo $(( ($n - 1) / 3)) `echo $line | cut -d " " -f 2`; fi;done | LANG=C sort -nk 2 | tail -n 20 | cut -d " " -f 1 | tr "\n" ,
Edit enabled channels in tetra-run.sh so they match your channels with the strongest power. Do not enable more than 30 channels per SDR (or change the offset in tetra2.py:128).
If you change the number of channels, you need to change this number in the demodulator too (self.channels).
You can change the loglevel of sds-parser.py in tetra-run.sh to DBG.
If you now run tetra-run.sh, it should work. You should see some GSMTAP traffic on localhost:udp/4729. SDSs should be seen and recordings should pile up in tetra-rec. The number in the filename after the timestamp is the timeslot, “o” is the channel and “i” is the SSI (group ID). The SSI is not always correctly recognized because the sniffer does not handle the FDM multiplex correctly.
Use cdp to play these files. Or play the OGG files with any audio player.
It can be useful to check after a day or so which channels produce audio data (ls|grep bz2|cut -d “o” -f 2|cut -d “.” -f 1|sort -n | uniq -c) and change the ones that do not for some new channels.
You may want to add “.timeout 5000” to your ~/.sqliterc.