Автоматизуємо процес прийому зображень з супутників NOAA

Опубліковано 13.05.2025, 14:28 в категорії YouTube

"Raspberry Pi + RTL-SDR"

Raspberry Pi, RTL-SDR, та гарна антена, це доволі потужна комбінація, яка може відкрити для вас цілу купу нових можливостей. Наприклад найбанальніше, але від того не менш цікаве - за одну команду підняти rtl_tcp сервер. Це дасть вам змогу не прив'язуватись до RTL-SDR та антени, і використовувати їх будь де, з будь якого пристрою, в зоні дії вашої домашньої мережі.

Таким чином можна наприклад суттєво укоротити фідерну лінію антени, адже не треба тягнути її аж до вашого основного пристрою. Але в даній статті я розповім, як я за допомогою цього всього автоматизував:

  • прийом
  • декодування
  • публікацію зображень в інтернет з супутників NOAA.

Спочатку трішки поговоримо про сам проєкт, а потім я розповім про скрипти, за допомогою яких я це все реалізував.

Проєкт noaa_in_ukraine

"Сторінка проєкту"

Ітак, перед вами сторінка проєкту в Mastodon. Саме сюди кожного дня публікуються зображення з супутників NOAA 18 або NOAA 19. Я обрав саме Mastodon, бо інших нормальних альтернатив не знаю, + це концептуально доволі цікава соц. мережа, і хоч я сам не фанат соц. мереж, але думаю багатьом це було б цікаво. Про дану соц. мережу є доволі гарне відео від каналу ҐАМЕРУА, тож раджу ознайомитись.

Відповідно, якщо у вас є там обліковий запис, можете підписатись, і отримувати щодня в стрічку актуальні зображення з супутників. Якщо його немає, то можна і без реєстрації зайти і подивитись. Зображень там вже вдосталь, і є навіть парочка цікавих.

"Циклон :0"

Антена з попередньої статті до речі вже доволі довго стоїть, і навіть вистояла пару дуже вітряних днів, тож думаю за неї можна не хвилюватись. Розтяжні мотузки роблять свою справу, хоч їх і довелось один раз переробляти. А на випадок блекаутів - для Raspberry Pi в мене є круте джерело безперебійного живлення, дякуючи Артему. Тож в теорії ніщо не повинно заважати всьому цьому працювати.

Сподіваюсь в мене вийде якомога довше підтримувати цей проєкт, і назбирати цілу купу зображень, поки супутники ще функціонують. Тож буду радий якщо ви завітаєте на сторінку проєкту.

Технічна сторона

А тепер поговоримо про реалізацію. Одразу хочу сказати, що для автоматичного прийому і декодування погодних супутників вже є готове рішення, яке називається raspberry-noaa-v2 - в ній реалізовано все що треба, ще й прикольний веб інтерфейс є.

Але цей проєкт мені не сподобався тим, що він заточений лише під Debian, ще й для його функціонування треба вимикати пароль на Root доступ. "Moving files around in the audio and image directory" звучить як дуже гарний привід використовувати Root доступ, ага)))0

Копатись в чужому коді я не хотів, тому я вирішив написати купку баш скриптів, які зроблять все що мені потрібно. Але перш ніж ми на них поглянемо, давайте спочатку подивимось на залежності цих скриптів.

Залежності скриптів

Спочатку йде пакет rtl-sdr. Сюди входить купка драйверів та різних утиліт, з яких нам потрібна rtl_fm — термінальна програма для взаємодії з приймачем RTL-SDR. Вона ініціалізує його, налаштовує на потрібну частоту, включає frequency modulation, й виводить все що чує в файл, або stdout.

Stdout - це потік, у який програми виводять свої вихідні дані - зазвичай це сам термінал, але ми маємо змогу через пайплайни перенаправити цей потік в іншу програму, яка наприклад вміє працювати зі звуком. Відповідно саме так скрипт і записує сигнал з супутника.

