Browse Source

First commit after refactoring

tags/v1.0
Benito Marcote 1 year ago
parent
commit
4c20b3fbdc
45 changed files with 4853 additions and 1294 deletions
  1. BIN
      __pycache__/html_template.cpython-36.pyc
  2. BIN
      __pycache__/plot_functions.cpython-36.pyc
  3. BIN
      __pycache__/sources.cpython-36.pyc
  4. BIN
      __pycache__/stations.cpython-36.pyc
  5. BIN
      __pycache__/util_functions.cpython-36.pyc
  6. +521
    -231
      app.py
  7. +7
    -0
      assets/bootstrap.min.css
  8. +3294
    -0
      assets/style.css
  9. +0
    -286
      dash_main.py
  10. +57
    -0
      data/station_location.txt
  11. +7
    -0
      data/style_bootstrap_ori.css
  12. +10
    -0
      doc/documentation.md
  13. +157
    -0
      doc/notes.md
  14. +0
    -33
      html_template.py
  15. +0
    -125
      index.html
  16. +0
    -57
      index.py
  17. BIN
      mockups/mockup-antennas.png
  18. BIN
      mockups/mockup-app.pdf
  19. BIN
      mockups/mockup-images.jpg
  20. BIN
      mockups/mockup-plots.jpg
  21. BIN
      mockups/mockup-sensitivity.png
  22. BIN
      mockups/plot-beam.png
  23. BIN
      mockups/plot-elev.png
  24. BIN
      mockups/plot-uv.png
  25. BIN
      mockups/plot-vis.png
  26. +0
    -33
      plot_functions.py
  27. +0
    -5
      requirements.txt
  28. +0
    -147
      sources.py
  29. BIN
      src/__pycache__/freqsetups.cpython-36.pyc
  30. BIN
      src/__pycache__/freqsetups.cpython-37.pyc
  31. BIN
      src/__pycache__/functions.cpython-36.pyc
  32. BIN
      src/__pycache__/functions.cpython-37.pyc
  33. BIN
      src/__pycache__/observation.cpython-36.pyc
  34. BIN
      src/__pycache__/observation.cpython-37.pyc
  35. BIN
      src/__pycache__/stations.cpython-36.pyc
  36. BIN
      src/__pycache__/stations.cpython-37.pyc
  37. +29
    -0
      src/freqsetups.py
  38. +116
    -0
      src/functions.py
  39. +330
    -0
      src/observation.py
  40. +233
    -0
      src/stations.py
  41. +0
    -67
      station_location.txt
  42. +0
    -223
      stations.py
  43. +0
    -36
      style.css
  44. +92
    -0
      test/test.py
  45. +0
    -51
      util_functions.py

BIN
__pycache__/html_template.cpython-36.pyc View File


BIN
__pycache__/plot_functions.cpython-36.pyc View File


BIN
__pycache__/sources.cpython-36.pyc View File


BIN
__pycache__/stations.cpython-36.pyc View File


BIN
__pycache__/util_functions.cpython-36.pyc View File


+ 521
- 231
app.py View File

@@ -1,281 +1,571 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""EVN Calculator.

Program to compute the source elevation visibility and expected thermal
noise level for a given EVN observation.
"""

__author__ = "Benito Marcote"
__copyright__ = "Copyright 2020, Joint Insitute for VLBI-ERIC (JIVE)"
__credits__ = "Benito Marcote"
__license__ = "GPL"
__date__ = "2020/04/21"
__version__ = "0.0.1"
__maintainer__ = "Benito Marcote"
__email__ = "marcote@jive.eu"
__status__ = "Development" # Prototype, Development, Production.

from os import path
from time import sleep
import itertools
import datetime
import numpy as np
import dash
from dash.dependencies import Input, Output
from dash.dependencies import Input, Output, State
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
from astropy.time import Time
from astropy import coordinates as coord
from astropy import units as u
from astroplan import FixedTarget
from src import freqsetups as fs
from src import stations
from src import functions as fx
from src import observation


current_directory = path.dirname(path.realpath(__file__))
stationList = stations.Stations()
stationList.add_from_file(current_directory+'/station_location.txt')
# stationList = stations.Stations()
# stationList.add_from_file(current_directory+'/station_location.txt')




all_antennas = fx.get_stations_from_file(f"{current_directory}/data/station_location.txt")
sorted_networks = ('EVN', 'eMERLIN', 'VLBA', 'LBA', 'KVN', 'Other')
default_arrays = {'EVN': ['Ef', 'Hh', 'Jb2', 'Mc', 'Nt', 'Ur', 'On', 'Sr', 'T6', 'Tr',
'Ys', 'Wb', 'Bd', 'Sv', 'Zc', 'Ir'],
'e-EVN': ['Ef', 'Hh', 'Ir', 'Jb2', 'Mc', 'Nt', 'On', 'T6', 'Tr', 'Ys', 'Wb',
'Bd', 'Sv', 'Zc', 'Ir', 'Sr', 'Ur'],
'eMERLIN': ['Cm', 'Kn', 'Pi', 'Da', 'De'],
'LBA': ['ATCA', 'Pa', 'Mo', 'Ho', 'Cd', 'Td', 'Ww'],
'VLBA': ['Br', 'Fd', 'Hh', 'Kp', 'La', 'Mk', 'Nl', 'Ov', 'Pt', 'Sc'],
'KVN': ['Ky', 'Ku', 'Kt'],
'Global VLBI': ['Ef', 'Hh', 'Jb2', 'Mc', 'Nt', 'Ur', 'On', 'Sr', 'T6',
'Tr', 'Ys', 'Wb', 'Bd', 'Sv', 'Zc', 'Ir', 'Br', 'Fd', 'Hn',
'Kp', 'La', 'Mk', 'Nl', 'Ov', 'Pt', 'Sc'],
'GMVA': ['Ef', 'Mh', 'On', 'Ys', 'Pv', 'Br', 'Fd', 'Kp', 'La', 'Mk', 'Nl',
'Ov', 'Pt'],
'EHT': ['ALMA', 'Pv', 'LMT', 'PdB', 'SMA', 'JCMT', 'APEX', 'SMTO', 'SPT']}

# Initial values
target_source = observation.Source('1h2m3s +50d40m30s', 'Source')
# obs_times = Time('1967-04-17 10:00') + np.arange(0, 600, 15)*u.min
selected_band = '18cm'

sensitivity_results_template = """

