Exemple #1
0
def main():
    parser = ArgumentParser(description='ATA Antenna Controller')
    parser.add_argument(
        '--frequency',
        '-f',
        type=int,
        default=3000,
        help=
        "Frequency in MHz to tune the LOs to default is 3000 (e.g. 3000 for 3 GHz)",
        required=False)

    args = parser.parse_args()

    ants_lo_a = ["1c", "2a", "4j"]  # Lo a
    ants_lo_b = ["1a", "1f", "4g", "5c"]  # LO b
    ants_lo_c = ["2h", "1k", "1h"]
    ants_lo_d = ["2b", "3c"]
    ant_list = ants_lo_a + ants_lo_b + ants_lo_c + ants_lo_d

    # Reserve antennas, and make sure to unreserve when code exists
    print("Reserving antennas " + str(ant_list))
    ata_control.reserve_antennas(ant_list)

    # Tune the RF over fiber power
    freq = args.frequency
    print("Autotuning to " + str(freq) + "...")
    ata_control.autotune(ant_list)
    ata_control.set_freq(freq, ants_lo_a, lo='a')
    ata_control.set_freq(freq, ants_lo_b, lo='b')
    ata_control.set_freq(freq, ants_lo_c, lo='c')
    ata_control.set_freq(freq, ants_lo_d, lo='d')

    # pick source and point dishes
    while True:
        cmd = input("'park' to exit or target name (e.g. casa or 3c84) ")

        if cmd == 'park':
            break

        source = cmd
        print("[" + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) +
              "] Tracking to " + source)
        ata_control.make_and_track_source(source, ant_list)
        print("[" + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) +
              "] Running.")
        _ = input("Press ENTER to change state")

    print("Releasing antennas...")
    ata_control.release_antennas(ant_list, True)
    print("Done.")
Exemple #2
0
def main():
    logger = logger_defaults.getProgramLogger("observe", loglevel=logging.INFO)

    # pulsar observation
    ant_list = ["1c", "2a", "2b", "2h"]
    ata_control.reserve_antennas(ant_list)
    freq = 1500
    ata_control.set_freq(freq, ant_list)

    source = "J0534+2200"
    ata_control.make_and_track_ephems(source, ant_list)
    obs_time = 3600
    os.system(ATTEMP_SCRIPT)
    ata_control.release_antennas(ant_list, True)
def main():

    alist = ['1a', '1c', '2h']  #define antennas list (alist)
    src = 'moon'  #define target source
    #az_off = 5 #define azimuth offset for on-off scan
    #el_off = 5 #define elevation offset for on-off scan
    freq = 1400  #set center frequency in MHz
    duration = 30  #duration of tracking

    #lock out the antennas you will be using
    ac.reserve_antennas(alist)

    #turn on the LNAs if they're not already on
    ac.try_on_lnas(alist)

    #set up with Autotune
    ac.autotune(alist)

    #display starting coordinates so
    #you can verify antennas have moved
    start_radec = ac.getRaDec(alist)
    print("The current coordinates are: ", start_radec)

    #create ephemeris file that tells the antenna
    #where it should be pointing at each timestamp
    ac.make_and_track_ephems(src, alist)

    #set the center frequency
    ac.set_freq(freq, alist)

    #print the coordinates after the antennas have
    #been given the point and track command to ensure
    #that they moved.
    src_radec = ac.getRaDec(alist)
    print("The source coordinates are: ", src_radec)

    #stay on source for the given duration
    time.sleep(duration)

    #unlock the antennas once you're done
    ac.release_antennas(alist, True)
Exemple #4
0
def main():
    logger = logger_defaults.getProgramLogger("observe", 
            loglevel=logging.INFO)
    #parser = argparse.ArgumentParser(description="Observing script")
    #parser.add_argument("ants", nargs="+", help="ant names")
    #args = parser.parse_args()
    #ants = args.ants
    nbatches = 15
    ant_list = ["1a","1c","1f","2a","2b","2h","4g","5c"]
    az_offset = 0
    el_offset = 10
    obs_time = 30
    source = "casa"
    #source = "moon"

    ata_control.try_on_lnas(ant_list)

    #ata_control.make_and_track_ephems(source, ant_list)
    ata_control.create_ephems2(source, az_offset, el_offset)


    #freqs = np.arange(1200, 8000, 400)
    freqs = np.arange(1000, 11000, 500)

    for freq in freqs:
        print (freq)
        ata_control.set_freq(freq, ant_list)

        # record on
        ata_control.point_ants2(source, "on", ant_list)
        ant_list.append('rfi')
        utc = snap_dada.start_recording(ant_list, obs_time)
        ant_list.remove('rfi')

        # record off
        ata_control.point_ants2(source, "off", ant_list)
        ant_list.append('rfi')
        utc = snap_dada.start_recording(ant_list, obs_time)
        ant_list.remove('rfi')
    ata_control.release_antennas(ant_list, True)
