Howto: Meteor M2 weather satellite image decoding

TL;DR: Got Meteor M2 Weather satellite decoding. Here are the steps:

I am new to Satnogs, and had just bought a Raspberry Pi 4 and a RTL-SDR dongle with the dipole antenna kit. I received some NOAA satellites, and was wondering why the Meteor M2 (40069) did not work. Looking at another station that uploaded beautiful pictures, I soon realized that my waterfall was to narrow. Searching this forum I found a lot of information ( and, but it seemed a bit out dated, and it was not a straight forward manual. I was also a bit surprised that Meteor M2 was not supported out of the box. I decided to give it a try and also document the steps. The fun part in doing so, is that I learned a lot!

Step 1: Install LRPT
The Meteor is using LRPT modulation, which needs to be added. Installing LRPT, requires cmake to be available on the Raspberry Pi. It can be installed with:

sudo apt-get install cmake

Next is somewhat based upon the of Clone the repository and switch to the LRPT branch and compile the source:

cd ~
git clone
cd satnogs-flowgraphs
git checkout lrpt
mkdir build
cd build
cmake ..

Here I made a small mistake. After make, I issued the make install, but I probably shouldn’t. Because it overwrites a couple of files, containing the other demodulators. I did not want that, but I realized it too late. Do NOT issue: sudo make install as this will install everything. See:

[  7%] Built target sstv_pd120_demod
[ 15%] Built target iq_receiver
[ 23%] Built target bpsk
[ 30%] Built target cw_decoder
[ 38%] Built target example_flowgraph
[ 46%] Built target fm
[ 53%] Built target afsk1200_ax25
[ 61%] Built target fsk
[ 69%] Built target argos_bpsk_ldr
[ 76%] Built target amsat_fox_duv_decoder
[ 84%] Built target lrpt_demod
[ 92%] Built target noaa_apt_decoder
[100%] Built target reaktor_hello_world_fsk9600_decoder
-- Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/
-- Installing: /usr/local/bin/

This worked for me, and it suggest to place the files in /usr/local/bin. But this did not work for others. They needed to place the in /usr/bin. I suggest you find out where your already installed demodulator python files are by issueing:

ls -l /usr/local/bin/*.py
ls -l /usr/bin/*.py

One of these two commands should give a folder like:

Copy build/satellites/ to the same folder as where the other demodulator python files are:

sudo cp satellites/ /usr/bin/
cd /usr/bin
sudo chown root:root
sudo chmod +755

If you are running satnogs client 1.5 or newer, then this file needs an update. There where some changes between version 1.4 and 1.5, which are not backwards compatible. Edit the file and added support for 4 new arguments to the definition:

sudo nano

Find the definition argument_parser:

def argument_parser():

At the end of it add:

        "--udp-IP", dest="udp_IP", type=str, default="",
        help="Set udp_IP [default=%(default)r]")
        "--udp-dump-host", dest="udp_dump_host", type=str, default="",
        help="Set udp_dump_host [default=%(default)r]")
        "--udp-dump-port", dest="udp_dump_port", type=intx, default=57356,
        help="Set udp_dump_port [default=%(default)r]")
        "--udp-port", dest="udp_port", type=intx, default=16887,
        help="Set udp_port [default=%(default)r]")

That is just before:

return parser

The demodulator is in place, but Satnogs still does not about know it. We need to edit some python script file to add it. Python uses indentation to figure out what blocks of code belong to each other. Make sure that you have the right amount of spaces for each line! Following this post The file is located here:

cd /var/lib/satnogs/lib/python3.7/site-packages/satnogsclient/radio
sudo nano

Append to

    'LRPT': ''

Don’t forget to add a , to the previous line! and make sure it looks like this:

Append to

        'LRPT': {
            'script_name': SATNOGS_FLOWGRAPH_SCRIPTS['LRPT'],
            'has_baudrate': False,
            'has_framing': False

Again, add the , to the previous line! Last part of the SATNOGS_FLOWGRAPH_MODES variable should look like this:

The satnogs-client needs to be restarted to adapt these changes, either by

sudo systemctl restart satnogs-client 

Or rebooting the Raspberry Pi.

Now we have completed the first step we can test this step. Schedule a Meteor M2 40069 pass and watch the /tmp directory during the observation. There should be a .s file placed like: data_3618506_2021-02-10T07-58-37.s and your waterfall should have a bandwidth from at least -60 to +60kHz:

Step 2: Decode to images
Now we have a successful recording of the satellite, we need to add a post observation script to process the data and generate the images. This script however depends on two binaries that need to be installed. One is artlav meteor_decoder, Luckily a binary is provided. Download medet_190825_arm.tar.gz from

cd ~
mkdir medet
cd medet
tar xvfz medet_190825_arm.tar.gz

Which gives:

-rwxr-xr-x   1 pi pi 158380 Aug 25  2019 medet_arm
-rw-r--r--   1 pi pi   2035 Aug 25  2019 readme.txt

We can test medet_arm by running:


This should give the help message.

Second binary needed is convert by ImageMagick:

sudo apt-get install imagemagick

Now the post processing script is needed:

cd ~
git clone

This will give the post processing script in /satnogs-extras/scripts We need to edit this script a bit, to point to the two binaries it uses.

cd satnogs-extras/scripts

Change the path of Medet to where the medet_arm binary lives. The path of convert does not need to be changed:

MEDET_PATH = "/home/pi/medet/medet_arm"
CONVERT_PATH = "convert"

A few lines further I also changed the waiting time to 10 seconds:


Next run it manually on the previously received .s file.
sudo python ./

This will take a few minutes and result in:

Waiting for 10 seconds before processing.
Attempting to process: /tmp/data_3614689_2021-02-09T18-07-17.s
Total:        159.856415
Processing:   2.866195
Correlation:  78.260475
Viterbi:      71.765358
ECC:          6.951093
Remainder:    0.013307
Packets:      1504 / 4342
Elapsed time: 00:03:32.988
convert-im6.q16: length and filesize do not match `/tmp/meteor_image_temp_vis.bmp' @ warning/bmp.c/ReadBMPImage/839.
Total:        2.846459
Processing:   2.844458
Correlation:  0.000000
Viterbi:      0.000000
ECC:          0.000000
Remainder:    0.002001
Packets:      1504 / 1504
Elapsed time: 00:03:32.988
convert-im6.q16: length and filesize do not match `/tmp/meteor_image_temp_ir.bmp' @ warning/bmp.c/ReadBMPImage/839.
VIS processing successful!
IR processing successful!

Two images are written to /tmp/.satnogs/data to be uploaded:

-rw-r--r-- 1 root    root     532430 Feb 10 10:01 data_3614689_2021-02-09T18-07-17_ir.png
-rw-r--r-- 1 root    root    1073324 Feb 10 10:01 data_3614689_2021-02-09T18-07-17_vis.png

The processed *.s files are moved from /tmp to /tmp/.satnogs/data/complete. Check if this directory already exists. Otherwise:

cd /tmp/.satnogs/data
sudo mkdir complete
sudo chown satnogs:satnogs complete
ls -l

Should give:

drwxr-xr-x 2 satnogs satnogs 40 Feb 14 13:09 complete
drwxr-xr-x 2 satnogs satnogs 40 Feb 13 10:16 incomplete

Step 3: Add post observation script
To finalize the automation, make a post observation script:

cd ~

Add the following:


#Maybe you need to do something with the Bias T:
#/home/pi/rtl_biast/build/src/rtl_biast -b 0
python /home/pi/satnogs-extras/scripts/  "$1" "$2" "$3" "$4"


Make it executeable:

sudo chmod +777

Edit your Satnogs setup to run this script:

sudo satnogs-setup

Go to Advanced -> Scripts -> SATNOGS_POST_OBSERVATI. Type:

/home/pi/ --id {{ID}} --tle {{TLE}}

Should look like:

Step 4: Finished
That’s it, it should work now. Schedule another pass of Meteor M2 (40069) and see if the images get uploaded. See this observation for the following image:

Step 5: Tweaking
After running this for some time, I found that a few things can be improved. But this is also a matter of personal taste. A problem is that the complete directory goes missing after a reboot, and the temporary files that are stored there are gone. But if everything works, there is no reason to store these files anyway, and we can have them removed. Secondly the images can use a bit of photoshop. Luckily we have the utility convert already working in the script, which can easily do these photoshop tasks. I like the pictures to have North pointing up, but if the satellite flies from south to north, it gets upside down. The convert can fix this with the -rotate 180 argument. The infrared image has the colors flipped. I like the clouds to be white and the sea to be black, but currently that is the other way around. The -negate argument fixes this. The color range used is quite small, giving a low contrast. There are several options to fix this, I used -linear-stretch. So fire up nano to edit the script:

cd ~
cd satnogs-extras/scripts/
cp process_meteor.backup

We are going to rotate based on time. Of course it should be better to use the TLE to figure out which way the Meteor satellite is flying, but that is a future project :slight_smile: So for time, Add

import time

At the imports:

Next locate the define of convert_image, and replace the code with:

def convert_image(suffix = ""):
    Use the 'convert' utility (from imagemagick) to convert
    a set of resultant METEOR images.

    raw_image_path = TEMP_DIR + TEMP_FILENAME + suffix + ".bmp"
    result_image = TEMP_DIR + TEMP_FILENAME + suffix + ".png"
    # get the current time.
    hour = time.localtime(time.time()).tm_hour

    # Call convert to convert the image
    # Check the suffix.
    if suffix == "_ir":
        # suffix is ir, need to -negate to invert colors, and -linear-stretch to improve contrast
        # Check time
        if (hour>12):
            # After 12 UT, so evening pass, need to rotate
  [CONVERT_PATH, "-negate", "-rotate", "180", "-linear-stretch", "1x1%", raw_image_path, result_image])
            # Before 12 UT, morning pass no need to rotate
  [CONVERT_PATH, "-negate", "-linear-stretch", "1x1%", raw_image_path, result_image])

        # suffix is vis.
        if (hour>12):
            # After 12 UT, so evening pass, need to rotate
  [CONVERT_PATH, "-rotate", "180", raw_image_path, result_image])
            # Before 12 UT, morning pass no need to rotate
  [CONVERT_PATH, raw_image_path, result_image])

    # See if a resultant image was produced.
    if os.path.isfile(result_image):
        return result_image
        return None

Depending on your time zone you may need to set the time of 12 UT to some other value to get a clear separation between the two type of passes. Also you can add or remove arguments to convert, if you like to change the images a bit more.

Almost at the end of the file there is a move command to move the .s file to the complete directory. i replaced it with a remove:

        # Move file processed file into complete directory
        # shutil.move(_file, RAW_DESTINATION_DIR + os.path.basename(_file))
        # Replaced the move, with remove:

Now save the file, and schedule some passes, to check if the pictures get north up, and if the infrared looks a bit more dramatic like:

Update 14-02-2021:
Added a sudo to the copy command, and added 2 pictures of how the file should look. Also added the creation of the complete directory.
Update 15-02-2021:
Added setting of rights and owner of
Update 16-02-2021:
Changed folder of to /usr/bin
Added screenshot of satnogs-update showing post observation script. Improved the command blocks.
Update 18-02-2021:
Updated the script to include a few echos to allow easier debugging. Added the Tweaking step.
Update 16-06-2021:
Added compatibility for satnogs client 1.5 by adding support for 4 new arguments to the definition of argument_parser.

I hope this helps others to get images from Meteor M2. Feel free to comment and improve! But be aware I don’t have all the answers! Best regards, Eelke.


You can do this much easier: just install r2cloud and it will produce LRPT images out-of-the-box:

1 Like

@EelkeVisser Great work! Thanks for this guide!

I would suggest you add it in the related wiki page to be more visible.

Yes, but this isn’t integrated with SatNOGS, which is kind of the point of this discussion.

1 Like

Good to see this working again, and a nice collation of information on how to get it going! I expected it could be done with a suitable flowgraph update, and it’s good to see this has happened.

Given only one flowgraph change is required, it may be worth pulling that particular flowgraph out of the satnogs-flowgraph fork rather than having to maintain a separate fork as other things change around it. I can probably add this updated flowgraph into my satnogs-extras repo - I may look into this on the weekend if I can find time.

I know there were plans to add a ‘live’ LRPT demodulator into gr-satnogs, but i dont know where this has gotten do. It may be possible to produce a compromise flowgraph which can be merged into satnogs-flowgraphs, with the soft-bit output enabled ‘on demand’ somehow, so that those interested in running the LRPT demod (as a post-processing script) can do so.

1 Like

Hi Eelke
you miss “sudo” with this line…


Is this normal my Rpi turn offline with server after Step1…?

Here my logs status after completing steps. VE2DSK

Hi, nice to hear that you are trying! Sorry it didn’t work out for you. I am no satnogs expert, but at the end of step one there is a restart of the client. It will be offline for a short while, but I think it is too short, to be noticed by the network. It seems that the real problem is that it doesn’t start properly. Maybe this gives a hint what is happening:

journalctl -f -u satnogs-client.service -n 400

@ve2dsk You are right. The sudo is indeed needed and it should be:

sudo cp satellites/ /usr/local/bin/

Also the editing of the could have been clearer. Python uses indentation to figure out what blocks of code belong to each other. Make sure that you have the right amount of spaces for each line! The variable SATNOGS_FLOWGRAPH_SCRIPTS should look like:

And the last part of the variable SATNOGS_FLOWGRAPH_MODES should look like:

Update 14-02-2021:
The missing sudo and the pictures where added to the first post.

Hi Eelke…
working now…
The only thing is post script does not delivery data.

Is the postob file should be own by satnogs:satnogs…??


Hi, no it is owned by pi:pi at my station.

How does /tmp look? Any *.s files?
And in /tmp/.satnogs/data/complete?
Files are first places in /tmp and after processing moved to complete.

Only other thing I can think of, is that I was impatient and reduced the waiting time from 120 to 10 seconds. Maybe your setup is working but too slow. See

I miss the last MetM2 pass, but i thing there is no .s file ceated.
I’ll confirm next pass later. Many thanks for support.

Maybe take it step by step? Disable the So you can see if the .s file gets generated and the waterfall gets uploaded. Then run postob manually?

I don’t know if i’m correct with this line…?
Is my ID is a number or “ID”…?

That is OK. The ID of the sat is passed to the script, but I have my doubt if it is being used.

Is there a space between --id and {{ ?
Same for --tle and {{ there should be a space.

Ops… no space are there…
Should be my problem… thanks… VE2DSK

Having followed the Howto I’m unable to get anything other than Failed Observations.
What and Where should i be looking to figure things out.

All the other sat observations are working as before on my station so it must be something not set correctly for the LRPT Observations.


Hi Karl,

Thanks for trying this! But it could be the rights and owner of Try:

sudo cp satellites/ /usr/local/bin/
cd /usr/local/bin
sudo chown root:root
sudo chmod +755

What does the log show:
journalctl -f -u satnogs-client.service -n 400
For this observation?



I hope I’ve done this correct.

Urrrrr, your directory looks a bit different. I had expected the other decoders in /usr/local/bin but have just found that I also have them in /usr/bin

Can you copy the also to /usr/bin And check if the other demodulators are there?

I am starting to think that the sudo make install, which I wanted to skip, did more than just copy the files.

The log you post appears to have been cleared after a restart. So nothing to see, unfortunately.