{band:.2n} ({freq:.2n}) observations with the following antennas:

{antennas}

{sb} ({sbbw:.2n}) subbands with {ch} channels each and {pols} polarization.

Total bandwidth of the observation: {bandwidth:.3n}
({bandwidth_channel:.3n} per spectral channel)


**Estimated image thermal noise** (assuming {ttarget:.3n} on target): {noise:.3n}


Estimated rms thernal noise per spectral channel: {noise_channel:.3n}

**Resulting FITS file size**: {filesize:.3n}

(note that this is only an estimation)


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']}
**Smearing in the Field of View** (for a 10% loss):

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'}
Field of View limited by bandwidth-smearing to: {bw_smearing:.3n}

data_rates = [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8]
Field of View limited by time-smearing to: {t_smearing:.3n}

"""

external_stylesheets = ['http://jive.eu/~marcote/style.css']
n_timestamps = 70 # Number of points (timestamps) for the whole observations.



# external_stylesheets = ["https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css", "http://jive.eu/~marcote/style.css"]
# external_stylesheets = ["https://bmarcote.github.io/temp/style.css"]
external_stylesheets = []
# 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 = 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"})

# 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.H1('EVN Source Visibility'),
# 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')
# ], className='banner'),
html.Div([html.Br()]), #style={'clear': 'both', 'margin-top': '20px'}),
# First row containing all buttons/options, list of telescopes, and button with text output
dcc.ConfirmDialog(id='error', message=''),
# Elements in second column (checkboxes with all stations)
html.Div(className='container-fluid', children=[
dcc.Tabs([
dcc.Tab(label='Observation Setup', className='container', children=[
# Elements in first column ()
html.Div(className='row-cols-3', children=[
html.Div(className='col-sm-3', style={'max-width': '300px','float': 'left',
'padding': '2%'}, children=[
html.Label('Observing Band'),
html.Br(),
dcc.Dropdown(id='band',
options=[{'label': fs.bands[b], 'value': b} for b \
in fs.bands], value='18cm'),
html.Br(),
html.Label('Select default VLBI Network(s)'),
html.Br(),
dcc.Dropdown(id='array', options=[{'label': n, 'value': n} \
for n in default_arrays], value=['EVN'], multi=True),
html.Br(),
html.Div(className='input-group-prepend', children=[
dcc.Checklist(id='e-EVN', className='checkbox',
options=[{'label': ' e-EVN (real-time) mode?',
'value': 'e-EVN'}], value=[]),
]),
html.Br(),
html.Label('Start of observation (UTC)'),
html.Br(),
# dcc.Input(id='starttime', value='DD/MM/YYYY HH:MM', type='text',
dcc.Input(id='starttime', value='17/04/1967 10:00', type='text',
className='form-control'),
html.Br(),
html.P(id='error_starttime', style={'color': 'red'}),
html.Br(),
html.Label('End of observation (UTC)'),
html.Br(),
# dcc.Input(id='endtime', value='DD/MM/YYYY HH:MM', type='text',
dcc.Input(id='endtime', value='17/04/1967 20:00', type='text',
className='form-control'),
html.Br(),
html.P(id='error_endtime', style={'color': 'red'}),
html.Br(),
html.Label('Target Source Coordinates'),
html.Br(),
# dcc.Input(id='source', value='hh:mm:ss dd:mm:ss', type='text',
dcc.Input(id='source', value='01:20:00 +50:40:30', type='text',
className='form-control'),
html.Br(),
html.P(id='error_source', style={'color': 'red'}),
html.Br(),
html.Label(id='onsourcetime-label', children='Percent. of on-target time'),
html.Br(),
dcc.Slider(id='onsourcetime', min=20, max=100, step=5, value=75,
marks= {i: str(i) for i in range(20, 101, 10)}),
html.Br(),
html.Label('Datarate per station (in Mbps)'),
html.Br(),
dcc.Dropdown(id='datarate', options=[{'label': str(dr), 'value': dr} \
for dr in fs.data_rates], value=1024),
html.Br(),
html.Label('Number of subbands'),
html.Br(),
dcc.Dropdown(id='subbands', options=[{'label': str(sb), 'value': sb} \
for sb in fs.subbands], value=8),
html.Br(),
html.Label('Number of spectral channels'),
html.Br(),
dcc.Dropdown(id='channels', options=[{'label': str(ch), 'value': ch} \
for ch in fs.channels], value=32),
html.Br(),
html.Label('Number of polarizations'),
html.Br(),
dcc.Dropdown(id='pols', options=[{'label': fs.polarizations[p], 'value': p} \
for p in fs.polarizations], value=4),
html.Br(),
html.Label('Integration time (s)'),
html.Br(),
dcc.Dropdown(id='inttime', options=[{'label': fs.inttimes[it], 'value': it} \
for it in fs.inttimes], value=2),
html.Br()
]),
# html.Div(style={'margin-top': '20px'}, children=[
html.Div(className='col-lg-7', style={'float': 'left'}, children=[
html.Div(id='antennas-div', className='container', children=[
# List with all antennas
html.Div(className='antcheck', children=[html.Br(),
html.Label(html.H4(f"{an_array}")),
html.Br(),
dcc.Checklist(id=f"list_stations_{an_array}",className='antcheck',
options=[{'label': s.name, 'value': s.codename,
'disabled': not s.has_band(selected_band)}
for s in all_antennas if s.network == an_array], value=[])
]) for an_array in sorted_networks
])
]),
html.Div(className='col-sm-2', style={'float': 'left'}, children=[
html.Button('Compute Observation', id='antenna-selection-button',
className='btn btn-primary btn-lg',
# style={'margin': '5px 5px 5px 5px'}),
style={'padding': '5px', 'margin-top': '50px'}),
])
])
]),

dcc.Tab(label='Sensitivity', children=[
html.Div(className='col-md-8', children=[
# Sensitivity calculations
dcc.Markdown(id='sensitivity-output',
children="Set the observation first.")
])
]),
dcc.Tab(label='Plots', children=[
html.Div(className='col-md-8', children=[
# Elevation VS time
html.Div([
dcc.Graph(id='fig-elev-time')
]),
# Antenna VS time (who can observe)
html.Div([
dcc.Graph(id='fig-ant-time')
])
])
]),
dcc.Tab(label='Images', children=[
# Images
html.Div(className='col-md-8', children=[
dcc.Markdown(children="""To be implemented.
The uv coverage and expected dirty images will go here.""")
])
]),
dcc.Tab(label='Help', children=[
# Documentation
html.Div(className='col-md-8', children=[
dcc.Markdown(children="""The Help/doc.
All explanations and technical details will go here.""")
])
])
])
])
])

# @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.
def convert_colon_coord(colon_coord):
"""Converts some coordinates given in a str format 'HH:MM:SS DD:MM:SS' to
'HHhMMmSSs DDdMMdSSs'.
If ':' are not present in colon_coord, then it returns the same str.
"""
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
if ':' not in colon_coord:
return colon_coord
for l in ('h', 'm', 'd', 'm'):
colon_coord = colon_coord.replace(':', l, 1)

return colon_coord.replace(' ', 's ')+'s'


def get_selected_antennas(list_of_selected_antennas):
"""Given a list of antenna codenames, it returns a Stations object containing
all given antennas.
"""
selected_antennas = stations.Stations('Observation', [])

noise_str = '''
### Thermal noise
for ant in list_of_selected_antennas:
selected_antennas.add(all_antennas[ant])

The image thermal noise is estimated to be {0:.2f} {1} (at 1-sigma level) using natural weighting.
return selected_antennas

'''
band_str = '''
Due to bandwidth smearing the FoV is limited to {} arcsec.

'''
time_str = '''
Due to time smearing the FoV is limited to {} arcsec.
def optimal_units(value, units):
"""Given a value (with some units), returns the unit choice from all
`units` possibilities that better suits the value.
It is meant for the following use:
Given 0.02*u.Jy and units = [u.kJy, u.Jy, u.mJy, u.uJy], it will
return 20*u.mJy.
units should have a decreasing-scale of units, and all of them
compatible with the units in `value`.
"""
for a_unit in units:
if 0.8 < value.to(a_unit).value <= 800:
return value.to(a_unit)

'''
fov = '''
### Limitations in field of view (FoV)
# Value too high or too low
if value.to(units[0]).value > 1:
return value.to(units[0])

{0}
{1}
return value.to(units[-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(' ', '')
def update_sensitivity(an_obs):
"""Given the observation, it sets the text about all properties of the observation.
"""
rms = optimal_units(an_obs.thermal_noise(), [u.MJy, u.kJy, u.Jy, u.mJy, u.uJy])
rms_channel = optimal_units(rms*np.sqrt(an_obs.subbands*an_obs.channels),
[u.MJy, u.kJy, u.Jy, u.mJy, u.uJy])

ants_up = an_obs.is_visible()
ant_no_obs = []
for an_ant in ants_up:
if len(ants_up[an_ant][0]) == 0:
ant_no_obs.append(an_ant)

antennas_text = ', '.join(an_obs.stations.keys())
if len(ant_no_obs) > 0:
antennas_text += f"\n (note that {', '.join(ant_no_obs)} cannot observe the source)."
return sensitivity_results_template.format(
band=optimal_units(an_obs.wavelength, [u.m, u.cm, u.mm]),
freq=optimal_units(an_obs.frequency, [u.GHz, u.MHz]),
antennas=antennas_text,
sb=an_obs.subbands,
sbbw=optimal_units(an_obs.bandwidth/an_obs.subbands, [u.GHz, u.MHz, u.kHz]),
ch=an_obs.channels,
pols={1: 'single', 2: 'dual', 4: 'full'}[an_obs.polarizations],
ttarget=optimal_units(an_obs.ontarget_fraction*(an_obs.times[-1]-an_obs.times[0]),
[u.h, u.min, u.s, u.ms]),
noise=rms,
noise_channel=rms_channel,
filesize=optimal_units(an_obs.datasize(), [u.TB, u.GB, u.MB, u.kB]),
bw_smearing=optimal_units(an_obs.bandwidth_smearing(), [u.arcmin, u.arcsec]),
t_smearing=optimal_units(an_obs.time_smearing(), [u.arcmin, u.arcsec]),
bandwidth=optimal_units(an_obs.bandwidth, [u.GHz, u.MHz, u.kHz]),
bandwidth_channel=optimal_units(an_obs.bandwidth/(an_obs.subbands*an_obs.channels),
[u.GHz, u.MHz, u.kHz, u.Hz]))



@app.callback(Output('onsourcetime-label', 'children'),
[Input('onsourcetime', 'value')])
def update_onsourcetime_label(onsourcetime):
return f"Percent. of on-target time ({onsourcetime}%)"


@app.callback(Output('antennas-div', 'children'),
[Input('band', 'value'), Input('array', 'value'), Input('e-EVN', 'value')])
def select_antennas(selected_band, selected_networks, is_eEVN):
"""Given a selected band and selected default networks, it selects the associated
antennas from the antenna list.
"""
selected_antennas = []
if is_eEVN:
selected_antennas = [ant for ant in default_arrays['e-EVN'] \
if (all_antennas[ant].has_band(selected_band) and \
(all_antennas[ant].network == 'EVN'))]

return [html.Div([html.Br(),
html.Label(html.H4(f"{an_array}")),
html.Br(),
dcc.Checklist(id=f"list_stations_{an_array}",
options=[{'label': s.name, 'value': s.codename,
'disabled': (not s.has_band(selected_band)) or \
(not s.codename in default_arrays['e-EVN'])}
for s in all_antennas if s.network == an_array],
value=selected_antennas if an_array=='EVN' else [],
className='antcheck')]) for an_array in sorted_networks]
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))
for an_array in selected_networks:
selected_antennas += [ant for ant in default_arrays[an_array] \
if all_antennas[ant].has_band(selected_band)]

return [html.Div([html.Br(),
html.Label(html.H4(f"{an_array}")),
html.Br(),
dcc.Checklist(id=f"list_stations_{an_array}",
options=[{'label': s.name, 'value': s.codename,
'disabled': not s.has_band(selected_band)}
for s in all_antennas if s.network == an_array],
value=[s.codename for s in all_antennas \
if (s.codename in selected_antennas) and (s.network == an_array)],
className='antcheck')]) for an_array in sorted_networks]


@app.callback([Output('error_starttime', 'children'),
Output('error_endtime', 'children')],
[Input('starttime', 'value'), Input('endtime', 'value')])
def check_obstime(starttime, endtime):
if starttime != 'DD/MM/YYYY HH:MM':
try:
time0 = Time(datetime.datetime.strptime(starttime, '%d/%m/%Y %H:%M'),
format='datetime')
except ValueError as e:
return 'Incorrect format (dd/mm/YYYY HH:MM)', dash.no_update

if endtime != 'DD/MM/YYYY HH:MM':
try:
time1 = Time(datetime.datetime.strptime(endtime, '%d/%m/%Y %H:%M'),
format='datetime')
except ValueError as e:
return dash.no_update, 'Incorrect format (dd/mm/YYYY HH:MM)'

if ('time1' in locals()) and ('time0' in locals()):
if (time1 - time0) > 5*u.d:
return ["Please, put a time range smaller than 5 days."]*2

return '', ''


@app.callback(Output('error_source', 'children'),
[Input('source', 'value')])
def get_source(source_coord):
if source_coord != 'hh:mm:ss dd:mm:ss':
try:
dummy_target = observation.Source(convert_colon_coord(source_coord), 'Source')
return ''
except ValueError as e:
return "Use 'hh:mm:ss dd:mm:ss' format"
else:
return dash.no_update



@app.callback([Output('sensitivity-output', 'children'),
Output('fig-elev-time', 'figure'),
Output('fig-ant-time', 'figure')],
[Input('antenna-selection-button', 'n_clicks')],
[State('band', 'value'),
State('starttime', 'value'),
State('endtime', 'value'),
State('source', 'value'),
State('onsourcetime', 'value'),
State('datarate', 'value'),
State('subbands', 'value'),
State('channels', 'value'),
State('pols', 'value'),
State('inttime', 'value'),
State('list_stations_EVN', 'value'),
State('list_stations_eMERLIN', 'value'),
State('list_stations_VLBA', 'value'),
State('list_stations_LBA', 'value'),
State('list_stations_KVN', 'value'),
State('list_stations_Other', 'value')])
def compute_observation(n_clicks, band, starttime, endtime, source, onsourcetime, datarate,
subbands, channels, pols, inttime, *ants):
# subbands, channels, pols, inttime, ants_evn, ants_emerlin,
# ants_vlba, ants_lba, ants_kvn, ants_other):
"""Computes all products to be shown concerning the set observation.
"""
if n_clicks is None:
return dash.no_update, dash.no_update, dash.no_update
try:
target_source = observation.Source(convert_colon_coord(source), 'Source')
except ValueError as e:
return f"""Incorrect format for source coordinates:
{source} found but 'hh:mm:ss dd:mm:ss' expected.
""", dash.no_update, dash.no_update
try:
time0 = Time(datetime.datetime.strptime(starttime, '%d/%m/%Y %H:%M'),
format='datetime')
except ValueError as e:
return "Incorrect format for starttime.", dash.no_update, dash.no_update

try:
time1 = Time(datetime.datetime.strptime(endtime, '%d/%m/%Y %H:%M'),
format='datetime')
except ValueError as e:
return "Incorrect format for endtime.", dash.no_update, dash.no_update

if time0 >= time1:
return "The start time of the observation must be earlier than the end time.", \
dash.no_update, dash.no_update

if (time1 - time0) > 5*u.d:
return "Please, put a time range smaller than 5 days.", dash.no_update, dash.no_update

# TODO: this should not be hardcoded...
obs_times = time0 + np.linspace(0, (time1-time0).to(u.min).value, 50)*u.min
# obs_times = time0 + np.arange(0, (time1-time0).to(u.min).value, 15)*u.min
all_selected_antennas = list(itertools.chain.from_iterable(ants))
obs = observation.Observation(target=target_source, times=obs_times, band=band,
datarate=datarate, subbands=subbands, channels=channels,
polarizations=pols, inttime=inttime, ontarget=onsourcetime/100.0,
stations=get_selected_antennas(all_selected_antennas))


# return update_sensitivity(obs), dash.no_update, dash.no_update
return update_sensitivity(obs), get_fig_ant_elev(obs), get_fig_ant_up(obs)



def get_fig_ant_elev(obs):
data_fig = []
data_dict = obs.elevations()
# Some reference lines at low elevations
for ant in data_dict:
data_fig.append({'x': obs.times.datetime, 'y': data_dict[ant].value,
'mode': 'lines', 'hovertemplate': "Elev: %{y:.2n}º",
'name': obs.stations[ant].name})

data_fig.append({'x': obs.times.datetime, 'y': np.zeros_like(obs.times)+10,
'mode': 'lines', 'hoverinfo': 'skip', 'name': 'Elev. limit 10º',
'line': {'dash': 'dash', 'opacity': 0.5, 'color': 'gray'}})
data_fig.append({'x': obs.times.datetime, 'y': np.zeros_like(obs.times)+20,
'mode': 'lines', 'hoverinfo': 'skip', 'name': 'Elev. limit 20º',
'line': {'dash': 'dot', 'opacity': 0.5, 'color': 'gray'}})
return {'data': data_fig,
'layout': {'title': 'Source elevation during the observation',
'xaxis': {'title': 'Time (UTC)', 'showgrid': False,
'ticks': 'inside', 'showline': True, 'mirror': "all",
'hovermode': 'closest', 'color': 'black'},
'yaxis': {'title': 'Elevation (degrees)', 'range': [0., 92.],
'ticks': 'inside', 'showline': True, 'mirror': "all",
'showgrid': False, 'hovermode': 'closest'}}}



def get_fig_ant_up(obs):
data_fig = []
data_dict = obs.is_visible()
for i,ant in enumerate(data_dict):
data_fig.append({'x': obs.times.datetime[data_dict[ant]],
'y': np.zeros_like(data_dict[ant][0])+i, 'type': 'scatter',
# 'mode': 'markers', 'hoverinfo': "skip",
'mode': 'markers', 'marker': {'symbol': "41"}, 'hoverinfo': "skip",
'name': obs.stations[ant].name})

return {'data': data_fig,
'layout': {'title': 'Source visible during the observation',
'xaxis': {'title': 'Time (UTC)', 'showgrid': False,
'ticks': 'inside', 'showline': True, 'mirror': "all",
'hovermode': 'closest', 'color': 'black'},
'yaxis': {'ticks': '', 'showline': True, 'mirror': True,
'showticklabels': False, 'zeroline': False,
'showgrid': False, 'hovermode': 'closest', 'startline':False}}}





# @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]
# }]
# }







+ 7
- 0
assets/bootstrap.min.css
File diff suppressed because it is too large
View File


+ 3294
- 0
assets/style.css
File diff suppressed because it is too large
View File


+ 0
- 286
dash_main.py View File

@@ -1,286 +0,0 @@
#! /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')



+ 57
- 0
data/station_location.txt View File

@@ -0,0 +1,57 @@
station code network x y z SEFD-92 SEFD-49 SEFD-30 SEFD-21 SEFD-18 SEFD-13 SEFD-6 SEFD-5 SEFD-3.6 SEFD-1.3 SEFD-0.7
Badary Bd EVN -838201.0685 3865751.5652 4987670.8885 -1 -1 -1 330 330 330 200 -1 200 710 -1
Effelsberg Ef EVN 4033947.2616 486990.7866 4900430.9915 600 600 65 20 19 300 20 25 20 70 200
Brewster Br VLBA -2112065.2062 -3705356.5048 4726813.6759 2742 2744 -1 289 314 347 210 278 327 543 640
Fort_Davis Fd VLBA -1324009.3266 -5332181.9547 3231962.3949 2742 2744 -1 289 314 347 210 278 327 543 640
Green_Bank Gb VLBA 882589.4102 -4924872.3416 3943729.4062 35 24 13 10 10 12 13 13 15 20 30
Hartebeesthoek Hh EVN 5085442.7637 2668263.7913 -2768696.7523 -1 -1 -1 -1 430 410 650 700 630 1800 -1
Hancock Hn VLBA 1446374.8658 -4447939.6774 4322306.1838 2742 2744 -1 289 314 347 210 278 327 543 640
Hobart Ho LBA -3950237.3590 2522347.6804 -4311561.8790 -1 -1 -1 470 470 650 640 640 560 1200 1800
Kitt_Peak Kp VLBA -1995678.8402 -5037317.6968 3357328.0251 2742 2744 -1 289 314 347 210 278 327 543 640
Kunming Km EVN -1281152.8362 5640864.3586 2682653.4434 -1 -1 -1 -1 -1 350 17 17 480 -1 -1
Los_Alamos La VLBA -1449752.5839 -4975298.5757 3709123.8459 2742 2744 -1 289 314 347 210 278 327 543 640
Medicina Mc EVN 4461369.6954 919597.1240 4449559.3812 -1 -1 -1 490 700 400 170 840 320 700 -1
Metsahovi Mh EVN 2892584.8489 1311715.5980 5512640.1545 -1 -1 -1 -1 -1 4500 -1 -1 3200 2608 4500
Mauna_Kea Mk VLBA -5464075.1874 -2495248.1168 2148297.3643 2742 2744 -1 289 314 347 210 278 327 543 640
North_Liberty Nl VLBA -130872.4987 -4762317.0925 4226851.0014 2742 2744 -1 289 314 347 210 278 327 543 640
Noto Nt EVN 4934562.8407 1321201.5428 3806484.7336 -1 -1 -1 820 784 770 260 1100 770 800 900
Onsala On EVN 3370605.8010 711917.7196 5349830.9004 -1 -1 900 350 350 1110 480 850 785 1200 1310
OVRO Ov VLBA -2409150.4018 -4478573.1180 3838617.3385 2742 2744 -1 289 314 347 210 278 327 543 640
Parkes Pa LBA -4554232.4864 2816758.8662 -3454035.0137 -1 -1 -1 40 40 30 110 110 43 370 810
Pie_Town Pt VLBA -1640953.9383 -5014816.0237 3575411.7916 2742 2744 -1 289 314 347 210 278 327 543 640
Sardinia Sr EVN 4865182.7660 791922.6890 4035137.1740 -1 -1 -1 67 67 -1 -1 50 -1 138 -1
St._Croix Sc VLBA 2607848.6379 -5488069.5358 1932739.7326 2742 2744 -1 289 314 347 210 278 327 543 640
Tianma T6 EVN -2826708.6030 4679237.0770 3274667.5510 -1 -1 -1 39000 39000 46 26 26 48 70 120
Shanghai Sh EVN -2826708.6030 4679237.0770 3274667.5510 -1 -1 -1 -1 670 800 720 1500 800 -1 -1
Urumqi Ur EVN 228310.2129 4631922.7656 4367064.0638 -1 -1 -1 300 300 560 250 -1 350 850 -1
Yebes Ys EVN 4848780.1314 -261701.7668 4123035.9350 -1 -1 -1 -1 -1 1400 160 160 200 200 480
Zelenchkaya Zc EVN 3451207.5353 3060375.4139 4391915.0384 -1 -1 -1 300 300 330 400 -1 200 710 -1
JVLA_1 VLA VLBA -1601185.4286 -5041977.1754 3554875.6231 3900 -1 -1 420 420 370 310 310 250 350 560
JVLA_27 Y27 VLBA -1601185.4286 -5041977.1754 3554875.6231 167 -1 -1 17.9 17.9 15.8 13.2 13.2 10.7 15 23.9
Robledo_70 Ro7 EVN 4849336.6927 -360488.7857 4114748.8272 -1 -1 -1 -1 35 20 -1 -1 18 83 -1
Robledo_34 Ro3 EVN 4849336.6927 -360488.7857 4114748.8272 -1 -1 -1 -1 -1 150 -1 -1 106 -1 -1
Arecibo Ar EVN 2390486.9000 -5564731.4400 1994720.4500 12 -1 3 3.5 3 3 5 5 6 -1 -1
Irbene Ir EVN 3183649.615 1276902.735 5359264.576 -1 -1 -1 -1 3600 -1 430 480 1300 -1 -1
Jodrell_Bank Jb2 EVN 3822846.7600 -153802.2800 5086285.9000 -1 -1 -1 350 320 -1 320 300 -1 910 -1
Lovell Jb1 EVN 3822626.0400 -154105.6500 5086486.0400 132 83 -1 36 65 -1 80 -1 -1 -1 -1
Torun Tr EVN 3638558.5100 1221969.7200 5077036.7600 -1 -1 2000 250 300 -1 220 650 -1 500 -1
Westerbork Wb EVN 3828767.2647 442446.1739 5064921.5700 2100 -1 1680 420 560 840 1680 1600 1680 -1 -1
ATCA ATCA LBA -4751639.85972 2791700.35670 -3200491.11339 -1 -1 -1 68 68 106 70 70 86 106 180
Svetloe Sv EVN 2730173.6723 1562442.7934 5529969.1413 -1 -1 -1 360 360 330 250 -1 200 710 -1
Tidbinbilla-70 Td LBA -4460895.2445 2682361.5103 -3674747.5813 -1 -1 -1 -1 23 16 -1 -1 25 -1 60
#Warkworth Ww LBA -5115324.4279 477843.2969 -3767192.8344
#Auscope stations
Cambridge Cm eMERLIN 3920356.1500 2542.0200 5014284.4200 -1 -1 -1 200 175 -1 175 225 -1 720 -1
Darnhall Da eMERLIN 3829087.8990 -169568.9550 5081082.3460 -1 -1 -1 450 450 -1 325 325 -1 720 -1
Defford De eMERLIN 3923442.5660 -146914.3300 5009755.1250 -1 -1 -1 350 350 -1 1000 1600 -1 720 -1
Knockin Kn eMERLIN 3860084.8980 -202105.0390 5056568.8480 -1 -1 -1 425 400 -1 325 325 -1 720 -1
Pickmere Pi eMERLIN 3817549.9560 -163031.1410 5089896.6540 -1 -1 -1 400 450 -1 325 325 -1 720 -1
Ceduna Cd LBA -3753442.7457 3912709.7530 -3348067.6095 -1 -1 -1 1000 1000 400 450 550 600 750 2500
Mopra Mo LBA -4682769.05850 2802619.04217 -3291759.33837 -1 -1 -1 240 240 530 350 350 430 1300 675
Yonsei Ky KVN -3042280.9035 4045902.6564 3867374.3087 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 1919
Ulsan Ku KVN -3287268.5430 4023450.1448 3687379.9675 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 1919
Tamna Kt KVN -3171731.5532 4292678.5258 3481038.7679 -1 -1 -1 -1 -1 -1 -1 -1 -1 1288 1919
ASKAP ASKAP Other -2556743.707 5097440.315 -2847749.657 -1 -1 -1 6000 6000 -1 -1 -1 3500 -1 -1
Wettzell Wz EVN 4075627.6441 931774.2773 4801552.3784 -1 -1 -1 -1 -1 1250 -1 -1 750 -1 -1
SKA1_MID SKA Other -2556743.707 5097440.315 -2847749.657 -1 -1 -1 3 3 -1 5 5 5 8 10
Kuntunse Nk Other 6346273.5210 -33779.7127 634844.9454 -1 -1 -1 -1 -1 -1 1200 -1 -1 -1 -1

+ 7
- 0
data/style_bootstrap_ori.css
File diff suppressed because it is too large
View File


+ 10
- 0
doc/documentation.md View File

@@ -0,0 +1,10 @@



## General Comments







+ 157
- 0
doc/notes.md View File

@@ -0,0 +1,157 @@

# EVN Source Elevation App


## Program structure

- app.py
- README.md
- docs/
- src/
- stations.py
- sources.py
- plots.py
- functions.py
- data/
- antenna_positions.txt
- antenna_sefd.txt
- frequency_setups.py # or .ini. containting bands, data_rates, etc


### app.py

Layout:
- band (keyval)
- array
- starttime
- endtime
- source
- onsourcetime
- datarate
- subbands
- channels
- pols
- inttime


### stations.py

Station
- observer : Observer
- name : str
- codename : str
- network : str
- location : EarthLocation
- bands : list
- sefds : dict
- min_elevation
+ __init__(name, codename, location, freqs_sefds, min_elevation=20)
+ elevation(obs_times, target) --> ndarray
+ is_visible(obs_times, target) --> ndarray of bool?
+ has_band(band) --> bool
+ sefd(band) --> float

Stations
- name
- stations
- number_of_stations
+ __init__(name, stations)
+ add(a_station)
+ keys() : tuple

+ stations_with_freq()



### observation.py

Source
- name : str
- coord : coord.SkyCord
- dec : coord.Latitute
- ra : coord.Longitude
+ __init__(coordinates : coord.SkyCoord, name=None)

Observation
- target : FixedTarget
- times : Time
- gstimes : Longitude (hourangle)
- band : str
- wavelength : u.Quantity
- frequency : u.Quantity
- datarate : u.Quantity
- subbands : int
- channels : int
- polarizations : int
- inttime : u.Quantity
- ontarget_fraction : float
- bandwidth : u.Quantity
- bitsampling : u.Quantity
- stations : Stations
+ __init__(target=None, times=None, band=None, datarate=None,subbands=None,
channels=None, polarizations=None, inttime=None, ontarget=1.0,
stations=None, bits=2)
+ elevations() --> dict[codename]: list
+ altaz() --> dict[codename]: list
+ is_visible() --> dict[codename]: list
+ bandwidth_smearing() --> u.Quantity
+ time_smearing() --> u.Quantity
+ datasize() --> u.Quantity
+ thermal_noise() --> u.Quantity
+ get_uv()

<!-- + \_get_baseline_numbers() -->
<!-- + \_get_baseline_number(ant1, ant2) -> int -->



### plots.py


### functions.py


+ get_stations_from_file(filename) --> dict
+ stations_with_band(networks : dict/Stations, band) --> Stations


+ get_bandwidth_smearing()
+ get_time_smearing()



### freqsetups.py

Band()
- name
- frequency

Bands([fromfile])
- names
- frequencies




## For the doc:

- See the summary part, modal popover... Like accordion but only text from Bootstrap. Useful for different sections.
- There is a `address` style in Bootstrap, probably to implement in doc.


## To Do list

- Strike antenna name text if does not have the given frequency.
- Put some "loading" times.
- Add GST times.
- In sensitivity: highlight antennas that cannot observe the source.
- Add some pop up (`card` on Bootstrap) on each antenna with info: picture?, codename, country, etc.
- observation.Observation.thermal_noise() can be highly optimized.
- Help buttons and explanatory dialog.
- Arecibo limits. Currently not shown.







+ 0
- 33
html_template.py View File

@@ -1,33 +0,0 @@





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>')




+ 0
- 125
index.html View File

@@ -1,125 +0,0 @@

<!-- 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>




+ 0
- 57
index.py View File

@@ -1,57 +0,0 @@
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





BIN
mockups/mockup-antennas.png View File

Before After
Width: 2119  |  Height: 1500  |  Size: 209 KiB

BIN
mockups/mockup-app.pdf View File


BIN
mockups/mockup-images.jpg View File

Before After
Width: 2120  |  Height: 1508  |  Size: 413 KiB

BIN
mockups/mockup-plots.jpg View File

Before After
Width: 2118  |  Height: 1503  |  Size: 650 KiB

BIN
mockups/mockup-sensitivity.png View File

Before After
Width: 2120  |  Height: 1502  |  Size: 266 KiB

BIN
mockups/plot-beam.png View File

Before After
Width: 1030  |  Height: 892  |  Size: 236 KiB

BIN
mockups/plot-elev.png View File

Before After
Width: 1166  |  Height: 850  |  Size: 270 KiB

BIN
mockups/plot-uv.png View File

Before After
Width: 554  |  Height: 458  |  Size: 80 KiB

BIN
mockups/plot-vis.png View File

Before After
Width: 1198  |  Height: 906  |  Size: 146 KiB

+ 0
- 33
plot_functions.py View File

@@ -1,33 +0,0 @@
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}



+ 0
- 5
requirements.txt View File

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

+ 0
- 147
sources.py View File

@@ -1,147 +0,0 @@
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


BIN
src/__pycache__/freqsetups.cpython-36.pyc View File


BIN
src/__pycache__/freqsetups.cpython-37.pyc View File


BIN
src/__pycache__/functions.cpython-36.pyc View File


BIN
src/__pycache__/functions.cpython-37.pyc View File


BIN
src/__pycache__/observation.cpython-36.pyc View File


BIN
src/__pycache__/observation.cpython-37.pyc View File


BIN
src/__pycache__/stations.cpython-36.pyc View File


BIN
src/__pycache__/stations.cpython-37.pyc View File


+ 29
- 0
src/freqsetups.py View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python3

import enum



# class bands():
# """Observing bands available for the array.
# """
# @property
#
# def __init__(self, fromfile='../data/frequency_setups.py'):

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 = (2**i for i in range(13, 2, -1)) # from 4 to 4096
subbands = (2**i for i in range(5, 0, -1)) # from 1 to 16
channels = (2**i for i in range(14, 4, -1)) # from 16 to 8192.
polarizations = {4: '4 (Full polarization)', 2: '2 (dual polarization)',
1: '1 (single polarization)'}
inttimes = {16: '16 s', 8: '8 s', 4: '4 s', 2: '2 s', 1: '1 s', 0.5: '0.5 s',
0.25: '0.25 s', 0.125: '125 ms', 0.06: '60 ms', 0.03: '30 ms', 0.015: '15 ms',
0.001: '1 ms'}




