Browse Source

First commit to GitHub

pull/1/head
bmarcote 3 years ago
parent
commit
94a08647e0
  1. 8
      README.md
  2. BIN
      __pycache__/html_template.cpython-36.pyc
  3. BIN
      __pycache__/plot_functions.cpython-36.pyc
  4. BIN
      __pycache__/sources.cpython-36.pyc
  5. BIN
      __pycache__/stations.cpython-36.pyc
  6. BIN
      __pycache__/util_functions.cpython-36.pyc
  7. 286
      app.py
  8. 286
      dash_main.py
  9. 33
      html_template.py
  10. 125
      index.html
  11. 57
      index.py
  12. 33
      plot_functions.py
  13. 5
      requirements.txt
  14. 147
      sources.py
  15. 67
      station_location.txt
  16. 223
      stations.py
  17. 36
      style.css

8
README.md

@ -0,0 +1,8 @@
# VLBI Calculator
This is an on-going project to replace the current [EVN Calculator](http://www.evlbi.org/cgi-bin/EVNcalc.pl) to an improved version that allows you to know how much time each antenna can observe an specified source during a give observing time.

BIN
__pycache__/html_template.cpython-36.pyc

Binary file not shown.

BIN
__pycache__/plot_functions.cpython-36.pyc

Binary file not shown.

BIN
__pycache__/sources.cpython-36.pyc

Binary file not shown.

BIN
__pycache__/stations.cpython-36.pyc

Binary file not shown.

BIN
__pycache__/util_functions.cpython-36.pyc

Binary file not shown.

286
app.py

@ -0,0 +1,286 @@
#! /usr/bin/env python3
from os import path
from time import sleep
import numpy as np
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from datetime import datetime as dt
from astroplan import Observer
import stations
import util_functions as uf
import plot_functions as pf
current_directory = path.dirname(path.realpath(__file__))
stationList = stations.Stations()
stationList.add_from_file(current_directory+'/station_location.txt')
arrays = {'EVN': ['EF', 'HH', 'JB2', 'MC', 'NT', 'UR', 'ON', 'SR', 'T6', 'TR', 'YS',
'WB', 'BD', 'SV', 'ZC', 'IR', 'MH'],
'e-EVN': ['EF', 'HH', 'IR', 'JB2', 'MC', 'NT', 'ON', 'T6', 'TR', 'YS', 'WB'],
'LBA': ['ATCA', 'PA', 'MO', 'HO', 'CD', 'TD70', 'WW'],
'VLBA': ['VLBA-BR', 'VLBA-FD', 'VLBA-HN', 'VLBA-KP', 'VLBA-LA', 'VLBA-MK',
'VLBA-NL', 'VLBA-OV', 'VLBA-PT', 'VLBA-SC'],
'Global': ['EF', 'HH', 'JB2', 'MC', 'NT', 'UR', 'ON', 'SR', 'T6', 'TR', 'YS',
'WB', 'BD', 'SV', 'ZC', 'IR', 'MH', 'VLBA-BR', 'VLBA-FD', 'VLBA-HN',
'VLBA-KP', 'VLBA-LA', 'VLBA-MK', 'VLBA-NL', 'VLBA-OV', 'VLBA-PT',
'VLBA-SC'],
'GMVA': ['EF', 'MH', 'ON', 'YS', 'PV', 'VLBA-BR', 'VLBA-FD', 'VLBA-KP',
'VLBA-LA', 'VLBA-MK', 'VLBA-NL', 'VLBA-OV', 'VLBA-PT'],
'EHT': ['ALMA', 'PV', 'LMT', 'PdB', 'SMA', 'JCMT', 'APEX', 'SMTO', 'SPT']}
bands = {'92cm': 'P - 92cm', '49cm': 'P - 49cm', '30cm': 'UHF - 30cm', '21cm': 'L - 21cm',
'18cm': 'L - 18cm', '13cm': 'S - 13cm', '6cm': 'C - 6cm', '5cm': 'C - 5cm',
'3.6cm': 'X - 3.6cm', '2cm': 'U - 2cm', '1.3cm': 'K - 1.3cm', '0.9cm': 'Ka - 0.9cm',
'0.7cm': 'Q - 0.7cm', '0.3cm': 'W - 0.3cm', '0.1cm': '0.1cm'}
data_rates = [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8]
external_stylesheets = ['http://jive.eu/~marcote/style.css']
n_timestamps = 70 # Number of points (timestamps) for the whole observations.
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# app = dash.Dash(__name__)
app.config.requests_pathname_prefix = ''
# app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/brPBPO.css"})
##################### This is the webpage layout
app.layout = html.Div([
html.Div([
html.H1('EVN Calculator'),
# html.Img(src='http://www.ira.inaf.it/evnnews/archive/evn.gif')
], className='banner'),
html.Div([html.Br()], style={'clear': 'both', 'margin-top': '100px'}),
# First row containing all buttons/options, list of telescopes, and button with text output
html.Div([
# Elements in first column ()
html.Div([
html.Label('Array'),
html.Br(),
dcc.Dropdown(id='array', options=[{'label': a, 'value': a} for a in arrays],
value='EVN'),# multi=True),
html.Br(),
html.Label('Observing Band'),
html.Br(),
dcc.Dropdown(id='band', options=[{'label': bands[b], 'value': b} for b in bands],
value='18cm'),
html.Br(),
html.Label('Data rate (in Mbps)'),
html.Br(),
dcc.Dropdown(id='data_rate', options=[{'label': str(dr), 'value': dr} for dr in data_rates],
value=1024),
html.Br(),
html.Label('Source Coordinates'),
html.Br(),
# dcc.Input(id='source', value='hh:mm:ss dd:mm:ss', type='text', style={'width': '80%'}),
dcc.Input(id='source', value='00:00:00 00:00:00', type='text', className='input_text'),
html.Br(),
html.Label('Start of observation (UTC)'),
html.Br(),
# dcc.Input(id='starttime', value='DD/MM/YYYY HH:MM', type='text', style={'width': '80%'}),
dcc.Input(id='starttime', value='01/04/2010 12:00', type='text', className='input_text'),
html.Br(),
html.Label('Duration (in hours)'),
html.Br(),
dcc.Input(id='duration', value='24.0', type='text', className='input_text'),
html.Br()
], className='column left'),
# Elements in second column (checkboxes with all stations)
html.Div([ # IN OPTIONS DICT, ADD 'DISABLED' : bool FOR DISABLED CHECKBOX. checked=checked
dcc.Checklist(id='stations_check', options=[{'label': s.name, 'value': s.code}
for s in stationList.stations], values=[],
className='check_stations')
], className='column middle'),
html.Div([
html.Button('Calculate', id='button'),
dcc.Markdown(''' ''', id='output-button'),
], className='column right')
]),
# Second row with Plots
html.Div([
html.Div([
dcc.Graph(id='plot-elevation', className='plot'),
]),
html.Div([
dcc.Graph(id='plot-visibility', className='plot')
]),
], className='element')
])
# @app.callback(Output('my-graph', 'figure'), [Input('my-dropdown', 'value')])
# def update_graph(selected_dropdown_value):
# #df = web.DataReader(
# # selected_dropdown_value, data_source='google',
# # start=dt(2017, 1, 1), end=dt.now())
# return {
# 'data': [{
# 'x': [1, 2, 3, 4, 5, 6],
# 'y': [3, 4, 5, 4, 5, 6]
# }]
# }
@app.callback(
dash.dependencies.Output('stations_check', 'values'),
[dash.dependencies.Input('array', 'value'),
dash.dependencies.Input('band', 'value')])
def select_antennas(array, band):
"""Once a button for a specific array is pressed, we select the corresponding
antennas.
"""
return [a for a in arrays[array] if stationList[a].has_frequency(band)]
@app.callback(
dash.dependencies.Output('stations_check', 'options'),
[dash.dependencies.Input('array', 'value'),
dash.dependencies.Input('band', 'value')])
def disable_antennas(array, band):
return [{'label': s.name, 'value': s.code, 'disabled': not s.has_frequency(band),
'className': 'disabled' if not s.has_frequency(band) else 'enabled'} for s
in stationList.stations]
@app.callback(
dash.dependencies.Output('output-button', 'children'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('data_rate', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def calculate_everything(button, array_codes, band, data_rate, source, starttime, duration):
if duration == '0.0' or not (float(duration) > 0.0):
return 'Set a duration and press "Calculate"'
duration = float(duration)
antennas = stationList.get_stations_with_codes(array_codes)
# If source and/or start of observation are not set, then regular sensitivity calc.
if source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM':
obs_times = uf.get_obs_times(uf.get_time('01/01/1993 03:14'), duration, duration)
ant_times = [obs_times]*len(antennas)
# Dummy elevation (40 deg) for all stations
elevations = np.ones((len(antennas), len(obs_times)))*40
else:
# NOTE: Check for source and startime format!!!!
obs_times = uf.get_obs_times(uf.get_time(starttime), duration, duration/n_timestamps)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
# Remove all stations that do not observe the source
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
rms = uf.get_thermal_noise(antennas, ant_times, data_rate*1e6, band)
return formatted_text(rms, uf.get_bandwidth_smearing(), uf.get_time_smearing())
def formatted_text(rms, fov_bandwidth=None, fov_time=None):
# rms is passed as Jy/beam. Here I pick the best units
if rms > 0.99:
rms_units = 'Jy/beam'
elif rms > 0.00099:
rms_units = 'mJy/beam'
rms *= 1000
else:
rms_units = 'μJy/beam'
rms *= 1e6
noise_str = '''
### Thermal noise
The image thermal noise is estimated to be {0:.2f} {1} (at 1-sigma level) using natural weighting.
'''
band_str = '''
Due to bandwidth smearing the FoV is limited to {} arcsec.
'''
time_str = '''
Due to time smearing the FoV is limited to {} arcsec.
'''
fov = '''
### Limitations in field of view (FoV)
{0}
{1}
These values have been calculated for 10% loss in the response of a point source, and they give
the FoV radius from the pointing center.
'''
if (fov_bandwidth is None) and (fov_time is None):
return noise_str.format(rms, rms_units).replace(' ', '')
else:
fov_f = fov.format(band_str.format('{:.2f}'.format(fov_bandwidth) if fov_bandwidth is not None else ''),
time_str.format('{:.2f}'.format(fov_time) if fov_time is not None else ''))
return (noise_str.format(rms, rms_units) + fov_f).replace(' ', '')
@app.callback(
dash.dependencies.Output('plot-elevation', 'figure'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def update_plot_elevation(button, antenna_codes, bands, source, starttime, duration):
if (source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM' or float(duration) <= 0.0):
return None
obs_times = uf.get_obs_times(uf.get_time(starttime), float(duration),
float(duration)/n_timestamps)
antennas = stationList.get_stations_with_codes(antenna_codes)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
return pf.time_elevation(ant_times, elevations, [s.name for s in antennas], xlim=(obs_times[0].datetime, obs_times[-1].datetime))
@app.callback(
dash.dependencies.Output('plot-visibility', 'figure'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def update_plot_visibility(button, antenna_codes, bands, source, starttime, duration):
if (source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM' or float(duration) <= 0.0):
return None
obs_times = uf.get_obs_times(uf.get_time(starttime), float(duration),
float(duration)/n_timestamps)
antennas = stationList.get_stations_with_codes(antenna_codes)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
return pf.time_visibility(ant_times, elevations, [s.name for s in antennas], xlim=(obs_times[0].datetime, obs_times[-1].datetime))
if __name__ == '__main__':
app.run_server(host='0.0.0.0', debug=True)
# app.run_server(host='0.0.0.0')

286
dash_main.py

@ -0,0 +1,286 @@
#! /usr/bin/env python3
from os import path
import numpy as np
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from datetime import datetime as dt
from astroplan import Observer
import stations
import util_functions as uf
import plot_functions as pf
current_directory = path.dirname(path.realpath(__file__))
stationList = stations.Stations()
stationList.add_from_file(current_directory+'/station_location.txt')
arrays = {'EVN': ['EF', 'HH', 'JB2', 'MC', 'NT', 'UR', 'ON', 'SR', 'T6', 'TR', 'YS',
'WB', 'BD', 'SV', 'ZC', 'IR', 'MH'],
'e-EVN': ['EF', 'HH', 'IR', 'JB2', 'MC', 'NT', 'ON', 'T6', 'TR', 'YS', 'WB'],
'LBA': ['ATCA', 'PA', 'MO', 'HO', 'CD', 'TD70', 'WW'],
'VLBA': ['VLBA-BR', 'VLBA-FD', 'VLBA-HN', 'VLBA-KP', 'VLBA-LA', 'VLBA-MK',
'VLBA-NL', 'VLBA-OV', 'VLBA-PT', 'VLBA-SC'],
'Global': ['EF', 'HH', 'JB2', 'MC', 'NT', 'UR', 'ON', 'SR', 'T6', 'TR', 'YS',
'WB', 'BD', 'SV', 'ZC', 'IR', 'MH', 'VLBA-BR', 'VLBA-FD', 'VLBA-HN',
'VLBA-KP', 'VLBA-LA', 'VLBA-MK', 'VLBA-NL', 'VLBA-OV', 'VLBA-PT',
'VLBA-SC'],
'GMVA': ['EF', 'MH', 'ON', 'YS', 'PV', 'VLBA-BR', 'VLBA-FD', 'VLBA-KP',
'VLBA-LA', 'VLBA-MK', 'VLBA-NL', 'VLBA-OV', 'VLBA-PT'],
'EHT': ['ALMA', 'PV', 'LMT', 'PdB', 'SMA', 'JCMT', 'APEX', 'SMTO', 'SPT']}
bands = {'92cm': 'P - 92cm', '49cm': 'P - 49cm', '30cm': 'UHF - 30cm', '21cm': 'L - 21cm',
'18cm': 'L - 18cm', '13cm': 'S - 13cm', '6cm': 'C - 6cm', '5cm': 'C - 5cm',
'3.6cm': 'X - 3.6cm', '2cm': 'U - 2cm', '1.3cm': 'K - 1.3cm', '0.9cm': 'Ka - 0.9cm',
'0.7cm': 'Q - 0.7cm', '0.3cm': 'W - 0.3cm', '0.1cm': '0.1cm'}
data_rates = [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8]
external_stylesheets = ['http://jive.eu/~marcote/style.css']
time_interval = 0.1 # in h
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# app = dash.Dash(__name__)
app.config.requests_pathname_prefix = ''
##################### This is the webpage layout
app.layout = html.Div([
html.Div([
html.H1('EVN Elevation and Visibility'),
# html.Img(src='http://www.ira.inaf.it/evnnews/archive/evn.gif')
], className='banner'),
# First row containing all buttons/options, list of telescopes, and button with text output
html.Div([
# Elements in first column ()
html.Div([
html.Label('Array'),
html.Br(),
dcc.Dropdown(id='array', options=[{'label': a, 'value': a} for a in arrays],
value='EVN'),# multi=True),
html.Br(),
html.Label('Observing Band'),
html.Br(),
dcc.Dropdown(id='band', options=[{'label': bands[b], 'value': b} for b in bands],
value='18cm'),
html.Br(),
html.Label('Data rate (in Mbps)'),
html.Br(),
dcc.Dropdown(id='data_rate', options=[{'label': str(dr), 'value': dr} for dr in data_rates],
value=1024),
html.Br(),
html.Label('Source Coordinates'),
html.Br(),
# dcc.Input(id='source', value='hh:mm:ss dd:mm:ss', type='text', style={'width': '80%'}),
dcc.Input(id='source', value='00:00:00 00:00:00', type='text', style={'width': '80%'}),
html.Br(), html.Br(),
html.Label('Start of observation'),
html.Br(),
# dcc.Input(id='starttime', value='DD/MM/YYYY HH:MM', type='text', style={'width': '80%'}),
dcc.Input(id='starttime', value='01/04/2010 12:00', type='text', style={'width': '80%'}),
html.Br(), html.Br(),
html.Label('Duration (in hours)'),
html.Br(),
dcc.Input(id='duration', value='24.0', type='text', style={'width': '80%'}),
html.Br()
], className='column left'),
# Elements in second column (checkboxes with all stations)
html.Div([ # IN OPTIONS DICT, ADD 'DISABLED' : bool FOR DISABLED CHECKBOX. checked=checked
dcc.Checklist(id='stations_check', options=[{'label': s.name, 'value': s.code}
for s in stationList.stations], values=[],
labelStyle={'white-space': 'nowrap', 'display': 'inline-block', 'min-width': '135pt'})
], className='column right'),
html.Div([
html.Button('Calculate', id='button'),
dcc.Markdown("", id='output-button')
], className='column right')
]),
# Second row with Plots
html.Div([
html.Div([
dcc.Graph(id='plot-elevation'),
]),
html.Div([
dcc.Graph(id='plot-visibility')
]),
#html.Img(src="https://bmarcote.github.io/temp/fig-elev.png"),
#html.Img(src="https://bmarcote.github.io/temp/fig-visib.png")
], className='element')
])
# @app.callback(Output('my-graph', 'figure'), [Input('my-dropdown', 'value')])
# def update_graph(selected_dropdown_value):
# #df = web.DataReader(
# # selected_dropdown_value, data_source='google',
# # start=dt(2017, 1, 1), end=dt.now())
# return {
# 'data': [{
# 'x': [1, 2, 3, 4, 5, 6],
# 'y': [3, 4, 5, 4, 5, 6]
# }]
# }
@app.callback(
dash.dependencies.Output('stations_check', 'values'),
[dash.dependencies.Input('array', 'value'),
dash.dependencies.Input('band', 'value')])
def select_antennas(array, band):
"""Once a button for a specific array is pressed, we select the corresponding
antennas.
"""
return [a for a in arrays[array] if stationList[a].has_frequency(band)]
@app.callback(
dash.dependencies.Output('stations_check', 'options'),
[dash.dependencies.Input('array', 'value'),
dash.dependencies.Input('band', 'value')])
def disable_antennas(array, band):
return [{'label': s.name, 'value': s.code, 'disabled': not s.has_frequency(band)} for s
in stationList.stations]
@app.callback(
dash.dependencies.Output('output-button', 'children'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('data_rate', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def calculate_everything(button, array_codes, band, data_rate, source, starttime, duration):
if duration == '0.0' or float(duration) <= 0.0:
return 'Set a duration and press "Calculate"'
duration = float(duration)
antennas = stationList.get_stations_with_codes(array_codes)
# If source and/or start of observation are not set, then regular sensitivity calc.
if source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM':
obs_times = uf.get_obs_times(uf.get_time('01/01/1993 03:14'), duration, duration)
ant_times = [obs_times]*len(antennas)
# Dummy elevation (40 deg) for all stations
elevations = np.ones((len(antennas), len(obs_times)))*40
else:
# NOTE: Check for source and startime format!!!!
obs_times = uf.get_obs_times(uf.get_time(starttime), duration, 0.3)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
# Remove all stations that do not observe the source
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
rms = uf.get_thermal_noise(antennas, ant_times, data_rate*1e6, band)
return formatted_text(rms, uf.get_bandwidth_smearing(), uf.get_time_smearing())
def formatted_text(rms, fov_bandwidth=None, fov_time=None):
# rms is passed as Jy/beam. Here I pick the best units
if rms > 0.99:
rms_units = 'Jy/beam'
elif rms > 0.00099:
rms_units = 'mJy/beam'
rms *= 1000
else:
rms_units = 'uJy/beam'
rms *= 1e6
noise_str = """
## Thermal noise
The image thermal noise is estimated to be {0:.2f} {1} (at 1-sigma level) using natural weighting.
"""
band_str = """
Due to bandwidth smearing the FoV is limited to {} arcsec.
"""
time_str = """
Due to time smearing the FoV is limited to {} arcsec.
"""
fov = """
## Limitations in field of view (FoV)
{0}
{1}
These values have been calculated for 10% loss in the response of a point source, and they give
the FoV radius from the pointing center.
"""
if (fov_bandwidth is None) and (fov_time is None):
return noise_str.format(rms, rms_units)
else:
fov_f = fov.format(band_str.format('{:.2f}'.format(fov_bandwidth) if fov_bandwidth is not None else ''),
time_str.format('{:.2f}'.format(fov_time) if fov_time is not None else ''))
return noise_str.format(rms, rms_units) + fov_f
@app.callback(
dash.dependencies.Output('plot-elevation', 'figure'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def update_plot_elevation(button, antenna_codes, bands, source, starttime, duration):
if (source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM' or float(duration) <= 0.0):
return None
obs_times = uf.get_obs_times(uf.get_time(starttime), float(duration), time_interval)
antennas = stationList.get_stations_with_codes(antenna_codes)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
return pf.time_elevation(ant_times, elevations, [s.name for s in antennas], xlim=(obs_times[0].datetime, obs_times[-1].datetime))
@app.callback(
dash.dependencies.Output('plot-visibility', 'figure'),
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('stations_check', 'values'),
dash.dependencies.State('band', 'value'),
dash.dependencies.State('source', 'value'),
dash.dependencies.State('starttime', 'value'),
dash.dependencies.State('duration', 'value')])
def update_plot_visibility(button, antenna_codes, bands, source, starttime, duration):
if (source == 'hh:mm:ss dd:mm:ss' or starttime == 'DD/MM/YYYY HH:MM' or float(duration) <= 0.0):
return None
obs_times = uf.get_obs_times(uf.get_time(starttime), float(duration), time_interval)
antennas = stationList.get_stations_with_codes(antenna_codes)
ant_times, elevations = uf.times_elev_with_source_above(uf.get_coordinates(source), antennas, obs_times)
antennas = [a for a,t in zip(antennas,ant_times) if len(t) > 0]
if len(antennas) > 0:
ant_times = [t for t in ant_times if len(t) > 0]
elevations = [e for e in elevations if len(e) > 0]
return pf.time_visibility(ant_times, elevations, [s.name for s in antennas], xlim=(obs_times[0].datetime, obs_times[-1].datetime))
if __name__ == '__main__':
app.run_server(host='0.0.0.0', debug=True)
# app.run_server(host='0.0.0.0')

33
html_template.py

@ -0,0 +1,33 @@
def head():
print('<head>')
# print('<link rel="stylesheet" type="text/css" href="evn_calculator.css">')
print('<style>\n.checkboxes label { white-space: nowrap; }</style>')
print('</head>')
def selections(tag, title, keys, values):
print('<div>')
print('<label for="{0}">{1}</label><br>'.format(tag, title))
print('<select name="{0}" id="{0}">'.format(tag))
for key, value in zip(keys, values):
print('<option value="{0}">{1}</option>'.format(key, value))
print('</select><br><br>')
print('</div>')
def checkboxes(tag, name, keys, values):
print('<div class="{}">'.format(tag))
for key, value in zip(keys, values):
print('<label class="{0}"><input type="{0}" name="{1}" value="{2}">{3}</label>'.format(
tag, name, key, value))
print('</div>')

125
index.html

@ -0,0 +1,125 @@
<!-- This is just a dummy HTML file to test how the actual website should look like.
Here I am testing the HTML code taht I should put and check how it looks like.
-->
<html>
<head>
<style>
.checkboxes label {
white-space: nowrap;
}
</style>
</head>
<body>
<h1>EVN elevation and more plots</h1>
<form name="evnform" method="post">
<div>
<label for="array">Array:</label><br>
<select name="Array" id="array">
<option value="EVN">EVN</option>
<option value="e-EVN">e-EVN</option>
<option value="VLBA">VLBA</option>
<option value="LBA">LBA</option>
<option value="Global">Global</option>
<option value="GMVA">GMVA</option>
<option value="EHT">EHT</option>
</select><br><br>
<label for="band">Observing band:</label><br>
<select name="band" id="band">
<option value="92cm">P - 92 cm</option>
<option value="49cm">P - 49 cm</option>
<option value="30cm">UFF - 30 cm</option>
<option value="21cm">L - 21 cm</option>
<option value="18cm">L - 18 cm</option>
<option value="13cm">S - 13 cm</option>
<option value="6cm">C - 6 cm</option>
<option value="5cm">C - 5 cm</option>
<option value="3.6cm">X - 3.6 cm</option>
<option value="2cm">U - 2 cm</option>
<option value="1.3cm">K - 1.3 cm</option>
<option value="0.9cm">Ka - 0.9 cm</option>
<option value="0.7cm">Q - 0.7 cm</option>
<option value="0.3cm">W - 0.3 cm</option>
<option value="0.1cm">0.1 cm</option>
</select><br><br>
</div>
<div class='checkboxes'>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
<label class='checkboxes'><input type="checkbox" name="EF" value="EF"> Effelsberg</label>
</div>
<!-- <div class='checkboxes'>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
<div class="col-md-1"><input type="checkbox" name="EF" value="EF"> Effelsberg</div>
</div>-->
</form>
</body>
</html>

57
index.py

@ -0,0 +1,57 @@
import cgi
import cgitb
cgitb.enable()
import numpy as np
import matplotlib.pyplot as plt
import mpld3
from mpld3 import plugins
print("Content-type: text/html")
# chmod 755 script.py
template = "<html><body><h1>Hello {}!</h1></body></html>"
print(template.format("Reader"))
form = cgi.FieldStorage()
# plot line + confidence interval
fig, ax = plt.subplots()
ax.grid(True, alpha=0.3)
ax.plot([1,2,3,4,5,6], [2,3,4,5,3,4], 'o')
# define interactive legend
# handles, labels = ax.get_legend_handles_labels() # return lines and labels
# interactive_legend = plugins.InteractiveLegendPlugin(zip(handles,
# ax.collections),
# labels,
# alpha_unsel=0.5,
# alpha_over=1.5,
# start_visible=True)
# plugins.connect(fig, interactive_legend)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Interactive legend', size=20)
mpld3.show()
# try: # NEW
# print("Content-type: text/html\n\n") # say generating html
# main()
# except:
# cgi.print_exception() # catch and print errors

33
plot_functions.py

@ -0,0 +1,33 @@
import numpy as np
import plotly.graph_objs as go
def time_elevation(times, elevations, ant_names, xlim=None):
"""
"""
data = []
for time,elev,ant in zip(times, elevations, ant_names):
data.append(go.Scatter(x=time.datetime, y=elev, mode='markers', name=ant))
layout = go.Layout(title='Source elevation vs time', showlegend=True,
xaxis={'title': 'Time (UTC)', 'range': xlim},
yaxis={'title': 'Elevation (deg)'}, hovermode='closest')
return {'data': data, 'layout': layout}
def time_visibility(times, elevations, ant_names, xlim=None):
data = []
for i,time,elev,ant in zip(np.arange(len(ant_names)), times, elevations, ant_names):
data.append(go.Scatter(x=time.datetime, y=np.zeros_like(elev).deg+i, mode='markers',
name=ant))
layout = go.Layout(title='Visibility of source vs time', showlegend=False,
xaxis={'title': 'Time (UTC)', 'range': xlim},
yaxis={'title': 'Antennas', 'tickmode': 'array',
'ticktext': ant_names, 'tickvals': np.arange(len(ant_names))},
hovermode=False)
return {'data': data, 'layout': layout}

5
requirements.txt

@ -0,0 +1,5 @@
numpy
matplotlib
astropy
astroplan
mpld3

147
sources.py

@ -0,0 +1,147 @@
from matplotlib import pyplot as plt
from astropy import coordinates as coord
import astropy.units as u
import numpy as np
from urllib import parse
from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup as bs
class Flux:
def __init__(self, resolvedFlux, unresolvedFlux):
"""Initialises a flux (contains unresolved and resolved flux)
Inputs
------
- resolvedFlux: the resolved flux of the source
- unresolvedFlux: the unresolved flux of the source
"""
self.resolved = resolvedFlux
self.unresolved = unresolvedFlux
assert isinstance(unresolvedFlux, float)
assert isinstance(resolvedFlux, float)
class Source:
def __init__(self, name, ivsname, coord, noObs, fluxes, calbool):
"""Initializes a source.
Inputs
------
- name: the J2000 name
- ivsname: the ivsname
- coord: astropy coord
- noObs: the number of times this source has been observed in the IVS cat (useful filter maybe)
- fluxes: dict of fluxes with band as key names.
"""
self.name = name
self.ivsname = ivsname
self.coord = coord
self._noObs = noObs
self.flux = fluxes
self.isCal = calbool
def plot_elevation(self, stations, obsTimes):
f = plt.figure(1, figsize=(10,5))
ax = f.add_subplot(111)
ax.set_title("Elevation vs time for {}".format(self.name))
ax.set_ylabel("Elevation (deg)")
ax.set_xlabel("Time")
for station in stations:
els = station.source_elevation(self.coord, obsTimes).deg
ax.plot(obsTimes.datetime, els, label=station.station)
plt.legend()
plt.show()
def find_nmes(self):
#scrape the ftp tests
ftpPage = urlopen("http://www.evlbi.org/tog/ftp_fringes/ftp.html")
bsFtpPage = bs(ftpPage.read(), features="html5lib")
links=[]
for link in bsFtpPage.findAll('a'):
href = link.get('href')
if "ftp_fringes" in href:
try:
ftPage = urlopen(href)
ftPageTxt = str(ftPage.read())
if self.name in ftPageTxt:
links.append(link.get('href'))
elif self.ivsname in ftPageTxt:
links.append(link.get('href'))
except HTTPError as err:
pass
return links
def get_astrogeo_link(self):
#get a link for the astrogeo html section for this source (contains maps/uvrad etc)
sourceCoordString = parse.quote("ra={:02.0f}:{:02.0f}:{:06.3f}&dec={:+03.0f}:{:02.0f}:{:06.3f}&num_sou=1&format=html".format(*self.coord.ra.hms, *self.coord.dec.dms), safe='=&')
return("http://astrogeo.org/cgi-bin/calib_search_form.csh?{}".format(sourceCoordString))
# def load_rfc_cat(filename):
# #loads the data initially from the rfc catalogue, note we have to take care of < in the fluxes. We ignore position error columns
# sourceCat = np.loadtxt(filename,
# comments='#',
# dtype={'names': ('cal', 'ivsname', 'name', 'raH', 'raM', 'raS', 'decD', 'decM',
# 'decS', 'noObs', 'fluxSR', 'fluxSU', 'fluxCR', 'fluxCU',
# 'fluxXR', 'fluxXU', 'fluxUR', 'fluxUU', 'fluxKR', 'fluxKU'),
# 'formats': ('bool', '|S15', '|S15', 'int', 'int', 'float', 'int', 'int',
# 'float', 'int', 'float', 'float', 'float', 'float', 'float',
# 'float', 'float', 'float', 'float', 'float')},
# usecols=(0,1,2,3,4,5,6,7,8,12,13,14,15,16,17,18,19,20,21,22),
# converters={0: lambda cal: True if cal.decode()=='C' else False,
# 13: lambda f: 0.0 if '<' in str(f) else f,
# 14: lambda f: 0.0 if '<' in str(f) else f,
# 15: lambda f: 0.0 if '<' in str(f) else f,
# 16: lambda f: 0.0 if '<' in str(f) else f,
# 17: lambda f: 0.0 if '<' in str(f) else f,
# 18: lambda f: 0.0 if '<' in str(f) else f,
# 19: lambda f: 0.0 if '<' in str(f) else f,
# 20: lambda f: 0.0 if '<' in str(f) else f,
# 21: lambda f: 0.0 if '<' in str(f) else f,
# 22: lambda f: 0.0 if '<' in str(f) else f})
# return sourceCat
def load_rfc_cat(filename, minFluxBand='c', minFlux=1.0):
with open(filename, 'rt') as fin:
coordStrings = []
sources = []
coord0 = coord.SkyCoord("00h00m00.0s +00d00m00.0s")
for line in fin:
if not (line.startswith('#') or line.startswith('N') or line.startswith('U')):
cols = line.split()
cols[13:23] = [float(f) if '<' not in f else 0.0 for f in cols[13:23]]
fluxes = {'s': Flux(cols[13], cols[14]),
'c': Flux(cols[15], cols[16]),
'x': Flux(cols[17], cols[18]),
'u': Flux(cols[19], cols[20]),
'k': Flux(cols[21], cols[22])}
if fluxes[minFluxBand].unresolved > minFlux:
name = cols[2]
ivsname = cols[1]
#coords = coord.SkyCoord("{}h{}m{}s {}d{}m{}s".format(*cols[3:9]))
coordStrings.append("{}h{}m{}s {}d{}m{}s".format(*cols[3:9]))
sources.append(Source(name, ivsname, coord0, int(cols[12]), fluxes, True))
coords = coord.SkyCoord(np.array(coordStrings))
for c,s in zip(coords, sources):
s.coord = c
return sources
def get_up_sources(stationList, sourceList, obsTimes, minEl=20, minFlux=0.5, minFluxBand='c'):
sources=[]
for source in sourceList:
for station in stationList:
if not station.is_source_visible(source.coord, obsTimes, minEl*u.deg):
break
else:
sources.append(source)
sources.sort(key=lambda source:source.flux[minFluxBand].unresolved, reverse=True)
return sources

67
station_location.txt

@ -0,0 +1,67 @@
station code x y z SEFD-92 SEFD-49 SEFD-30 SEFD-21 SEFD-18 SEFD-13 SEFD-6 SEFD-5 SEFD-3.6 SEFD-2 SEFD-1.3 SEFD-0.9 SEFD-0.7 SEFD-0.3 SEFD-0.1
Effelsberg EF 4033947.2616 486990.7866 4900430.9915 600 600 65 20 19 300 20 25 20 45 70 -1 200 930 -1
Westerbork WB 3828767.2647 442446.1739 5064921.5700 2100 -1 1680 420 560 840 1680 1600 1680 -1 -1 -1 -1 -1 -1
Medicina MC 4461369.6954 919597.1240 4449559.3812 -1 -1 -1 490 700 400 170 840 320 -1 700 -1 -1 -1 -1
Noto NT 4934562.8407 1321201.5428 3806484.7336 -1 -1 -1 820 784 770 260 1100 770 -1 800 -1 900 -1 -1
Onsala ON 3370605.8010 711917.7196 5349830.9004 -1 -1 900 350 350 1110 480 850 785 -1 1200 -1 1310 5100 -1
Shanghai SH -2826708.6030 4679237.0770 3274667.5510 -1 -1 -1 -1 670 800 720 1500 800 -1 -1 -1 -1 -1 -1
Tianma T6 -2826708.6030 4679237.0770 3274667.5510 -1 -1 -1 39000 39000 46 26 26 48 -1 70 -1 120 -1 -1
Torun TR 3638558.5100 1221969.7200 5077036.7600 -1 -1 2000 250 300 -1 220 650 -1 -1 500 -1 -1 -1 -1
Metsahovi MH 2892584.8489 1311715.5980 5512640.1545 -1 -1 -1 -1 -1 4500 -1 -1 3200 -1 2608 -1 4500 17650 -1
Yebes YS 4848780.1314 -261701.7668 4123035.9350 -1 -1 -1 -1 -1 1400 160 160 200 -1 200 -1 480 2540 -1
Sardinia SR 4865182.7660 791922.6890 4035137.1740 -1 -1 -1 67 67 -1 -1 50 -1 -1 138 -1 -1 -1 -1
Arecibo AR 2390486.9000 -5564731.4400 1994720.4500 12 -1 3 3.5 3 3 5 5 6 -1 -1 -1 -1 -1 -1
Wettzell WZ 4075627.6441 931774.2773 4801552.3784 -1 -1 -1 -1 -1 1250 -1 -1 750 -1 -1 -1 -1 -1 -1
Hartebeesthoek HH 5085442.7637 2668263.7913 -2768696.7523 -1 -1 -1 -1 430 410 650 700 630 -1 1800 -1 -1 -1 -1
Irbene IR 3183649.615 1276902.735 5359264.576 -1 -1 -1 -1 3600 -1 430 480 1300 -1 -1 -1 -1 -1 -1
Kunming KM -1281152.8362 5640864.3586 2682653.4434 -1 -1 -1 -1 -1 350 17 17 480 -1 -1 -1 -1 -1 -1
Svetloe SV 2730173.6723 1562442.7934 5529969.1413 -1 -1 -1 360 360 330 250 -1 200 -1 710 -1 -1 -1 -1
Zelenchkaya ZC 3451207.5353 3060375.4139 4391915.0384 -1 -1 -1 300 300 330 400 -1 200 -1 710 -1 -1 -1 -1
Badary BD -838201.0685 3865751.5652 4987670.8885 -1 -1 -1 330 330 330 200 -1 200 -1 710 -1 -1 -1 -1
Urumqi UR 228310.2129 4631922.7656 4367064.0638 -1 -1 -1 300 300 560 250 -1 350 -1 850 -1 -1 -1 -1
Robledo70 RO7 4849336.6927 -360488.7857 4114748.8272 -1 -1 -1 -1 35 20 -1 -1 18 -1 83 -1 -1 -1 -1
Robledo34 RO3 4849336.6927 -360488.7857 4114748.8272 -1 -1 -1 -1 -1 150 -1 -1 106 -1 -1 -1 -1 -1 -1
Lovell JB1 3822626.0400 -154105.6500 5086486.0400 132 83 -1 36 65 -1 80 -1 -1 -1 -1 -1 -1 -1 -1
JodrellBank-Mrk2 JB2 3822846.7600 -153802.2800 5086285.9000 -1 -1 -1 350 320 -1 320 300 -1 -1 910 -1 -1 -1 -1
Kuntunse NK 6346273.5210 -33779.7127 634844.9454 -1 -1 -1 -1 -1 -1 1200 -1 -1 -1 -1 -1 -1 -1 -1
Cambridge CM 3920356.1500 2542.0200 5014284.4200 -1 -1 -1 220 212 -1 136 410 -1 -1 720 -1 -1 -1 -1
Darnhall DA 3829087.8990 -169568.9550 5081082.3460 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
Defford DE 3923442.5660 -146914.3300 5009755.1250 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
Tabley TA 3817549.9560 -163031.1410 5089896.6540 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
Knockin KN 3860084.8980 -202105.0390 5056568.8480 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
KVN-Yonsei KY -3042280.9035 4045902.6564 3867374.3087 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 -1 1919 2500 4000
KVN-Ulsan KU -3287268.5430 4023450.1448 3687379.9675 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 -1 1919 2500 4000
KVN-Tamna KT -3171731.5532 4292678.5258 3481038.7679 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 -1 1919 2500 4000
JVLA-1 VLA1 -1601185.4286 -5041977.1754 3554875.6231 3900 -1 -1 420 420 370 310 310 250 350 560 710 1260 -1 -1
JVLA-27 VLA27 -1601185.4286 -5041977.1754 3554875.6231 167 -1 -1 17.9 17.9 15.8 13.2 13.2 10.7 15 23.9 30.3 53.8 -1 -1
VLBA-BR VLBA-BR -2112065.2062 -3705356.5048 4726813.6759 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-FD VLBA-FD -1324009.3266 -5332181.9547 3231962.3949 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-HN VLBA-HN 1446374.8658 -4447939.6774 4322306.1838 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 -1 -1
VLBA-KP VLBA-KP -1995678.8402 -5037317.6968 3357328.0251 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-LA VLBA-LA -1449752.5839 -4975298.5757 3709123.8459 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-MK VLBA-MK -5464075.1874 -2495248.1168 2148297.3643 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-NL VLBA-NL -130872.4987 -4762317.0925 4226851.0014 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-OV VLBA-OV -2409150.4018 -4478573.1180 3838617.3385 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-PT VLBA-PT -1640953.9383 -5014816.0237 3575411.7916 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 4236 -1
VLBA-SC VLBA-SC 2607848.6379 -5488069.5358 1932739.7326 2742 2744 -1 289 314 347 210 278 327 543 640 -1 1181 -1 -1
GreenBank GBT 882589.4102 -4924872.3416 3943729.4062 35 24 13 10 10 12 13 13 15 20 30 -1 60 140 -1
ATCA ATCA -4751639.85972 2791700.35670 -3200491.11339 -1 -1 -1 68 68 106 70 70 86 220 106 -1 180 1440 -1
Parkes PA -4554232.4864 2816758.8662 -3454035.0137 -1 -1 -1 40 40 30 110 110 43 370 810 -1 -1 -1 -1
Mopra MO -4682769.05850 2802619.04217 -3291759.33837 -1 -1 -1 240 240 530 350 350 430 1300 675 -1 900 3750 -1
Hobart HO -3950237.3590 2522347.6804 -4311561.8790 -1 -1 -1 470 470 650 640 640 560 1200 1800 -1 -1 -1 -1
Ceduna CD -3753442.7457 3912709.7530 -3348067.6095 -1 -1 -1 1000 1000 400 450 550 600 750 2500 -1 -1 -1 -1
Tidbinbilla70 TD70 -4460895.2445 2682361.5103 -3674747.5813 -1 -1 -1 -1 23 16 -1 -1 25 -1 60 -1 -1 -1 -1
Warkworth WW -5115324.4279 477843.2969 -3767192.8344 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
Goldstone DSS14 -2353621.2459 -4641341.5369 3677052.2305 -1 -1 -1 -1 49 -1 -1 -1 -1 -1 65 -1 -1 -1 -1
ASKAP ASKAP -2556743.707 5097440.315 -2847749.657 -1 -1 -1 6000 6000 -1 -1 -1 3500 -1 -1 -1 -1 -1 -1
SKA1_MID SKA -2556743.707 5097440.315 -2847749.657 -1 -1 -1 3 3 -1 5 5 5 8 10 -1 -1 -1 -1
Matera MA 4641938.78318 1393003.02605 4133325.52288 -1 -1 -1 -1 -1 1000 -1 -1 1000 -1 -1 -1 -1 -1 -1
Pico_Veleta PV 5088967.9000 -301681.6000 3825015.8000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 640 1160
ALMA ALMA 2225061.8731 -5440061.9534 -2481682.0838 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 75 85
APEX APEX 2225039.5297 -5441197.6292 -2479303.3597 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 4977
PdBure PdB 4524000.43 468042.14 4460309.76 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 450 4299
LMT LMT -768715.632 -5988507.072 2063354.852 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1714 3536
SPT SPT 792.600 -802.600 -6359569.200 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 7497
SMTO SMTO -1828796.2000 -5054406.8000 3427865.200 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 9057
JCMT JCMT -5464584.676 -2493001.170 2150653.982 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 3842
SMA SMA -5464588.447 -2492884.038 2150756.452 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 2374

223
stations.py

@ -0,0 +1,223 @@
import itertools
import numpy as np
import astropy.units as u
import astropy.coordinates as coord
from astropy.io import ascii
#from sources import Source
class Station:
def __init__(self, name, codename, location, sefds, observe=True):
"""Initializes a station. The given name must be the name of the station that
observes, with the typical 2-letter format used in the EVN (with exceptions).
Inputs
------
- name : str
Name of the observer (the station that is going to observe).
- codename : str
A code for the name of the station. It can be the same as name.
- location : EarthLocation
Position of the observer on Earth.
- sefds : dict
SEFDs at different frequencies. Dict where keys are the wavelengths
in cm. The values will be given in Jy. If a wavelngth is given, a correct
SEFD is assumed (i.e. no empty or n/a values are expected to be found).
"""
self.name = name
self.code = codename
self.location = location
self.sefd = sefds
def stations_from_file(filename):
"""The file must contain the following columns:
name_observer code_observer X Y Z
The header of this file should be "station code x y z", matching the previous fields
Returns a dict with the observers. The keys are the code_observer.
"""
file_with_stations = ascii.read(filename)
# Getting the frequencies with SEFD values
sefds = [acol for acol in file_with_stations.colnames if 'SEFD-' in acol]
stations = {}
for a_line in file_with_stations:
a_loc = coord.EarthLocation(a_line['x']*u.m, a_line['y']*u.m, a_line['z']*u.m)
stat_sefds = {}
for a_sefd in sefds:
if a_line[a_sefd] != -1:
stat_sefds[a_sefd.split('-')[1]] = a_line[a_sefd]
stations[a_line['code']] = Station(a_line['station'], a_line['code'], a_loc,
stat_sefds)
return stations
def source_elevation(self, source_coord, obs_times):
"""Returns the elevation of the source as seen by the Station during obs_times.
Inputs
------
- source_coord : astropy.coordinates
Coordinates of the source to observe.
- obs_times : astropy.time.Time
Time to compute the elevation of the source (either single time or a list of times).
Output
------
- elevations : ndarray
Elevation of the source at the given obs_times
"""
source_altaz = source_coord.transform_to(coord.AltAz(obstime=obs_times,
location=self.location))
return source_altaz.alt
def is_source_visible(self, source_coord, obs_times, min_elevation):
"""Return if the source is visible for this station at the given time (with an
elevation larger than the entered one).
"""
elevations = self.source_elevation(source_coord, obs_times)
return np.any(elevations >= min_elevation)
def has_frequency(self, band):
"""Returns if the station can observe at the given band. This is
expected to be given in wavelength (cm).
"""
return band in self.sefd
def get_sefd(self, band):
"""Return the SEFD of the station at the given band (given in cm).
The SEFD value is given in Jy units.
"""
return self.sefd[band]
class Stations(object):
"""Collection of stations.
"""
def __init__(self):
self.names = []
self.codes = []
self.stations = []
def add_station(self, station):
self.names.append(station.name)
self.codes.append(station.code)
self.stations.append(station)
def del_station(self, code):
ind = self.codes.index(code)
self.names.pop(ind)
self.codes.pop(ind)
self.stations.pop(ind)
def add_from_file(self, filename):
"""The file must contain the following columns:
name_observer code_observer X Y Z SEFD-**
The header of this file should be "station code x y z", matching the previous fields
Will add all found stations to the current Stations.
"""
data = ascii.read(filename)
# Getting the frequencies with SEFD values
sefds = [acol for acol in data.colnames if 'SEFD-' in acol]
locations = coord.EarthLocation(data['x']*u.m, data['y']*u.m, data['z']*u.m)
for a_row,a_loc in zip(data, locations):
self.names.append(a_row['station'])
self.codes.append(a_row['code'])
stat_sefds = {}
for a_sefd in sefds:
if a_row[a_sefd] != -1:
stat_sefds[a_sefd.split('-')[1]+'cm'] = a_row[a_sefd]
self.stations.append(Station(a_row['station'], a_row['code'], a_loc, stat_sefds))
def get_station(self, code):
return self.stations[self.codes.index(code)]
def get_stations_with_codes(self, list_of_codes):
new_stations = Stations()
for code in list_of_codes:
new_stations.add_station(self.get_station(code))
return new_stations
def has_frequency(self, band):
"""Returns a list of booleans describing if the stations can observe
at the given band.
"""
return map(lambda s : s.has_frequency(band), self.stations)
def can_observe_source(self, source, times, min_elevation=10):
"""Returns if stations can observe (at least during part of the observation)
the given source during the provided times, with at least an elevation larger
than min_elevation.
"""
return map(lambda s : s.is_source_visible(source, times, min_elevation), self.stations)
def get_stations_with_frequency(self, band):
"""Return a Stations object only including the stations that have the given band.
Input
-----
- band : str
The band to observe (18cm, 5cm, 0.1cm, ...)
Output
------
- stations_with_band : Stations
A Stations object including only the stations that can observe at that band.
"""
cond = self.has_frequency(band)
new_stations = Stations()
for a_station in itertools.compress(self, cond):
new_stations.add_station(a_station)
return new_stations
def get_stations_observing_source(self, source, times, min_elevation=10):
cond = self.can_observe_source(source, times, min_elevation)
new_stations = Stations()
for a_station in itertools.compress(self, cond):
new_stations.add_station(a_station)
return new_stations
def get_source_elevations(self, source, obs_times):
return [s.source_elevation(source, obs_times) for s in self]
def __iter__(self):
return iter(self.stations)
def __getitem__(self, key):
return self.stations[self.codes.index(key)]
def __delitem__(self, key):
self.del_station(key)
def __contains__(self, item):
return item in self.codes
def __len__(self):
return len(self.codes)

36
style.css

@ -0,0 +1,36 @@
.element {
padding: 10px;
clear: both;
}
.column {
float: left;
padding: 20px;
}
.left {
width: 25%;
background-color: #CCF;
}
.right {
width: 70%;
background-color: #FFA;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Responsive: only one column if small display */
/*@media screen and (max-width: 400px) {
.column {
width: 100%;
}
}*/
Loading…
Cancel
Save