Варто зазначити що через те що в мене RTL-SDR V4, довелось вручну компілювати цей пакет з репозиторіїв RTL-SDR. В мене насправді дуже довго не виходило змусити RTL-SDR працювати на Raspberry Pi. Я багато чого спробував, по декілька разів все компілював, але проблема виявилась в іншому місці - а саме в USB контролері, який встановлено в 4й версії Raspberry Pi. В ядрі був баг, через який RTL-SDR і не працював. Тому щоб це виправити, достатньо було всього лише встановити ядро від Raspberry Pi Foundation, де цей баг вже давно пофіксили. На щастя це ядро було в репозиторіях Arch Linux ARM, і мені не довелось його компілювати власноруч.

Далі йде sox, або ж Sound eXchange - це програма для усілякої роботи зі звуком і аудіофайлами - запис, програвання, конвертація, і тд. Саме в неї через пайплайн перенаправляються дані з rtl_fm, і sox робить з них .wav файл. Звісно rtl_fm і сам може записати аудіо в файл, але використання sound exchangera в даному випадку дає більше контролю над процесом запису.

Далі - програма під назвою predict - дуууже стара термінальна програма для відстеження супутників. Відповідно саме завдяки ній, скрипт і розуміє коли слід починати запис. На жаль скоріше за все ви не знайдете цю програму в репозиторіях вашого дистрибутиву, тому її треба буде скомпілювати з вихідного коду. Після встановлення програми, її треба запустити, та ввести свої координати та висоту, і після цього скрипт зможе використовувати predict для відстеження супутників.

Для декодування зображень скрипт використовує все той же noaa apt image decoder. Її теж може не бути репозиторіях вашого дистрибутиву, але компілювати її не обов'язково, адже на сайті є вже готові бінарники для всіх сучасних архітектур. Дана програма має термінальну реалізацію, що дає змогу не тільки автоматизувати рутинний UI-шний процес, а ще й легко додавати її до скриптів, що я і зробив. Але мені хотілось додати унікальності до отриманих зображень, тому давайте детальніше розглянемо як саме програма додає оверлей з кордонами країн.

Вона це робить, використовуючи так звані шейпфайли. Це доволі поширений формат який використовується у всяких різноманітних ГІС системах. Шейпфайли не вбудовані в бінарник, і в десктопній версії лежать за ось цим шляхом: /usr/share/noaa-apt/shapefiles/.а якщо ви качали бінарники з сайту, то вони знаходяться в директорії res.

Для самої програми потрібні лише файли в форматі .shp, але щоб нормально редагувати шейпфайли, потрібен ще й файл в форматі .dbf, в якому лежить інформація про кожний окремий шейп. Автор програми люб'язно залишив для нас посилання, перейшовши за яким, можна завантажити всі необхідні файли. Відповідно їх можна відредагувати за допомогою якоїсь програми для редагування шейпфайлів.

Я використав онлайн програму mapshaper, і за допомогою ось цієї команди, прибрав всі країни окрім України:

-filter 'ADMIN == "Ukraine"'

Тепер, ввівши флаг -o, від слова Output, можна зберегти нові шейпфайли, і якщо підмінити файл кантріс який використовує програма, вона буде малювати лише Україну:

-o ukraine_only.shp

Я також повністю очистив файл states.shp, щоб програма не малювала області.

"noaa apt image decoder з модифікованим .shp файлом"

Чудово. Тепер можна рухатись далі.

Наступна програма зі списку - ImageMagick. Вона потрібна для того щоб конвертувати зображення з PNG в JPG, тим самим зменшивши його розмір, бо без цього його не вийде запостити в Mastodon, так як PNG важить більше 10 мегабайт.

Ще для скриптів потрібен wget, щоб завантажити TLE файли, але про них згодом.

