Browse Source

First major release

pull/1/head
Benito Marcote 2 years ago
parent
commit
4621f48c17
  1. 236
      app.py
  2. 2050
      assets/bootstrap-grid.css
  3. 1
      assets/bootstrap-grid.css.map
  4. 7
      assets/bootstrap-grid.min.css
  5. 1
      assets/bootstrap-grid.min.css.map
  6. 330
      assets/bootstrap-reboot.css
  7. 1
      assets/bootstrap-reboot.css.map
  8. 8
      assets/bootstrap-reboot.min.css
  9. 1
      assets/bootstrap-reboot.min.css.map
  10. 6328
      assets/bootstrap.bundle.js
  11. 1
      assets/bootstrap.bundle.js.map
  12. 7
      assets/bootstrap.bundle.min.js
  13. 1
      assets/bootstrap.bundle.min.js.map
  14. 8975
      assets/bootstrap.css
  15. 1
      assets/bootstrap.css.map
  16. 3894
      assets/bootstrap.js
  17. 1
      assets/bootstrap.js.map
  18. 8
      assets/bootstrap.min.css
  19. 1
      assets/bootstrap.min.css.map
  20. 7
      assets/bootstrap.min.js
  21. 1
      assets/bootstrap.min.js.map
  22. 4
      assets/font-awesome.min.css
  23. BIN
      assets/logo_evn.png
  24. BIN
      assets/logo_jive.png
  25. 157
      assets/style.css
  26. 2
      data/station_location.txt
  27. 12
      doc/notes.md
  28. BIN
      src/__pycache__/freqsetups.cpython-37.pyc
  29. BIN
      src/__pycache__/functions.cpython-36.pyc
  30. BIN
      src/__pycache__/functions.cpython-37.pyc
  31. BIN
      src/__pycache__/observation.cpython-36.pyc
  32. BIN
      src/__pycache__/observation.cpython-37.pyc
  33. BIN
      src/__pycache__/stations.cpython-36.pyc
  34. 16
      src/freqsetups.py
  35. 38
      src/functions.py
  36. 78
      src/observation.py
  37. 2
      test/test.py

236
app.py