Exemple #5
0
def do_snap_waterfall(ant_str,freq, source, fpga_file, waterfalllen, do_park = False):

    logger = logger_defaults.getModuleLogger(__name__)

    ant_list = snap_array_helpers.string_to_array(ant_str);
    full_ant_str = snap_array_helpers.array_to_string(ant_list)

    if waterfalllen < 2:
        logger.error('waterfall len too short')
        raise RuntimeError('waterfall len too short')

    try:
        ant_groups = ata_control.get_snap_dictionary(ant_list)
    except:
        logstr = "unable to match antennas with snaps"
        logger.exception(logstr)
        raise
    
    if len(ant_list) != 1:
        logger.error('only 1 antenna allowed')
        raise RuntimeError('only 1 antenna allowed')

    if len(ant_groups) != 1:
        logger.error('only 1 antenna allowed')
        raise RuntimeError('only 1 antenna allowed')

    ant_dict = {}
    for csnap in ant_groups:
        if len(ant_groups[csnap]) != 1:
            logger.error('only one antenna per snap allowed, got {}: {}'.format(csnap,",".join(ant_groups[csnap])))
            raise RuntimeError("only 1 antenna per snap allowed")

        snaphost = csnap
        currAnt = ant_groups[csnap][0]
    
    logger.info("Reserving antennas %s in bfa antgroup" % full_ant_str)
    try:
        ata_control.reserve_antennas(ant_list)
    except:
        logstr = "unable to reserve the antennas"
        logger.exception(logstr)
        raise

    logger.info("starting plotting")
    try:
        ata_control.try_on_lna(currAnt)
        source_status = ata_positions.ATAPositions.getFirstInListThatIsUp([source])
        if not source_status:
            errormsg = 'source {} is not up (or too close to sun/moon)... terminating observation set {}'.format(source,obs_set_id)
            logger.error(errormsg)
            raise RuntimeError(errormsg)
        if source_status['status'] != 'up':
            if source_status['status'] == 'next_up':
                errormsg = 'source {} is not up (or too close to sun/moon). Will be up in {} minutes. Terminating observation set {}'.format(source,source_status['minutes'],obs_set_id)
            else:
                errormsg = 'source {} is not up (or too close to sun/moon)... terminating observation set {}'.format(source,obs_set_id)
            logger.error(errormsg)
            raise RuntimeError(errormsg)


        logger.info("pointing the antennas")
        ata_control.make_and_track_ephems(source, currAnt );
        logger.info("autotuning")
        ata_control.autotune(currAnt)
        ata_control.rf_switch_thread([currAnt])

        logger.info("changing to frequency {}".format(freq))
        ata_control.set_freq(freq, currAnt)
        snap_recorder.setSnapRMS(snaphost,currAnt,fpga_file,default_rms)
        snap_plot.plotWaterfall(snaphost, waterfalllen, freq,fpga_file)

    except KeyboardInterrupt:
        logger.info("Keyboard interuption")
    except Exception as e:
        logger.exception("something went wrong")
        errmsg = "Finishing recording - failed: {}".format(e)
        raise
    finally: 
        logger.info("shutting down")
        ata_control.release_antennas(ant_list, do_park)