+ 116
- 0
src/functions.py View File

@@ -0,0 +1,116 @@
"""Different functions that are required to operate the program
"""

from astropy import coordinates as coord
from astropy import units as u
from astropy.io import ascii

from src import stations


def get_networks_from_file(filename='data/station_location.txt'):
"""Retrieves all the stations given in the filename and
creates different arrays. Returns a dict with all arrays.
The file must contain the information for each antenna on each
row, with the following header (written at the beginning of the file):
- station : full name of the station
- code : code name of the station
- network : name of the network the station belongs to.
- x : x coordinates of the station, in meters.
- y : y coordinates of the station, in meters.
- z : z coordinates of the station, in meters.
- SEFD-XX : SEFD of the station at XX cm (-1 if this band cannot be observed)
"""
datafile = ascii.read(filename)
# Creates a `Stations` object for each network, for now with no stations
network_labels = set(datafile['network'])
networks = {}
for network in network_labels:
networks[network] = stations.Stations(network, [])

sefd_names = datafile.colnames[6:]
for a_line in datafile:
# Create each station and add it to the correct noetwork
sefds = {}
for sefd_name in sefd_names:
if a_line[sefd_name] > 0.0:
sefds[f"{sefd_name.replace('SEFD-','').strip()}cm"] = a_line[sefd_name]

