Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
tbb_freeze.py 4.65 KiB
#!/usr/bin/env python3

########################################################################
#
# Stop TBB recording on a bunch of stations
#
########################################################################

import argparse
import time
import subprocess
import logging
logger = logging.getLogger(__name__)

from lofar.mac.tbb.tbb_config import lcurun_command, tbb_command
from lofar.mac.tbb.tbb_util import split_stations_by_boardnumber
from lofar.common.lcu_utils import execute_in_parallel_over_stations, translate_user_station_string_into_station_list
from lofar.common.subprocess_utils import wrap_composite_command

def freeze_tbb(stations, dm, timesec, timensec):
    """
    :param stations: comma-separated list of stations
    :param dm: dispersion measure as float
    :param timesec: stop time in seconds (int)
    :param timensec: stop time offset in nanoseconds (int)
    :return:
    """

    stations = translate_user_station_string_into_station_list(stations)

    logger.info('Freezing TBB boards for stations: %s', ', '.join(stations))

    if dm is not None:
        logger.info('DM %s provided, performing timed stop on individual boards' % dm)

        # determine number of tbb boards per station:
        stationlists = split_stations_by_boardnumber(stations)

        # batch handle all stations with same number of boards through lcurun
        for num_boards in list(stationlists.keys()):
            stations_with_num_boards = stationlists[num_boards]
            logger.info('Handling stations with %s boards: %s', num_boards, stations_with_num_boards)
            station_str = ','.join(stationlists[num_boards])
            relay = lcurun_command + [station_str]

            slicenr = int(timensec // (5 * 1024))  # -> 5.12 microseconds per slice

            # string magic to create single cmdline ';' seperated tbbctl commands to set the dispersion measure and the stoptime
            set_dm_cmd = " ; ".join(['%s --dispmeas=%s,%s' % (tbb_command, board, dm) for board in range(num_boards)])
            set_stoptime_cmd = " ; ".join(['%s --stoptimed=%s,%s,%s' % (tbb_command, board, timesec, slicenr) for board in range(num_boards)])

            # apply all commands for each station
            for cmd in [set_dm_cmd, set_stoptime_cmd]:
                quoted_cmd = wrap_composite_command(cmd)
                execute_in_parallel_over_stations(quoted_cmd, stations_with_num_boards, timeout=60, max_parallel=10)

    # Note: Sander says it is still required to tbbctl --stop in subbands mode, although ICD seems to suggest otherwise,
    #       so we will issue that irrespective of mode in the following.
    # Note: This is not even close to nanosecond precision, but this is ridiculous outside the driver/firmware anyway...
    #       If we really have to be better than this, we could port the sleepuntil.sh that is apparently floating around
    #       lcuhead to the stations and chain it into the command, so that we are not delayed by lcurun/ssh.

    # wait for timestamp, then stop all boards on all stations.
    timestamp = float("%d.%09d" % (timesec, timensec))
    if dm is not None:
        timestamp += 0.32 * dm

    sleeptime = timestamp - time.time()
    if sleeptime > 0:
        logger.info('Waiting %s seconds before stopping TBB boards' % sleeptime)
        time.sleep(sleeptime)

    cmd = [tbb_command, '--stop']
    execute_in_parallel_over_stations(cmd, stations, timeout=60, max_parallel=10)

def parse_args():
    parser = argparse.ArgumentParser("This script will freeze TBB boards on a bunch of stations.")
    parser.add_argument('-s', '--stations', dest='stations', help="comma-separated list of station LCUs (e.g. cs030c,cs031c; also accepts lcurun aliases like 'today', 'nl', ...)", default='today')
    parser.add_argument('-d', '--dm', dest='dm', help="dispersion measure as float", type=float)
    parser.add_argument('-t', '--stoptime-seconds', dest='timesec', type=int, help="Freeze time since epoch in seconds")
    parser.add_argument('-n', '--stoptime-nanoseconds', dest='timensec', type=int, help="Freeze time offset in nanoseconds", default=0)
    args = parser.parse_args()

    if not args.timesec:
        logger.info('No timestamp provided, using current time instead.')
        stoptime = time.time()
        timesec_str, timensec_str = ("%.9f" % stoptime).split('.')
        args.timesec = int(timesec_str)
        args.timensec = int(timensec_str)

    if args.dm is None:
        logger.error("No dm provided")
        parser.print_help()
        exit(1)

    return args


def main():
    args = parse_args()
    freeze_tbb(args.stations, args.dm, args.timesec, args.timensec)

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    main()