Exemple #6
0
def main():
    logger = logger_defaults.getProgramLogger("observe", 
            loglevel=logging.INFO)

    """
    # moon observation
    az_offset = 0
    el_offset = 20
    freqs = np.arange(1000, 11000, 500)
    ant_list = ['1a', '1c', '1f', '2a', '2b', '2h', '4g', '5c']
    ata_control.reserve_antennas(ant_list)
    obs_time = 30
    source = "moon"
    ata_control.create_ephems2(source, az_offset, el_offset)
    for i in range(1):
        for freq in freqs:
            print (freq)
            ata_control.set_freq(freq, ant_list)
            os.system(ATTEMP_SCRIPT)

            # record on
            ata_control.point_ants2(source, "on", ant_list)
            ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time, 
                    acclen=160*80)
            ant_list.remove('rfi')

            # record off
            ata_control.point_ants2(source, "off", ant_list)
            ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time,
                    acclen=160*80)
            ant_list.remove('rfi')
    """

    # Casa observation
    az_offset = 0
    el_offset = 10
    freqs = np.arange(1000, 11000, 500)
    ant_list = ['1a', '1c', '2a', '2b', '2h', '4g']
    ata_control.reserve_antennas(ant_list)
    """
    obs_time = 30
    source = "casa"
    ata_control.create_ephems2(source, az_offset, el_offset)
    for i in range(1):
        for freq in freqs:
            print (freq)
            ata_control.set_freq(freq, ant_list)
            os.system(ATTEMP_SCRIPT)

            # record on
            ata_control.point_ants2(source, "on", ant_list)
            #ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time, 
                    acclen=160*80)
            #ant_list.remove('rfi')

            # record off
            ata_control.point_ants2(source, "off", ant_list)
            #ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time,
                    acclen=160*80)
            #ant_list.remove('rfi')
    """

    
    # Magnetar observation
    ant_list = ["1c", "2a", "2b", "2h"]
    freq = 1600
    ata_control.set_freq(freq, ant_list)

    source = "J1934+2153"
    ata_control.make_and_track_ephems(source, ant_list)
    obs_time = 3600
    os.system(ATTEMP_SCRIPT)

    for i in range(7):
        ata_control.make_and_track_ephems(source, ant_list)
        utc = snap_dada.start_recording(ant_list, obs_time)
        print (utc)


    # casa observation
    az_offset = 0
    el_offset = 10
    freqs = np.arange(1000, 11000, 500)
    ant_list = ['1a',  '1c', '2a', '2b', '2h', '4g']
    obs_time = 30
    source = "casa"
    ata_control.create_ephems2(source, az_offset, el_offset)
    for i in range(1):
        for freq in freqs:
            print (freq)
            ata_control.set_freq(freq, ant_list)
            os.system(ATTEMP_SCRIPT)

            # record on
            ata_control.point_ants2(source, "on", ant_list)
            #ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time, 
                    acclen=160*80)
            #ant_list.remove('rfi')

            # record off
            ata_control.point_ants2(source, "off", ant_list)
            #ant_list.append('rfi')
            utc = snap_dada.start_recording(ant_list, obs_time,
                    acclen=160*80)
            #ant_list.remove('rfi')
    ant_list = ['1a',  '1c', '2a', '2b', '2h', '4g']
    ata_control.release_antennas(ant_list, True)