Ну і на останок - cron та at. Саме завдяки цим програмам досягається повна автоматизація процесу, адже cron дозволяє регулярно виконувати команди за розкладом, а at дозволяє виконати команду один раз у заданий час. Обійтись одним лише cron-ом було б важко, адже час проходу супутників, хоч і в певному діапазоні, але постійно змінюється. Після встановлення програм головне не забути запустити їх сервіси:

sudo systemctl enable cronie
sudo systemctl start cronie

sudo systemctl enable atd
sudo systemctl start atd

Скрипти

Тепер можна перейти і до самих скриптів. Я написав 5 bash скриптів та 1 скрипт на Python. Звісно мабуть можна було б запхати все це в один скрипт, але мені захотілось розділити все. Але перш ніж ми поглянемо на код - хочу одразу сказати, що я не програміст. Знання англійської мови та деякого синтаксису дозволяють мені писати код, але цього недостатньо щоб бути програмістом. Я багато чого не знаю в цьому плані, тому прошу тримати це в голові. Також я не ставив за мету створити готовий проєкт, який будь хто зможе за одну команду запустити в себе. Скоріше за все вам треба буде адаптувати скрипти під себе, як мінімум редагувати шляхи.


get_tle.sh

#!/bin/bash

wget -q https://celestrak.org/NORAD/elements/weather.txt -O temp_tle && mv temp_tle tle.txt

Ітак, перший скрипт, get_tle.sh, за допомогою wget завантажує TLE для погодних супутників з сайту celestrak. TLE, або ж Two-Line Element Set це формат даних, що описує орбіту супутника. Саме за допомогою цих даних програми розуміють де знаходяться і де будуть знаходитись супутники, і саме завдяки ним, noaa apt image decoder може наносити на зображення кордони країн, бо програма по часу розуміє, де був супутник, і що він в цей момент бачив.

Слід зазначити, що через купу різних космічних і не тільки явищ, орбіта супутників постійно змінюється, і TLE файли з часом втрачають свою актуальність, тому цей скрипт треба запускати регулярно, наприклад кожні 2-3 дні. Я додав його в крон, він виглядає ось так, тобто я запускаю цей скрипт о третій ночі, кожні 2 дні:

0 3 */2 * * bash /home/ctl/get_tle.sh

Тут використовується тимчасовий файл, та подвійна амперсанда, щоб якщо шось піде не так - буде відсутній інтернет, або ще шось, не перезаписати існуючий TLE файл.


noaa_scheduler.sh

#!/bin/bash

# Set time range for satellites prediction (36000 seconds = 10 hours)
predict_start=$(date -d "now" +%s)
predict_end=$((predict_start + 36000))

# Determine highest elevation angle in specified time range of NOAA 18 and NOAA 19
read -r elev18 time18 <<< $(/usr/local/bin/predict -t tle.txt -f "NOAA 18" $predict_start $predict_end | awk '{print $5, $1}' | sort -nr | head -n1)
read -r elev19 time19 <<< $(/usr/local/bin/predict -t tle.txt -f "NOAA 19" $predict_start $predict_end | awk '{print $5, $1}' | sort -nr | head -n1)

# Determine which satellite has better elevation angle
better_pass=$(awk -v e18="$elev18" -v e19="$elev19" 'BEGIN {print (e18 > e19) ? 18 : 19}')
if [ "$better_pass" -eq 18 ]; then
    sat="noaa_18"
    nu=18
    peak=$time18
    elev=$elev18
else
    sat="noaa_19"
    nu=19
    peak=$time19
    elev=$elev19
fi

# Calculate time variables and set filenames
# 450 = 7.5 minutes usually half time of the good satellite pass
# 120 = 2 minutes pause between each 'at' command, to ensure that they do not overlap each other
start_pass=$((peak - 450))
end_pass=$((peak + 450))
process_image_time=$((end_pass + 120))
posting_image_time=$((process_image_time + 120))
formated_date=$(LC_TIME=uk_UA.UTF-8 date -d @$start_pass "+%A %d.%m.%Y %H:%M:%S")
filename="${sat}_$(date -d @$start_pass +%d_%m_%y_%H%M%S).wav"
image="${sat}_$(date -d @$start_pass +%d_%m_%y_%H%M%S).jpg"

