specifier must include a day number or range in the 'weekday' field, you entered %r", entry)
343 | return None, _end
344 |
345 | # allow Sunday to be specified as weekday 7
346 | if which == WEEK_OFFSET:
347 | _end_limit = 7
348 |
349 | increment = None
350 | # increments
351 | if '/' in entry:
352 | entry, increment = entry.split('/')
353 | increment = int(increment, 10)
354 | _assert(increment > 0,
355 | "you can only use positive increment values, you provided %r",
356 | increment)
357 | _assert(increment <= _end_limit,
358 | "increment value must be less than %r, you provided %r",
359 | _end_limit, increment)
360 |
361 | # handle singles and ranges
362 | good = _parse_piece(entry)
363 |
364 | # change Sunday to weekday 0
365 | if which == WEEK_OFFSET and 7 in good:
366 | good.discard(7)
367 | good.add(0)
368 |
369 | return good, _end
370 |
371 |
372 | _gv = lambda: str(random.randrange(60))
373 |
374 |
375 | class CronTab(object):
376 | __slots__ = 'matchers', 'rs'
377 | def __init__(self, crontab, loop=False, random_seconds=False):
378 | """
379 | inputs:
380 | `crontab` - crontab specification of "[S=0] Mi H D Mo DOW [Y=*]"
381 | `loop` - do we loop when we validate / construct counts
382 | (turning 55-5,1 -> 0,1,2,3,4,5,55,56,57,58,59 in a "minutes" column)
383 | `random_seconds` - randomly select starting second for tasks
384 | """
385 | self.rs = random_seconds
386 | self.matchers = self._make_matchers(crontab, loop, random_seconds)
387 |
388 | def __eq__(self, other):
389 | if not isinstance(other, CronTab):
390 | return False
391 | match_last = self.matchers[1:] == other.matchers[1:]
392 | return match_last and ((self.rs and other.rs) or (not self.rs and
393 | not other.rs and self.matchers[0] == other.matchers[0]))
394 |
395 | def _make_matchers(self, crontab, loop, random_seconds):
396 | '''
397 | This constructs the full matcher struct.
398 | '''
399 | crontab = _aliases.get(crontab, crontab)
400 | ct = crontab.split()
401 |
402 | if len(ct) == 5:
403 | ct.insert(0, _gv() if random_seconds else '0')
404 | ct.append('*')
405 | elif len(ct) == 6:
406 | ct.insert(0, _gv() if random_seconds else '0')
407 | _assert(len(ct) == 7,
408 | "improper number of cron entries specified; got %i need 5 to 7"%(len(ct,)))
409 |
410 | matchers = [_Matcher(which, entry, loop) for which, entry in enumerate(ct)]
411 |
412 | return Matcher(*matchers)
413 |
414 | def _test_match(self, index, dt):
415 | '''
416 | This tests the given field for whether it matches with the current
417 | datetime object passed.
418 | '''
419 | at = _attribute[index]
420 | attr = getattr(dt, at)
421 | if index == WEEK_OFFSET:
422 | attr = attr() % 7
423 | return self.matchers[index](attr, dt)
424 |
425 | def next(self, now=None, increments=_increments, delta=True, default_utc=WARN_CHANGE, return_datetime=False):
426 | '''
427 | How long to wait in seconds before this crontab entry can next be
428 | executed.
429 | '''
430 | if default_utc is WARN_CHANGE and (isinstance(now, _number_types) or (now and not now.tzinfo) or now is None):
431 | warnings.warn(WARNING_CHANGE_MESSAGE, FutureWarning, 2)
432 | default_utc = False
433 |
434 | now = now or (datetime.utcnow() if default_utc and default_utc is not WARN_CHANGE else datetime.now())
435 | if isinstance(now, _number_types):
436 | now = datetime.utcfromtimestamp(now) if default_utc else datetime.fromtimestamp(now)
437 |
438 | # handle timezones if the datetime object has a timezone and get a
439 | # reasonable future/past start time
440 | onow, now = now, now.replace(tzinfo=None)
441 | tz = onow.tzinfo
442 | future = now.replace(microsecond=0) + increments[0]()
443 | if future < now:
444 | # we are going backwards...
445 | _test = lambda: future.year < self.matchers.year
446 | if now.microsecond:
447 | future = now.replace(microsecond=0)
448 | else:
449 | # we are going forwards
450 | _test = lambda: self.matchers.year < future.year
451 |
452 | # Start from the year and work our way down. Any time we increment a
453 | # higher-magnitude value, we reset all lower-magnitude values. This
454 | # gets us performance without sacrificing correctness. Still more
455 | # complicated than a brute-force approach, but also orders of
456 | # magnitude faster in basically all cases.
457 | to_test = ENTRIES - 1
458 | while to_test >= 0:
459 | if not self._test_match(to_test, future):
460 | inc = increments[to_test](future, self.matchers)
461 | future += inc
462 | for i in xrange(0, to_test):
463 | future = increments[ENTRIES+i](future, inc)
464 | try:
465 | if _test():
466 | return None
467 | except:
468 | print(future, type(future), type(inc))
469 | raise
470 | to_test = ENTRIES-1
471 | continue
472 | to_test -= 1
473 |
474 | # verify the match
475 | match = [self._test_match(i, future) for i in xrange(ENTRIES)]
476 | _assert(all(match),
477 | "\nYou have discovered a bug with crontab, please notify the\n" \
478 | "author with the following information:\n" \
479 | "crontab: %r\n" \
480 | "now: %r", ' '.join(m.input for m in self.matchers), now)
481 |
482 | if return_datetime:
483 | return future.replace(tzinfo=tz)
484 |
485 | if not delta:
486 | onow = now = datetime(1970, 1, 1)
487 |
488 | delay = future - now
489 | if tz:
490 | delay += _fix_none(onow.utcoffset())
491 | if hasattr(tz, 'localize'):
492 | delay -= _fix_none(tz.localize(future).utcoffset())
493 | else:
494 | delay -= _fix_none(future.replace(tzinfo=tz).utcoffset())
495 |
496 | return delay.days * 86400 + delay.seconds + delay.microseconds / 1000000.
497 |
498 | def previous(self, now=None, delta=True, default_utc=WARN_CHANGE):
499 | return self.next(now, _decrements, delta, default_utc)
500 |
501 | def test(self, entry):
502 | if isinstance(entry, _number_types):
503 | entry = datetime.utcfromtimestamp(entry)
504 | for index in xrange(ENTRIES):
505 | if not self._test_match(index, entry):
506 | return False
507 | return True
508 |
509 | def _fix_none(d, _=timedelta(0)):
510 | if d is None:
511 | return _
512 | return d
513 |
--------------------------------------------------------------------------------
/_listvars.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | '''
4 | get the config file autowx2_conf and reports variables in a bash-like style
5 | '''
6 |
7 | from autowx2_conf import * # configuration
8 |
9 | d = locals().copy()
10 | for var in d:
11 | val=d[var]
12 | if not var.startswith("__"): # not global variables
13 | if not str(val).startswith( ("{", "<") ): # not dictionary
14 | print("%s=%s" % (var, val))
15 |
--------------------------------------------------------------------------------
/_listvars.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # get the config file autowx2_conf and reports variables in a bash-like style
4 |
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 |
8 | echo $baseDir
9 |
10 | for line in $(python $baseDir/_listvars.py); do
11 | if echo $line | grep -F = &>/dev/null; then
12 | varname=$(echo "$line" | cut -d '=' -f 1)
13 | value=$(echo "$line" | cut -d '=' -f 2-)
14 | eval "$varname=$value"
15 | fi
16 | done
17 |
18 | echo $stationName
19 |
20 | #
21 | # add some environmental variables
22 | #
23 |
24 | export autowx2version=$(cd $baseDir && git describe --tags)
25 |
--------------------------------------------------------------------------------
/autowx2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | #
5 | # autowx2 - The program for scheduling recordings and processing of the
6 | # satellite and ground radio transmissions (like capturing of the weather
7 | # APT images from NOAA satellites, voice messages from ISS, fixed time
8 | # recordings of WeatherFaxes etc.)
9 | # https://github.com/filipsPL/autowx2/
10 | #
11 |
12 | # from autowx2_conf import * # configuration
13 | from autowx2_functions import * # all functions and magic hidden here
14 |
15 | # ------------------------------------------------------------------------------------------------------ #
16 |
17 | if __name__ == "__main__":
18 | log("⚡ Program start")
19 | saveToFile("%s/start.tmp" % (wwwDir), str(time.time())) # saves program start date to file
20 |
21 | if cleanupRtl:
22 | log("Killing all remaining rtl_* processes...")
23 | justRun(["bin/kill_rtl.sh"], loggingDir)
24 |
25 | while True:
26 | t1 = Thread(target = mainLoop)
27 | t1.setDaemon(True)
28 | t1.start()
29 | # app.run(debug=True, port=webInterfacePort)
30 |
31 | socketio.run(app, port=webInterfacePort, debug=False)
32 |
--------------------------------------------------------------------------------
/autowx2_conf.py.example:
--------------------------------------------------------------------------------
1 | #
2 | # autowx2 - config file
3 | # variables used by autowx2
4 | #
5 |
6 | #
7 | # baseDir is taken from this script
8 | # do not change, run configure.sh to configure
9 | #
10 | from basedir_conf import *
11 |
12 |
13 | # satellites to record
14 | # for list of active satellites and used frequences, see
15 | # http://www.dk3wn.info/p/?page_id=29535
16 |
17 | satellitesData = {
18 | 'NOAA-18': {
19 | 'freq': '137912500',
20 | 'processWith': 'modules/noaa/noaa.sh',
21 | 'priority': 2},
22 | 'NOAA-15': {
23 | 'freq': '137620000',
24 | 'processWith': 'modules/noaa/noaa.sh',
25 | 'priority': 2},
26 | 'NOAA-19': {
27 | 'freq': '137100000',
28 | 'processWith': 'modules/noaa/noaa.sh',
29 | 'priority': 2},
30 | 'METEOR-M2': {
31 | 'freq': '137900000',
32 | 'processWith': 'modules/meteor-m2/meteor.sh',
33 | 'priority': 1},
34 | 'ISS': {
35 | #'freq': '145800000', # FM U/v VOICE Repeater (Worldwide) and FM SSTV downlink (Worldwide) [OK]
36 | 'freq': '143625000', # FM VHF-1 downlink. Main Russian communications channel. Often active over Moskow. [ ]
37 | #'freq': '145825000', # APRS -- AX.25 1200 Bd AFSK Packet Radio (Worldwide) Downlink [ ]
38 | #'freq': '437800000', # Cross band FM amateur radio repeater with a downlink on 437.800 MHz at the International Space Station (Worldwide) Downlink [ ]
39 | 'processWith': 'modules/iss/iss_voice_mp3.sh',
40 | 'priority': 3},
41 | # 'PR3_NEWS': {
42 | # 'freq': '98796500',
43 | # 'processWith': 'modules/fm/fm.sh',
44 | # 'fixedTime': '0 12 * * *',
45 | # 'fixedDuration': 300,
46 | # 'priority': 10},
47 | #'FOX-1A': { # http://www.dk3wn.info/p/?cat=80
48 | #'freq': '145980000',
49 | #'processWith': 'modules/iss/iss_voice_mp3.sh',
50 | #'priority': 4},
51 | #'FOX-1D': { # http://www.dk3wn.info/p/?cat=80
52 | #'freq': '145880000',
53 | #'processWith': 'modules/iss/iss_voice_mp3.sh',
54 | #'priority': 4},
55 | #'FOX-1B': { # http://www.dk3wn.info/p/?cat=80
56 | #'freq': '145960000',
57 | #'processWith': 'modules/iss/iss_voice_mp3.sh',
58 | #'priority': 4},
59 |
60 | }
61 |
62 |
63 | #
64 | # priority time margin - time margin between two overlapping transits with different priorities, in seconds
65 | #
66 | priorityTimeMargin = 240
67 |
68 |
69 | #
70 | # minimal elevation of pass to capture satellite
71 | #
72 | minElev = 20
73 |
74 | #
75 | # skip first n seconds of the pass
76 | #
77 | skipFirst = 20
78 |
79 | #
80 | # skip last n seconds of the pass
81 | #
82 | skipLast = 20
83 |
84 |
85 | # staion information
86 | # lon: positive values for W, negative for E
87 | stationLat = '50.34'
88 | stationLon = '-22.06'
89 | stationAlt = '179'
90 | stationName = 'San Escobar'
91 |
92 | #
93 | # dongle gain
94 | #
95 | dongleGain = '49.6'
96 |
97 |
98 | #
99 | # Kill all rtl_ related processes after/berofr recordings?
100 | # This should be enabled when the user doesn't use another instances which may run rtl software
101 | #
102 |
103 | cleanupRtl = True
104 |
105 |
106 | #
107 | # where to place the www data (static html pages)
108 | #
109 | wwwDir=baseDir + 'var/www/'
110 |
111 |
112 | #
113 | # recording direcotry - where to put recorded files, processed images etc. - the core
114 | #
115 | #recordingDir = baseDir + "recordings/" # will goto recording dir in local dir
116 | recordingDir = wwwDir + "recordings/" # will go to www dir
117 |
118 |
119 | #
120 | # location of the HTML file with a list of next passes
121 | #
122 | htmlNextPassList = wwwDir + 'nextpass.tmp'
123 |
124 | #
125 | # location of the Gantt chart file with a plot of next passes; PNG or SVG file extension possible
126 | #
127 | ganttNextPassList = wwwDir + 'nextpass.png'
128 |
129 | #
130 | # WWW root path - where is your main index.html file
131 | # this could be:
132 | # / - for page in the root www directory, eg. www.domain.org/index.html
133 | # /~filipsPL/ - for page located in filipsPL's home directory
134 | # file:///home/filipsPL/github/autowx2/var/www/ - for files stored locally in the given local directory
135 |
136 | wwwRootPath='/'
137 |
138 |
139 | #
140 | # galleries to include in the static index page
141 | # 1 = inclcude, 0 = don't include
142 | #
143 | includeGalleryNoaa=1 # NOAA recordings
144 | includeGalleryISS=0 # ISS recordings
145 | includeGalleryMeteor=0 # Meteor recordings
146 | includeGalleryLogs=1 # logs in plain text
147 | includeGalleryDump1090=0 # dump1090 data
148 |
149 |
150 |
151 | #
152 | # port at which webserver will be listening
153 | #
154 | webInterfacePort = 5010
155 |
156 | # script tha will be used whle waiting for the next pass; set False if we just want to sleep
157 | # by default, this script will get the parameter of duration of the time to be run and the recent dongleShift
158 | # scriptToRunInFreeTime = False # does nothing
159 | scriptToRunInFreeTime = baseDir + "bin/aprs.sh" # APRS monitor
160 | #scriptToRunInFreeTime = baseDir + "bin/radiosonde_auto_rx.sh" # radiosonde tracker, see https://github.com/projecthorus/radiosonde_auto_rx
161 | #scriptToRunInFreeTime = baseDir + "bin/pymultimonaprs.sh" # APRS iGate,
162 | #scriptToRunInFreeTime = baseDir + "bin/wspr.sh" # Weak Signal Propagation Reporter
163 |
164 | # pymultimonaprs must be installed, see: https://github.com/asdil12/pymultimonaprs/
165 | #scriptToRunInFreeTime = baseDir + "bin/dump1090.sh"
166 |
167 | scriptToRunInFreeTime = baseDir + "bin/rtl_433.sh" # monitor traffic on 433 MHz; see: rtl_433
168 |
169 |
170 | ### Logging to file (enter the path) or False to disable
171 | # loggingDir = False
172 | loggingDir = recordingDir + "/logs/"
173 |
174 |
175 | # Dongle PPM shift, hopefully this will change to reflect different PPM on freq
176 | dongleShift = '0'
177 |
178 |
179 | #
180 | # wether to turn bias t power on (i.e., for LNA)
181 | # see: rtl_fm --help | grep "-T"
182 | # biast=""
183 | # biast="-T"
184 | #
185 | biast=""
186 |
187 |
188 | #
189 | tleDir = baseDir + 'var/tle/'
190 | tleFile = 'all.txt'
191 |
192 | tleFileName = tleDir + tleFile
193 |
194 | # dongle shift file
195 | dongleShiftFile = baseDir + "var/dongleshift.txt"
196 |
197 | # dongle calibration program
198 | # should return the dongle ppm shift
199 |
200 | #calibrationTool = False # don't calibrate the dongle, use old/fixed shift
201 | calibrationTool = baseDir + "bin/calibrate.sh" # uses predefined GSM channel
202 | #calibrationTool = baseDir + "bin/calibrate_full.sh" # check for the best GSM channel and calibrates
203 |
204 |
205 | # DERIVATIVES #############################
206 |
207 | #
208 | # latlonalt for use with wxmap
209 | #
210 | latlonalt = "%s/%s/%s" % (stationLat, -1 * float(stationLon), stationAlt)
211 |
--------------------------------------------------------------------------------
/autowx2_functions.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # autowx2 - function and classes used by autowx2 and auxiliary programs
4 | #
5 | # GANTT Chart with Matplotlib
6 | # Sukhbinder
7 | # inspired by:
8 | # https://sukhbinder.wordpress.com/2016/05/10/quick-gantt-chart-with-matplotlib/
9 | # taken from
10 | # https://github.com/fialhocoelho/test/blob/master/plot/gantt.py
11 | #
12 |
13 | # for autowx2 itself
14 | import predict
15 | import time
16 | from datetime import datetime
17 | from time import strftime
18 | import subprocess
19 | import os
20 | from _crontab import *
21 | import re
22 | import sys
23 |
24 |
25 | # for plotting
26 | import matplotlib
27 | matplotlib.use('Agg') # Force matplotlib to not use any Xwindows backend.
28 | import matplotlib.pyplot as plt
29 | # import matplotlib.font_manager as font_manager
30 | import matplotlib.dates
31 | from matplotlib.dates import DateFormatter
32 | import numpy as np
33 |
34 | # webserver
35 | from flask import render_template, Flask
36 | from flask_socketio import SocketIO, emit
37 | import codecs
38 | from threading import Thread
39 |
40 | # configuration
41 | from autowx2_conf import *
42 |
43 | # ---------------------------------------------------------------------------- #
44 |
45 | satellites = list(satellitesData)
46 | qth = (stationLat, stationLon, stationAlt)
47 |
48 |
49 | def mkdir_p(outdir):
50 | ''' bash "mkdir -p" analog'''
51 | if not os.path.exists(outdir):
52 | os.makedirs(outdir)
53 |
54 |
55 | class bc():
56 |
57 | """Colors made easy"""
58 | HEADER = '\033[95m'
59 | CYAN = '\033[96m'
60 | YELLOW = '\033[93m'
61 | RED = '\033[91m'
62 | OKBLUE = '\033[94m'
63 | OKGREEN = '\033[97m'
64 | WARNING = '\033[93m'
65 | FAIL = '\033[91m'
66 | ENDC = '\033[0m'
67 | BOLD = '\033[1m'
68 | GRAY = '\033[37m'
69 | UNDERLINE = '\033[4m'
70 |
71 |
72 | def is_number(s):
73 | try:
74 | float(s)
75 | return True
76 | except ValueError:
77 | return False
78 |
79 |
80 | def getTleData(satellite):
81 | '''Open tle file and return a TLE record for the given sat'''
82 | tlefile = open(tleFileName, 'r')
83 | tledata = tlefile.readlines()
84 | tlefile.close()
85 |
86 | tleData = []
87 |
88 | for i, line in enumerate(tledata):
89 | if satellite in line:
90 | for n in tledata[i:i + 3]:
91 | tleData.append(n.strip('\r\n').rstrip())
92 | break
93 | if len(tleData) > 0:
94 | return tleData
95 | else:
96 | return False
97 |
98 |
99 | def parseCron(cron):
100 | entry = CronTab(cron).next(default_utc=False)
101 | return entry + time.time() # timestamp of the next occurence
102 |
103 |
104 | def getFixedRecordingTime(satellite):
105 | '''Reads from the config the fixed recording time'''
106 | try:
107 | fixedTime = satellitesData[satellite]["fixedTime"]
108 | fixedDuration = satellitesData[satellite]["fixedDuration"]
109 | return {"fixedTime": parseCron(fixedTime), "fixedDuration": fixedDuration}
110 | except KeyError:
111 | return False
112 |
113 |
114 | def genPassTable(satellites, qth, howmany=20):
115 | '''generate a table with pass list, sorted'''
116 |
117 | passTable = {}
118 | for satellite in satellites:
119 |
120 | tleData = getTleData(satellite)
121 | priority = satellitesData[satellite]['priority']
122 |
123 | if tleData: # if tle data was there in the file :: SATELLITES
124 |
125 | czasStart = time.time()
126 |
127 | p = predict.transits(tleData, qth, czasStart)
128 |
129 | # d = predict.observe(tleData, qth)
130 | # print d['doppler'] ## doppler : doppler shift between groundstation and satellite.
131 | # exit(1)
132 |
133 | for i in range(1, howmany):
134 | transit = next(p)
135 |
136 | # transitEnd = transit.start + transit.duration() - skipLast
137 |
138 | if not time.time() > transit.start + transit.duration() - skipLast - 1: # esttimate the end of the transit, minus last 10 seconds
139 | if int(transit.peak()['elevation']) >= minElev:
140 | passTable[transit.start] = [satellite, int(
141 | transit.start + skipFirst), int(
142 | transit.duration() - skipFirst - skipLast),
143 | int(transit.peak()['elevation']), int(
144 | transit.peak()['azimuth']), priority
145 | ]
146 | # transit.start - unix timestamp
147 |
148 | elif 'fixedTime' in satellitesData[satellite]: # if ['fixedTime'] exists in satellitesData => time recording
149 | # cron = getFixedRecordingTime(satellite)["fixedTime"]
150 | cron = satellitesData[satellite]['fixedTime']
151 | duration = getFixedRecordingTime(satellite)["fixedDuration"]
152 |
153 | delta = 0
154 | for i in range(0, howmany):
155 | entry = CronTab(
156 | cron).next(now=time.time() + delta,
157 | default_utc=False)
158 | delta += entry
159 | start = delta + time.time()
160 | passTable[start] = [
161 | satellite, int(start), int(duration), '0', '0', priority]
162 | else:
163 | log("✖ Can't find TLE data (in keplers) nor fixed time schedule (in config) for " +
164 | satellite, style=bc.FAIL)
165 |
166 | # Sort pass table
167 | passTableSorted = []
168 | for start in sorted(passTable):
169 | passTableSorted.append(passTable[start])
170 |
171 | # Clean the pass table according to the priority. If any pass overlaps,
172 | # remove one with less priority (lower priority number).
173 | passTableSortedPrioritized = passTableSorted[:]
174 | passCount = len(passTableSorted)
175 | for i in range(0, passCount - 1): # -1 or -2 :BUG?
176 | satelliteI, startI, durationI, peakI, azimuthI, priorityI = passTableSorted[
177 | i]
178 | satelliteJ, startJ, durationJ, peakJ, azimuthJ, priorityJ = passTableSorted[
179 | i + 1]
180 | endTimeI = startI + durationI
181 |
182 | if priorityI != priorityJ:
183 | if (startJ + priorityTimeMargin < endTimeI):
184 | # print "End pass:", satelliteI, t2human(endTimeI), "--- Start
185 | # time:", satelliteJ, t2human(startJ)
186 | if priorityJ < priorityI:
187 | log(" 1. discard %s, keep %s" % (satelliteI, satelliteJ))
188 | passTableSortedPrioritized[i] = ''
189 | elif priorityJ > priorityI:
190 | log(" 2. discard %s, keep %s" % (satelliteJ, satelliteI))
191 | passTableSortedPrioritized[i + 1] = ''
192 |
193 | # let's clean the table and remove empty (removed) records
194 | # and remove the priority record, it will not be useful later -- x[:5]
195 | passTableSortedPrioritized = [x[:5]
196 | for x in passTableSortedPrioritized if x != '']
197 |
198 | return passTableSortedPrioritized
199 |
200 |
201 | def t2human(timestamp):
202 | '''converts unix timestamp to human readable format'''
203 | return strftime('%Y-%m-%d %H:%M', time.localtime(timestamp))
204 |
205 |
206 | def t2humanMS(seconds):
207 | '''converts unix timestamp to human readable format MM:SS'''
208 | mm = int(seconds / 60)
209 | ss = seconds % 60
210 | return "%02i:%02i" % (mm, ss)
211 |
212 |
213 | def printPass(satellite, start, duration, peak, azimuth, freq, processWith):
214 | return "● " + bc.OKGREEN + "%10s" % (satellite) + bc.ENDC + " :: " \
215 | + bc.OKGREEN + t2human(start) + bc.ENDC + " to " + bc.OKGREEN + t2human(start + int(duration)) + bc.ENDC \
216 | + ", dur: " + t2humanMS(duration) \
217 | + ", max el. " + str(int(peak)) + "°" + "; azimuth: " + str(int(azimuth)) + \
218 | "° (" + azimuth2dir(azimuth) + ") f=" + str(
219 | freq) + "Hz; Decoding: " + str(processWith)
220 |
221 |
222 | def listNextPases(passTable, howmany):
223 | i = 1
224 | for satelitePass in passTable[0:howmany]:
225 | satellite, start, duration, peak, azimuth = satelitePass
226 | freq = satellitesData[satellite]['freq']
227 | processWith = satellitesData[satellite]['processWith']
228 | log(printPass(satellite, start, duration,
229 | peak, azimuth, freq, processWith))
230 | i += 1
231 |
232 |
233 | def runForDuration(cmdline, duration, loggingDir):
234 | outLogFile = logFile(loggingDir)
235 | teeCommand = ['tee', '-a', outLogFile ] # quick and dirty hack to get log to file
236 |
237 | cmdline = [str(x) for x in cmdline]
238 | print(cmdline)
239 | try:
240 | p1 = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
241 | _ = subprocess.Popen(teeCommand, stdin=p1.stdout)
242 | time.sleep(duration)
243 | p1.terminate()
244 | except OSError as e:
245 | log("✖ OS Error during command: " + " ".join(cmdline), style=bc.FAIL)
246 | log("✖ OS Error: " + e.strerror, style=bc.FAIL)
247 |
248 |
249 | def justRun(cmdline, loggingDir):
250 | '''Just run the command as long as necesary and return the output'''
251 | outLogFile = logFile(loggingDir)
252 | teeCommand = ['tee', '-a', outLogFile ] # quick and dirty hack to get log to file
253 |
254 | cmdline = [str(x) for x in cmdline]
255 | try:
256 | p1 = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
257 | p2 = subprocess.Popen(teeCommand, stdin=p1.stdout, close_fds=True) # stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
258 | result = p1.communicate()[0]
259 | return result
260 | except OSError as e:
261 | log("✖ OS Error during command: " + " ".join(cmdline), style=bc.FAIL)
262 | log("✖ OS Error: " + e.strerror, style=bc.FAIL)
263 |
264 |
265 | def runTest(duration=3):
266 | '''Check, if RTL_SDR dongle is connected'''
267 | child = subprocess.Popen('rtl_test', stdout=subprocess.PIPE,
268 | stderr=subprocess.PIPE)
269 | time.sleep(duration)
270 | child.terminate()
271 | _, err = child.communicate()
272 | # if no device: ['No', 'supported', 'devices', 'found.']
273 | # if OK: ['Found', '1', 'device(s):', '0:', 'Realtek,',
274 | # 'RTL2838UHIDIR,',...
275 | info = err.split()[0]
276 | if info == "No":
277 | log("✖ No SDR device found!", style=bc.FAIL)
278 | return False
279 | elif info == "Found":
280 | log("SDR device found!")
281 | return True
282 | else:
283 | log("Not sure, if SDR device is there...")
284 | return True
285 |
286 |
287 | def getDefaultDongleShift(dongleShift=dongleShift):
288 | log("Reading the default dongle shift")
289 | if os.path.exists(dongleShiftFile):
290 | f = open(dongleShiftFile, "r")
291 | newdongleShift = f.read().strip()
292 | f.close()
293 |
294 | if newdongleShift != '' and is_number(newdongleShift): # WARNING and newdongleShift is numeric:
295 | dongleShift = str(float(newdongleShift))
296 | log("Recently used dongle shift is: " + str(dongleShift) + " ppm")
297 | else:
298 | log("Using the default dongle shift: " + str(dongleShift) + " ppm")
299 |
300 | return dongleShift
301 |
302 |
303 | def calibrate(dongleShift=dongleShift):
304 | '''calculate the ppm for the device'''
305 | if (calibrationTool):
306 | cmdline = [calibrationTool]
307 | newdongleShift = justRun(cmdline, loggingDir).strip()
308 | if newdongleShift != '' and is_number(newdongleShift):
309 | log("Recalculated dongle shift is: " +
310 | str(newdongleShift) + " ppm")
311 | return str(float(newdongleShift))
312 | else:
313 | log("Using the good old dongle shift (a): " +
314 | str(dongleShift) + " ppm")
315 | return dongleShift
316 | else:
317 | log("Using the good old dongle shift: " + str(dongleShift) + " ppm")
318 | return dongleShift
319 |
320 |
321 | def azimuth2dir(azimuth):
322 | ''' convert azimuth in degrees to wind rose directions (16 wings)'''
323 | dirs = ["N↑", "NNE↑↗", "NE↗", "ENE→↗",
324 | "E→", "ESE→↘", "SE↘", "SSE↓↘",
325 | "S↓", "SSW↓↙", "SW↙", "WSW←↙",
326 | "W←", "WNW←↖", "NW↖", "NNW↑↖", ]
327 |
328 | part = int(float(azimuth) / 360 * 16)
329 | return dirs[part]
330 |
331 |
332 | def escape_ansi(line):
333 | '''remove ansi colors from the given string'''
334 | ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
335 | return ansi_escape.sub('', line)
336 |
337 |
338 | def log(string, style=bc.CYAN):
339 | message = "%s%s%s %s %s %s " % (
340 | bc.BOLD,
341 | datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M'),
342 | bc.ENDC,
343 | style,
344 | str(string),
345 | bc.ENDC)
346 | # socketio.emit('log', {'data': message}, namespace='/')
347 | handle_my_custom_event(escape_ansi(message) + " \n")
348 | print(message)
349 |
350 | # logging to file, if not Flase
351 | if loggingDir:
352 | logToFile(escape_ansi(message) + "\n", loggingDir)
353 |
354 |
355 | def logFile(logDir):
356 | '''Create output logging dir, returns name of the logfile'''
357 | mkdir_p(logDir)
358 | outfile = "%s/%s.txt" % (
359 | logDir,
360 | datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d'))
361 | return outfile
362 |
363 |
364 | def logToFile(message, logDir):
365 | # save message to the file
366 | outfile = logFile(logDir)
367 | file_append(outfile, message)
368 |
369 |
370 | def file_append(filename, content):
371 | f = open(filename, 'a')
372 | f.write(content)
373 | f.close()
374 |
375 |
376 | # ------------ functions for generation of pass table, saving, png image etc...
377 |
378 |
379 | def t2humanHM(timestamp):
380 | '''converts unix timestamp to human readable format'''
381 | # oggingDir
382 | return strftime('%H:%M', time.localtime(timestamp))
383 |
384 |
385 | def _create_date(timestamp):
386 | """Creates the date from timestamp"""
387 | mdate = matplotlib.dates.date2num(datetime.fromtimestamp(timestamp))
388 | return mdate
389 |
390 |
391 | def assignColorsToEvent(uniqueEvents):
392 | ''' returns dict of event -> color'''
393 |
394 | eventsColors = {'METEOR-M2': 'red',
395 | 'NOAA-15': '#0040FF',
396 | 'NOAA-18': '#0890FF',
397 | 'NOAA-19': '#07CAFF',
398 | 'ISS': '#38FF06',
399 | }
400 |
401 | colors = ['#F646FF', 'yellow', 'orange', 'silver', 'magenta', 'red', 'white', '#FF2894', '#FF6E14']
402 |
403 | fixedColorsEvents = [event for event in eventsColors] # events that have assigned fixed colors
404 |
405 | # generate list of events with not assigned fixed color
406 | uniqueEventsToAssignColors = []
407 | for uniqueEvent in uniqueEvents:
408 | if uniqueEvent not in fixedColorsEvents:
409 | uniqueEventsToAssignColors += [uniqueEvent]
410 |
411 | # assign colors to the events with not assigne fixed colrs and add them to dict
412 | for event, color in zip(uniqueEventsToAssignColors, colors):
413 | eventsColors[event] = color
414 |
415 |
416 | return eventsColors
417 |
418 |
419 | def CreateGanttChart(listNextPasesListList):
420 | """
421 | Create gantt charts with matplotlib
422 | """
423 |
424 | ylabels = []
425 | customDates = []
426 |
427 | i = 1
428 | for tx in listNextPasesListList:
429 | ylabel, startdate, enddate = tx
430 | # ylabels.append("%s (%1i)" % (ylabel, i) )
431 | ylabels.append("(%1i)" % (i))
432 | # ylabels.append("%s" % (ylabel) )
433 | customDates.append([_create_date(startdate), _create_date(enddate)])
434 | i += 1
435 |
436 | now = _create_date(time.time())
437 |
438 | uniqueEvents = list(set([x[0] for x in listNextPasesListList])) # unique list of satellites
439 | colorDict = assignColorsToEvent(uniqueEvents)
440 |
441 | ilen = len(ylabels)
442 | pos = np.arange(0.5, ilen * 0.5 + 0.5, 0.5)
443 | task_dates = {}
444 | for i, task in enumerate(ylabels):
445 | task_dates[task] = customDates[i]
446 | fig = plt.figure(figsize=(10, 5))
447 | ax = fig.add_subplot(111)
448 | for i, ylabel in enumerate(ylabels):
449 | ylabelIN, startdateIN, enddateIN = listNextPasesListList[i]
450 | start_date, end_date = task_dates[ylabels[i]]
451 |
452 | if i < (ilen/2):
453 | labelAlign = 'left'
454 | factor = 1
455 | else:
456 | labelAlign = 'right'
457 | factor = -1
458 |
459 |
460 | ax.barh(
461 | (i * 0.5) + 0.5,
462 | end_date - start_date,
463 | left=start_date,
464 | height=0.3,
465 | align='center',
466 | edgecolor='black',
467 | color=colorDict[ylabelIN],
468 | label='',
469 | alpha=0.95)
470 | ax.text(
471 | end_date,
472 | (i * 0.5) + 0.55,
473 | ' %s | %s ' % (t2humanHM(startdateIN),
474 | ylabelIN),
475 | ha=labelAlign,
476 | va='center',
477 | fontsize=8,
478 | color='#7B7B7B')
479 |
480 | locsy, labelsy = plt.yticks(pos, ylabels)
481 | plt.setp(labelsy, fontsize=8)
482 | ax.axis('tight')
483 | ax.set_ylim(ymin=-0.1, ymax=ilen * 0.5 + 0.5)
484 | ax.set_xlim(xmin=now)
485 | ax.grid(color='silver', linestyle=':')
486 | ax.xaxis_date()
487 |
488 | # FAKE,startdate,FAKE=listNextPasesListList[0]
489 | # minutOdPelnej = int(datetime.fromtimestamp(time.time()).strftime('%M'))
490 | # plotStart = int(time.time() - minutOdPelnej*60)
491 | # print t2human(plotStart)
492 | # ax.set_xlim(_create_date(plotStart), _create_date(enddate+600))
493 |
494 | Majorformatter = DateFormatter("%H:%M\n%d-%b")
495 | ax.xaxis.set_major_formatter(Majorformatter)
496 | labelsx = ax.get_xticklabels()
497 | # plt.setp(labelsx, rotation=30, fontsize=10)
498 | plt.setp(labelsx, rotation=0, fontsize=7)
499 | plt.title(
500 | 'Transit plan for %s, generated %s' %
501 | (stationName, t2human(time.time())))
502 |
503 | ax.invert_yaxis()
504 | plt.tight_layout()
505 | plt.savefig(ganttNextPassList)
506 |
507 | if ylabel == enddateIN:
508 | print(locsy) # "This is done only to satisfy the codacy.com. Sorry for that."
509 |
510 |
511 | def listNextPasesHtml(passTable, howmany):
512 | i = 1
513 | output = "\n"
514 | output += "# satellite start duration peak azimuth freq process with \n"
515 |
516 | # uniqueEvents
517 | # colorDict = assignColorsToEvent(listNextPasesListList)
518 | # print passTable[0:howmany]
519 | uniqueEvents = list(set([x[0] for x in passTable[0:howmany]])) # unique list of satellites
520 | colorDict = assignColorsToEvent(uniqueEvents)
521 |
522 | for satelitePass in passTable[0:howmany]:
523 | satellite, start, duration, peak, azimuth = satelitePass
524 | freq = satellitesData[satellite]['freq']
525 | processWith = satellitesData[satellite]['processWith']
526 |
527 | color = colorDict[satellite]
528 | output += " %i %s %s %s %s° %s° (%s) %sHz %s \n" % (
529 | color, i, satellite, t2human(start), t2humanMS(duration), peak, azimuth, azimuth2dir(azimuth), freq, processWith)
530 | i += 1
531 |
532 | output += "
\n"
533 |
534 | return output #.decode('utf-8')
535 |
536 |
537 | def listNextPasesTxt(passTable, howmany):
538 |
539 | txtTemplate = "%3s\t%10s\t%16s\t%9s\t%4s\t%3s\t%6s\t%10s\t%20s\n"
540 |
541 | i = 1
542 | output = ""
543 | output += txtTemplate % (
544 | "#",
545 | "satellite",
546 | "start",
547 | "dur MM:SS",
548 | "peak",
549 | "az",
550 | "az",
551 | "freq",
552 | "process with")
553 |
554 | for satelitePass in passTable[0:howmany]:
555 | satellite, start, duration, peak, azimuth = satelitePass
556 | freq = satellitesData[satellite]['freq']
557 | processWith = satellitesData[satellite]['processWith']
558 |
559 | output += txtTemplate % (
560 | i,
561 | satellite,
562 | t2human(start),
563 | t2humanMS(duration),
564 | peak,
565 | azimuth,
566 | azimuth2dir(azimuth),
567 | freq,
568 | processWith)
569 | i += 1
570 |
571 | output += "\n"
572 |
573 | return output
574 |
575 | def listNextPasesShort(passTable, howmany=4):
576 | ''' list next passes in a sentence '''
577 |
578 | output = ""
579 |
580 | for satelitePass in passTable[0:howmany]:
581 | satellite, start, duration, peak, azimuth = satelitePass
582 |
583 | output += "%s (%s); " % (satellite, strftime('%H:%M', time.localtime(start)) )
584 |
585 | return output
586 |
587 | def listNextPasesList(passTable, howmany):
588 | output = []
589 | for satelitePass in passTable[0:howmany]:
590 | satellite, start, duration, peak, azimuth = satelitePass
591 | # freq = satellitesData[satellite]['freq']
592 | # processWith = satellitesData[satellite]['processWith']
593 |
594 | output.append([satellite, start, start + duration])
595 | if peak:
596 | print("This is a miracle!") # codacy cheating, sorry.
597 | return output
598 |
599 |
600 | def saveToFile(filename, data):
601 | # print filename
602 | # print data
603 | # exit(1)
604 | plik = open(filename, "w")
605 | plik.write(data) # .encode("utf-8")
606 | plik.close()
607 |
608 |
609 | def generatePassTableAndSaveFiles(satellites, qth, verbose=True):
610 | # recalculate table of next passes
611 | log("Recalculating the new pass table and saving to disk.")
612 | passTable = genPassTable(satellites, qth, howmany=50)
613 | listNextPasesHtmlOut = listNextPasesHtml(passTable, 100)
614 | saveToFile(htmlNextPassList, listNextPasesHtmlOut)
615 |
616 | saveToFile(wwwDir + 'nextpassshort.tmp', listNextPasesShort(passTable, 6))
617 |
618 | listNextPasesListList = listNextPasesList(passTable, 20)
619 | CreateGanttChart(listNextPasesListList)
620 |
621 | if verbose:
622 | print(listNextPasesTxt(passTable, 100))
623 |
624 |
625 |
626 | # --------------------------------------------------------------------------- #
627 | # --------- THE WEBSERVER --------------------------------------------------- #
628 | # --------------------------------------------------------------------------- #
629 |
630 | app = Flask(__name__, template_folder="var/flask/templates/", static_folder='var/flask/static')
631 | socketio = SocketIO(app)
632 |
633 | def file_read(filename):
634 | with codecs.open(filename, 'r', encoding='utf-8', errors='replace') as f:
635 | lines = f.read()
636 | linesBr = " ".join( lines.split("\n") )
637 | return linesBr
638 |
639 |
640 | @app.route('/')
641 | def homepage():
642 | logfile = logFile(loggingDir)
643 | logs = file_read(logfile)
644 |
645 | body =""
646 | # log window
647 | body += "Recent logs File : %s
%s " % (logfile, logs)
648 |
649 | # next pass table
650 | passTable = genPassTable(satellites, qth)
651 | body += "Next passes %s " % ( listNextPasesHtml(passTable, 10) )
652 | return render_template('index.html', title="Home page", body=body)
653 |
654 | @socketio.on('my event')
655 | def handle_my_custom_event(text):
656 | socketio.emit('my response', { 'tekst': text } )
657 |
658 | @socketio.on('next pass table')
659 | def handle_next_pass_list(text):
660 | socketio.emit('response next pass table', { 'tekst': text } )
661 |
662 |
663 | #
664 | # show pass table
665 | #
666 |
667 | @app.route('/table')
668 | def passTable():
669 | body=file_read(htmlNextPassList)
670 | return render_template('index.html', title="Pass table", body=body)
671 |
672 |
673 | # --------------------------------------------------------------------------- #
674 | # --------- THE MAIN LOOP --------------------------------------------------- #
675 | # --------------------------------------------------------------------------- #
676 |
677 | def mainLoop():
678 | dongleShift = getDefaultDongleShift()
679 |
680 | while True:
681 |
682 | # each loop - reads the config file in case it has changed
683 | from autowx2_conf import satellitesData, scriptToRunInFreeTime
684 |
685 | # recalculate table of next passes
686 | passTable = genPassTable(satellites, qth)
687 |
688 | # save table to disk
689 | generatePassTableAndSaveFiles(satellites, qth, verbose=False)
690 |
691 | # show next five passes
692 | log("Next five passes:")
693 | listNextPases(passTable, 5)
694 |
695 | # pass table for webserver
696 | handle_next_pass_list(listNextPasesHtml(passTable, 10))
697 |
698 | # get the very next pass
699 | satelitePass = passTable[0]
700 | satellite, start, duration, peak, azimuth = satelitePass
701 | satelliteNoSpaces = satellite.replace(" ", "-") #remove spaces from the satellite name
702 |
703 | freq = satellitesData[satellite]['freq']
704 | processWith = satellitesData[satellite]['processWith']
705 |
706 | fileNameCore = datetime.fromtimestamp(
707 | start).strftime(
708 | '%Y%m%d-%H%M') + "_" + satelliteNoSpaces
709 |
710 | log("Next pass:")
711 | log(printPass(satellite, start, duration,
712 | peak, azimuth, freq, processWith))
713 |
714 | towait = int(start - time.time())
715 |
716 | if cleanupRtl:
717 | log("Killing all remaining rtl_* processes...")
718 | justRun(["bin/kill_rtl.sh"], loggingDir)
719 |
720 | # test if SDR dongle is available
721 | if towait > 15: # if we have time to perform the test?
722 | while not runTest():
723 | log("Waiting for the SDR dongle...")
724 | time.sleep(10)
725 |
726 | # It's a high time to record!
727 | if towait <= 1 and duration > 0:
728 | # here the recording happens
729 | log("!! Recording " + printPass(satellite, start, duration,
730 | peak, azimuth, freq, processWith), style=bc.WARNING)
731 |
732 | processCmdline = [
733 | processWith,
734 | fileNameCore,
735 | satellite,
736 | start,
737 | duration + towait,
738 | peak,
739 | azimuth,
740 | freq]
741 | print(justRun(processCmdline, loggingDir))
742 | time.sleep(10.0)
743 |
744 | # still some time before recording
745 | else:
746 | # recalculating waiting time
747 | if towait > 300:
748 | log("Recalibrating the dongle...")
749 | dongleShift = calibrate(dongleShift) # replace the global value
750 |
751 | towait = int(start - time.time())
752 | if scriptToRunInFreeTime:
753 | if towait >= 120: # if we have more than [some] minutes spare time, let's do something useful
754 | log("We have still %ss free time to the next pass. Let's do something useful!" %
755 | (t2humanMS(towait - 1)))
756 | log("Running: %s for %ss" %
757 | (scriptToRunInFreeTime, t2humanMS(towait - 1)))
758 | runForDuration(
759 | [scriptToRunInFreeTime,
760 | towait - 1,
761 | dongleShift],
762 | towait - 1, loggingDir)
763 | # scrript with run time and dongle shift as
764 | # arguments
765 | else:
766 | log("Sleeping for: " + t2humanMS(towait - 1) + "s")
767 | time.sleep(towait - 1)
768 | else:
769 | towait = int(start - time.time())
770 | log("Sleeping for: " + t2humanMS(towait - 1) + "s")
771 | time.sleep(towait - 1)
772 |
773 | logfile = logFile(loggingDir)
774 |
--------------------------------------------------------------------------------
/bin/aprs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 | logfile="$recordingDir/aprs/"$(date +"%Y/%m")/$(date +"%Y%m%d")".txt"
10 |
11 | mkdir -p $(dirname $logfile)
12 |
13 | duration=$1
14 | dongleShift=$2
15 |
16 | mkdir -p $(dirname $logfile)
17 |
18 |
19 | timeout --kill-after=1 $duration rtl_fm $biast -f 144800000 -s 22050 -o 4 -p $dongleShift -g 49.6 | multimon-ng -a AFSK1200 -A -t raw - | tee -a $logfile
20 |
--------------------------------------------------------------------------------
/bin/calibrate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## calibrating of the dongle
4 |
5 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
6 | # do not change the following three lines
7 | scriptDir="$(dirname "$(realpath "$0")")"
8 | source $scriptDir/basedir_conf.py > /dev/null
9 | source $baseDir/_listvars.sh > /dev/null
10 |
11 |
12 | shiftFile="$baseDir/var/dongleshift.txt"
13 | channelFile="$baseDir/var/gsm_channel.txt"
14 | shiftHistory="$baseDir/var/shifthistory.csv"
15 |
16 | channel=$(cat $channelFile)
17 |
18 |
19 | #-----------------------------------------------#
20 |
21 | mkdir -p $(dirname $shiftHistory)
22 | recentShift=$(cat $shiftFile)
23 |
24 | re='^-?[0-9]+([.][0-9]+)?$'
25 | if ! [[ $recentShift =~ $re ]] ; then
26 | recentShift=0
27 | fi
28 |
29 | newShift=$(timeout 100 kal -c $channel -g 49.6 -e $recentShift 2> /dev/null | tail -1 | cut -d " " -f 4)
30 | echo $newShift | tee $shiftFile
31 |
32 | echo $(date +"%Y%m%d_%H:%M:%S") $(date +"%s") $newShift >> $shiftHistory
33 |
--------------------------------------------------------------------------------
/bin/calibrate_full.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 |
6 | scriptDir="$(dirname "$(realpath "$0")")"
7 | source $scriptDir/basedir_conf.py > /dev/null
8 | source $baseDir/_listvars.sh > /dev/null
9 |
10 |
11 | channel=$(timeout --kill-after=1 300 kal -s GSM900 -e 0 -g 49.6 2> /dev/null | sed 's/ \+/\t/g' | cut -f 3,8 | sort -k2 -n -r | head -1 | cut -f 1)
12 | echo "$channel" > "$baseDir/var/gsm_channel.txt"
13 |
14 |
15 | source $baseDir/bin/calibrate.sh
16 |
17 |
--------------------------------------------------------------------------------
/bin/crontab/daily.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # tasks to perform daily
4 |
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py > /dev/null
7 | source $baseDir/_listvars.sh > /dev/null
8 |
9 | # generate home page from template
10 | $baseDir/genpasstable.py 1>/dev/null 2>&1
11 | $baseDir/bin/gen-static-page.sh 1>/dev/null 2>&1
12 |
13 |
14 | # generate heatmap for dump1090
15 | # $baseDir/bin/dump1090-draw_heatmap.sh 1>/dev/null 2>&1
16 |
17 |
18 |
19 | # compress and rotate old log files
20 | find $recordingDir/logs/*.txt -mtime +7 -exec bzip2 {} \;
21 | mkdir -p $recordingDir/logs/old/
22 | mv $recordingDir/logs/*.bz2 $recordingDir/logs/old/
--------------------------------------------------------------------------------
/bin/crontab/weekly.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # tasks to perform daily
4 |
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py > /dev/null
7 | source $baseDir/_listvars.sh > /dev/null
8 |
9 | # generate home page from template
10 | $baseDir/genpasstable.py 1>/dev/null 2>&1
11 | $baseDir/bin/gen-static-page.sh 1>/dev/null 2>&1
12 |
13 |
14 |
15 |
16 | # update Keplers
17 | $baseDir/bin/update-keps.sh 1>$recordingDir/logs/keps-update.txt 2>&1
18 |
19 |
--------------------------------------------------------------------------------
/bin/dump1090-draw_heatmap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 |
10 | database="$recordingDir/dump1090/adsb_messages.db"
11 | outdir="$recordingDir/dump1090/" # $(date +"%Y/%m")/$(date +"%Y%m%d").txt"
12 | tempdir="/tmp"
13 |
14 | mkdir -p $(dirname $outdir)
15 |
16 | # draw data on the map
17 | $baseDir/bin/heatmap.py -m 000ffffff -M 000ffffff -o $outdir/heatmap-osm.png -r 4 --margin 25 -k gaussian --height 900 --osm --sqlite_table squitters $database
18 |
19 |
20 | # draw data on the map
21 | $baseDir/bin/heatmap.py -o $outdir/heatmap-osm2.png -r 40 --height 900 --osm --margin 25 --sqlite_table squitters $database
22 |
23 | # draw data on the black canvas
24 | $baseDir/bin/heatmap.py -b black -r 30 -W 1200 -o $tempdir/h1.png -k gaussian --sqlite_table squitters $database
25 | $baseDir/bin/heatmap.py -r 5 -W 1200 -o $tempdir/h2.png --decay 0.3 --margin 25 -k gaussian --sqlite_table squitters $database
26 |
27 | # convert to jpg
28 |
29 | convert -quality 83 $outdir/heatmap-osm.png $outdir/heatmap-osm.jpg
30 | convert -quality 83 $outdir/heatmap-osm2.png $outdir/heatmap-osm2.jpg
31 |
32 |
33 | # remove unused files
34 | rm $tempdir/h1.png $tempdir/h2.png $outdir/heatmap-osm.png $outdir/heatmap-osm2.png
--------------------------------------------------------------------------------
/bin/dump1090-stream-parser.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | import socket
5 | import datetime
6 | import sqlite3
7 | import argparse
8 | import time
9 |
10 | #defaults
11 | HOST = "localhost"
12 | PORT = 30003
13 | DB = "adsb_messages.db"
14 | BUFFER_SIZE = 100
15 | BATCH_SIZE = 1
16 | CONNECT_ATTEMPT_LIMIT = 10000
17 | CONNECT_ATTEMPT_DELAY = 5.0
18 |
19 |
20 | def main():
21 |
22 | #set up command line options
23 | parser = argparse.ArgumentParser(description="A program to process dump1090 messages then insert them into a database")
24 | parser.add_argument("-l", "--location", type=str, default=HOST, help="This is the network location of your dump1090 broadcast. Defaults to %s" % (HOST,))
25 | parser.add_argument("-p", "--port", type=int, default=PORT, help="The port broadcasting in SBS-1 BaseStation format. Defaults to %s" % (PORT,))
26 | parser.add_argument("-d", "--database", type=str, default=DB, help="The location of a database file to use or create. Defaults to %s" % (DB,))
27 | parser.add_argument("--buffer-size", type=int, default=BUFFER_SIZE, help="An integer of the number of bytes to read at a time from the stream. Defaults to %s" % (BUFFER_SIZE,))
28 | parser.add_argument("--batch-size", type=int, default=BATCH_SIZE, help="An integer of the number of rows to write to the database at a time. If you turn off WAL mode, a lower number makes it more likely that your database will be locked when you try to query it. Defaults to %s" % (BATCH_SIZE,))
29 | parser.add_argument("--connect-attempt-limit", type=int, default=CONNECT_ATTEMPT_LIMIT, help="An integer of the number of times to try (and fail) to connect to the dump1090 broadcast before qutting. Defaults to %s" % (CONNECT_ATTEMPT_LIMIT,))
30 | parser.add_argument("--connect-attempt-delay", type=float, default=CONNECT_ATTEMPT_DELAY, help="The number of seconds to wait after a failed connection attempt before trying again. Defaults to %s" % (CONNECT_ATTEMPT_DELAY,))
31 |
32 | # parse command line options
33 | args = parser.parse_args()
34 |
35 | # print args.accumulate(args.in)
36 | count_since_commit = 0
37 | count_total = 0
38 | count_failed_connection_attempts = 1
39 |
40 | # connect to database or create if it doesn't exist
41 | conn = sqlite3.connect(args.database)
42 | cur = conn.cursor()
43 | cur.execute('PRAGMA journal_mode=wal')
44 |
45 | # set up the table if neccassary
46 | cur.execute("""CREATE TABLE IF NOT EXISTS
47 | squitters(
48 | message_type TEXT,
49 | transmission_type INT,
50 | session_id TEXT,
51 | aircraft_id TEXT,
52 | hex_ident TEXT,
53 | flight_id TEXT,
54 | generated_date TEXT,
55 | generated_time TEXT,
56 | logged_date TEXT,
57 | logged_time TEXT,
58 | callsign TEXT,
59 | altitude INT,
60 | ground_speed INT,
61 | track INT,
62 | lat REAL,
63 | lon REAL,
64 | vertical_rate REAL,
65 | squawk TEXT,
66 | alert INT,
67 | emergency INT,
68 | spi INT,
69 | is_on_ground INT,
70 | parsed_time TEXT
71 | )
72 | """)
73 |
74 | start_time = datetime.datetime.utcnow()
75 |
76 | # open a socket connection
77 | while count_failed_connection_attempts < args.connect_attempt_limit:
78 | try:
79 | s = connect_to_socket(args.location, args.port)
80 | count_failed_connection_attempts = 1
81 | print "Connected to dump1090 broadcast"
82 | break
83 | except socket.error:
84 | count_failed_connection_attempts += 1
85 | print "Cannot connect to dump1090 broadcast. Making attempt %s." % (count_failed_connection_attempts)
86 | time.sleep(args.connect_attempt_delay)
87 | else:
88 | quit()
89 |
90 | data_str = ""
91 |
92 | try:
93 | #loop until an exception
94 | while True:
95 | #get current time
96 | cur_time = datetime.datetime.utcnow()
97 | ds = cur_time.isoformat()
98 | ts = cur_time.strftime("%H:%M:%S")
99 |
100 | # receive a stream message
101 | try:
102 | message = ""
103 | message = s.recv(args.buffer_size)
104 | data_str += message.strip("\n")
105 | except socket.error:
106 | # this happens if there is no connection and is delt with below
107 | pass
108 |
109 | if len(message) == 0:
110 | print ts, "No broadcast received. Attempting to reconnect"
111 | time.sleep(args.connect_attempt_delay)
112 | s.close()
113 |
114 | while count_failed_connection_attempts < args.connect_attempt_limit:
115 | try:
116 | s = connect_to_socket(args.location, args.port)
117 | count_failed_connection_attempts = 1
118 | print "Reconnected!"
119 | break
120 | except socket.error:
121 | count_failed_connection_attempts += 1
122 | print "The attempt failed. Making attempt %s." % (count_failed_connection_attempts)
123 | time.sleep(args.connect_attempt_delay)
124 | else:
125 | quit()
126 |
127 | continue
128 |
129 | # it is possible that more than one line has been received
130 | # so split it then loop through the parts and validate
131 |
132 | data = data_str.split("\n")
133 |
134 | for d in data:
135 | line = d.split(",")
136 |
137 | #if the line has 22 items, it's valid
138 | if len(line) == 22:
139 | lat = line[14]
140 | lon = line[15]
141 | if lat != '' and lon != '':
142 | #print lat, lon
143 | #print d
144 |
145 | # add the current time to the row
146 | line.append(ds)
147 |
148 | try:
149 | # add the row to the db
150 | cur.executemany("""INSERT INTO squitters
151 | (
152 | message_type,
153 | transmission_type,
154 | session_id,
155 | aircraft_id,
156 | hex_ident,
157 | flight_id,
158 | generated_date,
159 | generated_time,
160 | logged_date,
161 | logged_time,
162 | callsign,
163 | altitude,
164 | ground_speed,
165 | track,
166 | lat,
167 | lon,
168 | vertical_rate,
169 | squawk,
170 | alert,
171 | emergency,
172 | spi,
173 | is_on_ground,
174 | parsed_time
175 | )
176 | VALUES (""" + ", ".join(["?"] * len(line)) + ")", (line,))
177 |
178 | # increment counts
179 | count_total += 1
180 | count_since_commit += 1
181 |
182 | # commit the new rows to the database in batches
183 | if count_since_commit % args.batch_size == 0:
184 | conn.commit()
185 | print "averging %s rows per second" % (float(count_total) / (cur_time - start_time).total_seconds(),)
186 | if count_since_commit > args.batch_size:
187 | print ts, "All caught up, %s rows, successfully written to database" % (count_since_commit)
188 | count_since_commit = 0
189 |
190 | except sqlite3.OperationalError:
191 | print ts, "Could not write to database, will try to insert %s rows on next commit" % (count_since_commit + args.batch_size,)
192 |
193 |
194 | # since everything was valid we reset the stream message
195 | data_str = ""
196 | else:
197 | # the stream message is too short, prepend to the next stream message
198 | data_str = d
199 | continue
200 |
201 | except KeyboardInterrupt:
202 | print "\n%s Closing connection" % (ts,)
203 | s.close()
204 |
205 | conn.commit()
206 | conn.close()
207 | print ts, "%s squitters added to your database" % (count_total,)
208 |
209 | except sqlite3.ProgrammingError:
210 | print "Error with ", line
211 | quit()
212 |
213 | def connect_to_socket(loc,port):
214 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
215 | s.connect((loc, port))
216 | return s
217 |
218 | if __name__ == '__main__':
219 | main()
220 |
--------------------------------------------------------------------------------
/bin/dump1090.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 |
10 | #### microconfiguration
11 |
12 | database="$recordingDir/dump1090/adsb_messages.db"
13 | mkdir -p $(dirname $database)
14 |
15 | duration=$1
16 | dongleShift=$2
17 |
18 |
19 | timeout --kill-after=1 $duration dump1090 --quiet --metric --net --aggressive --ppm $dongleShift &
20 | timeout --kill-after=1 $duration $baseDir/bin/dump1090-stream-parser.py --database $database
--------------------------------------------------------------------------------
/bin/gen-static-page.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # loop over dirs, crate list of files, generates static file
4 |
5 | # disable the warning
6 | # shellcheck disable=SC2034
7 |
8 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
9 | # do not change the following three lines
10 | scriptDir="$(dirname "$(realpath "$0")")"
11 | source $scriptDir/basedir_conf.py
12 | source $baseDir/_listvars.sh
13 |
14 | ################## FINE TUNING #####################
15 |
16 | # $recordingDir - real path on the system
17 | # $wwwRootPath/recordings/logs/ - www path
18 |
19 | # static values for tests
20 | # imgdir=/home/filips/github/autowx2/recordings/noaa/img/2018/09/08/
21 | # fileNameCore="20180908-1626_NOAA-19"
22 | # wwwDir="/home/filips/github/autowx2/var/www/"
23 |
24 |
25 | noaaDir=$recordingDir/noaa
26 | meteorDir=$recordingDir/meteor
27 |
28 | dirList="$wwwDir/noaa_dirlist.tmp"
29 | htmlTemplate="$wwwDir/index.tpl"
30 | htmlOutput="$wwwDir/index.html"
31 | htmlOutputTable="$wwwDir/table.html"
32 |
33 | currentDate=$(date -R)
34 | echo $currentDate
35 | echo "" > $dirList
36 |
37 |
38 | ##### keplers updated? #####
39 | lastkeps=$(cat "$wwwDir/keps.tmp")
40 | lastkepsU=$(cat "$wwwDir/kepsU.tmp")
41 |
42 | keplerDays=$(echo "(($(date +"%s") - $lastkepsU ) / (60*60*24))" | bc )
43 | echo $keplerDays
44 | if [ $keplerDays -le 7 ]; then keplerInfo="OK "
45 | else keplerInfo="outdated "; fi
46 |
47 | echo "lastkeps: $lastkeps"
48 |
49 | keplerInfo="Keplers updated: $lastkeps ($keplerDays days old) $keplerInfo "
50 |
51 | ##### autowx2 uptime ########
52 |
53 | autowxStart=$(cat "$wwwDir/start.tmp")
54 |
55 | autowxUptimeH=$(echo "(($(date +"%s") - $autowxStart ) / (60*60))" | bc )
56 | autowxUptimeD=$(echo "(($(date +"%s") - $autowxStart ) / (60*60*24))" | bc )
57 |
58 | echo $autowxUptimeH
59 |
60 | autowxUptime="autowx2 uptime: $autowxUptimeH h (~$autowxUptimeD d) "
61 |
62 | ### short list of next passes
63 |
64 | shortlistofnextpassess=" Next passes: $(cat "$wwwDir/nextpassshort.tmp") "
65 |
66 |
67 | # ---- NOAA list all dates and times -------------------------------------------------#
68 |
69 | function gallery_noaa {
70 |
71 | howManyToday=$(ls $noaaDir/img/$(date +"%Y/%m/%d")/*.log 2> /dev/null| wc -l)
72 |
73 | noaaLastDir=$(dirname $(cat $wwwDir/noaa-last-recording.tmp))
74 |
75 | echo "NOAA recordings " >> $dirList
76 | echo "Recent pass " >> $dirList
77 | echo "" >> $dirList
78 | echo " " >> $dirList
79 | echo " " >> $dirList
80 | echo " " >> $dirList
81 | echo " " >> $dirList
82 | echo " " >> $dirList
83 | echo "
" >> $dirList
84 |
85 | echo "Archive " >> $dirList
86 | echo "" >> $dirList
87 | echo "Today $howManyToday " >> $dirList
88 |
89 | for y in $(ls $noaaDir/img/ | sort -n)
90 | do
91 | echo "$y" >> $dirList
92 | for m in $(ls $noaaDir/img/$y | sort -n)
93 | do
94 | echo "($m)" >> $dirList
95 | for d in $(ls $noaaDir/img/$y/$m/ | sort -n)
96 | do
97 | # collect info about files in the directory
98 | echo "$d " >> $dirList
99 | done
100 | echo " " >> $dirList
101 | done
102 | echo " " >> $dirList
103 | done
104 | echo " " >> $dirList
105 |
106 | } # end function gallery noaa
107 |
108 | # ---- METEOR list all dates and times -------------------------------------------------#
109 |
110 |
111 | function gallery_meteor {
112 |
113 | howManyToday=$(ls $meteorDir/img/$(date +"%Y/%m/%d")/*-Ch0.jpg 2> /dev/null| wc -l)
114 | meteorLastDir=$(dirname $(cat $wwwDir/meteor-last-recording.tmp))
115 |
116 |
117 | echo "METEOR-M2 recordings " >> $dirList
118 | echo "Recent pass " >> $dirList
119 | echo "" >> $dirList
120 | echo " " >> $dirList
121 | echo " " >> $dirList
122 | echo " " >> $dirList
123 | echo " " >> $dirList
124 | echo " " >> $dirList
125 |
126 | echo "
" >> $dirList
127 |
128 | echo "Archive " >> $dirList
129 | echo "" >> $dirList
130 | echo "Today $howManyToday " >> $dirList
131 |
132 | for y in $(ls $meteorDir/img/ | grep -v 'raw' | sort -n)
133 | do
134 | echo "$y" >> $dirList
135 | for m in $(ls $meteorDir/img/$y | sort -n)
136 | do
137 | echo "($m)" >> $dirList
138 | for d in $(ls $meteorDir/img/$y/$m/ | sort -n)
139 | do
140 | # collect info about files in the directory
141 | echo "$d " >> $dirList
142 | done
143 | echo " " >> $dirList
144 | done
145 | echo " " >> $dirList
146 | done
147 | echo " " >> $dirList
148 |
149 | } # end function gallery meteor
150 |
151 |
152 |
153 | # ---- ISS loop all dates and times -------------------------------------------------#
154 | function gallery_iss {
155 |
156 | howManyToday=$(ls $recordingDir/iss/rec/$(date +"%Y/%m/")/*.log 2> /dev/null| wc -l)
157 |
158 | echo "ISS recordings " >> $dirList
159 | echo "" >> $dirList
160 | echo "Current month $howManyToday " >> $dirList
161 |
162 | for y in $(ls $recordingDir/iss/rec/ | sort -n)
163 | do
164 | echo "($y) " >> $dirList
165 | for m in $(ls $recordingDir/iss/rec/$y | sort -n)
166 | do
167 | echo "$m " >> $dirList
168 | done
169 | echo " " >> $dirList
170 | done
171 | echo " " >> $dirList
172 |
173 | }
174 |
175 | # ---- LOGS -------------------------------------------------#
176 |
177 | function gallery_logs {
178 |
179 |
180 | echo "Logs " >> $dirList
181 | echo "" >> $dirList
182 | echo "Today " >> $dirList
183 | echo "All logs " >> $dirList
184 | echo " " >> $dirList
185 | }
186 |
187 | # ---- dump1090 -------------------------------------------------#
188 |
189 | function gallery_dump1090 {
190 | echo "dump1090 heatmap " >> $dirList
191 | echo " " >> $dirList
192 | echo " " >> $dirList
193 | }
194 |
195 |
196 | # ------------------------------------------template engine --------------------- #
197 |
198 | # ----- RENDER APPROPRIATE PAGES ---- #
199 |
200 | if [ "$includeGalleryNoaa" = '1' ]; then
201 | gallery_noaa
202 | fi
203 |
204 | if [ "$includeGalleryMeteor" = '1' ]; then
205 | gallery_meteor
206 | fi
207 |
208 | if [ "$includeGalleryLogs" = '1' ]; then
209 | gallery_logs
210 | fi
211 |
212 | if [ "$includeGalleryISS" = '1' ]; then
213 | gallery_iss
214 | fi
215 |
216 | if [ "$includeGalleryDump1090" = '1' ]; then
217 | gallery_dump1090
218 | fi
219 |
220 |
221 | # ----- MAIN PAGE ---- #
222 |
223 | htmlTitle="Main page"
224 | htmlBody=$(cat $dirList)
225 | source $htmlTemplate > $htmlOutput
226 |
227 | # ----- PASS LIST ---- #
228 |
229 | htmlTitle="Pass table"
230 | htmlBody=$(cat $htmlNextPassList)
231 | htmlBody="
"$htmlBody
232 |
233 | source $htmlTemplate > $htmlOutputTable
234 |
--------------------------------------------------------------------------------
/bin/kill_rtl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | killall -9 rtl_fm
4 | killall -9 rtl_power
5 |
--------------------------------------------------------------------------------
/bin/multidemodulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | infile="$1"
4 |
5 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
6 | # do not change the following three lines
7 | scriptDir="$(dirname "$(realpath "$0")")"
8 | source $scriptDir/basedir_conf.py
9 | source $baseDir/_listvars.sh
10 |
11 |
12 |
13 | sox "$infile" -b 16 --encoding signed-integer --endian little -t raw - | multimon-ng -a AFSK1200 -A -t raw -
--------------------------------------------------------------------------------
/bin/pymultimonaprs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 |
10 | logfile="$baseDir/recordings/aprs/$(date +"%Y/%m")/$(date +"%Y%m%d").txt"
11 |
12 | mkdir -p $(dirname $logfile)
13 |
14 | duration=$1
15 | dongleShift=$2
16 |
17 | echo "Running for $duration with dongleshift $dongleShift"
18 |
19 | mkdir -p $(dirname $logfile)
20 |
21 | timeout --kill-after=1 $duration pymultimonaprs -v -c $baseDir/bin/pymultimonaprs.confs/pymultimonaprs.json | tee -a $logfile
22 |
23 |
--------------------------------------------------------------------------------
/bin/radiosonde_auto_rx.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 |
10 | # Track radiosondes
11 | # more info: see https://github.com/projecthorus/radiosonde_auto_rx
12 |
13 | duration=$1
14 | dongleShift=$2
15 |
16 | echo "$dongleShift"
17 |
18 | cd ~/progs/radiosonde_auto_rx/auto_rx/
19 | timeout --kill-after=20 --signal=SIGINT $duration python auto_rx.py
20 |
--------------------------------------------------------------------------------
/bin/rtl_433.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # runs rtl_433 and scans for 433 MHz data transfers
4 |
5 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
6 | # do not change the following three lines
7 | scriptDir="$(dirname "$(realpath "$0")")"
8 | source $scriptDir/basedir_conf.py
9 | source $baseDir/_listvars.sh
10 |
11 | #--------------------------------------#
12 |
13 | logfile="$recordingDir/rtl_433/"$(date +"%Y/%m")/$(date +"%Y%m%d")".txt"
14 |
15 | mkdir -p $(dirname $logfile)
16 |
17 | duration=$1
18 | dongleShift=$2
19 |
20 | mkdir -p $(dirname $logfile)
21 |
22 | timeout --kill-after=1 $duration rtl_433 -p $dongleShift -G | tee -a $logfile
23 |
--------------------------------------------------------------------------------
/bin/update-keps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
4 | # do not change the following three lines
5 | scriptDir="$(dirname "$(realpath "$0")")"
6 | source $scriptDir/basedir_conf.py
7 | source $baseDir/_listvars.sh
8 |
9 | ###
10 |
11 | TLEDIR=$baseDir/var/tle/
12 | mkdir -p $TLEDIR
13 |
14 | #rm $TLEDIR/weather.txt
15 | #wget --no-check-certificate -r http://www.celestrak.com/NORAD/elements/weather.txt -O $TLEDIR/weather.txt
16 |
17 | #rm $TLEDIR/noaa.txt
18 | #wget -r http://www.celestrak.com/NORAD/elements/noaa.txt -O $TLEDIR/noaa.txt
19 |
20 | rm $TLEDIR/nasa.txt
21 | wget -r https://www.amsat.org/amsat/ftp/keps/current/nasa.all -O $TLEDIR/nasa.txt
22 |
23 | rm $TLEDIR/amateur.txt
24 | wget -r http://www.celestrak.com/NORAD/elements/amateur.txt -O $TLEDIR/amateur.txt
25 |
26 | rm $TLEDIR/cubesat.txt
27 | wget -r http://www.celestrak.com/NORAD/elements/cubesat.txt -O $TLEDIR/cubesat.txt
28 |
29 |
30 | rm $TLEDIR/multi.txt
31 | wget -r http://www.pe0sat.vgnet.nl/kepler/mykepler.txt -O $TLEDIR/multi.txt
32 |
33 | rm $TLEDIR/all.txt
34 | cat $TLEDIR/*.txt > $TLEDIR/all.txt
35 |
36 |
37 | echo "$wwwDir"
38 | date
39 | date -R > $wwwDir/keps.tmp
40 | date +"%s" > $wwwDir/kepsU.tmp
41 | echo Updated
42 |
--------------------------------------------------------------------------------
/bin/wspr.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # runs WSPR receiver
4 |
5 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
6 | # do not change the following three lines
7 | scriptDir="$(dirname "$(realpath "$0")")"
8 | source $scriptDir/basedir_conf.py
9 | source $baseDir/_listvars.sh
10 |
11 | #--------------------------------------#
12 |
13 | # # # # Edit following variable with your correct data # # # #
14 | call="SA7BNT"
15 | gain="40"
16 | locator="JO77PP"
17 | hz="28.1246M"
18 | info_rx="Start reception 10 meters"
19 | sampling="0" #direct sampling [0,1,2] (default: 0 for Quadrature, 1 for I branch, 2 for Q branch)
20 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
21 |
22 | logfile="$recordingDir/rtlsdr_wspr/"$(date +"%Y/%m")/$(date +"%Y%m%d")".txt"
23 |
24 | mkdir -p $(dirname $logfile)
25 |
26 | duration=$1
27 | dongleShift=$2
28 |
29 | mkdir -p $(dirname $logfile)
30 |
31 | sleep 1
32 | pgrep rtlsdr_wsprd > /dev/null 2>&1
33 | if [ $? -eq 0 ]; then
34 | echo $'\n'"---Kill rtlsdr_wsprd pid---" >> $logfile
35 | killall rtlsdr_wsprd &>> $logfile
36 | fi
37 | echo $'\n'"$(date)" >> $logfile
38 | echo "$info_rx"$'\n' >> $logfile
39 | sleep 1
40 |
41 |
42 | cd ~/rtlsdr-wsprd
43 | timeout --kill-after=1 $duration ./rtlsdr_wsprd -p "$dongleShift" -f "$hz" -c "$call" -l "$locator" -g "$gain" -d "$sampling" | tee -a $logfile
44 |
--------------------------------------------------------------------------------
/configure.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | outfile="basedir_conf.py"
4 | pwdir=$(pwd)
5 |
6 | echo "# this is automaticaly generated script" > $outfile
7 | echo "# use configure.sh to generate the file" >> $outfile
8 | echo >> $outfile
9 | echo "baseDir=\"$pwdir/\"" >> $outfile
10 |
11 |
12 | ln -r -f -s $outfile "bin/$outfile"
13 | ln -r -f -s $outfile "bin/crontab/$outfile"
14 |
15 | echo "Creating symlinks to the config file..."
16 |
17 | for d in modules/*/
18 | do
19 | ln -r -f -s $outfile "$d/$outfile"
20 | done
--------------------------------------------------------------------------------
/docs/NOAA19-HVCT.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/NOAA19-HVCT.jpg
--------------------------------------------------------------------------------
/docs/NOAA19-therm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/NOAA19-therm.jpg
--------------------------------------------------------------------------------
/docs/Python-2.7-lightgreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/Python-2.7-lightgreen.png
--------------------------------------------------------------------------------
/docs/badge-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/badge-email.png
--------------------------------------------------------------------------------
/docs/meteor/meteor-ch2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/meteor/meteor-ch2.jpg
--------------------------------------------------------------------------------
/docs/meteor/meteor-combo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/meteor/meteor-combo.jpg
--------------------------------------------------------------------------------
/docs/nextpass.html:
--------------------------------------------------------------------------------
1 |
2 | # satellite start duration peak azimuth freq process with
3 | 1 PR3_NEWS 2018-01-29 21:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
4 | 2 ISS 2018-01-29 21:06 09:59 67° 191° (S) 145800000Hz modules/iss/iss_voice.sh
5 | 3 PR3_NEWS 2018-01-29 22:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
6 | 4 ISS 2018-01-29 22:42 09:30 24° 210° (SSW) 145800000Hz modules/iss/iss_voice.sh
7 | 5 PR3_NEWS 2018-01-29 23:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
8 | 6 NOAA-19 2018-01-30 02:54 14:22 30° 94° (E) 137100000Hz modules/noaa/noaa.sh
9 | 7 NOAA-19 2018-01-30 04:34 14:54 53° 294° (WNW) 137100000Hz modules/noaa/noaa.sh
10 | 8 NOAA-15 2018-01-30 05:20 13:16 22° 90° (E) 137620000Hz modules/noaa/noaa.sh
11 | 9 NOAA-15 2018-01-30 06:59 14:35 68° 290° (W) 137620000Hz modules/noaa/noaa.sh
12 | 10 NOAA-18 2018-01-30 07:25 14:57 61° 102° (E) 137912500Hz modules/noaa/noaa.sh
13 | 11 PR3_NEWS 2018-01-30 08:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
14 | 12 PR3_NEWS 2018-01-30 09:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
15 | 13 NOAA-18 2018-01-30 09:06 13:49 28° 302° (WNW) 137912500Hz modules/noaa/noaa.sh
16 | 14 PR3_NEWS 2018-01-30 10:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
17 | 15 PR3_NEWS 2018-01-30 11:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
18 | 16 PR3_NEWS 2018-01-30 12:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
19 | 17 NOAA-19 2018-01-30 12:47 13:03 20° 52° (NE) 137100000Hz modules/noaa/noaa.sh
20 | 18 PR3_NEWS 2018-01-30 13:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
21 | 19 PR3_NEWS 2018-01-30 14:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
22 | 20 NOAA-19 2018-01-30 14:26 15:04 88° 252° (WSW) 137100000Hz modules/noaa/noaa.sh
23 | 21 PR3_NEWS 2018-01-30 15:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
24 | 22 PR3_NEWS 2018-01-30 16:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
25 | 23 NOAA-15 2018-01-30 16:46 14:29 60° 68° (ENE) 137620000Hz modules/noaa/noaa.sh
26 | 24 PR3_NEWS 2018-01-30 17:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
27 | 25 ISS 2018-01-30 17:01 09:11 21° 146° (SE) 145800000Hz modules/iss/iss_voice.sh
28 | 26 NOAA-18 2018-01-30 17:17 14:25 37° 61° (NE) 137912500Hz modules/noaa/noaa.sh
29 | 27 PR3_NEWS 2018-01-30 18:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
30 | 28 NOAA-15 2018-01-30 18:26 13:38 25° 268° (WSW) 137620000Hz modules/noaa/noaa.sh
31 | 29 ISS 2018-01-30 18:37 09:58 62° 165° (SSE) 145800000Hz modules/iss/iss_voice.sh
32 | 30 NOAA-18 2018-01-30 18:58 14:40 43° 261° (WSW) 137912500Hz modules/noaa/noaa.sh
33 | 31 PR3_NEWS 2018-01-30 20:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
34 | 32 ISS 2018-01-30 20:13 10:01 77° 185° (S) 145800000Hz modules/iss/iss_voice.sh
35 | 33 PR3_NEWS 2018-01-30 21:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
36 | 34 ISS 2018-01-30 21:50 09:42 35° 205° (SSW) 145800000Hz modules/iss/iss_voice.sh
37 | 35 PR3_NEWS 2018-01-30 22:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
38 | 36 PR3_NEWS 2018-01-30 23:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
39 | 37 NOAA-19 2018-01-31 02:42 14:03 25° 91° (E) 137100000Hz modules/noaa/noaa.sh
40 | 38 NOAA-19 2018-01-31 04:23 15:04 64° 291° (W) 137100000Hz modules/noaa/noaa.sh
41 | 39 NOAA-15 2018-01-31 06:34 14:40 79° 105° (E) 137620000Hz modules/noaa/noaa.sh
42 | 40 PR3_NEWS 2018-01-31 07:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
43 | 41 NOAA-18 2018-01-31 07:13 14:50 50° 100° (E) 137912500Hz modules/noaa/noaa.sh
44 | 42 PR3_NEWS 2018-01-31 08:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
45 | 43 NOAA-15 2018-01-31 08:14 12:54 22° 305° (WNW) 137620000Hz modules/noaa/noaa.sh
46 | 44 NOAA-18 2018-01-31 08:54 14:06 33° 300° (WNW) 137912500Hz modules/noaa/noaa.sh
47 | 45 PR3_NEWS 2018-01-31 10:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
48 | 46 PR3_NEWS 2018-01-31 11:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
49 | 47 PR3_NEWS 2018-01-31 12:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
50 | 48 PR3_NEWS 2018-01-31 13:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
51 | 49 PR3_NEWS 2018-01-31 14:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
52 | 50 NOAA-19 2018-01-31 14:15 15:01 77° 70° (ENE) 137100000Hz modules/noaa/noaa.sh
53 | 51 PR3_NEWS 2018-01-31 15:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
54 | 52 NOAA-19 2018-01-31 15:57 13:22 20° 270° (W) 137100000Hz modules/noaa/noaa.sh
55 | 53 NOAA-15 2018-01-31 16:21 14:05 40° 63° (NE) 137620000Hz modules/noaa/noaa.sh
56 | 54 PR3_NEWS 2018-01-31 17:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
57 | 55 NOAA-18 2018-01-31 17:06 14:06 32° 58° (NE) 137912500Hz modules/noaa/noaa.sh
58 | 56 ISS 2018-01-31 17:45 09:59 47° 160° (SSE) 145800000Hz modules/iss/iss_voice.sh
59 | 57 PR3_NEWS 2018-01-31 18:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
60 | 58 NOAA-15 2018-01-31 18:01 14:14 38° 263° (WSW) 137620000Hz modules/noaa/noaa.sh
61 | 59 NOAA-18 2018-01-31 18:46 14:59 53° 258° (WSW) 137912500Hz modules/noaa/noaa.sh
62 | 60 PR3_NEWS 2018-01-31 19:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
63 | 61 ISS 2018-01-31 19:21 10:07 81° 179° (SSE) 145800000Hz modules/iss/iss_voice.sh
64 | 62 PR3_NEWS 2018-01-31 20:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
65 | 63 PR3_NEWS 2018-01-31 21:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
66 | 64 PR3_NEWS 2018-01-31 22:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
67 | 65 PR3_NEWS 2018-01-31 23:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
68 | 66 NOAA-19 2018-02-01 02:31 13:33 20° 89° (ENE) 137100000Hz modules/noaa/noaa.sh
69 | 67 NOAA-19 2018-02-01 04:11 15:10 77° 289° (W) 137100000Hz modules/noaa/noaa.sh
70 | 68 NOAA-15 2018-02-01 06:09 14:30 51° 100° (E) 137620000Hz modules/noaa/noaa.sh
71 | 69 PR3_NEWS 2018-02-01 07:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
72 | 70 NOAA-18 2018-02-01 07:02 14:37 41° 98° (E) 137912500Hz modules/noaa/noaa.sh
73 | 71 NOAA-15 2018-02-01 07:49 13:41 31° 300° (WNW) 137620000Hz modules/noaa/noaa.sh
74 | 72 PR3_NEWS 2018-02-01 08:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
75 | 73 NOAA-18 2018-02-01 08:43 14:22 39° 298° (WNW) 137912500Hz modules/noaa/noaa.sh
76 | 74 PR3_NEWS 2018-02-01 09:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
77 | 75 PR3_NEWS 2018-02-01 10:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
78 | 76 PR3_NEWS 2018-02-01 11:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
79 | 77 PR3_NEWS 2018-02-01 12:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
80 | 78 PR3_NEWS 2018-02-01 13:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
81 | 79 PR3_NEWS 2018-02-01 14:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
82 | 80 NOAA-19 2018-02-01 14:03 14:56 64° 68° (ENE) 137100000Hz modules/noaa/noaa.sh
83 | 81 PR3_NEWS 2018-02-01 15:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
84 | 82 NOAA-19 2018-02-01 15:45 13:58 24° 268° (WSW) 137100000Hz modules/noaa/noaa.sh
85 | 83 NOAA-15 2018-02-01 15:56 13:34 27° 58° (NE) 137620000Hz modules/noaa/noaa.sh
86 | 84 NOAA-18 2018-02-01 16:54 13:46 27° 56° (NE) 137912500Hz modules/noaa/noaa.sh
87 | 85 NOAA-15 2018-02-01 17:35 14:41 60° 258° (WSW) 137620000Hz modules/noaa/noaa.sh
88 | 86 PR3_NEWS 2018-02-01 18:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
89 | 87 NOAA-18 2018-02-01 18:34 15:05 65° 256° (WSW) 137912500Hz modules/noaa/noaa.sh
90 | 88 PR3_NEWS 2018-02-01 19:00 05:00 0° 0° (N) 98988000Hz modules/fm/fm.sh
91 | 89 ISS 2018-02-01 20:05 09:59 62° 193° (S) 145800000Hz modules/iss/iss_voice.sh
92 | 90 ISS 2018-02-01 21:42 09:12 21° 212° (SSW) 145800000Hz modules/iss/iss_voice.sh
93 | 91 NOAA-19 2018-02-02 04:00 15:13 88° 107° (E) 137100000Hz modules/noaa/noaa.sh
94 | 92 NOAA-19 2018-02-02 05:41 13:12 21° 307° (WNW) 137100000Hz modules/noaa/noaa.sh
95 | 93 NOAA-15 2018-02-02 05:44 14:02 33° 95° (E) 137620000Hz modules/noaa/noaa.sh
96 | 94 NOAA-18 2018-02-02 06:50 14:22 33° 95° (E) 137912500Hz modules/noaa/noaa.sh
97 | 95 NOAA-15 2018-02-02 07:24 14:14 45° 295° (WNW) 137620000Hz modules/noaa/noaa.sh
98 | 96 NOAA-18 2018-02-02 08:31 14:36 47° 295° (WNW) 137912500Hz modules/noaa/noaa.sh
99 | 97 NOAA-19 2018-02-02 13:52 14:47 53° 65° (NE) 137100000Hz modules/noaa/noaa.sh
100 | 98 NOAA-19 2018-02-02 15:33 14:14 30° 265° (WSW) 137100000Hz modules/noaa/noaa.sh
101 | 99 ISS 2018-02-02 16:01 09:20 24° 148° (SE) 145800000Hz modules/iss/iss_voice.sh
102 | 100 NOAA-18 2018-02-02 16:43 13:22 23° 54° (NE) 137912500Hz modules/noaa/noaa.sh
103 |
104 |
--------------------------------------------------------------------------------
/docs/nextpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/nextpass.png
--------------------------------------------------------------------------------
/docs/passtable.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/passtable.jpg
--------------------------------------------------------------------------------
/docs/sstv/iss1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/sstv/iss1.jpeg
--------------------------------------------------------------------------------
/docs/sstv/iss3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/sstv/iss3.jpeg
--------------------------------------------------------------------------------
/docs/www-dynamic+shadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/www-dynamic+shadow.jpg
--------------------------------------------------------------------------------
/docs/www-dynamic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/www-dynamic.png
--------------------------------------------------------------------------------
/docs/www-static+shadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/www-static+shadow.jpg
--------------------------------------------------------------------------------
/docs/www-static.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/docs/www-static.jpg
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | # run: conda env create --file environment.yml
2 | name: autowx2
3 | channels:
4 | - conda-forge
5 | - defaults
6 | dependencies:
7 | - python=3.9
8 | - numpy
9 | - matplotlib
10 | - scikit-learn
11 | - pip
12 | - pip:
13 | - Flask
14 | - Flask-SocketIO
15 | - pypredict
16 |
--------------------------------------------------------------------------------
/genpasstable.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | #
5 | # autowx2 - genpastable.py
6 | # generates the pass table and recording plan for tne next few days and the appropriate plot
7 | #
8 | # GANTT Chart with Matplotlib
9 | # Sukhbinder
10 | # inspired by:
11 | # https://sukhbinder.wordpress.com/2016/05/10/quick-gantt-chart-with-matplotlib/
12 | # taken from
13 | # https://github.com/fialhocoelho/test/blob/master/plot/gantt.py
14 | #
15 |
16 | from autowx2_functions import *
17 |
18 | if __name__ == "__main__":
19 | satellites = list(satellitesData)
20 | qth = (stationLat, stationLon, stationAlt)
21 | generatePassTableAndSaveFiles(satellites, qth)
22 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ### created for and tested at the debian-like systems (tested on debian, ubuntu and mint)
4 |
5 | ### for installing the dongle
6 | ### for details, see: http://www.instructables.com/id/rtl-sdr-on-Ubuntu/
7 | #sudo echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", GROUP="adm", MODE="0666", SYMLINK+="rtl_sdr"' >> /etc/udev/rules.d/20.rtlsdr.rules
8 | #sudo echo "blacklist dvb_usb_rtl28xxu" >> /etc/modprobe.d/rtl-sdr-blacklist.conf
9 |
10 | MACHINE_TYPE=$(uname -m)
11 | echo $MACHINE_TYPE
12 |
13 | bash ./configure.sh
14 |
15 | #echo "copy sample config file, but don't overwrite"
16 | cp --no-clobber autowx2_conf.py.example autowx2_conf.py
17 |
18 |
19 | echo "basedir_conf.py:"
20 | cat basedir_conf.py
21 |
22 | source basedir_conf.py
23 | echo $baseDir
24 |
25 | echo
26 | echo
27 | echo "******** Installing required packages"
28 | echo
29 | echo
30 | sudo apt update
31 | sudo apt install -y rtl-sdr git libpulse-dev fftw3 libc6 libfontconfig1 libx11-6 libxext6 libxft2 libusb-1.0-0-dev \
32 | libavahi-client-dev libavahi-common-dev libdbus-1-dev libfftw3-single3 libpulse-mainloop-glib0 librtlsdr0 librtlsdr-dev \
33 | libfftw3-dev libfftw3-double3 lame sox libsox-fmt-mp3 libtool automake imagemagick \
34 | bc imagemagick
35 |
36 |
37 | if [ ${MACHINE_TYPE} == 'armv6l' ] || [ ${MACHINE_TYPE} == 'armv7l' ]; then
38 | echo
39 | echo
40 | echo "******** Installing Rpi required packages"
41 | echo
42 | echo
43 | sudo apt-get install -y libtool qt4-default automake autotools-dev m4
44 | curl https://bootstrap.pypa.io/get-pip.py > get-pip.py
45 | sudo python get-pip.py
46 | else
47 | sudo apt-get install -y libfftw3-long3
48 | sudo apt-get install -y libfftw3-quad3
49 | fi
50 |
51 |
52 | PIP_OPTIONS=""
53 | if [ ${MACHINE_TYPE} == 'armv6l' ] || [ ${MACHINE_TYPE} == 'armv7l' ]; then
54 | PIP_OPTIONS="--no-cache-dir"
55 | fi
56 |
57 | # echo
58 | # echo
59 | # echo "******** Installing python requirements"
60 | # echo
61 | # echo
62 |
63 | # use pip:
64 | # pip $PIP_OPTIONS install -r requirements.txt
65 |
66 | # or conda:
67 | # conda env create --file environment.yml
68 |
69 |
70 | mkdir -p $baseDir/bin/sources/
71 |
72 | cd $baseDir/bin/sources/
73 |
74 | echo
75 | echo
76 | echo "******** Installing wxtoimg"
77 | echo
78 | echo
79 |
80 | if [ ${MACHINE_TYPE} == 'x86_64' ]; then
81 | echo "64-bit system"
82 | wget https://wxtoimgrestored.xyz/beta/wxtoimg-linux-amd64-2.11.2-beta.tar.gz
83 | gunzip < wxtoimg-linux-amd64-2.11.2-beta.tar.gz | sudo sh -c "(cd /; tar -xvf -)"
84 | elif [ ${MACHINE_TYPE} == 'armv6l' ] || [ ${MACHINE_TYPE} == 'armv7l' ]; then
85 | wget https://wxtoimgrestored.xyz/beta/wxtoimg-armhf-2.11.2-beta.deb
86 | sudo dpkg -i wxtoimg-armhf-2.11.2-beta.deb
87 | else
88 | echo "32-bit system"
89 | wget https://wxtoimgrestored.xyz/beta/wxtoimg-i386-2.11.2-beta.deb
90 | sudo dpkg -i wxtoimg_2.10.11-1_i386.deb # may generate some dependencies errors; if not, stop here
91 | # sudo apt-get -f install
92 | fi
93 |
94 | wxtoimg -h
95 |
96 |
97 | echo
98 | echo
99 | echo "******** Installing multimon-ng-stqc"
100 | echo
101 | echo
102 |
103 | cd $baseDir/bin/sources/
104 |
105 | git clone https://github.com/sq5bpf/multimon-ng-stqc.git
106 | cd multimon-ng-stqc
107 | mkdir build
108 | cd build
109 | qmake ../multimon-ng.pro
110 | make
111 | sudo make install
112 |
113 |
114 | multimon-ng -h
115 |
116 |
117 |
118 | echo
119 | echo
120 | echo "******** Installing kalibrate"
121 | echo
122 | echo
123 |
124 | cd $baseDir/bin/sources/
125 |
126 | git clone https://github.com/viraptor/kalibrate-rtl.git
127 | cd kalibrate-rtl
128 | ./bootstrap
129 | ./configure
130 | make
131 | sudo make install
132 |
133 | kal -h
134 |
135 |
136 | echo
137 | echo
138 | echo "******** Getting auxiliary programs"
139 | echo
140 | echo
141 |
142 | cd $baseDir/bin/
143 | wget https://raw.githubusercontent.com/filipsPL/heatmap/master/heatmap.py -O $baseDir/bin/heatmap.py
144 |
145 |
146 |
147 | echo
148 | echo
149 | echo "******** Getting fresh keplers"
150 | echo
151 | echo
152 |
153 | cd $baseDir
154 | bin/update-keps.sh
155 |
156 |
157 |
158 | echo "***************** default dongle shift...."
159 |
160 | echo -n "0" > var/dongleshift.txt
161 |
162 | echo
163 | echo "-------------------------------------------------------------------------"
164 | echo "The installation script seems to be finished."
165 | echo "please inspect the output. If there are no errors, your system is"
166 | echo "installed correctly."
167 | echo "Edit autowx2_conf.py to suit your needs and have fun!"
168 | echo "-------------------------------------------------------------------------"
169 | echo
170 |
171 | exit 0
172 |
--------------------------------------------------------------------------------
/modules/_template/fm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # test file to record fm radio for a given period of time
4 |
5 |
6 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
7 | # do not change the following three lines
8 | scriptDir="$(dirname "$(realpath "$0")")"
9 | source $scriptDir/basedir_conf.py
10 | source $baseDir/_listvars.sh
11 |
12 |
13 | #### microconfiguration
14 |
15 | recdir="$recordingDir/fm/rec/"$(date +"%Y/%m/")
16 |
17 | ### doing the job
18 |
19 | mkdir -p $recdir
20 |
21 | fileNameCore="$1"
22 | satellite="$2"
23 | start="$3"
24 | duration="$4"
25 | peak="$5"
26 | azimuth="$6"
27 | freq="$7"
28 |
29 | echo "fileNameCore=$fileNameCore"
30 | echo "satellite=$satellite"
31 | echo "start=$start"
32 | echo "duration=$duration"
33 | echo "peak=$peak"
34 | echo "azimuth=$azimuth"
35 | echo "freq=$freq"
36 |
37 |
38 | # fixed values for tests
39 | #freq="98988000"
40 | #duration="10s"
41 | #fileNameCore="trojka"
42 |
43 |
44 | timeout $duration rtl_fm -f $freq -M wbfm -g 49.6 | lame -r -s 32 -m m - $recdir/$fileNameCore.mp3
45 |
46 |
--------------------------------------------------------------------------------
/modules/fm/fm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # test file to record fm radio for a given period of time
4 |
5 |
6 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
7 | # do not change the following three lines
8 | scriptDir="$(dirname "$(realpath "$0")")"
9 | source $scriptDir/basedir_conf.py
10 | source $baseDir/_listvars.sh
11 |
12 |
13 | #### microconfiguration
14 |
15 | recdir="$recordingDir/fm/rec/"$(date +"%Y/%m/")
16 |
17 | ### doing the job
18 |
19 | mkdir -p $recdir
20 |
21 | fileNameCore="$1"
22 | satellite="$2"
23 | start="$3"
24 | duration="$4"
25 | peak="$5"
26 | azimuth="$6"
27 | freq="$7"
28 |
29 |
30 | echo "fileNameCore=$fileNameCore"
31 | echo "satellite=$satellite"
32 | echo "start=$start"
33 | echo "duration=$duration"
34 | echo "peak=$peak"
35 | echo "azimuth=$azimuth"
36 | echo "freq=$freq"
37 |
38 |
39 |
40 | # fixed values for tests
41 | # freq="98796500"
42 | # duration="10s"
43 | # fileNameCore="trojka"
44 |
45 |
46 | echo
47 | echo "Recording to:" $recdir/$fileNameCore.mp3
48 | echo
49 |
50 | timeout $duration rtl_fm -f $freq -M wfm -g 49.6 -l 0 -s 220k -A std -E deemp -r 44.1k | sox -t raw -e signed -c 1 -b 16 -r 44000 -V1 - "$recdir/$fileNameCore.mp3"
51 |
--------------------------------------------------------------------------------
/modules/iss/iss_voice_iq.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # file to record fm radio for a given period of time
4 | # audio files saved to iq/raw files!
5 |
6 |
7 | # variable(s) to adjust:
8 |
9 |
10 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
11 | # do not change the following three lines
12 | scriptDir="$(dirname "$(realpath "$0")")"
13 | source $scriptDir/basedir_conf.py
14 | source $baseDir/_listvars.sh
15 |
16 |
17 | #### microconfiguration
18 |
19 | recdir="$recordingDir/iss/rec/"$(date +"%Y/%m/")
20 | sample="44000"
21 |
22 | ### doing the job
23 |
24 | mkdir -p $recdir
25 |
26 | fileNameCore="$1"
27 | satellite="$2"
28 | start="$3"
29 | duration="$4"
30 | peak="$5"
31 | azimuth="$6"
32 | freq="$7"
33 |
34 |
35 | echo "fileNameCore=$fileNameCore"
36 | echo "satellite=$satellite"
37 | echo "start=$start"
38 | echo "duration=$duration"
39 | echo "peak=$peak"
40 | echo "azimuth=$azimuth"
41 | echo "freq=$freq"
42 |
43 |
44 | ### fixed values for tests
45 | #freq="98988000"
46 | #duration="10s"
47 | #fileNameCore="ISS"
48 |
49 | ### RECORDING TO WAV, for further processing, eg, for SSTV
50 | echo "Recording to dat/iq format"
51 |
52 | # timeout $duration rtl_fm -f $freq -s $sample -g $dongleGain -F 9 -A fast -E offset -p $dongleShift $recdir/$fileNameCore.raw | tee -a $logFile
53 | # sox -t raw -r $sample -es -b 16 -c 1 -V1 $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav rate $wavrate | tee -a $logFile
54 | # touch -r $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav
55 | # rm $recdir/$fileNameCore.raw
56 |
57 | #sample="2500000"
58 | sample="44000"
59 |
60 | timeout $duration rtl_fm -f $freq -g $dongleGain -s $sample -F 9 -A fast -E offset -p $dongleShift $recdir/$fileNameCore.iq | tee -a $logFile
61 | sox -t raw -r $sample -es -b 16 -c 1 -V1 $recdir/$fileNameCore.iq $recdir/$fileNameCore.wav rate $wavrate | tee -a $logFile
62 | touch -r $recdir/$fileNameCore.iq $recdir/$fileNameCore.wav
63 | sox "$recdir/$fileNameCore.wav" -n spectrogram -o "$recdir/$fileNameCore-spectrogram.png"
64 |
--------------------------------------------------------------------------------
/modules/iss/iss_voice_mp3.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # file to record fm radio for a given period of time
4 | # may be used to record ISS voice channel
5 | # *audio saved to mp3*
6 |
7 | # variable(s) to adjust:
8 |
9 |
10 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
11 | # do not change the following three lines
12 | scriptDir="$(dirname "$(realpath "$0")")"
13 | source $scriptDir/basedir_conf.py
14 | source $baseDir/_listvars.sh
15 |
16 |
17 | #### microconfiguration
18 |
19 | recdir="$recordingDir/iss/rec/"$(date +"%Y/%m/")
20 |
21 | ### doing the job
22 |
23 | mkdir -p $recdir
24 |
25 | fileNameCore="$1"
26 | satellite="$2"
27 | start="$3"
28 | duration="$4"
29 | peak="$5"
30 | azimuth="$6"
31 | freq="$7"
32 |
33 |
34 | echo "fileNameCore=$fileNameCore"
35 | echo "satellite=$satellite"
36 | echo "start=$start"
37 | echo "duration=$duration"
38 | echo "peak=$peak"
39 | echo "azimuth=$azimuth"
40 | echo "freq=$freq"
41 |
42 |
43 |
44 | # fixed values for tests
45 | # freq="98988000"
46 | # duration="10s"
47 | # fileNameCore="ISS"
48 |
49 |
50 | ### RECORDING TO MP3
51 | echo "Recording to mp3"
52 | timeout $duration rtl_fm -f $freq -M fm -g 49.6 -l 0 | lame -r -s 32k -m m - "$recdir/$fileNameCore.mp3"
53 | sox "$recdir/$fileNameCore.mp3" -n spectrogram -o "$recdir/$fileNameCore-spectrogram.png"
54 | sox "$recdir/$fileNameCore.mp3" "$recdir/$fileNameCore-silence.mp3" silence -l 1 0.3 10% -1 2.0 10%
55 | $baseDir/bin/multidemodulator.sh "$recdir/$fileNameCore.mp3" > "$recdir/$fileNameCore-demodulated.log"
56 |
--------------------------------------------------------------------------------
/modules/iss/iss_voice_wav.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # file to record fm radio for a given period of time
4 | # may be used to record ISS voice channel, or wav to SSTV conversion
5 | # audio files saved to wav!
6 |
7 |
8 | # variable(s) to adjust:
9 |
10 |
11 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
12 | # do not change the following three lines
13 | scriptDir="$(dirname "$(realpath "$0")")"
14 | source $scriptDir/basedir_conf.py
15 | source $baseDir/_listvars.sh
16 |
17 |
18 | #### microconfiguration
19 |
20 | recdir="$recordingDir/iss/rec/"$(date +"%Y/%m/")
21 | sample="44000"
22 |
23 | ### doing the job
24 |
25 | mkdir -p $recdir
26 |
27 | fileNameCore="$1"
28 | satellite="$2"
29 | start="$3"
30 | duration="$4"
31 | peak="$5"
32 | azimuth="$6"
33 | freq="$7"
34 |
35 |
36 | echo "fileNameCore=$fileNameCore"
37 | echo "satellite=$satellite"
38 | echo "start=$start"
39 | echo "duration=$duration"
40 | echo "peak=$peak"
41 | echo "azimuth=$azimuth"
42 | echo "freq=$freq"
43 |
44 |
45 | ### fixed values for tests
46 | # freq="98988000"
47 | # duration="10s"
48 | # fileNameCore="ISS"
49 |
50 |
51 | ### RECORDING TO WAV, for further processing, eg, for SSTV
52 | echo "Recording to wav"
53 | timeout $duration rtl_fm -f $freq -s $sample -g $dongleGain -F 9 -A fast -E offset -p $dongleShift $recdir/$fileNameCore.raw | tee -a $logFile
54 | sox -t raw -r $sample -es -b 16 -c 1 -V1 $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav rate $wavrate | tee -a $logFile
55 | touch -r $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav
56 | rm $recdir/$fileNameCore.raw
57 |
--------------------------------------------------------------------------------
/modules/meteor-m2/meteor.conf.example:
--------------------------------------------------------------------------------
1 | #
2 | # meteor m2 module configuration file
3 | #
4 |
5 | # where images from mrlpt are saved
6 | # (as declared in mlrptrc config file - check it!)
7 | # eg: /var/lrpt/images/
8 | rawImageDir="/meteor/img/raw/"
9 |
10 |
11 | # directory with meteor stuff
12 | meteorDir="$recordingDir/meteor/"
13 |
14 | # directory where the images finally will go
15 | imgdir="$meteorDir/img/"$(date +"%Y/%m/%d/")
16 |
17 | # resize images to the given size to avoid growing of the repository; in px
18 | resizeimageto=1024
19 |
--------------------------------------------------------------------------------
/modules/meteor-m2/meteor.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # module to record meteor m2 transmission
4 | # for configuration, see meteor.conf
5 |
6 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
7 | # do not change the following three lines
8 | scriptDir="$(dirname "$(realpath "$0")")"
9 | source $scriptDir/basedir_conf.py
10 | source $baseDir/_listvars.sh
11 |
12 | # read module config
13 |
14 | source $scriptDir/meteor.conf
15 |
16 | ### doing the job
17 |
18 | mkdir -p $imgdir
19 |
20 | #
21 | # Assigning variables
22 | #
23 |
24 | fileNameCore="$1"
25 | satellite="$2"
26 | start="$3"
27 | duration="$4"
28 | peak="$5"
29 | azimuth="$6"
30 | freq="$7"
31 |
32 | #
33 | # Saving to log file
34 | #
35 |
36 | logFile=$imgdir/$fileNameCore.log
37 | echo $logFile
38 |
39 | date > $logFile # initialize log file
40 | echo $fileNameCore >> $logFile
41 | echo $satellite >> $logFile
42 | echo $start >> $logFile
43 | echo $duration >> $logFile
44 | echo $peak >> $logFile
45 | echo $freq >> $logFile
46 |
47 |
48 | echo "fileNameCore=$fileNameCore"
49 | echo "satellite=$satellite"
50 | echo "start=$start"
51 | echo "duration=$duration"
52 | echo "peak=$peak"
53 | echo "azimuth=$azimuth"
54 | echo "freq=$freq"
55 |
56 |
57 |
58 | #
59 | # recording sumbodule
60 | #
61 |
62 | source $scriptDir/meteor_record.sh
63 |
64 | #
65 | # meteor gallery
66 | #
67 |
68 | source $scriptDir/meteor_gallery.sh
69 |
--------------------------------------------------------------------------------
/modules/meteor-m2/meteor_gallery.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # moving recorded images to the appropriate final dir
5 | #
6 |
7 | # value for some tests:
8 | # fileNameCore="20190118-1012_METEOR-M2"
9 | # rawImageDir="./"
10 |
11 |
12 |
13 | outHtml="$imgdir/$fileNameCore.html" # html for this single pass
14 | indexHtml="$imgdir/index.html" # main index file for a given day
15 | htmlTemplate="$wwwDir/index.tpl"
16 |
17 |
18 | # ---single gallery preparation------------------------------------------------#
19 |
20 | makethumb() {
21 | obrazek="$1"
22 | local thumbnail=$(basename "$obrazek" .jpg)".th.jpg"
23 | convert -define jpeg:size=200x200 "$obrazek" -thumbnail '200x200^' granite: +swap -gravity center -extent 200x200 -composite -quality 82 "$thumbnail"
24 | echo "$thumbnail"
25 | }
26 |
27 | # -----------------------------------------------------------------------------#
28 |
29 | logFile="$imgdir/$fileNameCore.log" # log file to read from
30 |
31 | varDate=$(sed '1q;d' $logFile)
32 | varSat=$(sed '3q;d' $logFile)
33 | varStart=$(sed '4q;d' $logFile) # unused
34 | varDur=$(sed '5q;d' $logFile)
35 | varPeak=$(sed '6q;d' $logFile)
36 | varFreq=$(sed '7q;d' $logFile)
37 |
38 | dateTime=$(date -d @$varStart +"%Y-%m-%d")
39 | dateTimeDir=$(date -d @$varStart +"%Y/%m/%d") # directory format of date, eg. 2018/11/22/
40 | wwwPath=$wwwRootPath/recordings/meteor/img/$dateTimeDir
41 |
42 |
43 |
44 |
45 | # -----------------------------------------------------------------------------#
46 |
47 |
48 | cd $rawImageDir
49 |
50 | if [ $(ls *.jpg 2> /dev/null | wc -l) = 0 ];
51 | then
52 | echo "no images";
53 | else
54 |
55 | #
56 | # should we resize images?
57 | #
58 |
59 | if [ "$resizeimageto" != "" ]; then
60 | echo "Resizing images to $resizeimageto px"
61 | mogrify -resize ${resizeimageto}x${resizeimageto}\> *.jpg
62 | fi
63 |
64 | #
65 | # some headers
66 | #
67 |
68 | echo "$varSat | $varDate " > $outHtml
69 | echo "f=${varFreq}Hz, peak: ${varPeak}°, duration: ${varDur}s
" >> $outHtml
70 |
71 | #
72 | # loop over images and generate thumbnails
73 | #
74 |
75 |
76 |
77 | for obrazek in *.jpg
78 | do
79 | echo "Thumb for $obrazek"
80 | base=$(basename $obrazek .jpg)
81 | sizeof=$(du -sh "$obrazek" | cut -f 1)
82 | # generate thumbnail
83 | thumbnail=$(makethumb "$obrazek")
84 | echo $thumbnail
85 | echo " " >> $outHtml
86 | done
87 |
88 |
89 | #
90 | # get image core name
91 | #
92 |
93 | meteorcorename=$(ls *.jpg | head -1 | cut -d "-" -f 1-2)
94 | echo $wwwPath/$meteorcorename > $wwwDir/meteor-last-recording.tmp
95 |
96 | #
97 | # move images to their destination
98 | #
99 |
100 | mv $rawImageDir/* $imgdir/
101 | # cp $rawImageDir/* $imgdir/
102 |
103 |
104 | # ----consolidate data from the given day ------------------------------------#
105 | # generates neither headers nor footer of the html file
106 |
107 | echo "" > $indexHtml.tmp
108 | for htmlfile in $(ls $imgdir/*.html | grep -v "index.html")
109 | do
110 | cat $htmlfile >> $indexHtml.tmp
111 | done
112 |
113 | # ---------- generates pages according to the template file -------------------
114 |
115 | currentDate=$(date)
116 | echo $currentDate
117 |
118 | htmlTitle="METEOR-M2 images | $dateTime"
119 | htmlBody=$(cat $indexHtml.tmp)
120 |
121 | source $htmlTemplate > $indexHtml
122 |
123 | #
124 | # generate static main page(s)
125 | #
126 |
127 | $baseDir/bin/gen-static-page.sh
128 |
129 |
130 | fi # there are images
131 |
--------------------------------------------------------------------------------
/modules/meteor-m2/meteor_record.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ### WARNING: all dates and times must be in the UTC!
4 |
5 | startT=$(date +%H%M -d "$DATE + 1 min" -u)
6 | stopT=$(date +%H%M -d "$DATE + $duration sec" -u)
7 | durationMin=$(bc <<< "$duration/60 +2")
8 |
9 | #
10 | # recording
11 | #
12 | echo "$startT-$stopT, duration: $durationMin min"
13 | mlrpt -s $startT-$stopT -t $durationMin
14 |
--------------------------------------------------------------------------------
/modules/noaa/_loop_noaa_gallery.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # NOAA sat pass gallery preparation
4 | # loop over passes in the given directory
5 |
6 | scriptDir="$(dirname "$(realpath "$0")")"
7 | source $scriptDir/basedir_conf.py
8 | source $baseDir/_listvars.sh
9 |
10 | # fileNameCore="$1"
11 | # satellite="$2"
12 | # start="$3"
13 | # duration="$4"
14 | # peak="$5"
15 | # azimuth="$6"
16 | # freq="$7"
17 |
18 | #imgdir=/home/filips/bin/autowx2/var/www/recordings/noaa/img/2018/11/14/
19 | #fileNameCore="20181114-1818_NOAA-18"
20 | #noaaDir="/home/filips/bin/autowx2/var/www/recordings/noaa/"
21 |
22 | imgdir="$1"
23 | # curdir=$(pwd)
24 |
25 | for logfile in $(ls $imgdir/*.log)
26 | do
27 | fileNameCore=$(basename $logfile .log)
28 | echo $fileNameCore
29 | source $scriptDir/noaa_gallery.sh
30 | done
31 |
--------------------------------------------------------------------------------
/modules/noaa/noaa-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # main file for recording and processing NOAA telementry data
4 | # configuration file: noaa.conf in the current directory
5 |
6 |
7 |
8 | scriptDir="$(dirname "$(realpath "$0")")"
9 |
10 | # read config from the NOAA config file
11 | source $scriptDir/noaa.conf
12 |
13 | #fileNameCore, satellite, start, duration+towait, peak, freq
14 |
15 | fileNameCore="$1"
16 | satellite="$2"
17 | start="$3"
18 | duration="$4"
19 | peak="$5"
20 | azimuth="$6"
21 | freq="$7"
22 |
23 | #-------------------------------#
24 | # to test the processing part:
25 | # comment the recording part and uncomment the following part
26 | # ./noaa.sh tests/20180118-1504_NOAA-19.wav tests/20180118-1504_NOAA-19 NOAA-19 1516284265 926
27 | fileNameCore="20180118-1504_NOAA-19"
28 | satellite="NOAA-19"
29 | start="1516284265"
30 | duration="926"
31 | peak="82"
32 | freq="144"
33 | imgdir="tests/"
34 | recdir="tests/"
35 | #-------------------------------#
36 |
37 |
38 | echo "fileNameCore=$fileNameCore"
39 | echo "satellite=$satellite"
40 | echo "start=$start"
41 | echo "duration=$duration"
42 | echo "peak=$peak"
43 | echo "azimuth=$azimuth"
44 | echo "freq=$freq"
45 |
46 | echo "imgdir=$imgdir"
47 | echo "recdir=$recdir"
48 |
49 |
50 | #
51 | # execute processing script and passing all arguments to the script
52 | #
53 |
54 | source $scriptDir/noaa_process.sh
55 |
--------------------------------------------------------------------------------
/modules/noaa/noaa.conf.example:
--------------------------------------------------------------------------------
1 | #
2 | # NOAA module configuration fine tuning
3 | # usually there is no need to modify anything
4 | #
5 |
6 |
7 | # directory with noaa stuff
8 | noaaDir="$recordingDir/noaa/"
9 |
10 | # directory for generated images
11 | imgdir="$noaaDir/img/"$(date +"%Y/%m/%d/")
12 |
13 | # directory for recorded raw and wav files
14 | recdir="$noaaDir/rec/"$(date +"%Y/%m/%d/")
15 |
16 |
17 | #
18 | # Sample rate, width of recorded signal - should include few kHz for doppler shift
19 | sample='48000'
20 | # Sample rate of the wav file. Shouldn't be changed
21 | wavrate='11025'
22 |
23 | #
24 | # Dongle index, is there any rtl_fm allowing passing serial of dongle?
25 | dongleIndex='0'
26 |
27 | # enchancements to apply to the pocessed images. See wxtoimg manual for available options
28 | enchancements=('MCIR-precip' 'HVC' 'MSA' 'therm' 'HVCT-precip' 'NO')
29 |
30 | # resize images to the given size to avoid growing of the repository; in px
31 | # otherwise, comment out the line
32 | resizeimageto=1024
33 |
--------------------------------------------------------------------------------
/modules/noaa/noaa.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # main file for recording and processing NOAA telementry data
4 | # for configuration, see noaa.conf
5 |
6 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
7 | # do not change the following three lines
8 | scriptDir="$(dirname "$(realpath "$0")")"
9 | source $scriptDir/basedir_conf.py
10 | source $baseDir/_listvars.sh
11 |
12 | #
13 | # read configuration file
14 | #
15 | source $scriptDir/noaa.conf
16 |
17 | ##################################################
18 |
19 | #fileNameCore, satellite, start, duration+towait, peak, freq
20 |
21 | fileNameCore="$1"
22 | satellite="$2"
23 | start="$3"
24 | duration="$4"
25 | peak="$5"
26 | azimuth="$6"
27 | freq="$7"
28 |
29 | echo "fileNameCore=$fileNameCore"
30 | echo "satellite=$satellite"
31 | echo "start=$start"
32 | echo "duration=$duration"
33 | echo "peak=$peak"
34 | echo "azimuth=$azimuth"
35 | echo "freq=$freq"
36 |
37 | echo "sample=$sample"
38 | echo "wavrate=$wavrate"
39 | echo "dongleIndex=$dongleIndex"
40 | echo "enchancements=${enchancements}"
41 |
42 | #-------------------------------#
43 | # to test the processing part:
44 | # comment the recording part and uncomment the following part
45 | # ./noaa.sh tests/20180118-1504_NOAA-19.wav tests/20180118-1504_NOAA-19 NOAA-19 1516284265 926
46 | # fileNameCore="20180118-1504_NOAA-19"
47 | # satellite="NOAA-19"
48 | # start="1516284265"
49 | # duration="926"
50 | # peak="82"
51 | # freq="144"
52 | # imgdir="tests/"
53 | # recdir="tests/"
54 | #-------------------------------#
55 |
56 | #
57 | # create directories
58 | #
59 |
60 | mkdir -p $imgdir
61 | mkdir -p $recdir
62 |
63 | #
64 | # logs are very important!
65 | #
66 | logFile=$imgdir/$fileNameCore.log
67 |
68 | date >$logFile # initialize log file
69 | echo $fileNameCore >>$logFile
70 | echo $satellite >>$logFile
71 | echo $start >>$logFile
72 | echo $duration >>$logFile
73 | echo $peak >>$logFile
74 | echo $freq >>$logFile
75 |
76 | #
77 | # execute recordigng scriptDir and passing all arguments to the script
78 | #
79 |
80 | source $scriptDir/noaa_record.sh
81 |
82 | #
83 | # execute processing script and passing all arguments to the script
84 | #
85 |
86 | source $scriptDir/noaa_process.sh
87 |
88 | #
89 | # generate gallery for a given pass
90 | #
91 |
92 | source $scriptDir/noaa_gallery.sh
93 |
94 | #
95 | # generate static pages
96 | #
97 |
98 | $baseDir/bin/gen-static-page.sh
99 |
--------------------------------------------------------------------------------
/modules/noaa/noaa_gallery.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # NOAA sat pass gallery preparation
4 | # generates a single html snippet with a current pass
5 |
6 | # fileNameCore="$1"
7 | # satellite="$2"
8 | # start="$3"
9 | # duration="$4"
10 | # peak="$5"
11 | # azimuth="$6"
12 | # freq="$7"
13 |
14 | # static values for tests
15 | # imgdir=/home/filips/bin/autowx2/var/www/recordings/noaa/img/2018/09/08/
16 | #imgdir=/home/filips/github/autowx2/var/www/recordings/noaa/img/2018/09/08/
17 | #fileNameCore="20181114-1818_NOAA-18"
18 | #fileNameCore=20180908-1626_NOAA-19
19 | #wwwDir="/home/filips/github/autowx2/var/www/"
20 | #wwwRootPath='file:///home/filips/github/autowx2/var/www/'
21 |
22 |
23 | #
24 | # config file
25 | #
26 | source $scriptDir/noaa.conf
27 |
28 | # prorgam itself - variables
29 | outHtml="$imgdir/$fileNameCore.html" # html for this single pass
30 | indexHtml="$imgdir/index.html" # main index file for a given day
31 | htmlTemplate="$wwwDir/index.tpl"
32 |
33 |
34 | # ---single gallery preparation------------------------------------------------#
35 |
36 | makethumb() {
37 | obrazek="$1"
38 | local thumbnail=$(basename "$obrazek" .jpg)".th.jpg"
39 | convert -define jpeg:size=200x200 "$obrazek" -thumbnail '200x200^' granite: +swap -gravity center -extent 200x200 -composite -quality 82 "$thumbnail"
40 | echo "$thumbnail"
41 | }
42 |
43 | # -----------------------------------------------------------------------------#
44 |
45 | logFile="$imgdir/$fileNameCore.log" # log file to read from
46 |
47 | varDate=$(sed '1q;d' $logFile)
48 | varSat=$(sed '3q;d' $logFile)
49 | varStart=$(sed '4q;d' $logFile) # unused
50 | varDur=$(sed '5q;d' $logFile)
51 | varPeak=$(sed '6q;d' $logFile)
52 | varFreq=$(sed '7q;d' $logFile)
53 |
54 | dateTime=$(date -d @$varStart +"%Y-%m-%d")
55 | dateTimeDir=$(date -d @$varStart +"%Y/%m/%d") # directory format of date, eg. 2018/11/22/
56 | wwwPath=$wwwRootPath/recordings/noaa/img/$dateTimeDir
57 |
58 | echo $wwwPath/$fileNameCore > $wwwDir/noaa-last-recording.tmp
59 |
60 | cd $imgdir
61 |
62 | echo "$varSat | $varDate " > $outHtml
63 | echo "f=${varFreq}Hz, peak: ${varPeak}°, duration: ${varDur}s
" >> $outHtml
64 |
65 | for enchancement in "${enchancements[@]}"
66 | do
67 | echo "**** $enchancement"
68 | obrazek="$fileNameCore-$enchancement+map.jpg"
69 | sizeof=$(du -sh "$obrazek" | cut -f 1)
70 | # generate thumbnail
71 | thumbnail=$(makethumb "$obrazek")
72 | echo " " >> $outHtml
73 | done
74 |
75 | thumbnail=$(makethumb "$fileNameCore-spectrogram.jpg")
76 | echo " " >> $outHtml
77 |
78 |
79 |
80 | # ----consolidate data from the given day ------------------------------------#
81 | # generates neither headers nor footer of the html file
82 |
83 | echo "" > $indexHtml.tmp
84 | for htmlfile in $(ls $imgdir/*.html | grep -v "index.html")
85 | do
86 | cat $htmlfile >> $indexHtml.tmp
87 | done
88 |
89 | # ---------- generates pages according to the template file -------------------
90 |
91 | currentDate=$(date)
92 | echo $currentDate
93 |
94 | htmlTitle="NOAA images | $dateTime"
95 | htmlBody=$(cat $indexHtml.tmp)
96 |
97 | source $htmlTemplate > $indexHtml
98 |
--------------------------------------------------------------------------------
/modules/noaa/noaa_process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # file to process NOAA wav file to produce weather images
4 | # all variables are provided by noaa.sh
5 |
6 |
7 |
8 | #
9 | # generate map
10 | #
11 | wxmap -T "$satellite" -a -H $tleFileName -o -O $duration -L "$latlonalt" $start $imgdir/$fileNameCore-mapa.png | tee -a $logFile
12 |
13 | #
14 | # should we resize images?
15 | #
16 |
17 | if [ "$resizeimageto" != "" ]; then
18 | echo "Resizing images to $resizeimageto px"
19 | resizeSwitch="-resize ${resizeimageto}x${resizeimageto}>"
20 | fi
21 |
22 | #
23 | # process wav file with various enchancements
24 | #
25 |
26 | for enchancement in "${enchancements[@]}"
27 | do
28 | echo "**** $enchancement"
29 | # wxtoimg -e $enchancement $recdir/$fileNameCore.wav $imgdir/$fileNameCore-${enchancement}.png | tee -a $logFile
30 | wxtoimg -e $enchancement -m $imgdir/$fileNameCore-mapa.png $recdir/$fileNameCore.wav $imgdir/$fileNameCore-${enchancement}+map.png | tee -a $logFile
31 | convert -quality 91 $resizeSwitch $imgdir/$fileNameCore-${enchancement}+map.png $imgdir/$fileNameCore-${enchancement}+map.jpg
32 | rm $imgdir/$fileNameCore-${enchancement}+map.png
33 | done
34 |
35 | sox $recdir/$fileNameCore.wav -n spectrogram -o $imgdir/$fileNameCore-spectrogram.png
36 | convert -quality 90 $imgdir/$fileNameCore-spectrogram.png $imgdir/$fileNameCore-spectrogram.jpg
37 |
38 | rm $imgdir/$fileNameCore-mapa.png
39 | rm $imgdir/$fileNameCore-spectrogram.png
40 | rm $recdir/$fileNameCore.wav
41 |
--------------------------------------------------------------------------------
/modules/noaa/noaa_record.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # file to record NOAA satellites via rtl_fm
4 | # all variables are provided by noaa.sh
5 |
6 |
7 | #
8 | # recording here
9 | #
10 |
11 | timeout $duration rtl_fm $biast -f $freq -s $sample -g $dongleGain -F 9 -A fast -E offset -p $dongleShift $recdir/$fileNameCore.raw | tee -a $logFile
12 |
13 | #
14 | # transcoding here
15 | #
16 |
17 | sox -t raw -r $sample -es -b 16 -c 1 -V1 $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav rate $wavrate | tee -a $logFile
18 | touch -r $recdir/$fileNameCore.raw $recdir/$fileNameCore.wav
19 | rm $recdir/$fileNameCore.raw
20 |
--------------------------------------------------------------------------------
/modules/noaa/tests/20180118-1504_NOAA-19.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filipsPL/autowx2/c91722c64afb2f018d0e6fcc0f7398aaaa2edfad/modules/noaa/tests/20180118-1504_NOAA-19.wav
--------------------------------------------------------------------------------
/modules/radiosonde/run_radiosonde_scanner.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # RUN radiosonde scanner for a given time
4 | # details, see: https://github.com/projecthorus/radiosonde_auto_rx/wiki
5 |
6 |
7 | # read the global configuration file autowx2_conf.py via the bash/python configuration parser
8 | # do not change the following three lines
9 | scriptDir="$(dirname "$(realpath "$0")")"
10 | source $scriptDir/basedir_conf.py
11 | source $baseDir/_listvars.sh
12 |
13 |
14 | #### microconfiguration
15 |
16 | radiosondeBin="/home/filips/progs/radiosonde_auto_rx/auto_rx"
17 | logfile="$recordingDir/radiosonde/log/$(date +"%Y%m%d").txt"
18 | mkdir -p "$recordingDir/radiosonde/log/"
19 |
20 | ### doing the job
21 |
22 |
23 | fileNameCore="$1"
24 | satellite="$2"
25 | start="$3"
26 | duration="$4"
27 | peak="$5"
28 | azimuth="$6"
29 | freq="$7"
30 |
31 | echo "fileNameCore=$fileNameCore"
32 | echo "satellite=$satellite"
33 | echo "start=$start"
34 | echo "duration=$duration"
35 | echo "peak=$peak"
36 | echo "azimuth=$azimuth"
37 | echo "freq=$freq"
38 |
39 | cd $radiosondeBin
40 | timeout $duration python auto_rx.py | tee -a $logfile
41 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pypredict
2 | matplotlib
3 | numpy
4 | Flask
5 | "Flask-SocketIO<=5.2.0"
6 |
--------------------------------------------------------------------------------
/var/flask/static/fancybox/jquery.fancybox.min.css:
--------------------------------------------------------------------------------
1 | body.compensate-for-scrollbar{overflow:hidden}.fancybox-active{height:auto}.fancybox-is-hidden{left:-9999px;margin:0;position:absolute!important;top:-9999px;visibility:hidden}.fancybox-container{-webkit-backface-visibility:hidden;height:100%;left:0;outline:none;position:fixed;-webkit-tap-highlight-color:transparent;top:0;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-transform:translateZ(0);transform:translateZ(0);width:100%;z-index:99992}.fancybox-container *{box-sizing:border-box}.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-stage{bottom:0;left:0;position:absolute;right:0;top:0}.fancybox-outer{-webkit-overflow-scrolling:touch;overflow-y:auto}.fancybox-bg{background:#1e1e1e;opacity:0;transition-duration:inherit;transition-property:opacity;transition-timing-function:cubic-bezier(.47,0,.74,.71)}.fancybox-is-open .fancybox-bg{opacity:.9;transition-timing-function:cubic-bezier(.22,.61,.36,1)}.fancybox-caption,.fancybox-infobar,.fancybox-navigation .fancybox-button,.fancybox-toolbar{direction:ltr;opacity:0;position:absolute;transition:opacity .25s ease,visibility 0s ease .25s;visibility:hidden;z-index:99997}.fancybox-show-caption .fancybox-caption,.fancybox-show-infobar .fancybox-infobar,.fancybox-show-nav .fancybox-navigation .fancybox-button,.fancybox-show-toolbar .fancybox-toolbar{opacity:1;transition:opacity .25s ease 0s,visibility 0s ease 0s;visibility:visible}.fancybox-infobar{color:#ccc;font-size:13px;-webkit-font-smoothing:subpixel-antialiased;height:44px;left:0;line-height:44px;min-width:44px;mix-blend-mode:difference;padding:0 10px;pointer-events:none;top:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fancybox-toolbar{right:0;top:0}.fancybox-stage{direction:ltr;overflow:visible;-webkit-transform:translateZ(0);transform:translateZ(0);z-index:99994}.fancybox-is-open .fancybox-stage{overflow:hidden}.fancybox-slide{-webkit-backface-visibility:hidden;display:none;height:100%;left:0;outline:none;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:absolute;text-align:center;top:0;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;white-space:normal;width:100%;z-index:99994}.fancybox-slide:before{content:"";display:inline-block;font-size:0;height:100%;vertical-align:middle;width:0}.fancybox-is-sliding .fancybox-slide,.fancybox-slide--current,.fancybox-slide--next,.fancybox-slide--previous{display:block}.fancybox-slide--image{overflow:hidden;padding:44px 0}.fancybox-slide--image:before{display:none}.fancybox-slide--html{padding:6px}.fancybox-content{background:#fff;display:inline-block;margin:0;max-width:100%;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:relative;text-align:left;vertical-align:middle}.fancybox-slide--image .fancybox-content{-webkit-animation-timing-function:cubic-bezier(.5,0,.14,1);animation-timing-function:cubic-bezier(.5,0,.14,1);-webkit-backface-visibility:hidden;background:transparent;background-repeat:no-repeat;background-size:100% 100%;left:0;max-width:none;overflow:visible;padding:0;position:absolute;top:0;-webkit-transform-origin:top left;transform-origin:top left;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:99995}.fancybox-can-zoomOut .fancybox-content{cursor:zoom-out}.fancybox-can-zoomIn .fancybox-content{cursor:zoom-in}.fancybox-can-pan .fancybox-content,.fancybox-can-swipe .fancybox-content{cursor:-webkit-grab;cursor:grab}.fancybox-is-grabbing .fancybox-content{cursor:-webkit-grabbing;cursor:grabbing}.fancybox-container [data-selectable=true]{cursor:text}.fancybox-image,.fancybox-spaceball{background:transparent;border:0;height:100%;left:0;margin:0;max-height:none;max-width:none;padding:0;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.fancybox-spaceball{z-index:1}.fancybox-slide--iframe .fancybox-content,.fancybox-slide--map .fancybox-content,.fancybox-slide--pdf .fancybox-content,.fancybox-slide--video .fancybox-content{height:100%;overflow:visible;padding:0;width:100%}.fancybox-slide--video .fancybox-content{background:#000}.fancybox-slide--map .fancybox-content{background:#e5e3df}.fancybox-slide--iframe .fancybox-content{background:#fff}.fancybox-iframe,.fancybox-video{background:transparent;border:0;display:block;height:100%;margin:0;overflow:hidden;padding:0;width:100%}.fancybox-iframe{left:0;position:absolute;top:0}.fancybox-error{background:#fff;cursor:default;max-width:400px;padding:40px;width:100%}.fancybox-error p{color:#444;font-size:16px;line-height:20px;margin:0;padding:0}.fancybox-button{background:rgba(30,30,30,.6);border:0;border-radius:0;box-shadow:none;cursor:pointer;display:inline-block;height:44px;margin:0;padding:10px;position:relative;transition:color .2s;vertical-align:top;visibility:inherit;width:44px}.fancybox-button,.fancybox-button:link,.fancybox-button:visited{color:#ccc}.fancybox-button:hover{color:#fff}.fancybox-button:focus{outline:none}.fancybox-button.fancybox-focus{outline:1px dotted}.fancybox-button[disabled],.fancybox-button[disabled]:hover{color:#888;cursor:default;outline:none}.fancybox-button div{height:100%}.fancybox-button svg{display:block;height:100%;overflow:visible;position:relative;width:100%}.fancybox-button svg path{fill:currentColor;stroke-width:0}.fancybox-button--fsenter svg:nth-child(2),.fancybox-button--fsexit svg:nth-child(1),.fancybox-button--pause svg:nth-child(1),.fancybox-button--play svg:nth-child(2){display:none}.fancybox-progress{background:#ff5268;height:2px;left:0;position:absolute;right:0;top:0;-webkit-transform:scaleX(0);transform:scaleX(0);-webkit-transform-origin:0;transform-origin:0;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;transition-timing-function:linear;z-index:99998}.fancybox-close-small{background:transparent;border:0;border-radius:0;color:#ccc;cursor:pointer;opacity:.8;padding:8px;position:absolute;right:-12px;top:-44px;z-index:401}.fancybox-close-small:hover{color:#fff;opacity:1}.fancybox-slide--html .fancybox-close-small{color:currentColor;padding:10px;right:0;top:0}.fancybox-slide--image.fancybox-is-scaling .fancybox-content{overflow:hidden}.fancybox-is-scaling .fancybox-close-small,.fancybox-is-zoomable.fancybox-can-pan .fancybox-close-small{display:none}.fancybox-navigation .fancybox-button{background-clip:content-box;height:100px;opacity:0;position:absolute;top:calc(50% - 50px);width:70px}.fancybox-navigation .fancybox-button div{padding:7px}.fancybox-navigation .fancybox-button--arrow_left{left:0;padding:31px 26px 31px 6px}.fancybox-navigation .fancybox-button--arrow_right{padding:31px 6px 31px 26px;right:0}.fancybox-caption{bottom:0;color:#eee;font-size:14px;font-weight:400;left:0;line-height:1.5;padding:25px 44px;right:0;text-align:center;z-index:99996}.fancybox-caption:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAD6CAQAAADKSeXYAAAAYklEQVQoz42RwQ3AMAgDjfcfup8WoRykfBAK5mQHKSz5rbXJPis1hjiV3CIqgG0hLZPkVkA4p4x5oR1bVeDrdCLrW2Q0D5bcwY3TGMHbdw3mPRuOtaspYP1w//G1OIcW148H0DMCqI/3mMMAAAAASUVORK5CYII=);background-repeat:repeat-x;background-size:contain;bottom:0;content:"";display:block;left:0;pointer-events:none;position:absolute;right:0;top:-44px;z-index:-1}.fancybox-caption a,.fancybox-caption a:link,.fancybox-caption a:visited{color:#ccc;text-decoration:none}.fancybox-caption a:hover{color:#fff;text-decoration:underline}.fancybox-loading{-webkit-animation:a 1s linear infinite;animation:a 1s linear infinite;background:transparent;border:4px solid #888;border-bottom-color:#fff;border-radius:50%;height:50px;left:50%;margin:-25px 0 0 -25px;opacity:.7;padding:0;position:absolute;top:50%;width:50px;z-index:99999}@-webkit-keyframes a{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes a{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fancybox-animated{transition-timing-function:cubic-bezier(0,0,.25,1)}.fancybox-fx-slide.fancybox-slide--previous{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.fancybox-fx-slide.fancybox-slide--next{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.fancybox-fx-slide.fancybox-slide--current{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}.fancybox-fx-fade.fancybox-slide--next,.fancybox-fx-fade.fancybox-slide--previous{opacity:0;transition-timing-function:cubic-bezier(.19,1,.22,1)}.fancybox-fx-fade.fancybox-slide--current{opacity:1}.fancybox-fx-zoom-in-out.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(1.5,1.5,1.5);transform:scale3d(1.5,1.5,1.5)}.fancybox-fx-zoom-in-out.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(.5,.5,.5);transform:scale3d(.5,.5,.5)}.fancybox-fx-zoom-in-out.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}.fancybox-fx-rotate.fancybox-slide--previous{opacity:0;-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}.fancybox-fx-rotate.fancybox-slide--next{opacity:0;-webkit-transform:rotate(1turn);transform:rotate(1turn)}.fancybox-fx-rotate.fancybox-slide--current{opacity:1;-webkit-transform:rotate(0deg);transform:rotate(0deg)}.fancybox-fx-circular.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(-100%,0,0);transform:scale3d(0,0,0) translate3d(-100%,0,0)}.fancybox-fx-circular.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(100%,0,0);transform:scale3d(0,0,0) translate3d(100%,0,0)}.fancybox-fx-circular.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1) translateZ(0);transform:scaleX(1) translateZ(0)}.fancybox-fx-tube.fancybox-slide--previous{-webkit-transform:translate3d(-100%,0,0) scale(.1) skew(-10deg);transform:translate3d(-100%,0,0) scale(.1) skew(-10deg)}.fancybox-fx-tube.fancybox-slide--next{-webkit-transform:translate3d(100%,0,0) scale(.1) skew(10deg);transform:translate3d(100%,0,0) scale(.1) skew(10deg)}.fancybox-fx-tube.fancybox-slide--current{-webkit-transform:translateZ(0) scale(1);transform:translateZ(0) scale(1)}@media (max-height:576px){.fancybox-caption{padding:12px}.fancybox-slide{padding-left:6px;padding-right:6px}.fancybox-slide--image{padding:6px 0}.fancybox-close-small{right:-6px}.fancybox-slide--image .fancybox-close-small{background:#4e4e4e;color:#f2f4f6;height:36px;opacity:1;padding:6px;right:0;top:0;width:36px}}.fancybox-share{background:#f4f4f4;border-radius:3px;max-width:90%;padding:30px;text-align:center}.fancybox-share h1{color:#222;font-size:35px;font-weight:700;margin:0 0 20px}.fancybox-share p{margin:0;padding:0}.fancybox-share__button{border:0;border-radius:3px;display:inline-block;font-size:14px;font-weight:700;line-height:40px;margin:0 5px 10px;min-width:130px;padding:0 15px;text-decoration:none;transition:all .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}.fancybox-share__button:link,.fancybox-share__button:visited{color:#fff}.fancybox-share__button:hover{text-decoration:none}.fancybox-share__button--fb{background:#3b5998}.fancybox-share__button--fb:hover{background:#344e86}.fancybox-share__button--pt{background:#bd081d}.fancybox-share__button--pt:hover{background:#aa0719}.fancybox-share__button--tw{background:#1da1f2}.fancybox-share__button--tw:hover{background:#0d95e8}.fancybox-share__button svg{height:25px;margin-right:7px;position:relative;top:-1px;vertical-align:middle;width:25px}.fancybox-share__button svg path{fill:#fff}.fancybox-share__input{background:transparent;border:0;border-bottom:1px solid #d7d7d7;border-radius:0;color:#5d5b5b;font-size:14px;margin:10px 0 0;outline:none;padding:10px 15px;width:100%}.fancybox-thumbs{background:#ddd;bottom:0;display:none;margin:0;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;padding:2px 2px 4px;position:absolute;right:0;-webkit-tap-highlight-color:transparent;top:0;width:212px;z-index:99995}.fancybox-thumbs-x{overflow-x:auto;overflow-y:hidden}.fancybox-show-thumbs .fancybox-thumbs{display:block}.fancybox-show-thumbs .fancybox-inner{right:212px}.fancybox-thumbs__list{font-size:0;height:100%;list-style:none;margin:0;overflow-x:hidden;overflow-y:auto;padding:0;position:absolute;position:relative;white-space:nowrap;width:100%}.fancybox-thumbs-x .fancybox-thumbs__list{overflow:hidden}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar{width:7px}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar-track{background:#fff;border-radius:10px;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar-thumb{background:#2a2a2a;border-radius:10px}.fancybox-thumbs__list a{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:rgba(0,0,0,.1);background-position:50%;background-repeat:no-repeat;background-size:cover;cursor:pointer;float:left;height:75px;margin:2px;max-height:calc(100% - 8px);max-width:calc(50% - 4px);outline:none;overflow:hidden;padding:0;position:relative;-webkit-tap-highlight-color:transparent;width:100px}.fancybox-thumbs__list a:before{border:6px solid #ff5268;bottom:0;content:"";left:0;opacity:0;position:absolute;right:0;top:0;transition:all .2s cubic-bezier(.25,.46,.45,.94);z-index:99991}.fancybox-thumbs__list a:focus:before{opacity:.5}.fancybox-thumbs__list a.fancybox-thumbs-active:before{opacity:1}@media (max-width:576px){.fancybox-thumbs{width:110px}.fancybox-show-thumbs .fancybox-inner{right:110px}.fancybox-thumbs__list a{max-width:calc(100% - 10px)}}
--------------------------------------------------------------------------------
/var/flask/static/fancybox/source.md:
--------------------------------------------------------------------------------
1 | https://github.com/fancyapps/fancyBox
2 |
--------------------------------------------------------------------------------
/var/flask/static/logo-nav.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Start Bootstrap - Logo Nav (https://startbootstrap.com/template-overviews/logo-nav)
3 | * Copyright 2013-2017 Start Bootstrap
4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-logo-nav/blob/master/LICENSE)
5 | */
6 |
7 | body {
8 | padding-top: 94px;
9 | }
10 |
11 | @media (min-width: 992px) {
12 | body {
13 | padding-top: 76px;
14 | }
15 | }
16 |
17 |
18 | h2 {
19 | padding-top: 16px;
20 | }
21 |
22 | img { padding: 10px;}
23 |
24 | h2:after
25 | {
26 | content:' ';
27 | display:block;
28 | border:1px dotted silver;
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/var/flask/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | autowx2 automated satellite receiving station :: {{title}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
81 |
82 |
83 |
84 |
85 |
{{title}}
86 |
87 | {% autoescape false %}
88 | {{ body }}
89 | {% endautoescape %}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/var/www/css/fancybox/jquery.fancybox.min.css:
--------------------------------------------------------------------------------
1 | body.compensate-for-scrollbar{overflow:hidden}.fancybox-active{height:auto}.fancybox-is-hidden{left:-9999px;margin:0;position:absolute!important;top:-9999px;visibility:hidden}.fancybox-container{-webkit-backface-visibility:hidden;height:100%;left:0;outline:none;position:fixed;-webkit-tap-highlight-color:transparent;top:0;-ms-touch-action:manipulation;touch-action:manipulation;-webkit-transform:translateZ(0);transform:translateZ(0);width:100%;z-index:99992}.fancybox-container *{box-sizing:border-box}.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-stage{bottom:0;left:0;position:absolute;right:0;top:0}.fancybox-outer{-webkit-overflow-scrolling:touch;overflow-y:auto}.fancybox-bg{background:#1e1e1e;opacity:0;transition-duration:inherit;transition-property:opacity;transition-timing-function:cubic-bezier(.47,0,.74,.71)}.fancybox-is-open .fancybox-bg{opacity:.9;transition-timing-function:cubic-bezier(.22,.61,.36,1)}.fancybox-caption,.fancybox-infobar,.fancybox-navigation .fancybox-button,.fancybox-toolbar{direction:ltr;opacity:0;position:absolute;transition:opacity .25s ease,visibility 0s ease .25s;visibility:hidden;z-index:99997}.fancybox-show-caption .fancybox-caption,.fancybox-show-infobar .fancybox-infobar,.fancybox-show-nav .fancybox-navigation .fancybox-button,.fancybox-show-toolbar .fancybox-toolbar{opacity:1;transition:opacity .25s ease 0s,visibility 0s ease 0s;visibility:visible}.fancybox-infobar{color:#ccc;font-size:13px;-webkit-font-smoothing:subpixel-antialiased;height:44px;left:0;line-height:44px;min-width:44px;mix-blend-mode:difference;padding:0 10px;pointer-events:none;top:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fancybox-toolbar{right:0;top:0}.fancybox-stage{direction:ltr;overflow:visible;-webkit-transform:translateZ(0);transform:translateZ(0);z-index:99994}.fancybox-is-open .fancybox-stage{overflow:hidden}.fancybox-slide{-webkit-backface-visibility:hidden;display:none;height:100%;left:0;outline:none;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:absolute;text-align:center;top:0;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;white-space:normal;width:100%;z-index:99994}.fancybox-slide:before{content:"";display:inline-block;font-size:0;height:100%;vertical-align:middle;width:0}.fancybox-is-sliding .fancybox-slide,.fancybox-slide--current,.fancybox-slide--next,.fancybox-slide--previous{display:block}.fancybox-slide--image{overflow:hidden;padding:44px 0}.fancybox-slide--image:before{display:none}.fancybox-slide--html{padding:6px}.fancybox-content{background:#fff;display:inline-block;margin:0;max-width:100%;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:relative;text-align:left;vertical-align:middle}.fancybox-slide--image .fancybox-content{-webkit-animation-timing-function:cubic-bezier(.5,0,.14,1);animation-timing-function:cubic-bezier(.5,0,.14,1);-webkit-backface-visibility:hidden;background:transparent;background-repeat:no-repeat;background-size:100% 100%;left:0;max-width:none;overflow:visible;padding:0;position:absolute;top:0;-webkit-transform-origin:top left;transform-origin:top left;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:99995}.fancybox-can-zoomOut .fancybox-content{cursor:zoom-out}.fancybox-can-zoomIn .fancybox-content{cursor:zoom-in}.fancybox-can-pan .fancybox-content,.fancybox-can-swipe .fancybox-content{cursor:-webkit-grab;cursor:grab}.fancybox-is-grabbing .fancybox-content{cursor:-webkit-grabbing;cursor:grabbing}.fancybox-container [data-selectable=true]{cursor:text}.fancybox-image,.fancybox-spaceball{background:transparent;border:0;height:100%;left:0;margin:0;max-height:none;max-width:none;padding:0;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.fancybox-spaceball{z-index:1}.fancybox-slide--iframe .fancybox-content,.fancybox-slide--map .fancybox-content,.fancybox-slide--pdf .fancybox-content,.fancybox-slide--video .fancybox-content{height:100%;overflow:visible;padding:0;width:100%}.fancybox-slide--video .fancybox-content{background:#000}.fancybox-slide--map .fancybox-content{background:#e5e3df}.fancybox-slide--iframe .fancybox-content{background:#fff}.fancybox-iframe,.fancybox-video{background:transparent;border:0;display:block;height:100%;margin:0;overflow:hidden;padding:0;width:100%}.fancybox-iframe{left:0;position:absolute;top:0}.fancybox-error{background:#fff;cursor:default;max-width:400px;padding:40px;width:100%}.fancybox-error p{color:#444;font-size:16px;line-height:20px;margin:0;padding:0}.fancybox-button{background:rgba(30,30,30,.6);border:0;border-radius:0;box-shadow:none;cursor:pointer;display:inline-block;height:44px;margin:0;padding:10px;position:relative;transition:color .2s;vertical-align:top;visibility:inherit;width:44px}.fancybox-button,.fancybox-button:link,.fancybox-button:visited{color:#ccc}.fancybox-button:hover{color:#fff}.fancybox-button:focus{outline:none}.fancybox-button.fancybox-focus{outline:1px dotted}.fancybox-button[disabled],.fancybox-button[disabled]:hover{color:#888;cursor:default;outline:none}.fancybox-button div{height:100%}.fancybox-button svg{display:block;height:100%;overflow:visible;position:relative;width:100%}.fancybox-button svg path{fill:currentColor;stroke-width:0}.fancybox-button--fsenter svg:nth-child(2),.fancybox-button--fsexit svg:nth-child(1),.fancybox-button--pause svg:nth-child(1),.fancybox-button--play svg:nth-child(2){display:none}.fancybox-progress{background:#ff5268;height:2px;left:0;position:absolute;right:0;top:0;-webkit-transform:scaleX(0);transform:scaleX(0);-webkit-transform-origin:0;transform-origin:0;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;transition-timing-function:linear;z-index:99998}.fancybox-close-small{background:transparent;border:0;border-radius:0;color:#ccc;cursor:pointer;opacity:.8;padding:8px;position:absolute;right:-12px;top:-44px;z-index:401}.fancybox-close-small:hover{color:#fff;opacity:1}.fancybox-slide--html .fancybox-close-small{color:currentColor;padding:10px;right:0;top:0}.fancybox-slide--image.fancybox-is-scaling .fancybox-content{overflow:hidden}.fancybox-is-scaling .fancybox-close-small,.fancybox-is-zoomable.fancybox-can-pan .fancybox-close-small{display:none}.fancybox-navigation .fancybox-button{background-clip:content-box;height:100px;opacity:0;position:absolute;top:calc(50% - 50px);width:70px}.fancybox-navigation .fancybox-button div{padding:7px}.fancybox-navigation .fancybox-button--arrow_left{left:0;padding:31px 26px 31px 6px}.fancybox-navigation .fancybox-button--arrow_right{padding:31px 6px 31px 26px;right:0}.fancybox-caption{bottom:0;color:#eee;font-size:14px;font-weight:400;left:0;line-height:1.5;padding:25px 44px;right:0;text-align:center;z-index:99996}.fancybox-caption:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAD6CAQAAADKSeXYAAAAYklEQVQoz42RwQ3AMAgDjfcfup8WoRykfBAK5mQHKSz5rbXJPis1hjiV3CIqgG0hLZPkVkA4p4x5oR1bVeDrdCLrW2Q0D5bcwY3TGMHbdw3mPRuOtaspYP1w//G1OIcW148H0DMCqI/3mMMAAAAASUVORK5CYII=);background-repeat:repeat-x;background-size:contain;bottom:0;content:"";display:block;left:0;pointer-events:none;position:absolute;right:0;top:-44px;z-index:-1}.fancybox-caption a,.fancybox-caption a:link,.fancybox-caption a:visited{color:#ccc;text-decoration:none}.fancybox-caption a:hover{color:#fff;text-decoration:underline}.fancybox-loading{-webkit-animation:a 1s linear infinite;animation:a 1s linear infinite;background:transparent;border:4px solid #888;border-bottom-color:#fff;border-radius:50%;height:50px;left:50%;margin:-25px 0 0 -25px;opacity:.7;padding:0;position:absolute;top:50%;width:50px;z-index:99999}@-webkit-keyframes a{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes a{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fancybox-animated{transition-timing-function:cubic-bezier(0,0,.25,1)}.fancybox-fx-slide.fancybox-slide--previous{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.fancybox-fx-slide.fancybox-slide--next{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.fancybox-fx-slide.fancybox-slide--current{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}.fancybox-fx-fade.fancybox-slide--next,.fancybox-fx-fade.fancybox-slide--previous{opacity:0;transition-timing-function:cubic-bezier(.19,1,.22,1)}.fancybox-fx-fade.fancybox-slide--current{opacity:1}.fancybox-fx-zoom-in-out.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(1.5,1.5,1.5);transform:scale3d(1.5,1.5,1.5)}.fancybox-fx-zoom-in-out.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(.5,.5,.5);transform:scale3d(.5,.5,.5)}.fancybox-fx-zoom-in-out.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}.fancybox-fx-rotate.fancybox-slide--previous{opacity:0;-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}.fancybox-fx-rotate.fancybox-slide--next{opacity:0;-webkit-transform:rotate(1turn);transform:rotate(1turn)}.fancybox-fx-rotate.fancybox-slide--current{opacity:1;-webkit-transform:rotate(0deg);transform:rotate(0deg)}.fancybox-fx-circular.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(-100%,0,0);transform:scale3d(0,0,0) translate3d(-100%,0,0)}.fancybox-fx-circular.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(100%,0,0);transform:scale3d(0,0,0) translate3d(100%,0,0)}.fancybox-fx-circular.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1) translateZ(0);transform:scaleX(1) translateZ(0)}.fancybox-fx-tube.fancybox-slide--previous{-webkit-transform:translate3d(-100%,0,0) scale(.1) skew(-10deg);transform:translate3d(-100%,0,0) scale(.1) skew(-10deg)}.fancybox-fx-tube.fancybox-slide--next{-webkit-transform:translate3d(100%,0,0) scale(.1) skew(10deg);transform:translate3d(100%,0,0) scale(.1) skew(10deg)}.fancybox-fx-tube.fancybox-slide--current{-webkit-transform:translateZ(0) scale(1);transform:translateZ(0) scale(1)}@media (max-height:576px){.fancybox-caption{padding:12px}.fancybox-slide{padding-left:6px;padding-right:6px}.fancybox-slide--image{padding:6px 0}.fancybox-close-small{right:-6px}.fancybox-slide--image .fancybox-close-small{background:#4e4e4e;color:#f2f4f6;height:36px;opacity:1;padding:6px;right:0;top:0;width:36px}}.fancybox-share{background:#f4f4f4;border-radius:3px;max-width:90%;padding:30px;text-align:center}.fancybox-share h1{color:#222;font-size:35px;font-weight:700;margin:0 0 20px}.fancybox-share p{margin:0;padding:0}.fancybox-share__button{border:0;border-radius:3px;display:inline-block;font-size:14px;font-weight:700;line-height:40px;margin:0 5px 10px;min-width:130px;padding:0 15px;text-decoration:none;transition:all .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}.fancybox-share__button:link,.fancybox-share__button:visited{color:#fff}.fancybox-share__button:hover{text-decoration:none}.fancybox-share__button--fb{background:#3b5998}.fancybox-share__button--fb:hover{background:#344e86}.fancybox-share__button--pt{background:#bd081d}.fancybox-share__button--pt:hover{background:#aa0719}.fancybox-share__button--tw{background:#1da1f2}.fancybox-share__button--tw:hover{background:#0d95e8}.fancybox-share__button svg{height:25px;margin-right:7px;position:relative;top:-1px;vertical-align:middle;width:25px}.fancybox-share__button svg path{fill:#fff}.fancybox-share__input{background:transparent;border:0;border-bottom:1px solid #d7d7d7;border-radius:0;color:#5d5b5b;font-size:14px;margin:10px 0 0;outline:none;padding:10px 15px;width:100%}.fancybox-thumbs{background:#ddd;bottom:0;display:none;margin:0;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;padding:2px 2px 4px;position:absolute;right:0;-webkit-tap-highlight-color:transparent;top:0;width:212px;z-index:99995}.fancybox-thumbs-x{overflow-x:auto;overflow-y:hidden}.fancybox-show-thumbs .fancybox-thumbs{display:block}.fancybox-show-thumbs .fancybox-inner{right:212px}.fancybox-thumbs__list{font-size:0;height:100%;list-style:none;margin:0;overflow-x:hidden;overflow-y:auto;padding:0;position:absolute;position:relative;white-space:nowrap;width:100%}.fancybox-thumbs-x .fancybox-thumbs__list{overflow:hidden}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar{width:7px}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar-track{background:#fff;border-radius:10px;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}.fancybox-thumbs-y .fancybox-thumbs__list::-webkit-scrollbar-thumb{background:#2a2a2a;border-radius:10px}.fancybox-thumbs__list a{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:rgba(0,0,0,.1);background-position:50%;background-repeat:no-repeat;background-size:cover;cursor:pointer;float:left;height:75px;margin:2px;max-height:calc(100% - 8px);max-width:calc(50% - 4px);outline:none;overflow:hidden;padding:0;position:relative;-webkit-tap-highlight-color:transparent;width:100px}.fancybox-thumbs__list a:before{border:6px solid #ff5268;bottom:0;content:"";left:0;opacity:0;position:absolute;right:0;top:0;transition:all .2s cubic-bezier(.25,.46,.45,.94);z-index:99991}.fancybox-thumbs__list a:focus:before{opacity:.5}.fancybox-thumbs__list a.fancybox-thumbs-active:before{opacity:1}@media (max-width:576px){.fancybox-thumbs{width:110px}.fancybox-show-thumbs .fancybox-inner{right:110px}.fancybox-thumbs__list a{max-width:calc(100% - 10px)}}
--------------------------------------------------------------------------------
/var/www/css/fancybox/source.md:
--------------------------------------------------------------------------------
1 | https://github.com/fancyapps/fancyBox
2 |
--------------------------------------------------------------------------------
/var/www/css/logo-nav.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Start Bootstrap - Logo Nav (https://startbootstrap.com/template-overviews/logo-nav)
3 | * Copyright 2013-2017 Start Bootstrap
4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-logo-nav/blob/master/LICENSE)
5 | */
6 |
7 | body {
8 | padding-top: 94px;
9 | }
10 |
11 | @media (min-width: 992px) {
12 | body {
13 | padding-top: 76px;
14 | }
15 | }
16 |
17 |
18 | h2 {
19 | padding-top: 16px;
20 | }
21 |
22 | img { padding: 10px;}
23 |
24 | h2:after
25 | {
26 | content:' ';
27 | display:block;
28 | border:1px dotted silver;
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/var/www/index.tpl:
--------------------------------------------------------------------------------
1 | cat <
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | autowx2 automated satellite receiving station $stationName :: $htmlTitle
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
$htmlTitle
60 | Page generated: $currentDate
61 | $keplerInfo
62 | $autowxUptime
63 | Software version: $autowx2version
64 | $shortlistofnextpassess
65 |
66 | $htmlBody
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | EOF
82 |
--------------------------------------------------------------------------------