Exemple #7
0
def theMainFunction(various_options):
    #ant_list = ['1a','2b']
    logger = logger_defaults.getModuleLogger(__name__)
    try:
        ata_control.reserve_antennas(ant_list)
    except:
        logger.exception("can't reserve antennas")
        raise

    try:
        #there is no guarantee that the antenna LNA's are on. Check it and if needed turn them on
        ata_control.try_on_lnas(ant_list)

        #point the antenna to the source after generation the ephemeris. This function may be 
        #insufficient for some non-standard targets. notify me (J.S. Kulpa) if it needs expanding
        ata_control.make_and_track_ephems(source, ant_list );

        #autotune the pam values
        ata_control.autotune(ant_list)

        #set LO A and feed focus
        ata_control.set_freq(freq, full_ant_str)

        #snap0-2 only:
        #if using RFSwitch, switch to those antennas. In general, using RFswitch means that you should use also 
        #ant_groups = ata_control.get_snap_dictionary(ant_list)
        #so you know which antennas are hooked up where andin general use only one ant from each group at a time
        #and build cirrent_ant_list based on ant_groups
        #ata_control.rf_switch_thread(current_ant_list)
        #also if using snaps, you should probably call
        #attendict = snap_observations.setRMS(ant_dict,fpga_file,default_rms)
        #cobsid = snap_observations.record_same(...)
        #obs_db.updateAttenVals(cobsid,attendict)
        #obs_db.markRecordingsOK([cobsid])
        #and that's it
        #but let's get away from snap0-2 from now on, so you are free to ignore it completely

        #grouping recordings into observation set
        #if you want to create new observation set, call:
        #obs_set_id = obs_db.getNewObsSetID("THIS IS MY OBS SET DESCRIPTION!")
        #if you already have an exiting observation set, make sure it does exist:
        #obs_db.getSetData(obs_set_id)
        #if it is a single-shot, leave it as none
        #obs_set_id=None

        #initializing the new recording for backend and type see:
        #ATAobs.obs_common.getRecType and ATAobs.obs_common.getRecBackend
        recid = obs_db.initRecording(freq,obstype,backend,desc,obsuser,obs_set_id)
        #now, we populate the second table with each antenna pointing
        #most likely az_ and el_offset should be set to 0
        #the last one is set to True to fetch pam settings to the db
        obs_db.initAntennasTable(recid,ant_list,source,az_offset,el_offset,True)
        #we are about to start the recordings, let's note what the starting time is
        obs_db.startRecording(recid)
        #here you should put your function call to do the recording and wait for it to be complete
        #if you are calculating the signal power, in the meantime of afterwards tou can you can call:
        obs_db.updateRMSVals(recid,rmsDict)
        #where rmsdict looks like that:
        #rmsDict={'1a':{'rmsx': 15.2,'rmsy':16.231}}
        #or
        #rmsDict={'1a':{'rmsx': 15.2,'rmsy':16.231},'2b':{'rmsx': 15.2,'rmsy':16.231}}
        #ok, measurements are done, so:
        obs_db.stopRecording(recid)
        #now, if it was a single shot or measurements in the set are independent from each other, call
        obs_db.markRecordingsOK([recid])
        #but if this is a set of recordings (e.g. On-Off where a you need several recordings close in time
        #to finish successfully, you should wait and populate a list of recordings, then call
        #obs_db.markRecordingsOK(all_that_recids)

    except:
        #do whatever you want if something crashed, Yes, some functions can raise the exception
    finally:
        #does not matter if that succeeded or not, you should always release the antennas
        # the do_park bool determines if the antennas should go back do default position
        # for the debugging process, it's better to let them be, because you won't waste time
        # to re-acquire the source, but in normal operation you should by default part the antennas
        ata_control.release_antennas(ant_list, do_park)
