Collection of scripts and small programs used by the EVN Support Scientists at JIVE during the regular data processing of EVN observations.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
8.6 KiB

Python port of Bob Campbell's IDL script to make nominal tsys tables.
It takes the nominal SEFD values from the sefd_values.txt table and generates an ANTAB file using
these values. Gains will be set to 1/SEFD, and all Tsys to 1.0.
Note that it will overwrite any existing ANTAB file in the current path.
Version: 4.0
Date: June 2018
Author: Benito Marcote ( & Jay Blanchard (
version 4.1 changes
- Minor issues (ordering input arguments, version)
version 4.0 changes
- Major code changes for a better exception handling
version 3.0 changes
- new argument to set the freq. interval (-fr / --freqrange)
version 2.0 changes
- interactive or argument-based
- documentation!!
- Takes SEFD values from status table of EVN
import sys
import argparse
import datetime as dt
from math import floor
from collections import defaultdict
help_str = """Writes a nominal SEFD ANTAB file. Gain will be set to 1/SEFD, and all Tsys to 1.0.
It will overwrite any previous antab file in the current path. uses the SEFD information from sefd_values.txt to compute the nominal values.
Creates (or overwrites) a file called <experiment><antenna>.antabfs, where <experiment> and
<antenna> are the input from the user.
parser = argparse.ArgumentParser(description=help_str, prog='')
parser.add_argument('antenna', type=str, default=None, help='Antenna name (two-letters syntax, except for Jb1 Jb2 Ro7 Ro3)')
parser.add_argument('experiment', type=str, default=None, help='Experiment name')
parser.add_argument('start', type=str, default=None, help='Start time (DOY/HH:MM, YYYY/DOY/HH:MM or YYYY/MM/DD/HH:MM)')
parser.add_argument('-v', '--version', action='version', version='%(prog)s 4.1')
parser.add_argument('-b', '--band', type=str, default=None, help='Observed band (in cm). REQUIRED unless SEFD provided')
parser.add_argument('-d', '--duration', type=float, default=24, help='Duration of the experiment (in hours). Default: 24 h')
parser.add_argument('-fr', '--freqrange', type=str, default='100,100000', help='Frequency range where the ANTAB is applicable (lower and upper limit, in MHz). Default 100,100000 (please, do not use spaces between the numbers).')
parser.add_argument('-s', '--sefd', type=float, default=None, help='SEFD to be used (optional). Default values are loaded.')
parser.add_argument('-i', '--interval', type=float, default=0.25, help='Interval between Tsys measurements (in min). Default: 0.5')
parser.add_argument('-sb', '--subbands', type=int, default=8, help='Number of subbands in the experiment. Default: 8 = L1|R1 L2|R2 ... L8|R8')
args = parser.parse_args()
i_already_warn_about_seconds = False
# def read_sefd_table(tablename='sefd_values.txt'):#'/jop83_0/pipe/in/marcote/scripts/sefd_values.txt'):
def read_sefd_table(tablename='/jop83_0/pipe/in/marcote/scripts/sefd_values.txt'):
sefd_table = open(tablename, 'r')
titles = sefd_table.readline().strip().split('|')
titles = [t.strip() for t in titles]
sefd_dict = defaultdict(dict)
for an_ant in sefd_table.readlines():
values = [ t.strip() for t in an_ant.strip().split('|')]
for i in range(1, len(values)):
if values[i] != '':
sefd_dict[values[0].lower()][titles[i]] = float(values[i])
return sefd_dict
def read_sefd_values(table, antenna, band):
return table[antenna][band]
except KeyError:
if antenna not in table:
print('ERROR: {} is not available.\n'.format(antenna))
print('The available antennas are: ', ' '.join(table.keys()))
print('ERROR: antenna {} does not have SEFD information for {}-cm observations'.format(antenna, band))
def index_header():
indexes = list()
for i in range(1, args.subbands+1):
return ','.join(indexes)
def get_header(antenna, gain, freqrange):
"""Returns the apropiate header for the given antenna using a given gain value.
antenna : str
The antenna name (two letters syntax)
gain : float
The gain (in 1/Jy) for this antenna
generic_header = '''!
! Nominal calibration data for {ant} by
! version 2.0, 2016 August 3, BM & JB
GAIN {ant} ELEV DPFU={gain},{gain} POLY=1.0 FREQ={freqrange}
TSYS {ant} FT=1.0 TIMEOFF=0
INDEX = {indexes}
return generic_header.format(ant=antenna[:2].upper(), gain=gain, indexes=index_header(),
freqrange=','.join([str(i) for i in freqrange]))
def hm2hhmmss(hhmm):
"""Takes a time in str format HH:MM.MM and returns int(hh), int(mm), float(ss)"""
hour, minute = hhmm.split(':')
except ValueError:
if not i_already_warn_about_seconds:
print('WARNING: only HH:MM are read. Seconds or other smaller numbers are going to be ignored.')
i_already_warn_about_seconds = True
hour, minute, *others = hhmm.split(':')
minute = float(minute)
second = int((minute-floor(minute))*60)
return int(hour), int(floor(minute)), second
def date2datetime(date):
"""Convert the given date to DOY, HH, MM.
date : str
if date.count('/') == 1:
# Uses a fake year
doy, hhmm = date.split('/')
return dt.datetime(1969, 1, 1, *hm2hhmmss(hhmm)) + dt.timedelta(int(doy)-1)
elif date.count('/') == 2:
year, doy, hhmm = date.split('/')
return dt.datetime(int(year), 1, 1, *hm2hhmmss(hhmm)) + dt.timedelta(int(doy)-1)
elif date.count('/') == 3:
year, month, day, hhmm = date.split('/')
return dt.datetime(int(year), int(month), int(day), *hm2hhmmss(hhmm))
print('Error: date must have the following format: DOY/HH:MM, YYYY/DOY/HH:MM or YYYY/MM/DD:HH:MM')
raise SyntaxError
def date2string(datetime):
"""Return a string with the date in the correct ANTAB format
return '{} {:02d}:{:05.2f}'.format(datetime.strftime('%j'), datetime.hour, datetime.minute+datetime.second/60.)
#currently asks for inputs, might change this in future to read from vex...
if args.experiment == None:
args.experiment = raw_input("Input experiment name: ")
if args.antenna == None:
args.antenna = raw_input("Input antenna name (two-letter syntax (except Jb1 Jb2 Ro7 Ro3): ")
if == None and args.sefd == None:
output = raw_input("Input frequency band (cm) or SEFD value (Jy). Write 'band VALUE' or 'sefd VALUE'").split(' ')
if output[0].lower() == 'band': = output[1]
elif output[0].lower() == 'sefd':
args.sefd = output[1]
print('Wrong format. It must be either: "band VALUE" or "sefd VALUE"')
raise ValueError
if args.start == None:
input_starttime = raw_input("Enter start day of the year, hour and minute (comma separated): ").split(',')
args.start = '{} {}:{}'.format(*input_starttime)
dur = raw_input("Enter duration (hours; enter to default): ")
if dur != '':
args.duration = float(dur)
# Read and interpretate the freqrange.
if args.freqrange.count(',') != 1:
print('The frequency range (--freqrange) must contain two values (comma-separated): the lower and upper frequency limit in MHz (please, do not use spaces between the numbers).')
args.freqrange = [int(i) for i in args.freqrange.split(',')]
if not (args.freqrange[0] < 30*1000/float( < args.freqrange[1]):
print('The provided frequency range must contain the frequency band, and this is not the case.')
print('Introduced band: {} GHz'.format(30/float(
print('Introduced frequency range: {}-{} GHz'.format(args.freqrange[0]/1e3, args.freqrange[1]/1e3))
sefd_info = read_sefd_table()
start_time = date2datetime(args.start)
end_time = start_time + dt.timedelta(args.duration/24.)
a_time = date2datetime(args.start)
# Creating the ANTAB file
antab_file = open('{}{}.antabfs'.format(args.experiment.lower(), args.antenna.lower()[:2]), 'wt')
antab_file.write(get_header(args.antenna.lower(), 1./read_sefd_values(sefd_info, args.antenna.lower(),,
while a_time < end_time:
antab_file.write('{}{}\n'.format(date2string(a_time), ' 1.0'*args.subbands))
a_time = a_time + dt.timedelta(args.interval/(60*24.))
antab_file.write('/\n') # antab expects trailing /
print('File {}{}.antabfs created successfully.'.format(args.experiment.lower(), args.antenna.lower()[:2]))