@ -62,7 +62,7 @@ default_arrays = {'EVN': ['Ef', 'Hh', 'Jb2', 'Mc', 'Nt', 'Ur', 'On', 'Sr', 'T6', @@ -62,7 +62,7 @@ default_arrays = {'EVN': ['Ef', 'Hh', 'Jb2', 'Mc', 'Nt', 'Ur', 'On', 'Sr', 'T6',
'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'],
'VLBA': ['Br', 'Fd', 'Hn', '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',
@ -83,36 +83,6 @@ target_source = observation.Source('1h2m3s +50d40m30s', 'Source') @@ -83,36 +83,6 @@ 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)
**Smearing in the Field of View** (for a 10% loss):
Field of View limited by bandwidth-smearing to: {bw_smearing:.3n}
Field of View limited by time-smearing to: {t_smearing:.3n}
"""
@ -139,8 +109,17 @@ server = app.server @@ -139,8 +109,17 @@ server = app.server
##################### This is the webpage layout
app.layout = html.Div([
html.Div([
html.H1('EVN Source Visibility'),
html.Div(id='banner', className='navbar-brand d-flex p-3 shadow-sm', children=[
html.A(className='d-inline-block mr-md-auto', href="https://www.evlbi.org", children=[
html.Img(height='70px', src=app.get_asset_url("logo_evn.png"),
alt='European VLBI Network (EVN)',
className="d-inline-block align-top"),
]),
html.H2('EVN Source Visibility', className='d-inline-block align-middle mx-auto'),
html.A(className='d-inline-block ml-auto pull-right', href="https://www.jive.eu", children=[
html.Img(src=app.get_asset_url("logo_jive.png"), height='70px',
alt='Joinst Institute for VLBI ERIC (JIVE)')
])
# html.Img(src='http://www.ira.inaf.it/evnnews/archive/evn.gif')
]),
# ], className='banner'),
@ -149,14 +128,16 @@ app.layout = html.Div([ @@ -149,14 +128,16 @@ app.layout = html.Div([
dcc.ConfirmDialog(id='global-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=[
dcc.Tabs(parent_className='custom-tabs', className='custom-tabs-container', children=[
dcc.Tab(label='Observation Setup', className='custom-tab',
selected_className='custom-tab--selected', children=[
# Elements in first column ()
html.Div(className='row-cols-3', children=[
html.Div(className='row justify-content-center', children=[
html.Div(className='col-sm-3', style={'max-width': '300px','float': 'left',
'padding': '2%'}, children=[
html.Div(className='form-group', children=[
html.Label('Observing Band'),
#data-content="This popover appears on the right", data-toggle="popover"),
dcc.Dropdown(id='band', persistence=True,
options=[{'label': fs.bands[b], 'value': b} for b \
in fs.bands], value='18cm'),
@ -169,7 +150,7 @@ app.layout = html.Div([ @@ -169,7 +150,7 @@ app.layout = html.Div([
]),
html.Div(className='input-group-prepend', children=[
dcc.Checklist(id='e-EVN', className='checkbox', persistence=True,
options=[{'label': ' e-EVN (real-time) mode?',
options=[{'label': ' e-EVN (real-time) mode',
'value': 'e-EVN'}], value=[]),
]),
html.Div(className='form-group', children=[
@ -238,7 +219,13 @@ app.layout = html.Div([ @@ -238,7 +219,13 @@ app.layout = html.Div([
])
]),
# html.Div(style={'margin-top': '20px'}, children=[
html.Div(className='col-lg-7', style={'float': 'left'}, children=[
html.Div(className='col-9', children=[
html.Div(className='col-9 text-center justify-content-center', children=[
html.Button('Compute Observation', id='antenna-selection-button',
className='btn btn-primary btn-lg'),
dcc.Loading(id="loading", children=[html.Div(id="loading-output")],
type="dot")
]),
html.Div(id='antennas-div', className='container', children=[
# List with all antennas
html.Div(className='antcheck', children=[html.Br(),
@ -254,23 +241,24 @@ app.layout = html.Div([ @@ -254,23 +241,24 @@ app.layout = html.Div([
]) 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'}),
])
# html.Div(className='col-sm-2', style={'float': 'left'}, children=[
# ])
])
]),
dcc.Tab(label='Sensitivity', children=[
html.Div(className='col-md-8', children=[
dcc.Tab(label='Summary', className='custom-tab',
selected_className='custom-tab--selected', children=[
html.Div(className='row justify-content-center', children=[
# Sensitivity calculations
dcc.Markdown(id='sensitivity-output',
children="Set the observation first.")
# dcc.Markdown(id='sensitivity-output',
# children="Set the observation first.")
html.Div(className='col-12', id='sensitivity-output',
children=[html.Br(), html.P("Set the observation first.")])
])
]),
dcc.Tab(label='Plots', children=[
dcc.Tab(label='Elevations', className='custom-tab',
selected_className='custom-tab--selected', children=[
html.Div(className='row justify-content-center', children=[
html.Div(className='col-md-8', children=[
# Elevation VS time
html.Div([
@ -280,22 +268,26 @@ app.layout = html.Div([ @@ -280,22 +268,26 @@ app.layout = html.Div([
html.Div([
dcc.Graph(id='fig-ant-time')
])
])
])])
]),
dcc.Tab(label='Images', children=[
dcc.Tab(label='Imaging', className='custom-tab',
selected_className='custom-tab--selected', children=[
# Images
html.Div(className='row justify-content-center', children=[
html.Div(className='col-md-8', children=[
# dcc.Markdown(children="""To be implemented.
# The uv coverage and expected dirty images will go here.""")
html.Div([dcc.Graph(id='fig-uvplane')])
])
])])
]),
dcc.Tab(label='Help', children=[
dcc.Tab(label='Documentation', className='custom-tab',
selected_className='custom-tab--selected', children=[
# Documentation
html.Div(className='row justify-content-center', children=[
html.Div(className='col-md-8', children=[
dcc.Markdown(children="""The Help/doc.
All explanations and technical details will go here.""")
])
])])
])
])
])
@ -355,40 +347,89 @@ def optimal_units(value, units): @@ -355,40 +347,89 @@ def optimal_units(value, units):
return value.to(units[-1])
def update_sensitivity(an_obs):
def create_sensitivity_card(title, message):
"""Defines one of the cards that are shown in the Sensitivity tab. Each tab
shows a title `title` and a message `message`.
If message is a list (of strings), it is assumed as different paragraphs.
It returns the HTML code of the card.
"""
ps = []
if type(message) is list:
for a_msg in message:
ps.append(html.P(className='card-text', children=a_msg))
else:
ps = [html.P(className='card-text', children=message)]
# return [html.Div(className='card', style={'min-width': '15rem', 'max-width': '25rem'}, children=[
return [html.Div(className='card m-3', children=[
html.Div(className='card-body', children=[
html.H5(className='card-title', children=title)] + ps)])
]
def update_sensitivity(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),
pol_dict = {1: 'single', 2: 'dual', 4: 'full'}
rms = optimal_units(obs.thermal_noise(), [u.MJy, u.kJy, u.Jy, u.mJy, u.uJy])
rms_time = optimal_units(obs.thermal_noise()/np.sqrt(obs.inttime/obs.duration),
[u.MJy, u.kJy, u.Jy, u.mJy, u.uJy])
rms_channel = optimal_units(rms*np.sqrt(obs.subbands*obs.channels),
[u.MJy, u.kJy, u.Jy, u.mJy, u.uJy])
ants_up = an_obs.is_visible()
ants_up = 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())
ant_text = ', '.join(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]))
ant_text += f"<p class='text-danger'>Note that {', '.join(ant_no_obs)} cannot observe the source.</p>"
cards = []
# The time card
temp_msg = [f"{fx.print_obs_times(obs)}."]
temp_msg += [f"Observing for {optimal_units(obs.duration, [u.h, u.min, u.s, u.ms]):.3n} in total, with {optimal_units(obs.ontarget_time, [u.h, u.min, u.s, u.ms]):.3n} on target."]
temp_msg += [f"With a time integration of {optimal_units(obs.inttime, [u.s,u.ms,u.us]):.2n} the expected FITS file size is {optimal_units(obs.datasize(), [u.TB, u.GB, u.MB, u.kB]):.3n}."]
cards += create_sensitivity_card('Observing Time', temp_msg)
# Antennas
longest_bl = obs.longest_baseline()[1]
# Using dummy units to allow the conversion
longest_bl_lambda = optimal_units(((longest_bl*u.m)/obs.wavelength).to(u.m),
[u.Gm, u.Mm, u.km])
temp_msg = [f"{len(ants_up)-len(ant_no_obs)} participating antennas: {ant_text}"]
temp_msg += [f"The longest (projected) baseline is {optimal_units(longest_bl, [u.km, u.m]):.5n} ({longest_bl_lambda.value:.3n} {longest_bl_lambda.unit.name[0]}lambda)."]
synthbeam = obs.synthesized_beam()
synthbeam_units = optimal_units(synthbeam['bmaj'], [u.arcsec, u.mas, u.uas]).unit
temp_msg += [f"The expected synthesized beam will be approx. {synthbeam['bmaj'].to(synthbeam_units).value:.2n} x {synthbeam['bmin'].to(synthbeam_units):.2n}^2, PA = {synthbeam['pa']:.2n}."]
cards += create_sensitivity_card('Antennas', temp_msg)
# Frequency
temp_msg = [f"Observing at a central frequency of {optimal_units(obs.frequency, [u.GHz, u.MHz]):.3n} ({optimal_units(obs.wavelength, [u.m, u.cm, u.mm]):.2n})."]
temp_msg += [f"The total bandwidth of {optimal_units(obs.bandwidth, [u.GHz, u.MHz, u.kHz]):.3n} will be divided into {obs.subbands} subbands of {optimal_units(obs.bandwidth/obs.subbands, [u.GHz, u.MHz, u.kHz]):.3n} each, with {obs.channels} channels ({optimal_units(obs.bandwidth/(obs.subbands*obs.channels), [u.GHz, u.MHz, u.kHz, u.Hz]):.3n} wide)."]
temp_msg += [f"Recording {pol_dict[obs.polarizations]} circular polarization."]
cards += create_sensitivity_card('Frequency Setup', temp_msg)
# RMS
temp_msg = [f"Considering the sensitivities of the antennas, the estimated thermal noise is {rms:.3n}/beam."]
temp_msg += [f"This would imply a rms of {rms_channel:.3n}/beam per spectral channel, or approx. {rms_time:.3n}/beam per time integration ({optimal_units(obs.inttime, [u.s,u.ms,u.us]):.3n})."]
cards += create_sensitivity_card('Sensitivity', temp_msg)
# resolution and FITS files
# temp_msg = [f""]
# cards += create_sensitivity_card('Resolution')
# FoV smearing
shortest_bl = obs.shortest_baseline()[1]
largest_ang_scales = ((2.063e8*u.mas)*(obs.wavelength/shortest_bl)).to(u.mas)
temp_msg = [f"The Field of View would be limited by time smearing to {optimal_units(obs.time_smearing(), [u.arcmin, u.arcsec]):.3n} and by frequency smearing to {optimal_units(obs.bandwidth_smearing(), [u.arcmin, u.arcsec]):.3n} (for a 10% loss)."]
temp_msg += [f"Considering the shortest baseline in the array ({optimal_units(shortest_bl, [u.km, u.m]):.5n}), you will filter out emission on angular scales larger than {optimal_units(largest_ang_scales, [u.arcmin, u.arcsec, u.mas]):.3n}."]
cards += create_sensitivity_card('FoV limitations', temp_msg)
return [html.Div(className='card-deck col-12', children=cards)]
@ -478,7 +519,8 @@ def get_source(source_coord): @@ -478,7 +519,8 @@ def get_source(source_coord):
@app.callback([Output('sensitivity-output', 'children'),
@app.callback([Output('loading-output', 'children'),
Output('sensitivity-output', 'children'),
Output('fig-elev-time', 'figure'),
Output('fig-ant-time', 'figure'),
Output('fig-uvplane', 'figure'), Output('global-error', 'message')],
@ -506,34 +548,37 @@ def compute_observation(n_clicks, band, starttime, endtime, source, onsourcetime @@ -506,34 +548,37 @@ def compute_observation(n_clicks, band, starttime, endtime, source, onsourcetime
"""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, dash.no_update, dash.no_update
return '', dash.no_update, dash.no_update, 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:
return "Incorrect format for source coordinates", f"""Incorrect format for source coordinates:
{source} found but 'hh:mm:ss dd:mm:ss' expected.
""", dash.no_update, dash.no_update, dash.no_update, dash.no_update
try:
time0 = Time(datetime.datetime.strptime(starttime, '%d/%m/%Y %H:%M'),
format='datetime')
format='datetime', scale='utc')
except ValueError as e:
return "Incorrect format for starttime.", dash.no_update, dash.no_update, \
dash.no_update, dash.no_update
return "Incorrect format for starttime", "Incorrect format for starttime.", \
dash.no_update, dash.no_update, dash.no_update, dash.no_update
try:
time1 = Time(datetime.datetime.strptime(endtime, '%d/%m/%Y %H:%M'),
format='datetime')
format='datetime', scale='utc')
except ValueError as e:
return "Incorrect format for endtime.", dash.no_update, dash.no_update, \
dash.no_update, dash.no_update
return "Incorrect format for endtime", "Incorrect format for endtime.", \
dash.no_update, dash.no_update, 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, dash.no_update, dash.no_update
return "The start time of the observation must be earlier than the end time", \
"The start time of the observation must be earlier than the end time.", \
dash.no_update, dash.no_update, 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, dash.no_update, dash.no_update
return "Please, put a time range smaller than 5 days", \
"Please, put a time range smaller than 5 days.", \
dash.no_update, dash.no_update, dash.no_update, dash.no_update
# try:
# TODO: this should not be hardcoded...
@ -550,8 +595,9 @@ def compute_observation(n_clicks, band, starttime, endtime, source, onsourcetime @@ -550,8 +595,9 @@ def compute_observation(n_clicks, band, starttime, endtime, source, onsourcetime
# return update_sensitivity(obs), dash.no_update, dash.no_update
return update_sensitivity(obs), get_fig_ant_elev(obs), get_fig_ant_up(obs), \
get_fig_uvplane(obs), dash.no_update
# TODO: parallelize all these functions
return 'You can check now the results in the different tabs', update_sensitivity(obs), \
get_fig_ant_elev(obs), get_fig_ant_up(obs), get_fig_uvplane(obs), dash.no_update
@ -598,7 +644,7 @@ def get_fig_ant_up(obs): @@ -598,7 +644,7 @@ def get_fig_ant_up(obs):
'name': obs.stations[ant].name})
return {'data': data_fig,
'layout': {'title': 'Source visible during the observation',
'layout': {'title': 'Source visible (>10 deg) during the observation',
'xaxis': {'title': 'Time (UTC)', 'showgrid': False,
'ticks': 'inside', 'showline': True, 'mirror': "all",
'hovermode': 'closest', 'color': 'black'},
@ -619,7 +665,7 @@ def get_fig_uvplane(obs): @@ -619,7 +665,7 @@ def get_fig_uvplane(obs):
uv[:len(bl_uv[bl_name]), :] = bl_uv[bl_name]
uv[len(bl_uv[bl_name]):, :] = -bl_uv[bl_name]
data_fig.append({'x': uv[:,0],
'y': uv[:,1],
'y': uv[:,1],
# 'type': 'scatter', 'mode': 'lines',
'type': 'scatter', 'mode': 'markers',
'marker': {'symbol': '.', 'size': 2},

2050
assets/bootstrap-grid.css vendored

File diff suppressed because it is too large Load Diff

1
assets/bootstrap-grid.css.map

File diff suppressed because one or more lines are too long

7
assets/bootstrap-grid.min.css vendored

File diff suppressed because one or more lines are too long

1
assets/bootstrap-grid.min.css.map

File diff suppressed because one or more lines are too long

330
assets/bootstrap-reboot.css vendored

@ -0,0 +1,330 @@ @@ -0,0 +1,330 @@
/*!
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: transparent;
}
@-ms-viewport {
width: device-width;
}
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg:not(:root) {
overflow: hidden;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: .5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

1
assets/bootstrap-reboot.css.map

File diff suppressed because one or more lines are too long

8
assets/bootstrap-reboot.min.css vendored

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

1
assets/bootstrap-reboot.min.css.map

File diff suppressed because one or more lines are too long

6328
assets/bootstrap.bundle.js

File diff suppressed because it is too large Load Diff

1
assets/bootstrap.bundle.js.map

File diff suppressed because one or more lines are too long

7
assets/bootstrap.bundle.min.js vendored

File diff suppressed because one or more lines are too long

1
assets/bootstrap.bundle.min.js.map

File diff suppressed because one or more lines are too long

8975
assets/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

1
assets/bootstrap.css.map

File diff suppressed because one or more lines are too long

3894
assets/bootstrap.js vendored

File diff suppressed because it is too large Load Diff

1
assets/bootstrap.js.map

File diff suppressed because one or more lines are too long

8
assets/bootstrap.min.css vendored

File diff suppressed because one or more lines are too long

1
assets/bootstrap.min.css.map

File diff suppressed because one or more lines are too long

7
assets/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

1
assets/bootstrap.min.js.map

File diff suppressed because one or more lines are too long

4
assets/font-awesome.min.css vendored

File diff suppressed because one or more lines are too long

BIN
assets/logo_evn.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
assets/logo_jive.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

157
assets/style.css

@ -45,7 +45,8 @@ @@ -45,7 +45,8 @@
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
text-size-adjust: 90%;
-webkit-text-size-adjust: 90%;
-webkit-tap-highlight-color:rgba(0, 0, 0, 0)
}
@ -3199,7 +3200,7 @@ input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="butto @@ -3199,7 +3200,7 @@ input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="butto
/* Custom styles */
input[type=checkbox] {
-webkit-appearance: none;
-moz-appearance: none;
@ -3208,9 +3209,11 @@ input[type=checkbox] { @@ -3208,9 +3209,11 @@ input[type=checkbox] {
}
input[type=checkbox] {
width: 25px;
height: 20px;
margin-right: 8px;
width: 17px;
height: 17px;
line-height: 17px;
margin-right: 0px;
margin-bottom: 20px;
cursor: pointer;
font-size: 17px;
visibility: hidden;
@ -3222,12 +3225,13 @@ input[type=checkbox]:after { @@ -3222,12 +3225,13 @@ input[type=checkbox]:after {
background-color: #fff;
display: inline-block;
color: #00BFF0;
width: 19px;
height: 19px;
width: 17px;
height: 17px;
visibility: visible;
border: 1px solid #00BFF0;
padding-left: 5px;
border-radius: 5px;
margin-bottom: 0px;
}
input[type=checkbox]:not(:disabled):after {
@ -3235,45 +3239,146 @@ input[type=checkbox]:not(:disabled):after { @@ -3235,45 +3239,146 @@ input[type=checkbox]:not(:disabled):after {
font-weight: bold;
color: #00BFF0;
border: 1px solid #00BFF0;
}
}
input[type=checkbox]:hover:after {
content: " ";
input[type=checkbox]:not(:disabled):not(checked):hover:after {
content: "";
color: gray;
background-color: rgb(232, 222, 222);
}
}
input[type=checkbox]:checked:after {
content: "\2714";
font-family: "FontAwesome";
/* content: "\f00c"; */
font-weight: bold;
color: green;
border: 1px solid green;
background-color: rgb(176, 225, 176);
font-size: 1.1rem;
-moz-font-size: 1.1rem;
/* line-height: 0px; */
border: none;
background-color: none;
margin-left: -0.1rem;
}
input[type=checkbox]:disabled:after {
content: "X";
border: 1px solid #FF304F;
color: red;
/* background-color: #dc3545; */
background-color: rgba(234, 116, 113);
input[type=checkbox]:not(:disabled):checked:hover:after {
content: " ";
color: gray;
background-color: rgb(232, 222, 222);
content: "\2714";
font-weight: bold;
color: green;
font-size: 1.5rem;
}
/* input[type=checkbox]:disabled:before+label { */
/* color: red; */
/* } */
input[type=checkbox]:disabled:after {
/* content: "X"; */
font-family: "FontAwesome";
content: "\2715";
border: none;
/* color: #a01d26; */
color: #F0959B;
background-color: none;
margin-left: -0.1rem;
}
.antcheck label {
padding-left: 10pt;
padding-right: 10pt;
padding-left: 10pt;
padding-right: 10pt;
width: 135pt;
}
.card {
min-width: 19rem;
max-width: 19rem;
border: 1px solid #CCCCCC;
border-left: 5px solid #a01d26;
}
.card-title {
color: #a01d26;
}
.btn-primary {
color: #a01d26;
background-color: #F0959B;
border-color: #F0959B;
margin-top: 50px;
width: 20rem;
}
.btn-primary:hover {
color: #fff;
background-color: #a01d26;
border-color: #a01d26;
}
.btn-primary:focus, .btn-primary.focus {
box-shadow:0 0 0 .2rem rgba(255, 79, 105, 0.5)
}
.custom-tab:active {
color: #586069;
/* border-top-left-radius: 3px; */
/* border-top-right-radius: 3px; */
/* border-top: 3px solid transparent !important; */
/* border-left: 0px !important; */
/* border-right: 0px !important; */
/* border-bottom: 0px !important; */
background-color: #f4f4ef;
border-color: #a01d26;
/* border-bottom: 3px solid #a01d26; */
/* padding: 12px !important; */
/* font-family: "system-ui"; */
/* display: flex !important; */
/* align-items: center; */
/* justify-content: center; */
}
.custom-tabs-container .tab--selected {
background-color: #F0959B;
color: #a01d26;
border-color: #a01d26;
border-top: 3px solid #a01d26 !important;
}
.navbar-brand {
height: 70px;
width: 100%;
margin: 0 auto;
padding: 0 20px;
border: none;
text-align: right;
min-height: 50px;
background: #f4f4ef;
}
.banner:after {
content: " ";
display: table;
clear: both;
}
.navbar-brand a {
margin: 0;
padding: 0;
}
.navbar-brand img {
margin: 0;
padding: 0;
margin-top: -15px;
}
/* FOR TEXT INPUTS I SHOULD ADD AN OFFSET SHADOW (REFACTORING UI TRICKS):
box-shadow: inset 0 2px 4px 0 hsla(0 0% 0& 0.08);
*/)
*/
/* evlbi.org color: #a01d26 */
/* --blue: #007bff; */
/* --indigo: #6610f2; */

2
data/station_location.txt

@ -25,7 +25,7 @@ Tianma T6 EVN -2826708.6030 4679237.0770 3274667.5510 -1 @@ -25,7 +25,7 @@ Tianma T6 EVN -2826708.6030 4679237.0770 3274667.5510 -1
Shanghai Sh EVN -2826708.6030 4679237.0770 3274667.5510 -1 -1 -1 -1 670 800 720 1500 800 -1 -1 -1 -1
Urumqi Ur EVN 228310.2129 4631922.7656 4367064.0638 -1 -1 -1 300 300 560 250 -1 350 850 -1 -1 -1
Yebes Ys EVN 4848780.1314 -261701.7668 4123035.9350 -1 -1 -1 -1 -1 1400 160 160 200 200 480 -1 -1
Zelenchkaya Zc EVN 3451207.5353 3060375.4139 4391915.0384 -1 -1 -1 300 300 330 400 -1 200 710 -1 -1 -1
Zelenchukskaya Zc EVN 3451207.5353 3060375.4139 4391915.0384 -1 -1 -1 300 300 330 400 -1 200 710 -1 -1 -1
JVLA_1 VLA VLBA -1601185.4286 -5041977.1754 3554875.6231 3900 -1 -1 420 420 370 310 310 250 350 560 -1 -1
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 -1 -1
Robledo_70 Ro7 EVN 4849336.6927 -360488.7857 4114748.8272 -1 -1 -1 -1 35 20 -1 -1 18 83 -1 -1 -1

12
doc/notes.md

@ -76,6 +76,7 @@ Observation @@ -76,6 +76,7 @@ Observation
- target : FixedTarget
- times : Time
- gstimes : Longitude (hourangle)
- duration : Time
- band : str
- wavelength : u.Quantity
- frequency : u.Quantity
@ -85,6 +86,7 @@ Observation @@ -85,6 +86,7 @@ Observation
- polarizations : int
- inttime : u.Quantity
- ontarget_fraction : float
- ontarget_time : Time
- bandwidth : u.Quantity
- bitsampling : u.Quantity
- stations : Stations
@ -94,6 +96,8 @@ Observation @@ -94,6 +96,8 @@ Observation
+ elevations() --> dict[codename]: list
+ altaz() --> dict[codename]: list
+ is_visible() --> dict[codename]: list
+ longest_baseline() --> (str, u.Quantity)
+ shortest_baseline() --> (str, u.Quantity)
+ bandwidth_smearing() --> u.Quantity
+ time_smearing() --> u.Quantity
+ datasize() --> u.Quantity
@ -113,11 +117,9 @@ Observation @@ -113,11 +117,9 @@ Observation
+ get_stations_from_file(filename) --> dict
+ stations_with_band(networks : dict/Stations, band) --> Stations
+ print_obs_times(obs, dateformat='%d &m &Y')
+ get_bandwidth_smearing()
+ get_time_smearing()
### freqsetups.py
@ -143,8 +145,8 @@ Bands([fromfile]) @@ -143,8 +145,8 @@ Bands([fromfile])
- 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.
X Add GST times.
X 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.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

16
src/freqsetups.py

@ -11,14 +11,14 @@ import enum @@ -11,14 +11,14 @@ import enum
#
# def __init__(self, fromfile='../data/frequency_setups.py'):
bands = {'92cm': 'P - 92 cm / 0.33 GHz', '49cm': 'P - 49 cm / 0.6 GHz',
'30cm': 'UHF - 30 cm / 1 GHz', '21cm': 'L - 21 cm / 1.4 GHz',
'18cm': 'L - 18 cm / 1.7 GHz', '13cm': 'S - 13 cm / 2.3 GHz',
'6cm': 'C - 6cm / 5 GHz', '5cm': 'C - 5 cm / 6 GHz',
'3.6cm': 'X - 3.6 cm / 8.3 GHz', '2cm': 'U - 2 cm / 15 GHz',
'1.3cm': 'K - 1.3 cm / 23 GHz', '0.9cm': 'Ka - 0.9 cm / 33 GHz',
'0.7cm': 'Q - 0.7 cm / 43 GHz', '0.3cm': 'W - 0.3 cm / 100 GHz',
'0.1cm': '0.1 cm / 300 GHz'}
bands = {'92cm': 'P band (92 cm - 0.33 GHz)', '49cm': 'P band (49 cm - 0.6 GHz)',
'30cm': 'UHF band (30 cm - 1 GHz)', '21cm': 'L band (21 cm - 1.4 GHz)',
'18cm': 'L band (18 cm - 1.7 GHz)', '13cm': 'S band (13 cm - 2.3 GHz)',
'6cm': 'C band (6cm - 5 GHz)', '5cm': 'M band (5 cm - 6 GHz)',
'3.6cm': 'X band (3.6 cm - 8.3 GHz)', '2cm': 'U band (2 cm - 15 GHz)',
'1.3cm': 'K band (1.3 cm - 23 GHz)', '0.9cm': 'Ka band (0.9 cm - 33 GHz)',
'0.7cm': 'Q band (0.7 cm - 43 GHz)', '0.3cm': 'W band (0.3 cm - 100 GHz)',
'0.1cm': '0.1 cm - 300 GHz'}
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

38
src/functions.py

@ -114,3 +114,41 @@ def stations_with_band(networks, band, output_network_name=None): @@ -114,3 +114,41 @@ def stations_with_band(networks, band, output_network_name=None):
return antennas
def print_obs_times(obs, date_format='%d %b %Y'):
"""Given an observation, it returns the time range (starttime-endtime) in a smart
way. If the observation lasts for less than one day it omits the end date:
20 Jan 1971 10:00-20:00UT
Input:
- obs : observation.Observation
It must already have set the .times part with an array of astropy.Time times.
- date_format : str [optional]
Format for the date part (only the date part) of the string to represent
the time range.
Output:
- printed_time : str
A string showing the time-range of the observation.
"""
if obs.times[0].datetime.date() == obs.times[-1].datetime.date():
return "{} {}-{} UTC".format(obs.times[0].datetime.strftime(date_format),
obs.times[0].datetime.strftime('%H:%M'),
obs.times[-1].datetime.strftime('%H:%M'))
else:
return "{} {} to {} {} UTC".format(obs.times[0].datetime.strftime(date_format),
obs.times[0].datetime.strftime('%H:%M'),
obs.times[-1].datetime.strftime(date_format),
obs.times[-1].datetime.strftime('%H:%M'))

78
src/observation.py

@ -77,6 +77,10 @@ class Observation(object): @@ -77,6 +77,10 @@ class Observation(object):
def gstimes(self):
return self._gstimes
@property
def duration(self):
return self.times[-1]-self.times[0]
@property
def band(self):
return self._band
@ -167,6 +171,10 @@ class Observation(object): @@ -167,6 +171,10 @@ class Observation(object):
assert 0.0 < ontarget <= 1.0
self._ontarget = ontarget
@property
def ontarget_time(self):
return self.duration*self.ontarget_fraction
@property
def bandwidth(self):
"""Returns the total bandwidth of the observation.
@ -227,9 +235,31 @@ class Observation(object): @@ -227,9 +235,31 @@ class Observation(object):
It retuns the tuple ((ant1,ant2), length)
where `ant1,ant2` are the antennas in such baseline, and `length` its length.
"""
# TODO: To Implement
return ('XX','XX'), 1000*u.km
raise NotImplemented('observation.longest_baseline')
uv = self.get_uv()
longest_bl = {'bl': '', 'value': None}
for a_bl in uv:
bl_length = np.sqrt(np.max((uv[a_bl]**2).sum(axis=1)))
if (longest_bl['value'] is None) or (bl_length > longest_bl['value']):
longest_bl['bl'] = a_bl
longest_bl['value'] = bl_length
return longest_bl['bl'], longest_bl['value']*self.wavelength
def shortest_baseline(self):
"""Returns the shortest baseline in the observation.
It retuns the tuple ((ant1,ant2), length)
where `ant1,ant2` are the antennas in such baseline, and `length` its length.
"""
uv = self.get_uv()
shortest_bl = {'bl': '', 'value': None}
for a_bl in uv:
bl_length = np.sqrt(np.max((uv[a_bl]**2).sum(axis=1)))
if (shortest_bl['value'] is None) or (bl_length < shortest_bl['value']):
# if (bl_length < shortest_bl['value']) or (shortest_bl['value'] is None):
shortest_bl['bl'] = a_bl
shortest_bl['value'] = bl_length
return shortest_bl['bl'], shortest_bl['value']*self.wavelength
def bandwidth_smearing(self):
"""Returns the bandwidth smearing expected for the given observation.
@ -343,6 +373,48 @@ class Observation(object): @@ -343,6 +373,48 @@ class Observation(object):
return bl_uv_up
def synthesized_beam(self):
"""Estimates the resulting synthesized beam of the observations based on
the expected uv coverage.
This is just an estimation made by a ellipse fitting to the uv coverage,
from which we obtain the resolution on the two axes following
https://science.nrao.edu/facilities/vlba/docs/manuals/oss/ang-res
theta_HPBW (mas) \sim 2063 x lambda(cm)/b_max^km
returns a dict with the following keys: bmaj, bmin, pa.
"""
resolution = lambda bl : ((2.063e8*u.mas)/bl).to(u.mas)
uvdata = self.get_uv()
# Generates a N 2-D array with all uv data.
tot_length = 0
for bl_name in uvdata:
tot_length += uvdata[bl_name].shape[0]
uvvis = np.empty((tot_length, 2))
i = 0
for bl_name in uvdata:
uvvis[i:i+uvdata[bl_name].shape[0],:] = uvdata[bl_name]
i += uvdata[bl_name].shape[0]
del uvdata
# Transform the uv points into r,theta (polar) points
uvvis_polar = np.empty_like(uvvis)
uvvis_polar[:,0] = np.sqrt((uvvis**2).sum(axis=1)) # radius
uvvis_polar[:,1] = np.arctan2(uvvis[:,1], uvvis[:,0]) # theta
# Defines the BMAJ and PA
bl_bmaj = np.max(uvvis_polar[:,0])
bl_bmaj_theta = uvvis_polar[:,1][np.where(uvvis_polar[:,0] == bl_bmaj)][0]
# Estimates the BMIN
cond = np.where((uvvis_polar[:,1] > (bl_bmaj_theta+np.pi/3) % (2*np.pi)) & (uvvis_polar[:,1] < (bl_bmaj_theta+2*np.pi/3) % (2*np.pi) ))
if len(cond[0]) == 0:
cond = np.where((uvvis_polar[:,1] > (bl_bmaj_theta+4*np.pi/3) % (2*np.pi)) & (uvvis_polar[:,1] < (bl_bmaj_theta+5*np.pi/3) % (2*np.pi) ))
bl_bmin = np.max( uvvis_polar[:,0][cond] )
return {'bmaj': resolution(bl_bmin), 'bmin': resolution(bl_bmaj), 'pa': (bl_bmaj_theta*u.rad).to(u.deg)}
# def get_dirtymap(self):
# uvdata = self._get_uv()
# # Generates a N 2-D array with all uv data.

2
test/test.py

@ -24,7 +24,7 @@ from src import observation @@ -24,7 +24,7 @@ from src import observation
target = observation.Source('10h10m10s +0d30m20s', name='Target')
obs = observation.Observation(target=target)
obs.times = Time('2020-04-01 10:00') + np.arange(0, 600, 10)*u.min
obs.times = Time('2020-04-01 10:00', scale='utc') + np.arange(0, 600, 10)*u.min
obs.band = '6cm'
obs.datarate = 1024
obs.subbands = 8

Loading…
Cancel
Save