# Set frequency depending on satellite
[[ $nu -eq 19 ]] && freq="137.1M" || freq="137.9125M"

# Schedule the command execution for satellite pass recording, image processing, and publishing image to Mastodon
# 'sleep' is used to achieve second precision, since 'at' does not work with seconds
echo "sleep $(date -d @$start_pass +%S); /usr/local/bin/rtl_fm -f $freq -s 80k -g 49.6 -p 0 -F 9 -E dc | sox -t raw -r 80k -e signed -b16 -c1 - $filename trim 0 900" | at $(date -d @$start_pass +%H%M)
echo "bash process_image.sh $filename $start_pass" | at $(date -d @$process_image_time +%H%M)
echo "bash post_image.sh $image \"$elev\" \"$formated_date\" && bash move_processed_files.sh $filename $image" | at $(date -d @$posting_image_time +%H%M) 

# Save logs into logfile_noaa
{
  echo $(date +"%Y-%m-%d %H:%M:%S")
  echo "Scheduled $sat"
  echo "Elevation: $elev°"
  echo "From $(date -d @$start_pass +"%H:%M:%S") to $(date -d @$end_pass +"%H:%M:%S")"
  echo "Peak elevation time: $(date -d @$peak +"%H:%M:%S")"
  echo "Filename: $filename"
  echo "Image name: $image"
  echo "Image Processing Time: $(date -d @$process_image_time +"%H:%M:%S")"
  echo "Image Posting Time: $(date -d @$posting_image_time +"%H:%M:%S")"
  echo ""
} | tee -a logfile_noaa

І тепер, маючи TLE файл, може функціонувати основний скрипт - noaa_scheduler.sh. Даний скрипт, за допомогою програми predict про яку я розповідав раніше, визначає найліпший прохід супутника в найближчі 10 годин, і за допомогою команди at, планує запис, обробку, та постинг зображення. Також тут формуються імена файлів, та логи, які потім записуються в лог файл, щоб можна було якщо що подивитись, де шось пішло не так.

Запис відбувається за допомогою rtl_fm та sox, а обробка та постинг за допомогою інших скриптів, які розглянемо далі. Варто зазначити, що розрахунки часу тут не супер точні, і інколи запис починається чи закінчується трішки раніше чи пізніше ніж треба. Більшої точності можна досягти, якщо використовувати predict трошки по іншому, але для спрощення скриптів я вирішив залишити все як є.

Відповідно, цей скрипт я також помістив в крон, щоб він виконувався кожного дня, о 5й ранку:

0 5 * * * bash /home/ctl/noaa_scheduler.sh

Таким чином крон запускає його, і скрипт знаходить найкращий ранковий прохід супутника, і відкладає на необхідний час запис, обробку, та публікацію зображення. Нічні зображення не дуже цікаві, тому я вирішив не записувати і не публікувати їх.

Невеличкою проблемою стало те, що програма at не працює з секундами, тому щоб досягти секундної точності виконання програм, довелось використовувати команду sleep, яка затримає виконання основної команди на потрібну кількість секунд, наприклад:

sleep 5 && echo "Hello World!"

process_image.sh

#!/bin/bash

wav_file=$1
time=$(($2+4))
satellite=${wav_file:0:7}
image_name="${satellite}_$(date -d @$2 +%d_%m_%y_%H%M%S).png"
compressed_image_name="${satellite}_$(date -d @$2 +%d_%m_%y_%H%M%S).jpg"

mv $wav_file noaa-apt/
cd noaa-apt/

./noaa-apt $wav_file -Fc histogram -P res/palettes/noaa-apt-daylight.png -R no -o $image_name -s $satellite -T ../tle.txt -t $(date -u -d @$time +"%Y-%m-%dT%H:%M:%S%:z") -m yes

