@@ -1,2 +1,3 @@ | |||
predict/ | |||
map/ | |||
panel.db |
@@ -11,6 +11,7 @@ | |||
- [Install the default configuration files](#install-the-default-configuration-files) | |||
- [Install Meteor software](#install-meteor-software) | |||
- [Setup Nginx](#setup-nginx) | |||
- [Setup Database](#setup-database) | |||
- [Setup RamFS](#setup-ramfs) | |||
- [Cron the scheduling job](#cron-the-scheduling-job) | |||
- [Set your Twitter credentials](#set-your-twitter-credentials) | |||
@@ -60,7 +61,13 @@ sudo apt install -yq predict \ | |||
python3-pip \ | |||
imagemagick \ | |||
libxft-dev \ | |||
libxft2 | |||
libxft2 \ | |||
libjpeg9 \ | |||
libjpeg9-dev \ | |||
socat \ | |||
php7.2-fpm \ | |||
php7.2-sqlite \ | |||
sqlite3 | |||
``` | |||
``` | |||
@@ -145,6 +152,12 @@ sudo chmod 775 /var/www/wx | |||
sudo cp templates/index.html /var/www/wx/index.html | |||
sudo cp templates/logo-small.png /var/www/wx/logo-small.png | |||
sudo systemctl restart nginx | |||
sudo cp -rp templates/webpanel/* /var/www/wx/ | |||
``` | |||
### Setup Database | |||
``` | |||
sqlite3 "panel.db" < "templates/webpanel_schema.sql" | |||
``` | |||
### Setup RamFS | |||
@@ -4,6 +4,7 @@ | |||
Most of the code and setup stolen from: [Instructables](https://www.instructables.com/id/Raspberry-Pi-NOAA-Weather-Satellite-Receiver/) | |||
## New Features! | |||
- A webpanel! | |||
- [Meteor M2 full decoding!](METEOR.md) | |||
- Nginx webserver to show images | |||
- Timestamp and satellite name overlay on every image | |||
@@ -25,7 +25,6 @@ log() { | |||
## current date and folder structure | |||
START_DATE=$(date '+%d-%m-%Y %H:%M') | |||
FOLDER_DATE="$(date +%Y)/$(date +%m)/$(date +%d)" | |||
## sane checks | |||
if [ ! -d "${NOAA_HOME}" ]; then | |||
@@ -44,8 +43,8 @@ if [ ! -d "${RAMFS_AUDIO}/audio/" ]; then | |||
mkdir -m 775 -p "${RAMFS_AUDIO}/audio/" | |||
fi | |||
if [ ! -d "${NOAA_OUTPUT}/image/" ]; then | |||
mkdir -m 775 -p "${NOAA_OUTPUT}/image/" | |||
if [ ! -d "${NOAA_OUTPUT}/images/thumb" ]; then | |||
mkdir -m 775 -p "${NOAA_OUTPUT}/images/thumb" | |||
fi | |||
if [ ! -d "${NOAA_HOME}/map/" ]; then | |||
@@ -55,7 +54,3 @@ fi | |||
if [ ! -d "${NOAA_HOME}/predict/" ]; then | |||
mkdir -m 775 -p "${NOAA_HOME}/predict/" | |||
fi | |||
if [ ! -d "${NOAA_OUTPUT}/image/${FOLDER_DATE}" ]; then | |||
mkdir -m 775 -p "${NOAA_OUTPUT}/image/${FOLDER_DATE}" | |||
fi |
@@ -54,11 +54,22 @@ sudo apt install -yq predict \ | |||
libxft2 \ | |||
libjpeg9 \ | |||
libjpeg9-dev \ | |||
socat | |||
socat \ | |||
php7.2-fpm \ | |||
php7.2-sqlite \ | |||
sqlite3 | |||
sudo pip3 install numpy ephem tweepy Pillow | |||
log_done "Packages installed" | |||
### Create the database schema | |||
if [ -e "$HOME/raspberry-noaa/panel.db" ]; then | |||
log_done "Database already created" | |||
else | |||
sqlite3 "panel.db" < "templates/webpanel_schema.sql" | |||
log_done "Database schema created" | |||
fi | |||
### Blacklist DVB modules | |||
if [ -e /etc/modprobe.d/rtlsdr.conf ]; then | |||
log_done "DVB modules were already blacklisted" | |||
@@ -173,12 +184,8 @@ sudo cp templates/nginx.cfg /etc/nginx/sites-enabled/default | |||
sudo chmod 775 /var/www/wx | |||
) | |||
sudo systemctl restart nginx | |||
if [ ! -e /var/www/wx/index.html ]; then | |||
sudo cp templates/index.html /var/www/wx/index.html | |||
fi | |||
if [ ! -e /var/www/wx/logo-small.png ]; then | |||
sudo cp templates/logo-small.png /var/www/wx/logo-small.png | |||
fi | |||
sudo cp -rp templates/webpanel/* /var/www/wx/ | |||
log_done "Nginx configured" | |||
### Setup ramFS | |||
@@ -229,8 +236,10 @@ sed -i -e "s/change_latitude/${lat}/g;s/change_longitude/${lon}/g" "$HOME/.wxtoi | |||
sed -i -e "s/change_latitude/${lat}/g;s/change_longitude/$(echo "$lon * -1" | bc)/g" "$HOME/.predict/predict.qth" | |||
sed -i -e "s/change_latitude/${lat}/g;s/change_longitude/${lon}/g;s/change_tz/$(echo "$timezone * -1" | bc)/g" "sun.py" | |||
# Running WXTOIMG to have the user accept the licensing agreement | |||
wxtoimg | |||
### Launch scheduler | |||
newgrp www-data << END | |||
/home/pi/raspberry-noaa/schedule.sh | |||
END | |||
success "Install done! Double check your $HOME/.noaa.conf settings" | |||
@@ -238,3 +247,6 @@ echo " | |||
If you want to post your images to Twitter, please setup | |||
your Twitter credentials on $HOME/.tweepy.conf | |||
" | |||
### Running WXTOIMG to have the user accept the licensing agreement | |||
wxtoimg |
@@ -0,0 +1,94 @@ | |||
#!/bin/bash | |||
## import common lib | |||
. "$HOME/.noaa.conf" | |||
. "$HOME/.tweepy.conf" | |||
. "$NOAA_HOME/common.sh" | |||
# Free disk space | |||
FREE_DISK="$(df | grep "/dev/root" | awk {'print $3'})" | |||
# This is the original path where images were stored | |||
BASEPATH="/var/www/wx/image" | |||
# Size of the old images folder | |||
IMAGEPATH_SIZE="$(du -s $BASEPATH | awk {'print $1'})" | |||
SPACE_NEEDED="$((IMAGEPATH_SIZE * 2))" | |||
if [ "$SPACE_NEEDED" -gt "$FREE_DISK" ]; then | |||
echo "You need more free space" | |||
exit 1 | |||
fi | |||
# This is the destination path (AKA the new path) | |||
FINALPATH="/var/www/wx/images" | |||
# This is a list of satellite names | |||
SAT_NAMES="NOAA15 NOAA18 NOAA19 METEOR-M2" | |||
# Here's where the database will live | |||
DB_PATH="${NOAA_HOME}/panel.db" | |||
( | |||
# To speed up the migration process and | |||
# reduce the SD card wear, the database | |||
# operations are done over the RAMFS | |||
# partition | |||
cd "${RAMFS_AUDIO}" || exit 1 | |||
sqlite3 "$DB_PATH" < "${NOAA_HOME}/templates/webpanel_schema.sql" | |||
) | |||
cd "$BASEPATH" || exit 1 | |||
# The webpanel have thumbnails! | |||
mkdir -p "$FINALPATH/thumb" | |||
# Find all the images | |||
for filename in $(find . -name *.jpg); do | |||
# Grab just the filename without the yyyy/mm/dd path | |||
basename="$(echo "$filename" | sed 's~.*/~~')" | |||
for prefix in $SAT_NAMES; do | |||
basedate="$(echo "$basename" | sed -e "s/^$prefix//" | cut -f1,2 -d'-' | sed -e "s/-//")" | |||
if [[ $basename == *"$prefix"* ]]; then | |||
# Grab the satellite name from the file name | |||
sat_name=$prefix | |||
fi | |||
done | |||
date_normalized=$(echo "$basedate" | sed -e "s/^$sat_name//;s/./&:/12;s/./&:/10;s/./& /8;s/./&\//6;s/./&\//4") | |||
epoch_date=$(date "+%s" -d "$date_normalized") | |||
if [[ $basename == *"METEOR"* ]]; then | |||
# Meteor files have one more dash on its name | |||
passname=$(echo "$basename" | cut -f1,2,3 -d'-') | |||
else | |||
passname=$(echo "$basename" | cut -f1,2 -d'-') | |||
fi | |||
echo "Migration in progress: $basename" | |||
cp "$BASEPATH/$filename" "$FINALPATH" | |||
# Create thumbnails for old images | |||
convert -thumbnail 300 "$BASEPATH/$filename" "$FINALPATH/thumb/$basename" | |||
if [[ $basename == *"METEOR"* ]]; then | |||
# Insert each pass on the database. Also insert the pass prediction | |||
sqlite3 "${RAMFS_AUDIO}/panel.db" "INSERT INTO decoded_passes (pass_start, file_path, daylight_pass, is_noaa) VALUES ($epoch_date,\"$passname\",1,0);" | |||
sqlite3 "${RAMFS_AUDIO}/panel.db" "INSERT OR REPLACE INTO predict_passes (sat_name,pass_start,pass_end,max_elev) VALUES (\"$sat_name\",$epoch_date,$epoch_date,0);" | |||
elif [[ $basename == *"ZA"* ]]; then | |||
sqlite3 "${RAMFS_AUDIO}/panel.db" "INSERT OR REPLACE INTO predict_passes (sat_name,pass_start,pass_end,max_elev) VALUES (\"$sat_name\",$epoch_date,$epoch_date,0);" | |||
if [[ -f "$FINALPATH/$passname-MSA.jpg" ]]; then | |||
# MSA requires a daylight pass and daylight pass is a column of decoded_passes so this is the way to grab them | |||
sqlite3 "${RAMFS_AUDIO}/panel.db" "INSERT INTO decoded_passes (pass_start, file_path, daylight_pass, is_noaa) VALUES ($epoch_date,\"$passname\",1,1);" | |||
else | |||
sqlite3 "${RAMFS_AUDIO}/panel.db" "INSERT INTO decoded_passes (pass_start, file_path, daylight_pass, is_noaa) VALUES ($epoch_date,\"$passname\",0,1);" | |||
fi | |||
fi | |||
# Move the database file to its final destination | |||
mv "${RAMFS_AUDIO}/panel.db" "$DB_PATH" | |||
echo "Done." | |||
echo "" | |||
done | |||
@@ -23,4 +23,4 @@ for filename in filenames: | |||
res = api.media_upload(filename) | |||
media_ids.append(res.media_id) | |||
api.update_status(status=argentinaFlag + ' Imagen satelital: ' + sys.argv[1] + '. Elevacion maxima: ' + sys.argv[2] + ' grados. #NOAA #weather #argentinaimagenes #noaasatellite #clima #wxtoimg #raspberrypi #argentina #argentinasat', media_ids=media_ids) | |||
api.update_status(status=argentinaFlag + ' Imagen satelital: ' + sys.argv[1] + '. Elevacion maxima: ' + sys.argv[2] + ' grados. #NOAA #weather #argentinaimagenes #noaasatellite #clima #wxtoimg #raspberrynoaa #argentina #argentinasat', media_ids=media_ids) |
@@ -28,39 +28,48 @@ fi | |||
log "Starting rtl_fm record" "INFO" | |||
timeout "${6}" /usr/local/bin/rtl_fm ${BIAS_TEE} -f "${2}"M -s 60k -g 50 -E wav -E deemp -F 9 - | /usr/bin/sox -t raw -e signed -c 1 -b 16 -r 60000 - "${RAMFS_AUDIO}/audio/${3}.wav" rate 11025 | |||
if [ ! -d "{NOAA_OUTPUT}/image/${FOLDER_DATE}" ]; then | |||
mkdir -m 775 -p "${NOAA_OUTPUT}/image/${FOLDER_DATE}" | |||
fi | |||
if [ "${SUN_ELEV}" -gt "${SUN_MIN_ELEV}" ]; then | |||
ENHANCEMENTS="ZA MCIR MCIR-precip MSA MSA-precip HVC-precip HVCT-precip HVC HVCT" | |||
daylight="true" | |||
else | |||
ENHANCEMENTS="ZA MCIR MCIR-precip" | |||
daylight="false" | |||
fi | |||
log "Bulding pass map" "INFO" | |||
/usr/local/bin/wxmap -T "${1}" -H "${4}" -p 0 -l 0 -o "${PASS_START}" "${NOAA_HOME}/map/${3}-map.png" | |||
for i in $ENHANCEMENTS; do | |||
log "Decoding image" "INFO" | |||
/usr/local/bin/wxtoimg -o -m "${NOAA_HOME}/map/${3}-map.png" -e "$i" "${RAMFS_AUDIO}/audio/${3}.wav" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-$i.jpg" | |||
/usr/bin/convert -quality 90 -format jpg "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-$i.jpg" -undercolor black -fill yellow -pointsize 18 -annotate +20+20 "${1} $i ${START_DATE} Elev: $7°" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-$i.jpg" | |||
/usr/local/bin/wxtoimg -o -m "${NOAA_HOME}/map/${3}-map.png" -e "$i" "${RAMFS_AUDIO}/audio/${3}.wav" "${NOAA_OUTPUT}/images/${3}-$i.jpg" | |||
/usr/bin/convert -quality 90 -format jpg "${NOAA_OUTPUT}/images/${3}-$i.jpg" -undercolor black -fill yellow -pointsize 18 -annotate +20+20 "${1} $i ${START_DATE} Elev: $7°" "${NOAA_OUTPUT}/images/${3}-$i.jpg" | |||
/usr/bin/convert -thumbnail 300 "${NOAA_OUTPUT}/images/${3}-$i.jpg" "${NOAA_OUTPUT}/images/thumb/${3}-$i.jpg" | |||
done | |||
rm "${NOAA_HOME}/map/${3}-map.png" | |||
if [ "${SUN_ELEV}" -gt "${SUN_MIN_ELEV}" ]; then | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "insert into decoded_passes (pass_start, file_path, daylight_pass, is_noaa) values ($5,\"$3\", 1,1);" | |||
else | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "insert into decoded_passes (pass_start, file_path, daylight_pass, is_noaa) values ($5,\"$3\", 0,1);" | |||
fi | |||
pass_id=$(sqlite3 /home/pi/raspberry-noaa/panel.db "select id from decoded_passes order by id desc limit 1;") | |||
if [ -n "$CONSUMER_KEY" ]; then | |||
log "Posting to Twitter" "INFO" | |||
if [ "${SUN_ELEV}" -gt "${SUN_MIN_ELEV}" ]; then | |||
python3 "${NOAA_HOME}/post.py" "$1 ${START_DATE}" "$7" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-MCIR-precip.jpg" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-MSA-precip.jpg" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-HVC-precip.jpg" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-HVCT-precip.jpg" | |||
python3 "${NOAA_HOME}/post.py" "$1 ${START_DATE} Mas imagenes: https://weather.reyni.co/detail.php?id=$pass_id" "$7" "${NOAA_OUTPUT}/images/$3-MCIR-precip.jpg" "${NOAA_OUTPUT}/images/$3-MSA-precip.jpg" "${NOAA_OUTPUT}/images/$3-HVC-precip.jpg" "${NOAA_OUTPUT}/images/$3-HVCT-precip.jpg" | |||
else | |||
python3 "${NOAA_HOME}/post.py" "$1 ${START_DATE}" "$7" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-MCIR-precip.jpg" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/$3-MCIR.jpg" | |||
python3 "${NOAA_HOME}/post.py" "$1 ${START_DATE} Mas imagenes: https://weather.reyni.co/detail.php?id=$pass_id" "$7" "${NOAA_OUTPUT}/images/$3-MCIR-precip.jpg" "${NOAA_OUTPUT}/images/$3-MCIR.jpg" | |||
fi | |||
fi | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "update predict_passes set is_active = 0 where (predict_passes.pass_start) in (select predict_passes.pass_start from predict_passes inner join decoded_passes on predict_passes.pass_start = decoded_passes.pass_start where decoded_passes.id = $pass_id);" | |||
if [ "$DELETE_AUDIO" = true ]; then | |||
log "Deleting audio files" "INFO" | |||
rm "${RAMFS_AUDIO}/audio/${3}.wav" | |||
rm "${RAMFS_AUDIO}/audio/${3}.wav" | |||
else | |||
log "Moving audio files out of the SD card" "INFO" | |||
mv "${RAMFS_AUDIO}/audio/${3}.wav" "${NOAA_OUTPUT}/audio/${3}.wav" | |||
log "Moving audio files out to the SD card" "INFO" | |||
mv "${RAMFS_AUDIO}/audio/${3}.wav" "${NOAA_OUTPUT}/audio/${3}.wav" | |||
fi |
@@ -25,8 +25,8 @@ SUN_ELEV=$(python3 "$NOAA_HOME"/sun.py "$PASS_START") | |||
if pgrep "rtl_fm" > /dev/null | |||
then | |||
log "There is an already running rtl_fm instance but I dont care for now, I prefer this pass" "INFO" | |||
pkill -9 -f rtl_fm | |||
log "There is an already running rtl_fm instance but I dont care for now, I prefer this pass" "INFO" | |||
pkill -9 -f rtl_fm | |||
fi | |||
# $1 = Satellite Name | |||
@@ -40,14 +40,15 @@ fi | |||
log "Starting rtl_fm record" "INFO" | |||
timeout "${6}" /usr/local/bin/rtl_fm ${BIAS_TEE} -M raw -f "${2}"M -s 288k -g 48 | sox -t raw -r 288k -c 2 -b 16 -e s - -t wav "${RAMFS_AUDIO}/audio/${3}.wav" rate 96k | |||
log "Normalization in progress" "INFO" | |||
sox "${RAMFS_AUDIO}/audio/${3}.wav" "${METEOR_OUTPUT}/${3}.wav" gain -n | |||
log "Demodulation in progress (QPSK)" "INFO" | |||
meteor_demod -B -o "${METEOR_OUTPUT}/${3}.qpsk" "${METEOR_OUTPUT}/${3}.wav" | |||
meteor_demod -B -o "${METEOR_OUTPUT}/${3}.qpsk" "${RAMFS_AUDIO}/audio/${3}.wav" | |||
if [ "$DELETE_AUDIO" = true ]; then | |||
log "Deleting audio files" "INFO" | |||
rm "${RAMFS_AUDIO}/audio/${3}.wav" | |||
else | |||
log "Moving audio files out to the SD card" "INFO" | |||
mv "${RAMFS_AUDIO}/audio/${3}.wav" "${NOAA_OUTPUT}/audio/${3}.wav" | |||
rm "${METEOR_OUTPUT}/audio/${3}.wav" | |||
rm "${RAMFS_AUDIO}/audio/${3}.wav" | |||
fi | |||
@@ -61,23 +62,29 @@ if [ -f "${METEOR_OUTPUT}/${3}.dec" ]; then | |||
if [ "${SUN_ELEV}" -lt "${SUN_MIN_ELEV}" ]; then | |||
log "I got a successful ${3}.dec file. Decoding APID 68" "INFO" | |||
medet_arm "${METEOR_OUTPUT}/${3}.dec" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122" -r 68 -g 68 -b 68 -d | |||
/usr/bin/convert -negate $FLIP "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122.bmp" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122.bmp" | |||
medet_arm "${METEOR_OUTPUT}/${3}.dec" "${NOAA_OUTPUT}/images/${3}-122" -r 68 -g 68 -b 68 -d | |||
/usr/bin/convert $FLIP -negate "${NOAA_OUTPUT}/images/${3}-122.bmp" "${NOAA_OUTPUT}/images/${3}-122.bmp" | |||
else | |||
log "I got a successful ${3}.dec file. Creating false color image" "INFO" | |||
medet_arm "${METEOR_OUTPUT}/${3}.dec" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122" -r 65 -g 65 -b 64 -d | |||
medet_arm "${METEOR_OUTPUT}/${3}.dec" "${NOAA_OUTPUT}/images/${3}-122" -r 65 -g 65 -b 64 -d | |||
fi | |||
log "Rectifying image to adjust aspect ratio" "INFO" | |||
python3 "${NOAA_HOME}/rectify.py" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122.bmp" | |||
convert "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122-rectified.jpg" -channel rgb -normalize -channel rgb -normalize -undercolor black -fill yellow -pointsize 60 -annotate +20+60 "${1} ${START_DATE} Elev: $7°" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122-rectified.jpg" | |||
python3 "${NOAA_HOME}/rectify.py" "${NOAA_OUTPUT}/images/${3}-122.bmp" | |||
convert "${NOAA_OUTPUT}/images/${3}-122-rectified.jpg" -channel rgb -normalize -undercolor black -fill yellow -pointsize 60 -annotate +20+60 "${1} ${START_DATE} Elev: $7°" "${NOAA_OUTPUT}/images/${3}-122-rectified.jpg" | |||
/usr/bin/convert -thumbnail 300 "${NOAA_OUTPUT}/images/${3}-122-rectified.jpg" "${NOAA_OUTPUT}/images/thumb/${3}-122-rectified.jpg" | |||
rm "${NOAA_OUTPUT}/images/${3}-122.bmp" | |||
rm "${METEOR_OUTPUT}/${3}.bmp" | |||
rm "${METEOR_OUTPUT}/${3}-122.bmp" | |||
rm "${METEOR_OUTPUT}/${3}.dec" | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "insert into decoded_passes (pass_start, file_path, daylight_pass, is_noaa) values ($5,\"$3\", 1,0);" | |||
pass_id=$(sqlite3 /home/pi/raspberry-noaa/panel.db "select id from decoded_passes order by id desc limit 1;") | |||
if [ -n "$CONSUMER_KEY" ]; then | |||
log "Posting to Twitter" "INFO" | |||
python3 "${NOAA_HOME}/post.py" "$1 EXPERIMENTAL ${START_DATE} Resolución completa: http://weather.reyni.co/image/${FOLDER_DATE}/${3}-122-rectified.jpg" "$7" "${NOAA_OUTPUT}/image/${FOLDER_DATE}/${3}-122-rectified.jpg" | |||
python3 "${NOAA_HOME}/post.py" "$1 ${START_DATE} Resolución completa: https://weather.reyni.co/detail.php?id=$pass_id" "$7" "${NOAA_OUTPUT}/images/${3}-122-rectified.jpg" | |||
fi | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "update predict_passes set is_active = 0 where (predict_passes.pass_start) in (select predict_passes.pass_start from predict_passes inner join decoded_passes on predict_passes.pass_start = decoded_passes.pass_start where decoded_passes.id = $pass_id);" | |||
else | |||
log "Decoding failed, either a bad pass/low SNR or a software problem" "ERROR" | |||
fi |
@@ -8,7 +8,7 @@ from math import atan,sin,cos,sqrt,tan,acos,ceil | |||
from PIL import Image | |||
EARTH_RADIUS = 6371.0 | |||
SAT_HEIGHT = 822.5 | |||
SAT_HEIGHT = 830.0 | |||
SAT_ORBIT_RADIUS = EARTH_RADIUS + SAT_HEIGHT | |||
SWATH_KM = 2800.0 | |||
THETA_C = SWATH_KM / EARTH_RADIUS | |||
@@ -25,6 +25,7 @@ while [ "$(date --date="@${var2}" +%D)" = "$(date +%D)" ]; do | |||
echo "${SATNAME}" "${OUTDATE}" "$MAXELEV" | |||
echo "${NOAA_HOME}/receive_meteor.sh \"${1}\" $2 ${SATNAME}${OUTDATE} "${NOAA_HOME}"/predict/weather.tle \ | |||
${var1} ${TIMER} ${MAXELEV}" | at "$(date --date="TZ=\"UTC\" ${START_TIME}" +"%H:%M %D")" | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "insert or replace into predict_passes (sat_name,pass_start,pass_end,max_elev,is_active) values (\"$SATNAME\",$var1,$var2,$MAXELEV,1);" | |||
fi | |||
NEXTPREDICT=$(expr "${var2}" + 60) | |||
PREDICTION_START=$(/usr/bin/predict -t "${NOAA_HOME}"/predict/weather.tle -p "${1}" "${NEXTPREDICT}" | head -1) | |||
@@ -31,6 +31,7 @@ while [ "$(date --date="@${var2}" +%D)" = "$(date +%D)" ]; do | |||
echo "${SATNAME}" "${OUTDATE}" "$MAXELEV" | |||
echo "${NOAA_HOME}/receive.sh \"${1}\" $2 ${SATNAME}${OUTDATE} "${NOAA_HOME}"/predict/weather.tle \ | |||
${var1} ${TIMER} ${MAXELEV}" | at "$(date --date="TZ=\"UTC\" ${START_TIME}" +"%H:%M %D")" | |||
sqlite3 /home/pi/raspberry-noaa/panel.db "insert or replace into predict_passes (sat_name,pass_start,pass_end,max_elev,is_active) values (\"$SATNAME\",$var1,$var2,$MAXELEV, 1);" | |||
fi | |||
NEXTPREDICT=$(expr "${var2}" + 60) | |||
PREDICTION_START=$(/usr/bin/predict -t "${NOAA_HOME}"/predict/weather.tle -p "${1}" "${NEXTPREDICT}" | head -1) | |||
@@ -2,8 +2,8 @@ | |||
import ephem | |||
import time | |||
import sys | |||
timezone = change_tz | |||
date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(sys.argv[1])+(timezone*60*60))) | |||
timezone = change_tz + time.localtime().tm_isdst | |||
date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(sys.argv[1])-(timezone*60*60))) | |||
obs=ephem.Observer() | |||
obs.lat='change_latitude' | |||
@@ -1,7 +1,7 @@ | |||
server { | |||
listen 80 default_server; | |||
root /var/www/wx; | |||
index index.html index.htm index.nginx-debian.html; | |||
index index.php index.html index.htm index.nginx-debian.html; | |||
autoindex on; | |||
server_name wx.home; | |||
@@ -9,4 +9,12 @@ server { | |||
location / { | |||
try_files $uri $uri/ =404; | |||
} | |||
location ~ \.php$ { | |||
include snippets/fastcgi-php.conf; | |||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; | |||
} | |||
# Rewrite the old path yyyy/mm/dd/ to /images/ | |||
rewrite "^/image/\d{4}/\d{2}/\d{2}/(.+)$" /images/$1 permanent; | |||
} |
@@ -0,0 +1,7 @@ | |||
<?php | |||
return (object) array( | |||
'base_url' => '/images/', | |||
'img_per_page' => 12, | |||
'lang' => 'es' | |||
); | |||
?> |
@@ -0,0 +1,6 @@ | |||
<?php | |||
require('Model/Conn.php'); | |||
$con = new Conn(); | |||
$passes = $con->getPasses(); | |||
require('Views/V_viewPasses.php'); | |||
?> |
@@ -0,0 +1,8 @@ | |||
<?php | |||
require('Model/Conn.php'); | |||
$con = new Conn(); | |||
if ($pass_id < 1) $pass_id = 1; | |||
$enhacements = $con->getEnhacements($pass_id); | |||
$path = $con->getPath($pass_id); | |||
require('Views/V_viewDetail.php'); | |||
?> |
@@ -0,0 +1,6 @@ | |||
<?php | |||
require('Model/Conn.php'); | |||
$con = new Conn(); | |||
$images = $con->getLastImage(); | |||
require('Views/V_viewLastImage.php'); | |||
?> |
@@ -0,0 +1,11 @@ | |||
<?php | |||
require('Model/Conn.php'); | |||
$con = new Conn(); | |||
if ($page < 1) $page = 1; | |||
$img_per_page = $configs->img_per_page; | |||
$page_count = $con->totalPages($img_per_page); | |||
if ($page < 1) $page = 1; | |||
if ($page > $page_count) $page = $page_count; | |||
$images = $con->getImages($page, $img_per_page); | |||
require('Views/V_viewLastImages.php'); | |||
?> |
@@ -0,0 +1,74 @@ | |||
<?php | |||
class Conn { | |||
private $con; | |||
public function __construct() { | |||
$this->con = new SQLite3("/home/pi/raspberry-noaa/panel.db"); | |||
} | |||
public function getPasses() { | |||
$today = strtotime(date('Y-m-d', time())); | |||
$query = $this->con->query("SELECT sat_name, is_active, | |||
pass_start, pass_end, | |||
max_elev FROM predict_passes | |||
WHERE (pass_start > $today) ORDER BY | |||
pass_start ASC;"); | |||
$passes = []; | |||
$i = 0; | |||
while($row = $query->fetchArray()){ | |||
$passes[$i] = $row; | |||
$i++; | |||
} | |||
return $passes; | |||
} | |||
public function totalPages($img_per_page) { | |||
$total_pages = $this->con->querySingle("SELECT count() from decoded_passes;"); | |||
return ceil($total_pages/$img_per_page); | |||
} | |||
public function getImages($page, $img_per_page) { | |||
$query = $this->con->prepare("SELECT decoded_passes.id, predict_passes.pass_start, | |||
file_path, is_noaa, predict_passes.sat_name, predict_passes.max_elev | |||
FROM decoded_passes INNER JOIN predict_passes | |||
ON predict_passes.pass_start = decoded_passes.pass_start | |||
ORDER BY decoded_passes.pass_start DESC LIMIT ? OFFSET ?;"); | |||
$query->bindValue(1, $img_per_page); | |||
$query->bindValue(2, $img_per_page * ($page-1)); | |||
$result = $query->execute(); | |||
$images = []; | |||
$i = 0; | |||
while($row = $result->fetchArray()){ | |||
$images[$i] = $row; | |||
$i++; | |||
} | |||
return $images; | |||
} | |||
public function getEnhacements($id) { | |||
$query = $this->con->prepare('SELECT daylight_pass, is_noaa | |||
FROM decoded_passes WHERE id = ?;'); | |||
$query->bindValue(1, $id); | |||
$result = $query->execute(); | |||
$pass = $result->fetchArray(); | |||
if ($pass['is_noaa'] == 0) { | |||
$enhacements = ['-122-rectified.jpg']; | |||
} else { | |||
if ($pass['daylight_pass'] == 1) { | |||
$enhacements = ['-ZA.jpg','-MCIR.jpg','-MCIR-precip.jpg','-MSA.jpg','-MSA-precip.jpg','-HVC.jpg','-HVC-precip.jpg','-HVCT.jpg','-HVCT-precip.jpg']; | |||
} else { | |||
$enhacements = ['-ZA.jpg','-MCIR.jpg','-MCIR-precip.jpg']; | |||
} | |||
} | |||
return $enhacements; | |||
} | |||
public function getPath($id) { | |||
$query = $this->con->prepare('SELECT file_path FROM decoded_passes | |||
WHERE id = ?;'); | |||
$query->bindValue(1, $id); | |||
$result = $query->execute(); | |||
$image = $result->fetchArray(); | |||
return $image['file_path']; | |||
} | |||
} | |||
?> |
@@ -0,0 +1,23 @@ | |||
<div style="overflow-x:auto;"> | |||
<table id="passes"> | |||
<?php | |||
$row_count=0; | |||
$col_count=0; | |||
$baseurl = $configs->base_url; | |||
foreach ($enhacements as $enhacement) { | |||
if($row_count%3==0) { | |||
echo "<tr>"; | |||
$col_count=1; | |||
} | |||
echo "<td><div id =\"satimgdiv\"><a href=". $baseurl . $path . $enhacement ."><img id=\"satimg\" src=". $baseurl . "thumb/" . $path . $enhacement ."></img></a></div>"; | |||
if($col_count==3) { | |||
echo "</tr>"; | |||
} | |||
$row_count++; | |||
$col_count++; | |||
} | |||
?> | |||
</table> | |||
</div> | |||
</body> | |||
</html> |
@@ -0,0 +1,24 @@ | |||
<table id="passes" class="img-grid"> | |||
<?php | |||
$row_count=0; | |||
$col_count=0; | |||
$baseurl = $configs->base_url; | |||
foreach ($images as $image) { | |||
if($row_count%3==0) { | |||
echo "<tr>"; | |||
$col_count=1; | |||
} | |||
echo "<td><div id =\"satimgdiv\"><a href=". $baseurl . $image['file_path'] ."><img id=\"satimg\" src=". $baseurl . "thumb/" . $image['file_path'] ."></img></a></div>"; | |||
echo "<ul><li>". $image['sat_name'] ."</li>"; | |||
echo "<li>". date('d/m/Y H:i:s', $image['pass_start']) ."</li></ul></td>"; | |||
if($col_count==3) { | |||
echo "</tr>"; | |||
} | |||
$row_count++; | |||
$col_count++; | |||
} | |||
?> | |||
</table> | |||
</body> | |||
</html> |
@@ -0,0 +1,42 @@ | |||
<div style="overflow-x:auto;"> | |||
<table id="passes" class="img-grid"> | |||
<?php | |||
if ($page > 1) { | |||
echo "<tr><th><a href=\"?page=" . ($page-1) . "\">" . $lang['prev'] . "</a></th>"; | |||
} else { | |||
echo "<tr><th></th>"; | |||
} | |||
echo "<th>" . $lang['page'] . " $page " . $lang['of'] . " $page_count</th>"; | |||
if ($page < $page_count) { | |||
echo "<th><a href=\"?page=" . ($page+1) . "\">" . $lang['next'] . "</a></th></tr>"; | |||
} else { | |||
echo "<th></th></tr>"; | |||
} | |||
$row_count=0; | |||
$col_count=0; | |||
$baseurl = $configs->base_url; | |||
foreach ($images as $image) { | |||
if($row_count%3==0) { | |||
echo "<tr>"; | |||
$col_count=1; | |||
} | |||
if ($image['is_noaa'] == true) { | |||
$ending = "-MCIR.jpg"; | |||
} else { | |||
$ending = "-122-rectified.jpg"; | |||
} | |||
echo "<td><div id =\"satimgdiv\"><a href=". "detail.php?id=" . $image['id'] ."><img id=\"satimg\" src=". $baseurl . "thumb/" . $image['file_path'] . $ending ."></img></a></div>"; | |||
echo "<ul><li>". $image['sat_name'] ."</li>"; | |||
echo "<li> " . $lang['elev'] . ": ". $image['max_elev'] ."°</li>"; | |||
echo "<li>". date('d/m/Y H:i:s', $image['pass_start']) ."</li></ul></td>"; | |||
if($col_count==3) { | |||
echo "</tr>"; | |||
} | |||
$row_count++; | |||
$col_count++; | |||
} | |||
?> | |||
</table> | |||
</div> | |||
</body> | |||
</html> |
@@ -0,0 +1,24 @@ | |||
<div style="overflow-x:auto;"> | |||
<table id="passes"> | |||
<tr> | |||
<th><?php echo $lang['satellite']; ?></th> | |||
<th><?php echo $lang['pass_start']; ?></th> | |||
<th><?php echo $lang['pass_end']; ?></th> | |||
<th><?php echo $lang['max_elev']; ?></th> | |||
</tr> | |||
<?php | |||
foreach ($passes as $pass) { | |||
if ($pass['is_active'] == false) { | |||
echo "<tr class='inactive'>"; | |||
} else { | |||
echo "<tr>"; | |||
} | |||
echo "<td>". $pass['sat_name'] ."</td>"; | |||
echo "<td>". date('H:i:s', $pass['pass_start']) ."</td>"; | |||
echo "<td>". date('H:i:s', $pass['pass_end']) ."</td>"; | |||
echo "<td>". $pass['max_elev'] ."</td>"; | |||
echo "</tr>"; | |||
} | |||
?> | |||
</table> | |||
</div> |
@@ -0,0 +1,7 @@ | |||
<?php | |||
$configs = include('Config.php'); | |||
include_once('header.php'); | |||
$pass_id = isset($_GET['id']) ? intval($_GET['id']) : 1; | |||
require('Controller/C_showDetail.php'); | |||
include_once("footer.php") | |||
?> |
@@ -0,0 +1,6 @@ | |||
</div> | |||
<footer> | |||
<a href="https://github.com/reynico/raspberry-noaa"><img class="img-footer" src="logo-small.png"></a> | |||
</footer> | |||
</body> | |||
</html> |
@@ -0,0 +1,27 @@ | |||
<?php | |||
ini_set('display_errors', 1); | |||
ini_set('display_startup_errors', 1); | |||
error_reporting(E_ALL); | |||
date_default_timezone_set('America/Argentina/Buenos_Aires'); | |||
$page = basename($_SERVER['PHP_SELF']); | |||
$configs = include('Config.php'); | |||
$lang = $configs->lang; | |||
include_once('language/' . $lang . '.php'); | |||
?> | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |||
<link rel="stylesheet" type="text/css" href="style.css"> | |||
<title><?= isset($PageTitle) ? $PageTitle : "Raspberry NOAA"?></title> | |||
</head> | |||
<body> | |||
<div class="topnav"> | |||
<a class="<?php if($page == 'passes.php'){ echo ' active"';}?>" href="passes.php"><?php echo $lang['passes']; ?></a> | |||
<a class="<?php if($page == 'index.php' || $page == 'detail.php'){ echo ' active"';}?>" href="index.php"><?php echo $lang['images']; ?></a> | |||
</div> | |||
<div class="container"> | |||
@@ -0,0 +1,7 @@ | |||
<?php | |||
include_once('header.php'); | |||
$page = isset($_GET['page']) ? intval($_GET['page']) : 1; | |||
require('Controller/C_showLastImages.php'); | |||
include_once("footer.php") | |||
?> |
@@ -0,0 +1,15 @@ | |||
<?php | |||
$lang = array( | |||
"passes" => "Passes", | |||
"satellite" => "Satellite", | |||
"images" => "Images", | |||
"pass_start" => "Pass start", | |||
"pass_end" => "Pass end", | |||
"max_elev" => "Max elevation", | |||
"elev" => "Elevation", | |||
"page" => "Page", | |||
"of" => "of", | |||
"prev" => "Prev", | |||
"next" => "Next", | |||
); | |||
?> |
@@ -0,0 +1,15 @@ | |||
<?php | |||
$lang = array( | |||
"passes" => "Pasadas", | |||
"satellite" => "Satélite", | |||
"images" => "Imagenes", | |||
"pass_start" => "Inicio", | |||
"pass_end" => "Fin", | |||
"max_elev" => "Elevación", | |||
"elev" => "Elevación", | |||
"page" => "Página", | |||
"of" => "de", | |||
"prev" => "Anterior", | |||
"next" => "Siguiente", | |||
); | |||
?> |
@@ -0,0 +1,5 @@ | |||
<?php | |||
include_once('header.php'); | |||
require('Controller/C_predict.php'); | |||
include_once("footer.php") | |||
?> |
@@ -0,0 +1,99 @@ | |||
body { | |||
margin: 0; | |||
font-family: Arial, Helvetica, sans-serif; | |||
} | |||
.container { | |||
margin: 20px auto; | |||
max-width: 800px; | |||
} | |||
.topnav { | |||
overflow: hidden; | |||
background-color: #333; | |||
} | |||
.topnav a { | |||
float: left; | |||
color: #f2f2f2; | |||
text-align: center; | |||
padding: 14px 16px; | |||
text-decoration: none; | |||
font-size: 17px; | |||
} | |||
.topnav a:hover { | |||
background-color: #ddd; | |||
color: black; | |||
} | |||
.topnav a.active { | |||
background-color: #4CAF50; | |||
color: white; | |||
} | |||
#passes { | |||
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; | |||
border-collapse: collapse; | |||
max-width: 500px; | |||
width: 100%; | |||
margin: auto; | |||
} | |||
#passes.img-grid { | |||
max-width: 700px; | |||
} | |||
#passes td, | |||
#passes th { | |||
border: 1px solid #ddd; | |||
padding: 8px; | |||
} | |||
#passes tr:nth-child(even) { | |||
background-color: #f2f2f2; | |||
} | |||
/* #passes tr:hover {background-color: #ddd;} */ | |||
#passes th { | |||
padding-top: 12px; | |||
padding-bottom: 12px; | |||
background-color: #4CAF50; | |||
text-align: center; | |||
color: white; | |||
} | |||
#passes tr.inactive { | |||
color: lightgray; | |||
} | |||
#satimg { | |||
max-width: 200px; | |||
max-height: 200px; | |||
width: 100%; | |||
} | |||
#satimgdiv { | |||
width: 100%; | |||
height: 100%; | |||
max-width: 200px; | |||
background-color: antiquewhite; | |||
/* margin: 10px; */ | |||
display: inline-block; | |||
vertical-align: middle; | |||
text-align: center; | |||
} | |||
footer { | |||
/* position: absolute; */ | |||
margin: 20px; | |||
bottom: 0; | |||
max-width: 100%; | |||
text-align: center; | |||
} | |||
img.img-footer { | |||
max-width: 100%; | |||
} |
@@ -0,0 +1,14 @@ | |||
create table predict_passes( | |||
sat_name text not null, | |||
pass_start timestamp primary key default (strftime('%s', 'now')) not null, | |||
pass_end timestamp default (strftime('%s', 'now')) not null, | |||
max_elev int not null, | |||
is_active boolean); | |||
CREATE TABLE decoded_passes( | |||
id integer primary key autoincrement, | |||
pass_start integer, | |||
file_path text not null, | |||
daylight_pass boolean, is_noaa boolean, | |||
foreign key(pass_start) references passes(pass_start)); |