a_loc = coord.EarthLocation(a_line['x']*u.m, a_line['y']*u.m, a_line['z']*u.m)
# For now this is hard-coded
if a_line['station'] is 'Arecibo':
min_elev = 80*u.deg
else:
min_elev = 10*u.deg

new_station = stations.SelectedStation(a_line['station'], a_line['code'],
location=a_loc, freqs_sefds=sefds, min_elevation=min_elev, selected=False)

networks[a_line['network']].add(new_station)

return networks

def get_stations_from_file(filename='data/station_location.txt'):
"""Retrieves all the stations given in the filename and
creates a Stations object containing all stations, which is returned.
The file must contain the information for each antenna on each
row, with the following header (written at the beginning of the file):
- station : full name of the station
- code : code name of the station
- network : name of the network the station belongs to.
- x : x coordinates of the station, in meters.
- y : y coordinates of the station, in meters.
- z : z coordinates of the station, in meters.
- SEFD-XX : SEFD of the station at XX cm (-1 if this band cannot be observed)
"""
datafile = ascii.read(filename)
networks = stations.Stations('network', [])
sefd_names = datafile.colnames[6:]
for a_line in datafile:
# Create each station and add it to the correct noetwork
sefds = {}
for sefd_name in sefd_names:
if a_line[sefd_name] > 0.0:
sefds[f"{sefd_name.replace('SEFD-','').strip()}cm"] = a_line[sefd_name]