magick $image_name -flatten -quality 80 $compressed_image_name

rm $image_name

Наступний скрипт - process_image.sh. Даний скрипт, передає аудіо файл і купку інших параметрів в noaa apt image decoder, після чого стискає отримане зображення в JPG, а оригінал видаляє.


post_image.sh

#!/bin/bash

cd $HOME/python-mastodon/
source venv/bin/activate

python poster.py $1 "$2" "$3"  >> logfile_python

post_image.sh навіть скриптом назвати важко - це просто невеличкий враппер, який запускає пайтонівський скрипт, і передає йому необхідні параметри. Ну і про всяк випадок ще записує аутпут скрипта в логфайл. Я вирішив зробити саме окремий скрипт для цього, бо для запуску пайтонівського скрипта треба спочатку активувати віртуальне середовище, щоб Python зміг використовувати бібліотеку Mastodon.py - тому, щоб все виглядало гарненько, використовується окремий скрипт.


poster.py

import sys
from mastodon import Mastodon

def main():
    print(sys.argv)
    if len(sys.argv) != 4:
        print("Usage: python poster.py <file> <angle> <date>")
        sys.exit(1)
    file_name = sys.argv[1]
    angle = sys.argv[2]
    date = sys.argv[3]
    satellite = file_name[0:7].upper().replace("_", " ")
    # Replace with your basedir
    basedir = "/home/ctl/noaa-apt/"

    post_text = f"""Супутник: {satellite}
Кут піднесення: {angle}°
Початок запису: {date}"""

    mastodon = Mastodon(
        access_token = '',
        api_base_url = ''
    )
    media = mastodon.media_post(basedir + file_name, description=file_name)
    mastodon.status_post(post_text, media_ids=[media])

if __name__ == "__main__":
    main()

А говорячи про пайтонівський скрипт - ось він. Тут взагалі нічого складного, скрипт просто складає необхідний текст, і постить з ним відповідне зображення, використовуючи спеціальну бібліотеку для роботи з API Мастодона. Тут вказується посилання на інстанс Мастодона (api_base_url), та токен для API (access_token), який можна отримати в налаштуваннях акаунта. Також слід змінити basedir відповідно до ваших шляхів.

Перед першим запуском скрипта, треба створити віртуальне середовище, активувати його, і встановити бібліотеку Masotdon.py:

python -m venv venv
source venv/bin/activate
pip install Mastodon.py

move_processed_files.sh

#!/bin/bash

cd $HOME/noaa-apt/
mv $1 $HOME/Recordings/
mv $2 $HOME/Pictures/

Ну і останній скрипт, move_processed_files.sh - просто переносить відпрацьовані аудіо файл та зображення в відповідні для них директорії. Без Root доступу до речі)))0

Знову ж таки, якщо будете використовувати ці скрипти, передивіться всі шляхи, і адаптуйте їх за потреби під себе. Варто також пам'ятати, що аудіофайли важать немало, і можуть дуже швидко забити карту пам'яті Raspberry Pi, тому можливо краще видаляти їх, але я вирішив певний час зберігати їх про всяк випадок.


Ось такий ось вийшов проєкт. Ось посилання на GitHub репозиторій буде в описі, можете ознайомитись більш детально. Звісно ці скрипти дууууже далекі від ідеалу, але ще жодного разу не виникало ніяких проблем, і все продовжує працювати. Тож буду радий якщо завітаєте і подивитесь на проєкт у мастадоні. Ну і також якщо не читали, можете почитати інші статті на цю тему. В мене їх ціла купа.

Щиро вдячний Артему за Raspberry Pi, а також щиро вдячний вам, моїм глядачам та читачам, бо без вас не було б сенсу все це робити.

Post Scriptum

Це текстова версія мого відео, доступного за посиланням.