Exemple #8
0
def doOnOffObservations(ant_str,freq_str, pointings_str,az_offset,el_offset,repetitions,ncaptures,obs_set_id,fpga_file):

    logger = logger_defaults.getModuleLogger(__name__)

    ant_list = snap_array_helpers.string_to_array(ant_str);
    pointings = snap_array_helpers.string_to_array(pointings_str);
    freq_list = snap_array_helpers.string_to_numeric_array(freq_str);
    ant_list = remove_dups(ant_list)
    full_ant_str = snap_array_helpers.array_to_string(ant_list)


    info_string = ("OnOff Started\nDataset ID {7}\n\nAnts: {0!s}\nFreq: {1!s}\n"
            "Pointings: {2!s}\nOff positions: Az={3:3.2f} El={4:3.2f}\n"
            "Repetitions {5:d}\nCaptures {6:d}").format(full_ant_str, freq_str, pointings_str,az_offset,el_offset,repetitions,ncaptures,obs_set_id)

    logger.info(info_string)
    logger.warning("Communication disabled, edit code")
    ATAComm.sendMail("SNAP Obs started",info_string)
    ATAComm.postSlackMsg(info_string)

    try:
        ant_groups = ata_control.get_snap_dictionary(ant_list)
    except:
        logstr = "unable to match antennas with snaps"
        logger.exception(logstr)
        ATAComm.sendMail("SNAP Obs exception",logstr)
        raise

    #getting the antennas. From now on we can modify any antenna parameters
    # Reserve the antennas
    logger.info("Reserving antennas %s in bfa antgroup" % full_ant_str)
    try:
        ata_control.reserve_antennas(ant_list)
    except:
        logstr = "unable to reserve the antennas"
        logger.exception(logstr)
        ATAComm.sendMail("SNAP Obs exception",logstr)
        raise

    # For each SNAP. set the minicircuits attenuators to 12.0
    # To do this, get a list of the first antenna in each snap group
    try:
        default_atten_db = 12 # Suggested by jack
        antpols_list_list = []
        atten_list_list = []

        for a in ant_groups.keys():
            antpols_list_list.append(["%sx"%ant_groups[a][0],"%sy"%ant_groups[a][0]])
            atten_list_list.append([default_atten_db, default_atten_db])

        ata_control.set_atten_thread(antpols_list_list,atten_list_list)
    except:
        logstr = "unable to set attenuators"
        logger.exception(logstr)
        ATAComm.sendMail("SNAP Obs exception",logstr)
        ata_control.release_antennas(ant_list, True)
        raise

    current_source = None
    new_antennas = True

    logger.info("starting observations")
    
    try:
        ata_control.try_on_lnas(ant_list)
        while(1):
            new_antennas = True
            #gets a antenna dictionary and 
            curr_ant_dict,curr_freq_list = onoff_db.get_obs_params(obs_set_id,pointings,ant_groups,freq_list)
            
            #if None, it meast that all was measured
            if not curr_ant_dict:
                logger.info("all seems to be measured")
                break

            for curr_freq in curr_freq_list:

                current_source,was_changed = ata_positions.ATAPositions.getPreferedSourceUp(current_source,pointings)

                if not current_source:
                    errormsg = 'no source is up ({}). terminating observation set {}'.format(','.join(pointings),obs_set_id)
                    logger.error(errormsg)
                    raise RuntimeError(errormsg)


                #we either changed antennas or changed source.
                #need to generate the ephemeris and autotune PAMs
                if was_changed:
                    #if we only switched the antennas, we don't need to regenerate
                    # the ephemeris
                    logger.info("source changed to {}".format(current_source))
                    ata_control.create_ephems(current_source, az_offset, el_offset);

                if( was_changed or new_antennas):
                    logger.info("need to (re)run autotune")
                    curr_ant_list = snap_array_helpers.dict_to_list(curr_ant_dict)
                    curr_ant_string = snap_array_helpers.array_to_string(curr_ant_list)

                    logger.info("pointing the antennas")
                    ata_control.point_ants("on", curr_ant_string );
                    logger.info("autotuning")
                    ata_control.autotune(curr_ant_string)
                    ata_control.rf_switch_thread(curr_ant_list)
                    new_antennas = False

                logger.info("changing to frequency {}".format(curr_freq))
                ata_control.set_freq(curr_freq, curr_ant_string)

                onoff_observations(curr_ant_dict,obs_set_id,curr_freq,fpga_file,current_source,repetitions,ncaptures,az_offset,el_offset)
                #snap_control.do_onoff_obs(args.hosts, \
                #    "/home/sonata/dev/ata_snap/snap_adc5g_spec/outputs/snap_adc5g_spec_2018-06-23_1048.fpg", \
                #    source, args.ncaptures, args.repetitions, ants_to_observe, freq, obsid, 0.0, 10.0)
    
            #now, we believe we have measured all frequencies for curr_ant_dict, so we may
            #remove the content of it from our original ant_groups. Note that this function
            #alters the ant_groups!
            onoff_db.remove_antennas_from_dict(ant_groups,curr_ant_dict);
            
    
        ATAComm.sendMail("SNAP Obs End","Finishing measurements - success")
        ATAComm.postSlackMsg("Finishing measurements - success")
    except KeyboardInterrupt:
        logger.info("Keyboard interuption")
        ATAComm.sendMail("SNAP Obs End","Finishing measurements - keyboard interrupt, obsid {}".format(obs_set_id))
        ATAComm.postSlackMsg("Finishing measurements - keyboard interrupt")
    except Exception as e:
        logger.exception("something went wrong")
        errmsg = "Finishing measurements - failed, obsid {}: {}".format(obs_set_id,e)
        ATAComm.sendMail("SNAP Obs End",errmsg)
        ATAComm.postSlackMsg(errmsg)
        raise
    finally: 
        logger.info("shutting down")
        ata_control.release_antennas(ant_list, True)