a_loc = coord.EarthLocation(a_line['x']*u.m, a_line['y']*u.m, a_line['z']*u.m)
# For now this is hard-coded
if a_line['station'] is 'Arecibo':
min_elev = 80*u.deg
else:
min_elev = 10*u.deg

new_station = stations.SelectedStation(a_line['station'], a_line['code'],
network=a_line['network'], location=a_loc, freqs_sefds=sefds,
min_elevation=min_elev, selected=False)

networks.add(new_station)

return networks


def stations_with_band(networks, band, output_network_name=None):
"""For a given collection of networks or Stations, returns a Stations object
including all stations that can observe at the given band.

- networks : dict [name_network]: Stations or Stations
- band : str
"""
if output_network_name is None:
output_network_name = f"Stations@{band}"

antennas = stations.Stations(output_network_name, [])
if isinstance(networks, dict):
for network in networks:
for station in networks[network]:
if band in station.bands:
antennas.add(station)
elif isinstance(networks, stations.Stations):
for station in networks:
if band in station.bands:
antennas.add(station)
else:
raise ValueError(f"{networks} expected to be either dict of Stations type.")
return antennas



+ 330
- 0
src/observation.py View File

@@ -0,0 +1,330 @@

"""Defines an observation with given network(s), a target source,
time range and the observing band.
"""

