Dla przypomnienia moja nowa stacja to konsola WiFi o symbolu
DP1500 kupiona u Niemca (czyli w sklepie froggit.de): ze standardowym
zestawem czujników + czujnik pyłu zawieszonego DP200 (też
Froggita). BTW DP1500 to to samo co GW1000 firmy Ecowitt.
Zaś DP 200 Froggita to WH41 Ecowitta
(http://www.ecowitt.com/wifi_weather/83.html
), albo nawet
PM25 firmy AcuWeather
(https://www.ambientweather.com/ampm25.html
)
Zamiast konsoli kupiłem na OLX 5 calowy ekran (110 PLN) i podłączyłem do Raspberry Pi (bezproblemowo).
# konfiguruję (logowanie bez hasła) Boot Options -> Desktop/CLI -> Desktop Autologin/Desktop GUI # # start chromium-browser załadowanie pliku vi ~/.config/lxsession/LXDE-pi/autostart ## wpisuję @/usr/bin/chromium-browser --disable-restore-session-state \ file:///var/www/html/DP1500_live.html
Ekran jest dotykowy. Jak chcę obejrzeć dane pogodowe to dotykam i się wyświetla.
Że jednak brakowało konsoli, to w końcu jednak kupiłem Froggit HP 1000SE PRO (która jest klonem Ecowitt HP 2551). Froggit sprzedaje konsolę jako ersatz, bez niezbędnego specjalnego czujnika temperatury/wilgotności/ciśnienia. Sama stacja jest bowiem dumb -- nic nie mierzy. Ja miałem czujnik temperatury/wilgotności DP50, ale on nie mierzy ciśnienia. Musiałem dokupić oddzielnie ten specjalny, bo się nie dogadałem a dokumentacja/informacja jest w tym aspekcie mało jasna.
Konfigurowanie HP 1000SE PRO to już nie był żaden problem, bo stacja ma aż 8 klawiszy. Połączyłem ją z routerem a potem z dwoma serwisami: ecowitt.net (www.ecowitt.net; oglądanie wymaga zarejestrowania się w sieci ecowitt.net) oraz z WOW ( wow.metoffice.gov.uk)
Kiedyś kupiłem też inny czujnik pyłu zawieszonego (Nova SDS011) i też go podłączyłem do raspberry (bezproblemowo). Teraz przykręciłem go na ścianie obok DP 200 z zamiarem porównania odczytów.
Reasumując posiadam: starą stację WH 2080 działającą od +10 lat.
Nową stację DP1500/WH1000SE PRO oraz czujnik SDS101.
WH 2080 podłączona jest przez kabel USB
do Sheevaplug (to też zaszłość, planuję docelowo przejść na Raspberry)
a obsługiwania jest przez pywws
.
DP1500/WH1000SE podłączona jest przez router, przy czym DP1500 skonfigurowana
jest na serwer lokalny/weewx, a WH1000SE wysyła dane od razu do WOW/ecowitt.net.
Całość kosztował ponad 2500 PLN (wliczając starą stację WH2080). W sumie mogłem kupić HP1000SE PRO + DP200 za 330 EUR, a kupiłem na raty powyższe plus dodatkowy czujnik DP50/DP200 za 450 EUR. Przepłaciłem...
Po długich namysłach kupiłem nową stację (trzeba się rozwijać) pn DP 1500 SmartHub wifi Gateway (za 50 EUR). Jest to konsola bez ekranu z czujnikiem temperatury/ciśnienia i wilgotności (na metrowym kablu); przedmiot wielkości dużego pudełka od zapałek. Do tego pudełka dokupiłem:
Cenowo to wyszło tak (por www.froggit.de), że maszt + DP 1500 jest w zestawie za 135 EUR (czyli sam maszt kosztuje 85 EUR); czujnik DP 50 to następne 13 EUR (kupiłem dwa na wszelki wypadek); czujnik DP 200 aż 90 EUR. Razem wyszło około 250EUR czyli ponad 1100 PLN z wysyłką i płatnością przez PayPal, bo innej opcji nie ma (nikt nie mówił że będzie tanio)
Wszystko w niemieckiej firmie Froggit. BTW Froggit to klon produktów bardziej znanej firmy Ecowitt. To co Froggit sprzedaje jako DP1500 u Ecowitta nazywa się GW 1000. Są jeszcze inne firmy, które robią ten sprzęt pod inną marką.
W stacjach pogodowych standardem jest teraz wysyłanie danych
w chmurę. Nie ma natomiast możliwości bezpośredniego pobierania danych
z urządzenia. Co najwyżej można wysłać na własną chmurę.
Do tego konfiguruje się urządzenie przez smartfona za pomocą dedykowanej
aplikacji pn. WSView
.
Ot tak to sobie branża wymyśliła.
Średnio mi się to podoba, ale
po prostu urządzeń, z których można pobrać bezpośrednio dane w starym stylu
się nie produkuje (no nie do końca jest to prawdą: produkuje się to co
mam od 10 lat, ale
ja nie chę kupować jeszcze raz tej samej stacji). Skoro trzeba mieć własną chmurę, to trzeba ją założyć.
Na szczęście ponieważ potrzeba jest matką wynalazków, to w tej
sprawie inni ludzie już przygotowali stosowne rozwiązania.
Program weewx
, który tradycyjnie obsługiwał stacje,
że tak powiem
kablowe został uzupełniony o weewx-interceptor
,
który przechwytuje dane
wysyłane na (pseudo) własną chmurę.
No więc rozpakowałem to co przyszło z Niemiec. Skręciłem maszt z czujnikami wiatru/deszczu, nasłonecznienia. Czujnik PM2.5 przykręciłem do ściany we wnęce okiennej, żeby deszcz na niego nie kapał. Czujnik DP 50 wstawiłem do klatki meteo. Maszt umieściłem na dachu budynku 3 kondygnacyjnego (nie ma żadnych problemów z zasięgiem, a mieszkam na parterze.)
Ściągnąłem przez Google Play
aplikację WSView
.
Postępując zgodnie z procedurą
(opisaną w podręczniku) połączyłem się z konsolą, która zaczęła
pokazywać dane z czujników (na smartfonie). Uwaga: WSView
dość wolno się
łączy i na dokładkę wysyła mylące komunikaty o odrzuconych
połączeniach, co powoduje pewien niepokój. Nie należy w tym momencie
wykonywać nerwowych ruchów, tylko poczekać...
weeWX
(na RaspberryPi)Mając ustawione połączenie konsola-router można pójść dalej. Znowu jest to dokładnie opisane i co więcej opis jest zgodny ze stanem faktycznym (cf github.com/weewx/weewx/wiki/gw1000-recipe):
Instalujemy weeWX
(wybierając 'Simulator' jako 'station type').
Później należy zmienić 'station type' na 'Interceptor' uruchamiając wee_config --reconfigure
:
wget -qO - http://weewx.com/keys.html | sudo apt-key add - wget -qO - http://weewx.com/apt/weewx.list | sudo tee /etc/apt/sources.list.d/weewx.list sudo apt-get update sudo apt-get install weewx # shut down weeWX sudo /etc/init.d/weewx stop # install weewx-interceptor extension and enable the driver git clone https://github.com/matthewwall/weewx-interceptor.git sudo wee_extension --install weewx-interceptor sudo wee_config --reconfigure
Sprawdzenie czy interceptor
przechwytuje dane.
W oknie terminala uruchamiamy interceptor.py
ze wskazaniem
na port 8000:
PYTHONPATH=/usr/share/weewx python /usr/share/weewx/user/interceptor.py \ --device=fineoffset-bridge --port 8000 --debug
Uruchamiamy przeglądarkę, wpisujemy następujący URL (192.168.xx.xx to IP komputera z weeWXem):
http://192.168.xx.xx:8000/data/report?PASSKEY=XXX&stationtype=GW1000B_V1.5.5 &dateutc=2019-12-29+16:27:27&tempinf=67.1&humidityin=39 &baromrelin=30.138&baromabsin=30.138&freq=915M&model=GW1000
W oknie terminala z uruchomionym interceptor.py
powinno się
pojawić:
raw data: PASSKEY=XXX&stationtype=GW1000B_V1.5.5&dateutc=2019-12-29+16:27:27&tempinf=67.1 &humidityin=39&baromrelin=30.138&baromabsin=30.138&freq=915M&model=GW1000 raw packet: {'humidity_in': 39.0, 'temperature_in': 67.1, 'barometer': 30.138, 'usUnits': 1, 'dateTime': 1577636847} mapped packet: {'inHumidity': 39.0, 'barometer': 30.138, 'inTemp': 67.1, 'usUnits': 1, 'dateTime': 1577636847}
W aplikacji WSView
przechodzimy do strony 'Weather Services'.
Klikamy 'Next' aż wyświetli się 'Customized'. Na tej stronie wpisujemy IP komputera
z zainstalowanym weeWXem jako wartość pola 'Server IP/Hostname'.
Wpisujemy '/' jako wartość pola 'Path' oraz 8000 jako wartość pola 'Port'.
Domyślny upload jest ustawiony na 60 sekund (zmieniłem na 300).
Ustawienia weeWX są w pliku /etc/weewx/weewx.conf
. W szczególności
sekcja Interceptor
powinna wyglądać następująco:
[Interceptor] driver = user.interceptor device_type = fineoffset-bridge port = 8000
Uruchamianie/zatrzymanie/sprawdzanie usługi:
# Start weewx (as daemon) sudo /etc/init.d/weewx start # Stop weewx # sudo /etc/init.d/weewx stop # Check status # /etc/init.d/weewx status # systemctl status -l weewx # ls -l /etc/rc2.d
WeeWX zapisuje dane do bazy /var/lib/weewx/weewx.sdb
.
Można je oglądać uruchamiając:
sqlite3 /var/lib/weewx.sdb ## wyświetl wszystkie tabele .tables ## wyświetl schemat tabeli archive .schema archive PRAGMA table_info(archive); ## zawartość archive select * from archive; ## zakończ .q
Główną tabelą danych weeWXa
jest archive
:
sqlite> PRAGMA table_info(archive); select * from archive 0|dateTime|INTEGER|1||1 1|usUnits|INTEGER|1||0 2|interval|INTEGER|1||0 3|barometer|REAL|0||0 4|pressure|REAL|0||0 5|altimeter|REAL|0||0 6|inTemp|REAL|0||0 7|outTemp|REAL|0||0 8|inHumidity|REAL|0||0 9|outHumidity|REAL|0||0 10|windSpeed|REAL|0||0 11|windDir|REAL|0||0 12|windGust|REAL|0||0 13|windGustDir|REAL|0||0 14|rainRate|REAL|0||0 15|rain|REAL|0||0 16|dewpoint|REAL|0||0 17|windchill|REAL|0||0 18|heatindex|REAL|0||0 19|ET|REAL|0||0 20|radiation|REAL|0||0 21|UV|REAL|0||0 22|extraTemp1|REAL|0||0 23|extraTemp2|REAL|0||0 24|extraTemp3|REAL|0||0 25|soilTemp1|REAL|0||0 26|soilTemp2|REAL|0||0 27|soilTemp3|REAL|0||0 28|soilTemp4|REAL|0||0 29|leafTemp1|REAL|0||0 30|leafTemp2|REAL|0||0 31|extraHumid1|REAL|0||0 32|extraHumid2|REAL|0||0 33|soilMoist1|REAL|0||0 34|soilMoist2|REAL|0||0 35|soilMoist3|REAL|0||0 36|soilMoist4|REAL|0||0 37|leafWet1|REAL|0||0 38|leafWet2|REAL|0||0 39|rxCheckPercent|REAL|0||0 40|txBatteryStatus|REAL|0||0 41|consBatteryVoltage|REAL|0||0 42|hail|REAL|0||0 43|hailRate|REAL|0||0 44|heatingTemp|REAL|0||0 45|heatingVoltage|REAL|0||0 46|supplyVoltage|REAL|0||0 47|referenceVoltage|REAL|0||0 48|windBatteryStatus|REAL|0||0 49|rainBatteryStatus|REAL|0||0 50|outTempBatteryStatus|REAL|0||0 51|inTempBatteryStatus|REAL|0||0
Nie ma kolumny PM2.5 w szczególności.
To się samo robi. Trzeba tylko zarejestrować stację na ecowitt.net
podając MAC-adres urządzenia, ale ecowitt.net
nie udostępnia danych
publicznie.
Żeby oglądać zawartość ecowitt.net
trzeba się zalogować.
No a żeby się zalogować, to trzeba mieć konto. Inna sprawa że nie trzeba mieć stacji, żeby
mieć konto. Jest też stronka
pod adresem www.weewx.com/stations.html
z wykazem zarejestrowanych stacji obsługiwanych przez WeeWX.
Żeby tam zaistnieć trzeba
wstawić do /etc/weewx/weewx.conf
:
register_this_station = True station_url = http://pinkaccordions.homelinux.org/
Co jest opisane w dokumentacji WeeWXhttp://www.weewx.com/docs/usersguide.htm#station_registry
Nie ma kolumny PM2.5, ale interceptor.py
pobiera co trzeba bo na
ecowitt.net
są dane dotyczące PM2.5 tylko lokalnie nie są rejestrowane
w bazie. Wychodzi na to, że to WeeWx 'nie widzi' ekstra danych.
Jest oficjalny sposób na rozszerzenie schematu bazy
(http://www.weewx.com/docs/customizing.htm#add_archive_type
) ale ja póki co
zrobiłem to na szybko w ten sposób, że zmodyfikowałem działanie
/usr/share/weewx/user/interceptor.py
, który nie tylko wypisuje co przechwycił
(co zapewne przetwarza dalej weeWX), ale także zapisuje przechwycony
rekord do pliku tekstowego. W tym celu dodałem takie coś:
def genLoopPackets(self): last_ts = 0 while True: try: data = self._device.get_queue().get(True, self._queue_timeout) logdbg('raw data: %s' % data) ## LOG every 15 minutes nowMM = time.strftime("%M", time.gmtime()) if (nowMM == '00' or nowMM == "15" or nowMM == "30" or nowMM == "45"): ffex = open("/var/log/weewx_exlog.txt", "a+") ffex.write ('[WEEWX#RAW] %s\n' % data) ffex.close() ##loginf('[WEEWX#RAW]: %s' % data)
Co 15 minut, a dokładniej w 15/30/45 oraz zerowej minucie każdej godziny
zapisywany jest rekord danych do pliku /var/log/weewx_exlog.txt
.
cp interceptor.py interceptor_orig.py ## po zmodyfikowaniu interceptor.py w wyżej opisany sposób python -m py_compile interceptor.py
Standardowe raporty WeeWXa są w katalogu: /var/www/html/weewx
.
W sumie z nich nie korzystam, robię raport po swojemu
z pliku /var/log/weewx_exlog.txt
Dokładniej raport jest tworzony na komputerku bez dostępu do internetu a następnie co godzinę kopiowany na ten, który dostęp ma czyli na pinkaccordions.homelinux.org
W planach jest zakup ekranu LCD 5--7 cali do raspberry co będzie robił za wyświetlacz mojej stacji.
Minimalizowanie liczby zapisów (przeciwdziałanie zużyciu się karty SDHC)
https://github.com/weewx/weewx/wiki/Minimize-writes-on-SD-cards
.
Zakupiłem używaną (z przeznaczeniem do rejestrowania parametrów pogody w innym miejscu BTW -- nie żebym chciał używać dwie różne stacje na raz w tym samym miejscu). Ponieważ akurat pod ręką był c.h.i.p (aka the world's first 9USD computer), podłączyłem na razie stację do chipa.
chip@chip:~/Crontab$ lsusb Bus 002 Device 003: ID 1941:8021 Dream Link WH1080 \ Weather Station / USB Missile Launcher root@chip:~# apt-get update root@chip:~# apt-get install python-pip root@chip:~# pip install pywws ... Successfully installed pywws tzlocal pytz Cleaning up... root@chip:~# pywws-hourly -v /home/chip/Logs/weather\ >> /home/chip/Logs/weather/Hourly.log cat /home/chip/Logs/weather/Hourly.log Unknown weather station type. Please edit weather.ini and set 'ws type' to '1080' or '3080', as appropriate. Your station is probably a '1080' type. # wpisać 1080 w weather.ini # zmienić wpis dotyczący /tmp/weather w weather.ini root@chip:~/Crontab# pywws-hourly -v /home/chip/Logs/weather\ >> /home/chip/Logs/weather/Hourly.log 03:07:00:pywws.Logger:pywws version 16.12.0, build 1367 (e917ba9) 03:07:00:pywws.Logger:Python version 2.7.9 (default, Mar 1 2015, 13:48:22) [GCC 4.9.2] 03:07:00:pywws.WeatherStation.CUSBDrive:using pywws.device_libusb1 03:07:02:pywws.DataLogger:Synchronising to weather station 03:09:00:pywws.weather_station:live_data missed 03:09:44:pywws.DataLogger:Fetching data 03:09:45:pywws.Process:Generating summary data 03:09:45:pywws.Calib:Using default calibration
I działa...
Ciąg dlaszy wpisu Rejestrowanie wysokości przez odbiorniki GPS
Ciąg dalszy bo ,,ciąg technologiczny'' FIT →TCX →GPX →srtm.py →lepszy_GPX→Endomondo/Strava pozbawia niestety przesyłany plik GPX danych, które nie są wspierane przez format GPX (takie jak tętno dla przykładu).
Problem można rozwiązać dodając do pliku TCX poprawione wysokości z pliku lepszy_GPX:
#!/usr/bin/perl use XML::LibXML; use XML::LibXML::XPathContext; use Getopt::Long; binmode(STDOUT, ":utf8"); my $Gpx_file; my $Tcx_file; ## gpx -- plik GPX z poprawionymi wysokościami ## tcx -- oryginalny plik TCX (w którym mają być poprawione wysokości) GetOptions( "gpx=s" => \$Gpx_file, "tcx=s" => \$Tcx_file, ) ; my $parser = XML::LibXML->new(); for my $file2parse ("$Gpx_file") { my $doc = $parser->parse_file($file2parse); my $root = $doc->documentElement(); my $xc = XML::LibXML::XPathContext->new( $root ); $xc->registerNs('gpx', 'http://www.topografix.com/GPX/1/0'); foreach my $t ($xc->findnodes('//gpx:trkpt')) { my $xpe = XML::LibXML::XPathContext->new( $t ); $xpe->registerNs('gpx', 'http://www.topografix.com/GPX/1/0'); $node++; $gmttime = ($xpe->findnodes('gpx:time'))[0]->textContent(); $altitude = ($xpe->findnodes('gpx:ele'))[0]->textContent(); $GpxPoints{"$gmttime"} = $altitude; } } ## for $e (keys %GpxPoints) { print "$e => $GpxPoints{$e}\n"; } for my $file2parse ("$Tcx_file") { # http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 my $doc = $parser->parse_file($file2parse); my $root = $doc->documentElement(); my $xc = XML::LibXML::XPathContext->new( $root ); $xc->registerNs('tcx', 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'); foreach my $t ($xc->findnodes('//tcx:Trackpoint')) { my $xpe = XML::LibXML::XPathContext->new( $t ); $xpe->registerNs('tcx', 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'); $gmttime = ($xpe->findnodes('tcx:Time'))[0]->textContent(); foreach my $xpa ( $xpe->findnodes('tcx:AltitudeMeters') ) { $oldAltitude = $xpa->textContent(); ## jeżeli istnieje w pliku GPX punkt z tym samym stemplem ## czasu zmień zawartość elementu tcx:AltitudeMeters if (exists $GpxPoints{$gmttime} ) { ## replace content of tcx:AltitudeMeters $xpa->removeChildNodes(); $xpa->appendText("$GpxPoints{$gmttime}"); $changedNodesNo++; } else { ## jeżeli nie istnieje usuń cały węzeł my $parent = $t->parentNode; $parent->removeChild( $t ); $droppedNodes .= "$gmttime;"; $droppedNodesNo++; } } } ### ### print "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>\n"; print $root->toString; ### ### } print STDERR "Zmieniono: $changedNodesNo / Usunięto: $droppedNodesNo\n"; print STDERR "($droppedNodes)\n";
Ponieważ skrypt srtm.py
nie tworzy pliku GPX zawierającego wszystkie punkty z pliku TCX
(z jakiś powodów niewielka część jest pomijana); warunek exists $GpxPoints{$gmttime}
sprawdza czy istnieje w pliku GPX punkt z podanym stemplem czasowym. Jeżeli istnieje zmieniana
jest wysokość, jeżeli nie to punkt jest usuwany.
Rejestrowanie wysokości przez odbiorniki GPS jak wiadomo odbywa się z błędem. Do tej pory mi to wisiało, ale problem stanął, gdy zachciało mi się dodać nachylenie do danych GPS (wyświetlanych jako napisy generowane autorskimi skryptami z pliku GPX/TCX), które to nachylenie nie zgadzało się wielokrotnie ze stanem faktycznym. Szczególnie tajemnicze były różnice na plus/minus 300% i więcej...
W trakcie prób rozwiązania tego problemu dowiedziałem się tego i owego na temat rejestracji wysokości. I o tym jest ten wpis. Natomiast problem z obliczaniem nachylenia został nierozwiązany. Wartości nachylenia pokazywane w trakcie jazdy przez urządzenia, takie jak Garmin Edge 500 są wiarygodnie, co by świadczyło, że zaimplementowano w nich jakiś całkiem sprytny algorytm wygładzający. Szukałem co na ten temat wie google, ale nic nie znalazłem. Próbowałem wygładzać wysokość/nachylenie w R za pomocą funkcji loess (a także średniej ruchomej) -- rezultaty były kiepskie.
Znalazłem natomiast informacje w jaki sposób można poprawić dokładność danych o wysokości, zarejestrowaną (niedokładnie) przez urządzenie GPS. Otóż można albo skorzystać z usługi Google Elevation (GE) albo użyć danych SRTM, przy czym minusem GE jest dzienny limit 2500 zapytań.
Zaczniejmy od prostszego przypadku, tj. dodania/zmiany wysokości z modelu SRTM.
W tym celu pobieramy/instalujemy pakiet
srtm.py. Pakiet
zawiera m.in narzędzie pn. gpxelevations
:
## Dodaje dane SRTM, zapisuje do pliku PLIK_SRTMS.gpx gpxelevations -s -o PLIK.gpx -f PLIK_SRTMS.gpx ## Wygładza dane i zapisuje do pliku PLIK_SRTMS.gpx gpxelevations -o 20160511.gpx -f PLIK_SRTMS.gpx
Dane SRTM można też dodać/zamienić posługując się okienkową aplikacją pn. GPSPrune, jak ktoś lubi klikać ale nie lubi wiersza poleceń.
Zakładając, że dane są w pliku GPX pobranym z urządzenia, poniższy skrypt wygeneruje plik CSV zawierający m.in. wysokość oryginalną, wysokość z modelu SRTM oraz wysokość wygładzoną:
#!/bin/bash FILE=`basename $1 .gpx` if [ -f "$FILE.gpx" ] ; then ## Dodanie lepszych wysokości gpxelevations -o $FILE.gpx -f ${FILE}_SRTM.gpx gpxelevations -s -o $FILE.gpx -f ${FILE}_SRTMS.gpx ## Zamiana na CSV (skrypt gpx2csv.pl zamieszczono dalej) gpx2csv.pl ${FILE}.gpx > ${FILE}.csv && \ gpx2csv.pl ${FILE}_SRTM.gpx > ${FILE}_SRTM.csv && \ gpx2csv.pl ${FILE}_SRTMS.gpx > ${FILE}_SRTMS.csv ## Scalenie w jeden plik CSV paste -d ';' ${FILE}.csv ${FILE}_SRTM.csv ${FILE}_SRTMS.csv | \ awk -F';' ' BEGIN{ print "daytime;lat;long;ele;srtm;srtms"; };\ {print $1 ";" $2 ";" $3 ";" $4 ";" $8 ";" $12}' > ${FILE}_ALLE.csv else echo "*** USAGE: $0 PLIK.gpx ***" fi
Teraz można porównać wysokość oryginalną, wysokość SRTM oraz wygładzoną na wykresie:
library(reshape) require(ggplot2) args = commandArgs(trailingOnly = TRUE); if (length(args)==0) { stop("Podaj nazwę pliku CSV", call.=FALSE) } fileBase <- gsub(".csv", "", args[1]); outFile <- paste (fileBase, ".pdf", sep = ""); d <- read.csv(args[1], sep = ';', header=T, na.string="NA"); ggplot(d, aes(x = as.POSIXct(daytime, format="%Y-%m-%dT%H:%M:%SZ"))) + geom_line(aes(y = ele, colour = 'zarejestrowana', group = 1), size=.5) + geom_line(aes(y = srtm, colour = 'srtm', group = 1), size=.5) + geom_line(aes(y = srtms, colour = "srtm (wygładzona)", group = 1), size=.5) + ylab(label="Wysokość [mnpm]") + xlab(label="czas") + labs(colour = paste("Plik:", fileBase )) + theme(legend.position="top") + theme(legend.text=element_text(size=12)); ggsave(file=outFile, width=12, height=5) ## Uruchomienie: ## Rscript gps_vs_srtm.R PLIK.csv
Oryginalne dane z urządzenia są systematycznie zaniżone.
Dodanie danych z Google Elevation jest równie proste. Poniższy skrypt -- jako przykład -- pobiera wysokość dla punktu o współrzędnych podanych jako argumenty wywołania (szerokość/długość):
use LWP::Simple; use JSON qw( decode_json ); my $geocodeapi = "https://maps.googleapis.com/maps/api/elevation/json?locations"; ## szerokość = $ARGV[0] ; długość = $ARGV[1] my $url = $geocodeapi . "=$ARGV[0],$ARGV[1]"; my $json = get($url); my $d_json = decode_json( $json ); if ( $d_json->{status} eq 'OK' ) { $elevationG = $d_json->{results}->[0]->{elevation}; $resolutionG = $d_json->{results}->[0]->{resolution}; $latG = $d_json->{results}->[0]->{location}->{lat}; $lngG = $d_json->{results}->[0]->{location}->{lng}; } print "$elevationG\n";
Drugi z wykresów zawiera dane pobrane z Google Elevation Service.
Na zakończenie jeszcze skrypt zamieniający gpx na csv:
#!/usr/bin/perl # Wykorzystanie gpx2csv.pl PLIK.gpx > PLIK.csv # use XML::DOM; my $parser = new XML::DOM::Parser; for my $file2parse (@ARGV) { my $doc = $parser->parsefile ($file2parse); for my $t ( $doc->getElementsByTagName ("trk") ) { for my $p ( $t->getElementsByTagName ("trkpt") ) { $node++; my $latitude = $p->getAttributeNode ("lat")->getValue() ; my $longitude = $p->getAttributeNode ("lon")->getValue() ; $gmttime = $p->getElementsByTagName ("time")-> item(0)->getFirstChild->getNodeValue(); $altitude = $p->getElementsByTagName ("ele")-> item(0)->getFirstChild->getNodeValue(); printf "%s;%s;%s;%s\n", $gmttime, $latitude, $longitude, $altitude; } } }
The script: 1) suspend capturing images at night for obvious reason
and 2) writes images to files named as:
image_<odd-or-even-week><day-of-week>_<hour><minute>.jpg
Thus only images from the last two weeks are stored and the older ones are deleted ``automatically''.
#!/bin/bash OUTPUT_DIR=/var/www/cam/fswebcam-output # Max resolution for C270 webcam is 1280x720 RESOLUTION="1280x720" QUALITY="85" MINUTE=`date +%M` HOUR=`date +%H` MONTH=`date +%m` DAY_OF_WEEK=`date +%u` WEEK=`date +%U` FILE_OUT=`date +%d%H%M` TODAY=`date +%Y/%m/%d` ## Suspend capturing pictures at night DAY_START=`sun_calc.py -date "$TODAY" -city Sopot -param dawn` DAY_STOP=`sun_calc.py -date "$TODAY" -city Sopot -param dusk` if [ "$DAY_START" = "False" -o "$DAY_STOP" = "True" ] ; then NIGHT="YES"; exit; fi fswebcam -r $RESOLUTION -S 11 --jpeg $QUALITY \ --title "Sopot/Abrahama Street (PL)" \ --subtitle "View from my window" \ --info "Logitech_Webcam_C270@raspberryPi ($RESOLUTION)" \ --save $OUTPUT_DIR/image.jpg -q ## Rotate photos every two weeks ## Week number modulo 2 (0 or 1) WEEK_NO=$(($WEEK % 2)) FILE_OUT="${WEEK_NO}${DAY_OF_WEEK}_$HOUR$MINUTE" ## Wundergound expects the file is `image.jpg'. ## To preserve from overwriting rename: cd $OUTPUT_DIR && cp image.jpg image_$FILE_OUT.jpg
./sun_calc.py
is a Python script (I am not
Python programmer BTW):
#!/usr/bin/python import datetime import argparse import pytz from astral import Astral city_name = 'Sopot' parser = argparse.ArgumentParser() parser.add_argument("-date", type=str, help="Date as yyyy/mm/dd") parser.add_argument("-city", type=str, help="City name") parser.add_argument("-param", type=str, help="dusk sunrise sunset or dawn") parser.add_argument("-verbose", help="Icrease verbosity", action="store_true") args = parser.parse_args() dat = args.date city_name = args.city param = args.param verb_level = args.verbose [year, month, day] = dat.split("/"); year = int(year) month = int(month) day = int(day) a = Astral() city = a[city_name] a.solar_depression = 6 ## sd can be: 6, 12, 18 timezone = city.timezone sun = city.sun(date=datetime.date(year, month, day), local=False) sun_local = city.sun(date=datetime.date(year, month, day), local=True) time_now = datetime.datetime.utcnow().replace(tzinfo = pytz.utc) if verb_level > 0: print('solar_depressions: ' + str(a.solar_depression)) print city_name + " " + str(time_now) + " " + str(sun[param]) \ + " (" + str(sun_local[param]) + ")" print time_now > sun[param]
The script based on astral
package can compute
dusk/dawn time (among other things) and compares it to the current time.
If current time is past dusk/dawn (specified as a command line parameter)
./sun_calc.py
returns True
.
Otherwise it returns False
.
The astral
package does not contains Sopot but it is easy
to add it just
by editing astral.py
(before installation of course):
Sopot,Poland,54°44'N,18°55'E,Europe/Warsaw
A short test demonstrating that for Sopot and Warsow the script returns significantly different results:
./sun_calc.py -date 2013/01/21 -city Sopot -param dawn -verbose solar_depressions: 6.0 Sopot 2013-01-21 09:17:11.368979+00:00 2013-01-21 06:09:47+00:00 (2013-01-21 07:09:47+01:00) ./sun_calc.py -date 2013/01/21 -city Warsaw -param dawn -verbose solar_depressions: 6.0 Warsaw 2013-01-21 09:17:46.172157+00:00 2013-01-21 05:53:22+00:00 (2013-01-21 06:53:22+01:00) True
So there is circa 15 minutes difference between Sopot which is some 300 km to the North from Warsaw.