Exemple #9
0
def do_snap_rec(ant_str,
                freq,
                source,
                ncaptures,
                obs_set_id,
                fpga_file,
                obsuser,
                obstype,
                obsdesc,
                do_park=False):

    logger = logger_defaults.getModuleLogger(__name__)

    ant_list = snap_array_helpers.string_to_array(ant_str)
    full_ant_str = snap_array_helpers.array_to_string(ant_list)

    try:
        ant_groups = ata_control.get_snap_dictionary(ant_list)
    except:
        logstr = "unable to match antennas with snaps"
        logger.exception(logstr)
        raise

    ant_dict = {}
    for csnap in ant_groups:
        if len(ant_groups[csnap]) != 1:
            logger.error(
                'only one antenna per snap allowed, got {}: {}'.format(
                    csnap, ",".join(ant_groups[csnap])))
            raise RuntimeError("only 1 antenna per snap allowed")
        ant_dict[csnap] = ant_groups[csnap][0]

    if obs_set_id:
        info_string = (
            "Starting observation:\nDataset ID {4:d}\n\nAnts: {0!s}\nFreq: {1:0.2f}\n"
            "Source: {2:s}\nCaptures {3:d}\n"
            "Type: {5:s} by {6:s} [{7:s}]").format(full_ant_str, freq, source,
                                                   ncaptures, obs_set_id,
                                                   obstype, obsuser, obsdesc)
    else:
        info_string = (
            "Starting observation:\nNO Dataset ID!\n\nAnts: {0!s}\nFreq: {1:0.2f}\n"
            "Source: {2:s}\nCaptures {3:d}\n"
            "Type: {5:s} by {6:s} [{7:s}]").format(full_ant_str, freq, source,
                                                   ncaptures, obs_set_id,
                                                   obstype, obsuser, obsdesc)

    logger.info(info_string)
    ATAComm.sendMail("SNAP Obs started", info_string)
    ATAComm.postSlackMsg(info_string)

    logger.info("Reserving antennas %s in bfa antgroup" % full_ant_str)
    try:
        ata_control.reserve_antennas(ant_list)
    except:
        logstr = "unable to reserve the antennas"
        logger.exception(logstr)
        ATAComm.sendMail("SNAP Obs exception", logstr)
        raise

    logger.info("starting observations")
    try:
        ata_control.try_on_lnas(ant_list)
        source_status = ata_positions.ATAPositions.getFirstInListThatIsUp(
            [source])
        if not source_status:
            errormsg = 'source {} is not up (or too close to sun/moon)... terminating observation set {}'.format(
                source, obs_set_id)
            logger.error(errormsg)
            raise RuntimeError(errormsg)
        if source_status['status'] != 'up':
            if source_status['status'] == 'next_up':
                errormsg = 'source {} is not up (or too close to sun/moon). Will be up in {} minutes. Terminating observation set {}'.format(
                    source, source_status['minutes'], obs_set_id)
            else:
                errormsg = 'source {} is not up (or too close to sun/moon)... terminating observation set {}'.format(
                    source, obs_set_id)
            logger.error(errormsg)
            raise RuntimeError(errormsg)

        logger.info("pointing the antennas")
        ata_control.make_and_track_ephems(source, full_ant_str)
        logger.info("autotuning")
        ata_control.autotune(full_ant_str)
        ata_control.rf_switch_thread(ant_list)

        logger.info("changing to frequency {}".format(freq))
        ata_control.set_freq(freq, full_ant_str)

        single_snap_recording(ant_dict, obs_set_id, obstype, obsuser, obsdesc,
                              freq, fpga_file, source, ncaptures)

        ATAComm.sendMail("SNAP recording ended",
                         "Finishing measurements - success")
        ATAComm.postSlackMsg("Finishing recording - success")
    except KeyboardInterrupt:
        logger.info("Keyboard interuption")
        ATAComm.sendMail(
            "SNAP recording ended",
            "Finishing measurements - keyboard interrupt, obsid {}".format(
                obs_set_id))
        ATAComm.postSlackMsg("Finishing recording - keyboard interrupt")
    except Exception as e:
        logger.exception("something went wrong")
        errmsg = "Finishing recording - failed, obsid {}: {}".format(
            obs_set_id, e)
        ATAComm.sendMail("SNAP recording ended", errmsg)
        ATAComm.postSlackMsg(errmsg)
        raise
    finally:
        logger.info("shutting down")
        ata_control.release_antennas(ant_list, do_park)