import numpy as np
from astropy.time import Time
from astropy import coordinates as coord
from astropy import units as u
from astroplan import FixedTarget

from . import stations


class Source(FixedTarget):
"""Defines a source with some coordinates and a name.
Inputs
- coordinates : str
In a format recognized by astropy.coordinates.SkyCoord
(e.g. XXhXXmXXs XXdXXmXXs)
- name : str (optional)
Name associated to the source
"""
def __init__(self, coordinates, name=None):
super().__init__(coord.SkyCoord(coordinates), name)


class Observation(object):
def __init__(self, target=None, times=None, band=None, datarate=None,subbands=None,
channels=None, polarizations=None, inttime=None, ontarget=1.0,
stations=None, bits=2):
if target is not None:
self.target = target
if times is not None:
self.times = times
if band is not None:
self.band = band
if datarate is not None:
self.datarate = datarate
if subbands is not None:
self.subbands = subbands
if channels is not None:
self.channels = channels
if polarizations is not None:
self.polarizations = polarizations
if inttime is not None:
self.inttime = inttime
if stations is not None:
self.stations = stations

self.bitsampling = bits
self.ontarget_fraction = ontarget


@property
def target(self):
return self._target

@target.setter
def target(self, new_target):
assert isinstance(new_target, Source)
self._target = new_target
# TODO: update the elevation...

@property
def times(self):
return self._times

@times.setter
def times(self, new_times):
assert isinstance(new_times, Time)
self._times = new_times
self._gstimes = self._times.sidereal_time('mean', 'greenwich')