Skip to content

Commit ddc92cd

Browse files
committed
First commit
0 parents  commit ddc92cd

File tree

202 files changed

+5062
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

202 files changed

+5062
-0
lines changed

.DS_Store

10 KB
Binary file not shown.

otool.py

+375
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
from flask import Flask, render_template, send_from_directory, url_for, request, redirect
2+
import ephem
3+
from pylab import *
4+
import matplotlib.pyplot as plt
5+
import matplotlib.dates as mdates
6+
import numpy as np
7+
import mpld3
8+
from json import JSONEncoder
9+
import logging
10+
11+
#from io import BytesIO
12+
#import base64
13+
'''
14+
import bokeh.plotting as plt
15+
from bokeh.embed import components
16+
from bokeh.resources import CDN
17+
from bokeh.embed import components
18+
'''
19+
app = Flask(__name__)
20+
logger = logging.getLogger(__name__)
21+
22+
@app.route('/otool/calculate/', methods=['POST', 'GET'])
23+
def calculate():
24+
"""Main method."""
25+
26+
fightml = ""
27+
if request.method == 'POST':
28+
29+
lofar = ephem.Observer()
30+
lofar.lon = '6.869882'
31+
lofar.lat = '52.915129'
32+
lofar.elevation = 15.
33+
lofar.pressure = 0. # no refraction at horizon
34+
lofar.horizon = '-0:34' # idem
35+
dateTokens = []
36+
if request.form['date'] != None and request.form['date'] != "":
37+
dateTokens = request.form['date'].split('/')
38+
lofar.date = str(dateTokens[2] + '/' + dateTokens[0] + '/' + dateTokens[1])
39+
endOfDay = lofar.date
40+
endOfDay += 1.
41+
42+
targets = []
43+
aTeam = []
44+
solSystem = []
45+
calibrators = []
46+
time = []
47+
lst = []
48+
targetList = ""
49+
minElevation = 0.
50+
51+
if request.form['elevation'] != None and request.form['elevation'] != "":
52+
minElevation = np.double(request.form['elevation'])
53+
54+
if request.form['targetList'] != None and request.form['targetList'] != "":
55+
targetList = request.form['targetList']
56+
57+
if request.form['names'] != None and request.form['names'] != "" and request.form['ra'] != None and request.form['ra'] != "" and request.form['dec'] != None and request.form['dec'] != "":
58+
for name, ra, dec in zip(request.form['names'].split(","), request.form['ra'].split(","), request.form['dec'].split(",")):
59+
target = Target()
60+
target.name = name
61+
target.elevation = []
62+
target.body = createFixedBody(str(ra), str(dec), '2000')
63+
target.body.compute(lofar)
64+
target.note = "</br>" + name + " and "
65+
targets.append(target)
66+
67+
if request.form.get('CygA') != None:
68+
cygA = Target()
69+
cygA.name = 'CygA'
70+
cygA.elevation = []
71+
cygA.body = createFixedBody('19:59:28:3566', '+40:44:02.096', '2000')
72+
cygA.body.compute(lofar)
73+
for target in targets:
74+
target.note += "<li>Cyg A: " + str(ephem.separation(target.body, cygA.body)) + "</li>"
75+
aTeam.append(cygA)
76+
77+
if request.form.get('CasA') != None:
78+
casA = Target()
79+
casA.name = 'CasA'
80+
casA.elevation = []
81+
casA.body = createFixedBody('23:23:27.94', '+58:48:42.4', '2000')
82+
casA.body.compute(lofar)
83+
for target in targets:
84+
target.note += "<li>Cas A: " + str(ephem.separation(target.body, casA.body)) + "</li>"
85+
aTeam.append(casA)
86+
87+
if request.form.get('TauA') != None:
88+
tauA = Target()
89+
tauA.name = 'TauA'
90+
tauA.elevation = []
91+
tauA.body = createFixedBody('05:34:31.971', '+22:00:52.06', '2000')
92+
tauA.body.compute(lofar)
93+
for target in targets:
94+
target.note += "<li>Tau A: " + str(ephem.separation(target.body, tauA.body)) + "</li>"
95+
aTeam.append(tauA)
96+
97+
if request.form.get('VirA') != None:
98+
virA = Target()
99+
virA.name = 'VirA'
100+
virA.elevation = []
101+
virA.body = createFixedBody('12:30:49.4233', '+12:23:28.043', '2000')
102+
virA.body.compute(lofar)
103+
for target in targets:
104+
target.note += "<li>Vir A: " + str(ephem.separation(target.body, virA.body)) + "</li>"
105+
aTeam.append(virA)
106+
107+
if request.form.get('Sun') != None:
108+
sun = Target()
109+
sun.name = 'Sun'
110+
sun.elevation = []
111+
sun.body = ephem.Sun()
112+
sun.body.compute(lofar)
113+
for target in targets:
114+
target.note += "<li>Sun: " + str(ephem.separation(target.body, sun.body)) + "</li>"
115+
solSystem.append(sun)
116+
117+
if request.form.get('Jupiter') != None:
118+
jup = Target()
119+
jup.name = 'Jupiter'
120+
jup.elevation = []
121+
jup.body = ephem.Jupiter()
122+
jup.body.compute(lofar)
123+
for target in targets:
124+
target.note += "<li>Jupiter: " + str(ephem.separation(target.body, jup.body)) + "</li>"
125+
solSystem.append(jup)
126+
127+
if request.form.get('Saturn') != None:
128+
sat = Target()
129+
sat.name = 'Saturn'
130+
sat.elevation = []
131+
sat.body = ephem.Saturn()
132+
sat.body.compute(lofar)
133+
for target in targets:
134+
target.note += "<li>Saturn: " + str(ephem.separation(target.body, sat.body)) + "</li>"
135+
solSystem.append(sat)
136+
137+
if request.form.get('3C48') != None:
138+
c48 = Target()
139+
c48.name = '3C 48'
140+
c48.elevation = []
141+
c48.body = createFixedBody('01:37:41.2994', '+33:09:35.134', '2000')
142+
c48.body.compute(lofar)
143+
for target in targets:
144+
target.note += "<li>3C 48: " + str(ephem.separation(target.body, c48.body)) + "</li>"
145+
calibrators.append(c48)
146+
147+
if request.form.get('3C147') != None:
148+
c147 = Target()
149+
c147.name = '3C 147'
150+
c147.elevation = []
151+
c147.body = createFixedBody('05:42:36.1379', '+49:51:07.234', '2000')
152+
c147.body.compute(lofar)
153+
for target in targets:
154+
target.note += "<li>3C 147: " + str(ephem.separation(target.body, c147.body)) + "</li>"
155+
calibrators.append(c147)
156+
157+
if request.form.get('3C295') != None:
158+
c295 = Target()
159+
c295.name = '3C 295'
160+
c295.elevation = []
161+
c295.body = createFixedBody('14:11:20.519', '+52:12:09.97', '2000')
162+
c295.body.compute(lofar)
163+
for target in targets:
164+
target.note += "<li>3C 295: " + str(ephem.separation(target.body, c295.body)) + "</li>"
165+
calibrators.append(c295)
166+
167+
if request.form.get('3C196') != None:
168+
c196 = Target()
169+
c196.name = '3C196'
170+
c196.elevation = []
171+
c196.body = createFixedBody('08:13:36.033', '+48:13:02.56', '2000')
172+
c196.body.compute(lofar)
173+
for target in targets:
174+
target.note += "<li>3C 196: " + str(ephem.separation(target.body, c196.body)) + "</li>"
175+
calibrators.append(c196)
176+
177+
if request.form.get('3C380') != None:
178+
c380 = Target()
179+
c380.name = '3C 380'
180+
c380.elevation = []
181+
c380.body = createFixedBody('18:29:31.7809', '+48:44:46.160', '2000')
182+
c380.body.compute(lofar)
183+
for target in targets:
184+
target.note += "<li>3C 380: " + str(ephem.separation(target.body, c380.body)) + "</li>"
185+
calibrators.append(c380)
186+
187+
if request.form.get('3C286') != None:
188+
c286 = Target()
189+
c286.name = '3C 286'
190+
c286.elevation = []
191+
c286.body = createFixedBody('13:31:08.28', '+30:30:32.9', '2000')
192+
c286.body.compute(lofar)
193+
for target in targets:
194+
target.note += "<li>3C 286: " + str(ephem.separation(target.body, c286.body)) + "</li>"
195+
calibrators.append(c286)
196+
197+
if request.form.get('CTD93') != None:
198+
c93 = Target()
199+
c93.name = 'CTD 93'
200+
c93.elevation = []
201+
c93.body = createFixedBody('16:09:13.32', '+26:41:29.0', '2000')
202+
c93.body.compute(lofar)
203+
for target in targets:
204+
target.note += "<li>CTD 93: " + str(ephem.separation(target.body, c93.body)) + "</li>"
205+
calibrators.append(c93)
206+
207+
previous = None
208+
lock = 0
209+
210+
while lofar.date < endOfDay - ephem.minute:
211+
lofar.date += ephem.minute
212+
213+
for target in targets:
214+
target.body.compute(lofar)
215+
target.elevation.append(ephem.degrees(target.body.alt) * (180. / math.pi))
216+
217+
for at in aTeam:
218+
at.body.compute(lofar)
219+
at.elevation.append(ephem.degrees(at.body.alt) * (180. / math.pi))
220+
221+
for calibrator in calibrators:
222+
calibrator.body.compute(lofar)
223+
calibrator.elevation.append(ephem.degrees(calibrator.body.alt) * (180. / math.pi))
224+
225+
for sol in solSystem:
226+
sol.body.compute(lofar)
227+
sol.elevation.append(ephem.degrees(sol.body.alt) * (180. / math.pi))
228+
229+
time.append(lofar.date.datetime())
230+
231+
if previous > lofar.sidereal_time():
232+
lock = 1
233+
234+
if lock:
235+
lst.append(ephem.Date(str(lofar.date).split(' ')[0].split("/")[0] + "/" + str(lofar.date).split(' ')[0].split("/")[1] + "/" + str(int(str(lofar.date).split(' ')[0].split("/")[2]) + 1) + ' ' + str(lofar.sidereal_time())).datetime())
236+
else:
237+
lst.append(ephem.Date(str(lofar.date).split(' ')[0] + ' ' + str(lofar.sidereal_time())).datetime())
238+
239+
previous = lofar.sidereal_time()
240+
241+
fig, ax = plt.subplots(2, 1, figsize=(15, 10),sharex=False, sharey=False)
242+
fig.canvas.draw()
243+
fig.subplots_adjust(hspace=0.5)
244+
245+
ax[0].set_yticks([20, 60, 80])
246+
247+
for target in targets:
248+
ax[0].plot(lst, target.elevation, '--', label=target.name)
249+
250+
sun = ephem.Sun()
251+
sun.compute(lofar)
252+
253+
ax[0].hlines(minElevation, lst[0], lst[len(lst) - 1], colors='k', linestyles='solid', lw=2)
254+
255+
256+
# Does not work with mpld3
257+
#axis1.axvspan(ephem.Date(lofar.previous_rising(sun) - ephem.hour).datetime(), ephem.Date(lofar.previous_rising(sun) + ephem.hour).datetime(), facecolor='0.5', alpha=0.5)
258+
#trans = transforms.blended_transform_factory(axis1.transData, axis1.transAxes)
259+
#rect = patches.Rectangle((ephem.Date(lofar.previous_rising(sun) - ephem.hour).datetime(), 20), width=ephem.Date(ephem.hour).datetime(), height=90, transform=trans, color='yellow', alpha=0.5)
260+
261+
#axis1.add_patch(rect)
262+
263+
ax[0].set_ylabel(r'Elevation [degrees]', fontsize=18, fontweight='bold', color='#000000')
264+
ax[0].set_title('LOFAR target visibility', fontsize=20, fontweight='bold')
265+
266+
time = np.array(time)
267+
cnt = 1
268+
269+
for target in targets:
270+
ax[1].plot(time, cnt * produceSegments(np.array(target.elevation), minElevation), '--', solid_capstyle='round', lw=5)
271+
cnt+=1
272+
273+
for calibrator in calibrators:
274+
ax[0].plot(lst, calibrator.elevation, label=calibrator.name)
275+
cnt+=1
276+
ax[1].plot(time, cnt * produceSegments(np.array(calibrator.elevation), minElevation), solid_capstyle='round', lw=5)
277+
278+
for at in aTeam:
279+
ax[0].plot(lst, at.elevation, label=at.name)
280+
cnt+=1
281+
ax[1].plot(time, cnt * produceSegments(np.array(at.elevation), minElevation), solid_capstyle='round', lw=5)
282+
283+
for sol in solSystem:
284+
ax[0].plot(lst, sol.elevation, label=sol.name)
285+
cnt+=1
286+
ax[1].plot(time, cnt * produceSegments(np.array(sol.elevation), minElevation), solid_capstyle='round', lw=5)
287+
288+
sep_message = "The angular distance on the sky (dd:mm:ss.s) between: "
289+
290+
for target in targets:
291+
sep_message+=target.note
292+
sep_message+= "<ul>"
293+
for calibrator in calibrators:
294+
sep_message+=calibrator.note
295+
for at in aTeam:
296+
sep_message+=at.note
297+
for sol in solSystem:
298+
sep_message+=sol.note
299+
sep_message+= "</ul>"
300+
301+
ax[1].vlines((lofar.previous_rising(sun)).datetime(), 0, cnt + 1, colors='r', linestyles='dashed', lw=2)
302+
ax[1].vlines((lofar.previous_setting(sun)).datetime(), 0, cnt + 1, colors='r', linestyles='dashed', lw=2)
303+
304+
ax[1].vlines(ephem.Date(lofar.previous_rising(sun) - ephem.hour).datetime(), 0, cnt + 1, colors='k', linestyles='dashdot', lw=2)
305+
ax[1].vlines(ephem.Date(lofar.previous_rising(sun) + ephem.hour).datetime(), 0, cnt + 1, colors='k', linestyles='dashdot', lw=2)
306+
307+
ax[1].vlines(ephem.Date(lofar.previous_setting(sun) - ephem.hour).datetime(), 0, cnt + 1, colors='k', linestyles='dashdot', lw=2)
308+
ax[1].vlines(ephem.Date(lofar.previous_setting(sun) + ephem.hour).datetime(), 0, cnt + 1, colors='k', linestyles='dashdot', lw=2)
309+
310+
ax[1].text(ephem.Date(lofar.previous_rising(sun) - 2. * ephem.hour).datetime(), cnt + 1.5, "Sunrise +/- 1hr")
311+
ax[1].text(ephem.Date(lofar.previous_setting(sun) - 2. * ephem.hour).datetime(), cnt + 1.5, "Sunset +/- 1hr")
312+
313+
ax[1].set_ylabel(r'Sources', fontsize=18, fontweight='bold', color='#000000', labelpad=30)
314+
plt.ylim(0, cnt + 3)
315+
316+
ax[0].grid()
317+
ax[0].set_xlabel('LST')
318+
ax[0].xaxis.labelpad = 20
319+
ax[0].yaxis.labelpad = 20
320+
321+
ax[1].axes.get_yaxis().set_ticks([])
322+
ax[1].set_xlabel('UT')
323+
ax[1].xaxis.tick_top()
324+
ax[1].xaxis.set_label_position('top')
325+
ax[1].xaxis.labelpad = 20
326+
327+
legend1 = ax[0].legend(loc='upper center', shadow=True, labelspacing=0.01, ncol=4, bbox_to_anchor=(0.5, 1.))
328+
329+
fightml = mpld3.fig_to_html(fig)
330+
331+
logger.info("Finished computations, rendering...")
332+
return render_template('otool.html', form=request.form, plot=fightml, info=sep_message)
333+
334+
# Array masks don't plot with mpld3
335+
def produceSegments(elevArray, minElevation):
336+
"""Replace entries in the elevation array with NaNs if they are below the elevation selected by the user."""
337+
targetVisibility = np.ones(len(elevArray))
338+
for i, elev in enumerate(elevArray):
339+
if elev < minElevation:
340+
targetVisibility[i] = np.nan
341+
342+
return targetVisibility
343+
344+
def createFixedBody(ra, dec, epoch):
345+
"""Fixed body adapted to us."""
346+
fixedBody = ephem.FixedBody()
347+
fixedBody._ra = ra
348+
fixedBody._dec = dec
349+
fixedBody._epoch = epoch
350+
return fixedBody
351+
352+
class Target(object):
353+
"""Encapsulates celestial bodies."""
354+
def __init__(self, name="", body=None, elevation=[], note = ""):
355+
self.name = name
356+
self.body = body
357+
self.elevation = elevation
358+
self.note = note
359+
360+
@app.route('/otool/static/<path:path>')
361+
def send_js(path):
362+
return send_from_directory('static', path)
363+
364+
# Home page
365+
@app.route('/otool/')
366+
def otool():
367+
"""The landing page"""
368+
return render_template('otool.html')
369+
370+
if __name__ == "__main__":
371+
logging.basicConfig(level=logging.INFO)
372+
app.run(debug=True)
373+
logger.info("Server running.")
374+
#app.run()
375+
#app.run(host='0.0.0.0')

otool_tests.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import os
2+
import otool
3+
import unittest
4+
import tempfile
5+
6+
class OtoolTestCase(unittest.TestCase):
7+
8+
# Test method
9+
def test_empty(self):
10+
"""Just a simple test case."""
11+
otool.app.testing = True
12+
self.app = otool.app.test_client()
13+
rv = self.app.get('/otool/')
14+
assert '<head>' in rv.data
15+
16+
if __name__ == '__main__':
17+
unittest.main()

static/.DS_Store

6 KB
Binary file not shown.

static/images/Astron_logo.jpg

100 KB
Loading

static/images/LOFAR_logo.jpg

52.3 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The contents of */octicons* */svg* are generated by an automated process. Changes to these files may be accepted, but may also be overwritten.
2+
3+
Octicons are a scalable set of icons created by GitHub. At this time, new icons will only be added when they are needed for GitHub products.

0 commit comments

Comments
 (0)