Exemple #1
0
    def test_sdf(self):
        """Test building a SDF from a tarball."""

        sdf = metabundle.get_sdf(mdbFile)
Exemple #2
0
def main(args):
    # Set the site
    site = None
    if args.lwa1:
        site = 'lwa1'
    elif args.lwasv:
        site = 'lwasv'
        
    # Open the file and file good data (not raw DRX data)
    fh = open(args.filename, 'rb')

    try:
        for i in xrange(5):
            junkFrame = drx.read_frame(fh)
        raise RuntimeError("ERROR: '%s' appears to be a raw DRX file, not a DR spectrometer file" % args.filename)
    except errors.SyncError:
        fh.seek(0)
        
    # Interrogate the file to figure out what frames sizes to expect, now many 
    # frames there are, and what the transform length is
    FRAME_SIZE = drspec.get_frame_size(fh)
    nFrames = os.path.getsize(args.filename) // FRAME_SIZE
    nChunks = nFrames
    LFFT = drspec.get_transform_size(fh)

    # Read in the first frame to figure out the DP information
    junkFrame = drspec.read_frame(fh)
    fh.seek(-FRAME_SIZE, 1)
    srate = junkFrame.sample_rate
    t0 = junkFrame.time
    tInt = junkFrame.header.nints*LFFT/srate
    
    # Offset in frames for beampols beam/tuning/pol. sets
    offset = int(round(args.skip / tInt))
    fh.seek(offset*FRAME_SIZE, 1)
    
    # Iterate on the offsets until we reach the right point in the file.  This
    # is needed to deal with files that start with only one tuning and/or a 
    # different sample rate.  
    while True:
        ## Figure out where in the file we are and what the current tuning/sample 
        ## rate is
        junkFrame = drspec.read_frame(fh)
        srate = junkFrame.sample_rate
        t1 = junkFrame.time
        tInt = junkFrame.header.nints*LFFT/srate
        fh.seek(-FRAME_SIZE, 1)
        
        ## See how far off the current frame is from the target
        tDiff = t1 - (t0 + args.skip)
        
        ## Half that to come up with a new seek parameter
        tCorr = -tDiff / 2.0
        cOffset = int(round(tCorr / tInt))
        offset += cOffset
        
        ## If the offset is zero, we are done.  Otherwise, apply the offset
        ## and check the location in the file again/
        if cOffset is 0:
            break
        fh.seek(cOffset*FRAME_SIZE, 1)
        
    # Update the offset actually used
    args.skip = t1 - t0
    nChunks = (os.path.getsize(args.filename) - fh.tell()) // FRAME_SIZE
    
    # Update the file contents
    beam = junkFrame.id
    central_freq1, central_freq2 = junkFrame.central_freq
    srate = junkFrame.sample_rate
    data_products = junkFrame.data_products
    t0 = junkFrame.time
    tInt = junkFrame.header.nints*LFFT/srate
    beginDate = junkFrame.time.datetime
        
    # Report
    print("Filename: %s" % args.filename)
    if args.metadata is not None:
        print("Metadata: %s" % args.metadata)
    elif args.sdf is not None:
        print("SDF: %s" % args.sdf)
    print("Date of First Frame: %s" % beginDate)
    print("Beam: %i" % beam)
    print("Sample Rate: %i Hz" % srate)
    print("Tuning Frequency: %.3f Hz (1); %.3f Hz (2)" % (central_freq1, central_freq2))
    print("Data Products: %s" % ','.join(data_products))
    print("Frames: %i (%.3f s)" % (nFrames, nFrames*tInt))
    print("---")
    print("Offset: %.3f s (%i frames)" % (args.skip, offset))
    print("Transform Length: %i" % LFFT)
    print("Integration: %.3f s" % tInt)
    
    # Setup the output file
    outname = os.path.split(args.filename)[1]
    outname = os.path.splitext(outname)[0]
    outname = '%s-waterfall.hdf5' % outname
    
    if os.path.exists(outname):
        if not args.force:
            yn = raw_input("WARNING: '%s' exists, overwrite? [Y/n] " % outname)
        else:
            yn = 'y'
            
        if yn not in ('n', 'N'):
            os.unlink(outname)
        else:
            raise RuntimeError("Output file '%s' already exists" % outname)
            
    f = hdfData.create_new_file(outname)
    obsList = {}
    if args.metadata is not None:
        try:
            project = metabundle.get_sdf(args.metadata)
        except Exception as e:
            if adpReady:
                project = metabundleADP.get_sdf(args.metadata)
            else:
                raise e
                
        sdfBeam  = project.sessions[0].drx_beam
        spcSetup = project.sessions[0].spcSetup
        if sdfBeam != beam:
            raise RuntimeError("Metadata is for beam #%i, but data is from beam #%i" % (sdfBeam, beam))
            
        for i,obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop  = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsChunks = int(numpy.ceil(obs.dur/1000.0 * drx.FILTER_CODES[obs.filter] / (spcSetup[0]*spcSetup[1])))
            
            obsList[i+1] = (sdfStart, sdfStop, obsChunks)
            
        hdfData.fill_from_metabundle(f, args.metadata)
        
    elif args.sdf is not None:
        try:
            project = sdf.parse_sdf(args.sdf)
        except Exception as e:
            if adpReady:
                project = sdfADP.parse_sdf(args.sdf)
            else:
                raise e
                
        sdfBeam  = project.sessions[0].drx_beam
        spcSetup = project.sessions[0].spcSetup
        if sdfBeam != beam:
            raise RuntimeError("Metadata is for beam #%i, but data is from beam #%i" % (sdfBeam, beam))
            
        for i,obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop  = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsChunks = int(numpy.ceil(obs.dur/1000.0 * drx.FILTER_CODES[obs.filter] / (spcSetup[0]*spcSetup[1])))
            
            obsList[i+1] = (sdfStart, sdfStop, obsChunks)
            
        hdfData.fill_from_sdf(f, args.sdf, station=site)
        
    else:
        obsList[1] = (beginDate, datetime(2222,12,31,23,59,59), nChunks)
        
        hdfData.fill_minimum(f, 1, beam, srate, station=site)
        
    data_products = junkFrame.data_products
    for o in sorted(obsList.keys()):
        for t in (1,2):
            hdfData.create_observation_set(f, o, t, numpy.arange(LFFT, dtype=numpy.float64), obsList[o][2], data_products)
            
    f.attrs['FileGenerator'] = 'drspec2hdf.py'
    f.attrs['InputData'] = os.path.basename(args.filename)
    
    # Create the various HDF group holders
    ds = {}
    for o in sorted(obsList.keys()):
        obs = hdfData.get_observation_set(f, o)
        
        ds['obs%i' % o] = obs
        ds['obs%i-time' % o] = hdfData.get_time(f, o)
        
        for t in (1,2):
            ds['obs%i-freq%i' % (o, t)] = hdfData.get_data_set(f, o, t, 'freq')
            for p in data_products:
                ds["obs%i-%s%i" % (o, p, t)] = hdfData.get_data_set(f, o, t, p)
            ds['obs%i-Saturation%i' % (o, t)] = hdfData.get_data_set(f, o, t, 'Saturation')
            
    # Loop over DR spectrometer frames to fill in the HDF5 file
    pbar = progress.ProgressBar(max=nChunks)
    o = 1
    j = 0
    
    firstPass = True
    for i in xrange(nChunks):
        frame = drspec.read_frame(fh)
        
        cTime = frame.time.datetime
        if cTime > obsList[o][1]:
            # Increment to the next observation
            o += 1
            
            # If we have reached the end, exit...
            try:
                obsList[o]
                
                firstPass = True
            except KeyError:
                sys.stdout.write('%s\r' % (' '*pbar.span))
                sys.stdout.flush()
                print("End of observing block according to SDF, exiting")
                break
                
        if cTime < obsList[o][0]:
            # Skip over data that occurs before the start of the observation
            continue
            
        try:
            if frame.time > oTime + 1.001*tInt:
                print('Warning: Time tag error at frame %i; %.3f > %.3f + %.3f' % (i, frame.time, oTime, tInt))
        except NameError:
            pass
        oTime = frame.time
        
        if firstPass:
            # Otherwise, continue on...
            central_freq1, central_freq2 = frame.central_freq
            srate = frame.sample_rate
            tInt  = frame.header.nints*LFFT/srate
            
            freq = numpy.fft.fftshift( numpy.fft.fftfreq(LFFT, d=1.0/srate) )
            freq = freq.astype(numpy.float64)
            
            sys.stdout.write('%s\r' % (' '*pbar.span))
            sys.stdout.flush()
            print("Switching to Obs. #%i" % o)
            print("-> Tunings: %.1f Hz, %.1f Hz" % (central_freq1, central_freq2))
            print("-> Sample Rate: %.1f Hz" % srate)
            print("-> Integration Time: %.3f s" % tInt)
            sys.stdout.write(pbar.show()+'\r')
            sys.stdout.flush()
            
            j = 0
            ds['obs%i-freq1' % o][:] = freq + central_freq1
            ds['obs%i-freq2' % o][:] = freq + central_freq2
            
            obs = ds['obs%i' % o]
            obs.attrs['tInt'] = tInt
            obs.attrs['tInt_Units'] = 's'
            obs.attrs['LFFT'] = LFFT
            obs.attrs['nChan'] = LFFT
            obs.attrs['RBW'] = freq[1]-freq[0]
            obs.attrs['RBW_Units'] = 'Hz'
            
            firstPass = False
            
        # Load the data from the spectrometer frame into the HDF5 group
        ds['obs%i-time' % o][j] = (frame.time[0], frame.time[1])
        
        ds['obs%i-Saturation1' % o][j,:] = frame.payload.saturations[0:2]
        ds['obs%i-Saturation2' % o][j,:] = frame.payload.saturations[2:4]
        
        for t in (1,2):
            for p in data_products:
                ds['obs%i-%s%i' % (o, p, t)][j,:] = getattr(frame.payload, "%s%i" % (p, t-1), None)
        j += 1
        
        # Update the progress bar
        pbar.inc()
        if i % 10 == 0:
            sys.stdout.write(pbar.show()+'\r')
            sys.stdout.flush()
            
    sys.stdout.write(pbar.show()+'\n')
    sys.stdout.flush()
    
    # Done
    fh.close()

    # Save the output to a HDF5 file
    f.close()
Exemple #3
0
def main(args):
    # Get the site and observer
    site = stations.lwa1
    observer = site.get_observer()

    # Filenames in an easier format
    inputTGZ = args.filename

    # Parse the input file and get the dates of the observations.  Be default
    # this is for LWA1 but we switch over to LWA-SV if an error occurs.
    try:
        # LWA1
        project = metabundle.get_sdf(inputTGZ)
        obsImpl = metabundle.get_observation_spec(inputTGZ)
        fileInfo = metabundle.get_session_metadata(inputTGZ)
        aspConfigB = metabundle.get_asp_configuration_summary(
            inputTGZ, which='Beginning')
        aspConfigE = metabundle.get_asp_configuration_summary(inputTGZ,
                                                              which='End')
    except:
        # LWA-SV
        ## Site changes
        site = stations.lwasv
        observer = site.get_observer()
        ## Try again
        project = metabundleADP.get_sdf(inputTGZ)
        obsImpl = metabundleADP.get_observation_spec(inputTGZ)
        fileInfo = metabundleADP.get_session_metadata(inputTGZ)
        aspConfigB = metabundleADP.get_asp_configuration_summary(
            inputTGZ, which='Beginning')
        aspConfigE = metabundleADP.get_asp_configuration_summary(inputTGZ,
                                                                 which='End')

    nObs = len(project.sessions[0].observations)
    tStart = [
        None,
    ] * nObs
    for i in range(nObs):
        tStart[i] = utcjd_to_unix(project.sessions[0].observations[i].mjd +
                                  MJD_OFFSET)
        tStart[i] += project.sessions[0].observations[i].mpm / 1000.0
        tStart[i] = datetime.utcfromtimestamp(tStart[i])
        tStart[i] = _UTC.localize(tStart[i])

    # Get the LST at the start
    observer.date = (min(tStart)).strftime('%Y/%m/%d %H:%M:%S')
    lst = observer.sidereal_time()

    # Report on the file
    print("Filename: %s" % inputTGZ)
    print(" Project ID: %s" % project.id)
    print(" Session ID: %i" % project.sessions[0].id)
    print(" Observations appear to start at %s" %
          (min(tStart)).strftime(_FORMAT_STRING))
    print(" -> LST at %s for this date/time is %s" % (site.name, lst))

    lastDur = project.sessions[0].observations[nObs - 1].dur
    lastDur = timedelta(seconds=int(lastDur / 1000),
                        microseconds=(lastDur * 1000) % 1000000)
    sessionDur = max(tStart) - min(tStart) + lastDur

    print(" ")
    print(" Total Session Duration: %s" % sessionDur)
    print(" -> First observation starts at %s" %
          min(tStart).strftime(_FORMAT_STRING))
    print(" -> Last observation ends at %s" %
          (max(tStart) + lastDur).strftime(_FORMAT_STRING))
    if project.sessions[0].observations[0].mode not in ('TBW', 'TBN'):
        drspec = 'No'
        if project.sessions[0].spcSetup[0] != 0 and project.sessions[
                0].spcSetup[1] != 0:
            drspec = 'Yes'
        drxBeam = project.sessions[0].drx_beam
        if drxBeam < 1:
            drxBeam = "MCS decides"
        else:
            drxBeam = "%i" % drxBeam
        print(" DRX Beam: %s" % drxBeam)
        print(" DR Spectrometer used? %s" % drspec)
        if drspec == 'Yes':
            print(" -> %i channels, %i windows/integration" %
                  tuple(project.sessions[0].spcSetup))
    else:
        tbnCount = 0
        tbwCount = 0
        for obs in project.sessions[0].observations:
            if obs.mode == 'TBW':
                tbwCount += 1
            else:
                tbnCount += 1
        if tbwCount > 0 and tbnCount == 0:
            print(" Transient Buffer Mode: TBW")
        elif tbwCount == 0 and tbnCount > 0:
            print(" Transient Buffer Mode: TBN")
        else:
            print(" Transient Buffer Mode: both TBW and TBN")
    print(" ")
    print("File Information:")
    for obsID in fileInfo.keys():
        print(" Obs. #%i: %s" % (obsID, fileInfo[obsID]['tag']))

    print(" ")
    print("ASP Configuration:")
    print('  Beginning')
    for k, v in aspConfigB.items():
        print('    %s: %i' % (k, v))
    print('  End')
    for k, v in aspConfigE.items():
        print('    %s: %i' % (k, v))

    print(" ")
    print(" Number of observations: %i" % nObs)
    print(" Observation Detail:")
    for i in range(nObs):
        currDur = project.sessions[0].observations[i].dur
        currDur = timedelta(seconds=int(currDur / 1000),
                            microseconds=(currDur * 1000) % 1000000)

        print("  Observation #%i" % (i + 1, ))
        currObs = None
        for j in range(len(obsImpl)):
            if obsImpl[j]['obsID'] == i + 1:
                currObs = obsImpl[j]
                break

        ## Basic setup
        print("   Target: %s" % project.sessions[0].observations[i].target)
        print("   Mode: %s" % project.sessions[0].observations[i].mode)
        print("   Start:")
        print("    MJD: %i" % project.sessions[0].observations[i].mjd)
        print("    MPM: %i" % project.sessions[0].observations[i].mpm)
        print("    -> %s" % get_observation_start_stop(
            project.sessions[0].observations[i])[0].strftime(_FORMAT_STRING))
        print("   Duration: %s" % currDur)

        ## DP setup
        if project.sessions[0].observations[i].mode not in ('TBW', ):
            print("   Tuning 1: %.3f MHz" %
                  (project.sessions[0].observations[i].frequency1 / 1e6, ))
        if project.sessions[0].observations[i].mode not in ('TBW', 'TBN'):
            print("   Tuning 2: %.3f MHz" %
                  (project.sessions[0].observations[i].frequency2 / 1e6, ))
        if project.sessions[0].observations[i].mode not in ('TBW', ):
            print("   Filter code: %i" %
                  project.sessions[0].observations[i].filter)
        if currObs is not None:
            if project.sessions[0].observations[i].mode not in ('TBW', ):
                if project.sessions[0].observations[i].mode == 'TBN':
                    print("   Gain setting: %i" % currObs['tbnGain'])
                else:
                    print("   Gain setting: %i" % currObs['drxGain'])
        else:
            print(
                "   WARNING: observation specification not found for this observation"
            )

        ## Comments/notes
        print("   Observer Comments: %s" %
              project.sessions[0].observations[i].comments)
Exemple #4
0
def main(args):
    # Parse the command line
    filenames = args.filename

    # Check if the first argument on the command line is a directory.  If so,
    # use what is in that directory
    if os.path.isdir(filenames[0]):
        filenames = [
            os.path.join(filenames[0], filename)
            for filename in os.listdir(filenames[0])
        ]
        filenames.sort()

    # Convert the filenames to absolute paths
    filenames = [os.path.abspath(filename) for filename in filenames]

    # Open the database connection to NRAO to find the antenna locations
    try:
        db = database('params')
    except Exception as e:
        sys.stderr.write("WARNING: %s" % str(e))
        sys.stderr.flush()
        db = None

    # Pass 1 - Get the LWA metadata so we know where we are pointed
    context = {
        'observer': 'Unknown',
        'project': 'Unknown',
        'session': None,
        'vlaref': None
    }
    setup = None
    sources = []
    metadata = {}
    lwasite = {}
    for filename in filenames:
        # Figure out what to do with the file
        ext = os.path.splitext(filename)[1]
        if ext == '.tgz':
            ## LWA Metadata
            try:
                ## Extract the SDF
                if len(sources) == 0:
                    try:
                        sdf = metabundle.get_sdf(filename)
                    except Exception as e:
                        sdf = metabundleADP.get_sdf(filename)

                    context['observer'] = sdf.observer.name
                    context['project'] = sdf.id
                    context['session'] = sdf.sessions[0].id

                    comments = sdf.project_office.sessions[0]
                    mtch = CORR_CHANNELS.search(comments)
                    if mtch is not None:
                        corr_channels = int(mtch.group('channels'), 10)
                    else:
                        corr_channels = None
                    mtch = CORR_INTTIME.search(comments)
                    if mtch is not None:
                        corr_inttime = float(mtch.group('inttime'))
                    else:
                        corr_inttime = None
                    mtch = CORR_BASIS.search(comments)
                    if mtch is not None:
                        corr_basis = mtch.group('basis')
                    else:
                        sys.stderr.write(
                            "WARNING: No output correlation polarization basis defined, assuming 'linear'.\n"
                        )
                        corr_basis = 'linear'
                    if corr_channels is not None and corr_inttime is not None:
                        setup = {
                            'channels': corr_channels,
                            'inttime': corr_inttime,
                            'basis': corr_basis
                        }
                    else:
                        sys.stderr.write(
                            "WARNING: No or incomplete correlation configuration defined, setting to be defined at correlation time.\n"
                        )

                    for o, obs in enumerate(sdf.sessions[0].observations):
                        if type(obs).__name__ == 'Solar':
                            name = 'Sun'
                            intent = 'target'
                            ra = None
                            dec = None
                        elif type(obs).__name__ == 'Jovian':
                            name = 'Jupiter'
                            intent = 'target'
                            ra = None
                            dec = None
                        else:
                            name = obs.target
                            intent = obs.name
                            ra = ephem.hours(str(obs.ra))
                            dec = ephem.degrees(str(obs.dec))
                        tStart = mjdmpm_to_datetime(obs.mjd, obs.mpm)
                        tStop = mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
                        sources.append({
                            'name': name,
                            'intent': intent,
                            'ra2000': ra,
                            'dec2000': dec,
                            'start': tStart,
                            'stop': tStop
                        })

                        ### Alternate phase centers
                        comments = sdf.project_office.observations[0][o]

                        alts = {}
                        for mtch in ALT_TARGET.finditer(comments):
                            alt_id = int(mtch.group('id'), 10)
                            alt_name = mtch.group('target')
                            try:
                                alts[alt_id]['name'] = alt_name
                            except KeyError:
                                alts[alt_id] = {
                                    'name': alt_name,
                                    'intent': 'dummy',
                                    'ra': None,
                                    'dec': None
                                }
                        for mtch in ALT_INTENT.finditer(comments):
                            alt_id = int(mtch.group('id'), 10)
                            alt_intent = mtch.group('intent')
                            try:
                                alts[alt_id]['intent'] = alt_intent
                            except KeyError:
                                alts[alt_id] = {
                                    'name': None,
                                    'intent': alt_intent,
                                    'ra': None,
                                    'dec': None
                                }
                        for mtch in ALT_RA.finditer(comments):
                            alt_id = int(mtch.group('id'), 10)
                            alt_ra = ephem.hours(mtch.group('ra'))
                            try:
                                alts[alt_id]['ra'] = alt_ra
                            except KeyError:
                                alts[alt_id] = {
                                    'name': None,
                                    'intent': 'dummy',
                                    'ra': alt_ra,
                                    'dec': None
                                }
                        for mtch in ALT_DEC.finditer(comments):
                            alt_id = int(mtch.group('id'), 10)
                            alt_dec = ephem.degrees(mtch.group('dec'))
                            try:
                                alts[alt_id]['dec'] = alt_dec
                            except KeyError:
                                alts[alt_id] = {
                                    'name': None,
                                    'intent': 'dummy',
                                    'ra': None,
                                    'dec': alt_dec
                                }
                        for alt_id in sorted(alts.keys()):
                            alt_name, alt_ra, alt_dec = alts[alt_id]
                            if alt_name is None or alt_ra is None or alt_dec is None:
                                sys.stderr.write(
                                    "WARNING: Incomplete alternate phase center %i, skipping.\n"
                                    % alt_id)
                            else:
                                sources.append({
                                    'name': alt_name,
                                    'ra2000': alt_ra,
                                    'dec2000': alt_dec,
                                    'start': tStart,
                                    'stop': tStop
                                })

                ## Extract the file information so that we can pair things together
                fileInfo = metabundle.get_session_metadata(filename)
                for obsID in fileInfo.keys():
                    metadata[fileInfo[obsID]['tag']] = filename

                ## Figure out LWA1 vs LWA-SV
                try:
                    cs = metabundle.get_command_script(filename)
                    for c in cs:
                        if c['subsystem_id'] == 'DP':
                            site = 'LWA1'
                            break
                        elif c['subsystem_id'] == 'ADP':
                            site = 'LWA-SV'
                            break
                except (RuntimeError, ValueError):
                    site = 'LWA-SV'
                for obsID in fileInfo.keys():
                    lwasite[fileInfo[obsID]['tag']] = site

            except Exception as e:
                sys.stderr.write("ERROR reading metadata file: %s\n" % str(e))
                sys.stderr.flush()

    # Setup what we need to write out a configuration file
    corrConfig = {
        'context': context,
        'setup': setup,
        'source': {
            'name': '',
            'ra2000': '',
            'dec2000': ''
        },
        'inputs': []
    }

    metadata = {}
    for filename in filenames:
        #print("%s:" % os.path.basename(filename))

        # Skip over empty files
        if os.path.getsize(filename) == 0:
            continue

        # Open the file
        fh = open(filename, 'rb')

        # Figure out what to do with the file
        ext = os.path.splitext(filename)[1]
        if ext == '':
            ## DRX
            try:
                ## Get the site
                try:
                    sitename = lwasite[os.path.basename(filename)]
                except KeyError:
                    sitename = 'LWA1'

                ## Get the location so that we can set site-specific parameters
                if sitename == 'LWA1':
                    xyz = LWA1_ECEF
                    off = args.lwa1_offset
                elif sitename == 'LWA-SV':
                    xyz = LWASV_ECEF
                    off = args.lwasv_offset
                else:
                    raise RuntimeError("Unknown LWA site '%s'" % site)

                ## Move into the LWA1 coordinate system
                ### ECEF to LWA1
                rho = xyz - LWA1_ECEF
                sez = numpy.dot(LWA1_ROT, rho)
                enz = sez[[1, 0, 2]]  # pylint: disable=invalid-sequence-index
                enz[1] *= -1

                ## Read in the first few frames to get the start time
                frames = [drx.read_frame(fh) for i in xrange(1024)]
                streams = []
                freq1, freq2 = 0.0, 0.0
                for frame in frames:
                    beam, tune, pol = frame.id
                    if tune == 1:
                        freq1 = frame.central_freq
                    else:
                        freq2 = frame.central_freq
                    if (beam, tune, pol) not in streams:
                        streams.append((beam, tune, pol))
                tStart = frames[0].time.datetime
                tStartAlt = (frames[-1].time - 1023 // len(streams) * 4096 /
                             frames[-1].sample_rate).datetime
                tStartDiff = tStart - tStartAlt
                if abs(tStartDiff) > timedelta(microseconds=10000):
                    sys.stderr.write(
                        "WARNING: Stale data found at the start of '%s', ignoring\n"
                        % os.path.basename(filename))
                    sys.stderr.flush()
                    tStart = tStartAlt
                ### ^ Adjustment to the start time to deal with occasional problems
                ###   with stale data in the DR buffers at LWA-SV

                ## Read in the last few frames to find the end time
                fh.seek(os.path.getsize(filename) - 1024 * drx.FRAME_SIZE)
                backed = 0
                while backed < 2 * drx.FRAME_SIZE:
                    try:
                        drx.read_frame(fh)
                        fh.seek(-drx.FRAME_SIZE, 1)
                        break
                    except errors.SyncError:
                        backed += 1
                        fh.seek(-drx.FRAME_SIZE - 1, 1)
                for i in xrange(32):
                    try:
                        frame = drx.read_frame(fh)
                        beam, tune, _ = frame.id
                        if tune == 1:
                            freq1 = frame.central_freq
                        else:
                            freq2 = frame.central_freq
                    except errors.SyncError:
                        continue
                tStop = frame.time.datetime

                ## Save
                corrConfig['inputs'].append({
                    'file':
                    filename,
                    'type':
                    'DRX',
                    'antenna':
                    sitename,
                    'pols':
                    'X, Y',
                    'location': (enz[0], enz[1], enz[2]),
                    'clockoffset': (off, off),
                    'fileoffset':
                    0,
                    'beam':
                    beam,
                    'tstart':
                    tStart,
                    'tstop':
                    tStop,
                    'freq': (freq1, freq2)
                })

            except Exception as e:
                sys.stderr.write("ERROR reading DRX file: %s\n" % str(e))
                sys.stderr.flush()

        elif ext == '.vdif':
            ## VDIF
            try:
                ## Read in the GUPPI header
                header = vdif.read_guppi_header(fh)

                ## Read in the first frame
                vdif.FRAME_SIZE = vdif.get_frame_size(fh)
                frame = vdif.read_frame(fh)
                antID = frame.id[0] - 12300
                tStart = frame.time.datetime
                nThread = vdif.get_thread_count(fh)

                ## Read in the last frame
                nJump = int(os.path.getsize(filename) / vdif.FRAME_SIZE)
                nJump -= 30
                fh.seek(nJump * vdif.FRAME_SIZE, 1)
                mark = fh.tell()
                while True:
                    try:
                        frame = vdif.read_frame(fh)
                        tStop = frame.time.datetime
                    except Exception as e:
                        break

                ## Find the antenna location
                pad, edate = db.get_pad('EA%02i' % antID, tStart)
                x, y, z = db.get_xyz(pad, tStart)
                #print("  Pad: %s" % pad)
                #print("  VLA relative XYZ: %.3f, %.3f, %.3f" % (x,y,z))

                ## Move into the LWA1 coordinate system
                ### relative to ECEF
                xyz = numpy.array([x, y, z])
                xyz += VLA_ECEF
                ### ECEF to LWA1
                rho = xyz - LWA1_ECEF
                sez = numpy.dot(LWA1_ROT, rho)
                enz = sez[[1, 0, 2]]  # pylint: disable=invalid-sequence-index
                enz[1] *= -1

                ## Set an apparent position if WiDAR is already applying a delay model
                apparent_enz = (None, None, None)
                if args.no_vla_delay_model:
                    apparent_xyz = VLA_ECEF
                    apparent_rho = apparent_xyz - LWA1_ECEF
                    apparent_sez = numpy.dot(LWA1_ROT, apparent_rho)
                    apparent_enz = apparent_sez[[1, 0, 2]]  # pylint: disable=invalid-sequence-index
                    apparent_enz[1] *= -1

                ## VLA time offset
                off = args.vla_offset

                ## Save
                corrConfig['context']['observer'] = header['OBSERVER']
                try:
                    corrConfig['context']['project'] = header[
                        'BASENAME'].split('_')[0]
                    corrConfig['context']['session'] = header[
                        'BASENAME'].split('_')[1].replace('sb', '')
                except IndexError:
                    corrConfig['context']['project'] = header[
                        'BASENAME'].split('.')[0]
                    corrConfig['context']['session'] = header[
                        'BASENAME'].split('.')[1].replace('sb', '')
                corrConfig['context']['vlaref'] = re.sub(
                    '\.[0-9]+\.[0-9]+\.[AB][CD]-.*', '', header['BASENAME'])
                corrConfig['source']['name'] = header['SRC_NAME']
                corrConfig['source']['intent'] = 'target'
                corrConfig['source']['ra2000'] = header['RA_STR']
                corrConfig['source']['dec2000'] = header['DEC_STR']
                corrConfig['inputs'].append({
                    'file':
                    filename,
                    'type':
                    'VDIF',
                    'antenna':
                    'EA%02i' % antID,
                    'pols':
                    'Y, X',
                    'location': (enz[0], enz[1], enz[2]),
                    'apparent_location':
                    (apparent_enz[0], apparent_enz[1], apparent_enz[2]),
                    'clockoffset': (off, off),
                    'fileoffset':
                    0,
                    'pad':
                    pad,
                    'tstart':
                    tStart,
                    'tstop':
                    tStop,
                    'freq':
                    header['OBSFREQ']
                })

            except Exception as e:
                sys.stderr.write("ERROR reading VDIF file: %s\n" % str(e))
                sys.stderr.flush()

        elif ext == '.tgz':
            ## LWA Metadata
            try:
                ## Extract the file information so that we can pair things together
                fileInfo = metabundle.get_session_metadata(filename)
                for obsID in fileInfo.keys():
                    metadata[fileInfo[obsID]['tag']] = filename

            except Exception as e:
                sys.stderr.write("ERROR reading metadata file: %s\n" % str(e))
                sys.stderr.flush()

        # Done
        fh.close()

    # Close out the connection to NRAO
    try:
        db.close()
    except AttributeError:
        pass

    # Choose a VDIF reference file, if there is one, and mark whether or
    # not DRX files were found
    vdifRefFile = None
    isDRX = False
    for cinp in corrConfig['inputs']:
        if cinp['type'] == 'VDIF':
            if vdifRefFile is None:
                vdifRefFile = cinp
        elif cinp['type'] == 'DRX':
            isDRX = True

    # Set a state variable so that we can generate a warning about missing
    # DRX files
    drxFound = False

    # Purge DRX files that don't make sense
    toPurge = []
    drxFound = False
    lwasvFound = False
    for cinp in corrConfig['inputs']:
        ### Sort out multiple DRX files - this only works if we have only one LWA station
        if cinp['type'] == 'DRX':
            if vdifRefFile is not None:
                l0, l1 = cinp['tstart'], cinp['tstop']
                v0, v1 = vdifRefFile['tstart'], vdifRefFile['tstop']
                ve = (v1 - v0).total_seconds()
                overlapWithVDIF = (v0 >= l0 and v0 < l1) or (l0 >= v0
                                                             and l0 < v1)
                lvo = (min([v1, l1]) - max([v0, l0])).total_seconds()
                if not overlapWithVDIF or lvo < 0.25 * ve:
                    toPurge.append(cinp)
                drxFound = True
            if cinp['antenna'] == 'LWA-SV':
                lwasvFound = True
    for cinp in toPurge:
        del corrConfig['inputs'][corrConfig['inputs'].index(cinp)]

    # Sort the inputs based on the antenna name - this puts LWA1 first,
    # LWA-SV second, and the VLA at the end in 'EA' antenna order, i.e.,
    # EA01, EA02, etc.
    corrConfig['inputs'].sort(key=lambda x: 0 if x['antenna'] == 'LWA1' else (
        1 if x['antenna'] == 'LWA-SV' else int(x['antenna'][2:], 10)))

    # VDIF/DRX warning check/report
    if vdifRefFile is not None and isDRX and not drxFound:
        sys.stderr.write(
            "WARNING: DRX files provided but none overlapped with VDIF data")

    # Duplicate antenna check
    antCounts = {}
    for cinp in corrConfig['inputs']:
        try:
            antCounts[cinp['antenna']] += 1
        except KeyError:
            antCounts[cinp['antenna']] = 1
    for ant in antCounts.keys():
        if antCounts[ant] != 1:
            sys.stderr.write("WARNING: Antenna '%s' is defined %i times" %
                             (ant, antCounts[ant]))

    # Update the file offsets to get things lined up better
    tMax = max([cinp['tstart'] for cinp in corrConfig['inputs']])
    for cinp in corrConfig['inputs']:
        diff = tMax - cinp['tstart']
        offset = diff.days * 86400 + diff.seconds + diff.microseconds / 1e6
        cinp['fileoffset'] = max([0, offset])

    # Reconcile the source lists for when we have eLWA data.  This is needed so
    # that we use the source information contained in the VDIF files rather than
    # the stub information contained in the SDFs
    if len(sources) <= 1:
        if corrConfig['source']['name'] != '':
            ## Update the source information with what comes from the VLA
            try:
                sources[0] = corrConfig['source']
            except IndexError:
                sources.append(corrConfig['source'])
    # Update the dwell time using the minimum on-source time for all inputs if
    # there is only one source, i.e., for full eLWA runs
    if len(sources) == 1:
        sources[0]['start'] = max(
            [cinp['tstart'] for cinp in corrConfig['inputs']])
        sources[0]['stop'] = min(
            [cinp['tstop'] for cinp in corrConfig['inputs']])

    # Render the configuration
    startRef = sources[0]['start']
    s = 0
    for source in sources:
        startOffset = source['start'] - startRef
        startOffset = startOffset.total_seconds()

        dur = source['stop'] - source['start']
        dur = dur.total_seconds()

        ## Skip over dummy scans and scans that start after the files end
        if source['intent'] in (None, 'dummy'):
            continue
        if source['start'] > max(
            [cinp['tstop'] for cinp in corrConfig['inputs']]):
            print(
                "Skipping scan of %s which starts at %s, %.3f s after the data end"
                % (source['name'], source['start'],
                   (source['start'] -
                    max([cinp['tstop']
                         for cinp in corrConfig['inputs']])).total_seconds()))
            continue

        ## Small correction for the first scan to compensate for stale data at LWA-SV
        if lwasvFound and s == 0:
            startOffset += 10.0
            dur -= 10.0

        ## Skip over scans that are too short
        if dur < args.minimum_scan_length:
            continue

        ## Setup
        if args.output is None:
            fh = sys.stdout
        else:
            outname = args.output
            if len(sources) > 1:
                outname += str(s + 1)
            fh = open(outname, 'w')

        try:
            repo = git.Repo(os.path.dirname(os.path.abspath(__file__)))
            try:
                branch = repo.active_branch.name
                hexsha = repo.active_branch.commit.hexsha
            except TypeError:
                branch = '<detached>'
                hexsha = repo.head.commit.hexsha
            shortsha = hexsha[-7:]
            dirty = ' (dirty)' if repo.is_dirty() else ''
        except git.exc.GitError:
            branch = 'unknown'
            hexsha = 'unknown'
            shortsha = 'unknown'
            dirty = ''

        ## Preamble
        fh.write("# Created\n")
        fh.write("#  on %s\n" % datetime.now())
        fh.write("#  using %s, revision %s.%s%s\n" %
                 (os.path.basename(__file__), branch, shortsha, dirty))
        fh.write("\n")
        ## Observation context
        fh.write("Context\n")
        fh.write("  Observer  %s\n" % corrConfig['context']['observer'])
        fh.write("  Project   %s\n" % corrConfig['context']['project'])
        if corrConfig['context']['session'] is not None:
            fh.write("  Session   %s\n" % corrConfig['context']['session'])
        if corrConfig['context']['vlaref'] is not None:
            fh.write("  VLARef    %s\n" % corrConfig['context']['vlaref'])
        fh.write("EndContext\n")
        fh.write("\n")
        ## Configuration, if present
        if corrConfig['setup'] is not None:
            fh.write("Configuration\n")
            fh.write("  Channels     %i\n" % corrConfig['setup']['channels'])
            fh.write("  IntTime      %.3f\n" % corrConfig['setup']['inttime'])
            fh.write("  PolBasis     %s\n" % corrConfig['setup']['basis'])
            fh.write("EndConfiguration\n")
            fh.write("\n")
        ## Source
        fh.write("Source\n")
        fh.write("# Observation start is %s\n" % source['start'])
        fh.write("# Duration is %s\n" % (source['stop'] - source['start'], ))
        fh.write("  Name     %s\n" % source['name'])
        fh.write("  Intent   %s\n" % source['intent'].lower())
        if source['name'] not in ('Sun', 'Jupiter'):
            fh.write("  RA2000   %s\n" % source['ra2000'])
            fh.write("  Dec2000  %s\n" % source['dec2000'])
        fh.write("  Duration %.3f\n" % dur)
        fh.write("SourceDone\n")
        fh.write("\n")
        ## Input files
        for cinp in corrConfig['inputs']:
            fh.write("Input\n")
            fh.write("# Start time is %s\n" % cinp['tstart'])
            fh.write("# Stop time is %s\n" % cinp['tstop'])
            try:
                fh.write("# Beam is %i\n" % cinp['beam'])
            except KeyError:
                pass
            try:
                fh.write("# VLA pad is %s\n" % cinp['pad'])
            except KeyError:
                pass
            try:
                fh.write("# Frequency tuning 1 is %.3f Hz\n" % cinp['freq'][0])
                fh.write("# Frequency tuning 2 is %.3f Hz\n" % cinp['freq'][1])
            except TypeError:
                fh.write("# Frequency tuning is %.3f Hz\n" % cinp['freq'])
            fh.write("  File             %s\n" % cinp['file'])
            try:
                metaname = metadata[os.path.basename(cinp['file'])]
                fh.write("  MetaData         %s\n" % metaname)
            except KeyError:
                if cinp['type'] == 'DRX':
                    sys.stderr.write(
                        "WARNING: No metadata found for '%s', source %i\n" %
                        (os.path.basename(cinp['file']), s + 1))
                    sys.stderr.flush()
                pass
            fh.write("  Type             %s\n" % cinp['type'])
            fh.write("  Antenna          %s\n" % cinp['antenna'])
            fh.write("  Pols             %s\n" % cinp['pols'])
            fh.write("  Location         %.6f, %.6f, %.6f\n" %
                     cinp['location'])
            try:
                if cinp['apparent_location'][0] is not None:
                    fh.write("  ApparentLocation %.6f, %.6f, %.6f\n" %
                             cinp['apparent_location'])
            except KeyError:
                pass
            fh.write("  ClockOffset      %s, %s\n" % cinp['clockoffset'])
            fh.write("  FileOffset       %.3f\n" %
                     (startOffset + cinp['fileoffset'], ))
            fh.write("InputDone\n")
            fh.write("\n")
        if fh != sys.stdout:
            fh.close()

        # Increment the source/file counter
        s += 1
def main(args):
    # Length of the FFT and the window to use
    LFFT = args.fft_length
    if args.bartlett:
        window = numpy.bartlett
    elif args.blackman:
        window = numpy.blackman
    elif args.hanning:
        window = numpy.hanning
    else:
        window = fxc.null_window
    args.window = window

    # Open the file and find good data
    idf = LWA1DataFile(args.filename,
                       ignore_timetag_errors=args.ignore_time_errors)

    # Metadata
    nFramesFile = idf.get_info('nframe')
    srate = idf.get_info('sample_rate')
    antpols = idf.get_info('nantenna')

    # Number of frames to integrate over
    nFramesAvg = int(args.average * srate / 512) * antpols
    nFramesAvg = int(1.0 * (nFramesAvg // antpols) * 512 /
                     float(LFFT)) * LFFT / 512 * antpols
    args.average = 1.0 * (nFramesAvg // antpols) * 512 / srate
    maxFrames = nFramesAvg

    # Offset into the file, if needed
    offset = idf.offset(args.skip)

    # Number of remaining chunks (and the correction to the number of
    # frames to read in).
    if args.metadata is not None:
        args.duration = 0
    if args.duration == 0:
        args.duration = 1.0 * nFramesFile / antpols * 512 / srate
        args.duration -= args.skip
    else:
        args.duration = int(
            round(args.duration * srate * antpols / 512) // antpols * 512 //
            srate)
    nChunks = int(round(args.duration / args.average))
    if nChunks == 0:
        nChunks = 1
    nFrames = nFramesAvg * nChunks

    # Date & Central Frequency
    t1 = idf.get_info('start_time')
    beginDate = t1.datetime
    central_freq1 = idf.get_info('freq1')

    # File summary
    print("Filename: %s" % args.filename)
    print("Date of First Frame: %s" % str(beginDate))
    print("Antenna/Pols: %i" % antpols)
    print("Sample Rate: %i Hz" % srate)
    print("Tuning Frequency: %.3f Hz" % (central_freq1, ))
    print("Frames: %i (%.3f s)" %
          (nFramesFile, 1.0 * nFramesFile / antpols * 512 / srate))
    print("---")
    print("Offset: %.3f s (%i frames)" % (args.skip, offset))
    print("Integration: %.3f s (%i frames; %i frames per antenna/pol)" %
          (args.average, nFramesAvg, nFramesAvg // antpols))
    print("Duration: %.3f s (%i frames; %i frames per antenna/pol)" %
          (args.average * nChunks, nFrames, nFrames // antpols))
    print("Chunks: %i" % nChunks)
    print(" ")

    # Estimate clip level (if needed)
    if args.estimate_clip_level:
        estimate = idf.estimate_levels(sigma=5.0)
        clip1 = 1.0 * sum(estimate) / len(estimate)
    else:
        clip1 = args.clip_level

    # Get the antennas for Stokes calculation
    if args.metadata is not None:
        try:
            project = metabundle.get_sdf(args.metadata)
            station = stations.lwa1
        except Exception as e:
            project = metabundleADP.get_sdf(args.metadata)
            station = stations.lwasv
    elif args.lwasv:
        station = stations.lwasv
    else:
        station = stations.lwa1
    antennas = station.antennas

    # Setup the output file
    outname = os.path.split(args.filename)[1]
    outname = os.path.splitext(outname)[0]
    outname = '%s-tbn-waterfall.hdf5' % outname

    if os.path.exists(outname):
        if not args.force:
            yn = raw_input("WARNING: '%s' exists, overwrite? [Y/n] " % outname)
        else:
            yn = 'y'

        if yn not in ('n', 'N'):
            os.unlink(outname)
        else:
            raise RuntimeError("Output file '%s' already exists" % outname)

    f = hdfData.create_new_file(outname)

    # Look at the metadata and come up with a list of observations.  If
    # there are no metadata, create a single "observation" that covers the
    # whole file.
    obsList = {}
    if args.metadata is not None:
        try:
            project = metabundle.get_sdf(args.metadata)
        except Exception as e:
            project = metabundleADP.get_sdf(args.metadata)

        sdfBeam = project.sessions[0].drx_beam
        if sdfBeam != 5:
            raise RuntimeError(
                "Metadata is for beam #%i, but data is from beam #%i" %
                (sdfBeam, 5))

        for i, obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsDur = obs.dur / 1000.0
            obsSR = tbn.FILTER_CODES[obs.filter]

            obsList[i + 1] = (sdfStart, sdfStop, obsDur, obsSR)

        print("Observations:")
        for i in sorted(obsList.keys()):
            obs = obsList[i]
            print(" #%i: %s to %s (%.3f s) at %.3f MHz" %
                  (i, obs[0], obs[1], obs[2], obs[3] / 1e6))
        print(" ")

        hdfData.fill_from_metabundle(f, args.metadata)

    elif args.sdf is not None:
        try:
            project = sdf.parse_sdf(args.sdf)
        except Exception as e:
            project = sdfADP.parse_sdf(args.sdf)

        sdfBeam = project.sessions[0].drx_beam
        if sdfBeam != 5:
            raise RuntimeError(
                "Metadata is for beam #%i, but data is from beam #%i" %
                (sdfBeam, 5))

        for i, obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsDur = obs.dur / 1000.0
            obsSR = tbn.FILTER_CODES[obs.filter]

            obsList[i + 1] = (sdfStart, sdfStop, obsDur, obsSR)

        site = 'lwa1'
        if args.lwasv:
            site = 'lwasv'
        hdfData.fill_from_sdf(f, args.sdf, station=site)

    else:
        obsList[1] = (datetime.utcfromtimestamp(t1),
                      datetime(2222, 12, 31, 23, 59, 59), args.duration, srate)

        site = 'lwa1'
        if args.lwasv:
            site = 'lwasv'
        hdfData.fill_minimum(f, 1, 5, srate, station=site)

    if (not args.stokes):
        data_products = ['XX', 'YY']
    else:
        data_products = ['I', 'Q', 'U', 'V']

    for o in sorted(obsList.keys()):
        for t in range(len(antennas) // 2):
            hdfData.create_observation_set(
                f, o, t + 1, numpy.arange(LFFT, dtype=numpy.float64),
                int(round(obsList[o][2] / args.average)), data_products)

    f.attrs['FileGenerator'] = 'tbnWaterfall.py'
    f.attrs['InputData'] = os.path.basename(args.filename)

    # Create the various HDF group holders
    ds = {}
    for o in sorted(obsList.keys()):
        obs = hdfData.get_observation_set(f, o)

        ds['obs%i' % o] = obs
        ds['obs%i-time' % o] = hdfData.get_time(f, o)

        for t in range(len(antennas) // 2):
            ds['obs%i-freq%i' % (o, t + 1)] = hdfData.get_data_set(
                f, o, t + 1, 'freq')
            for p in data_products:
                ds["obs%i-%s%i" % (o, p, t + 1)] = hdfData.get_data_set(
                    f, o, t + 1, p)
            ds['obs%i-Saturation%i' % (o, t + 1)] = hdfData.get_data_set(
                f, o, t + 1, 'Saturation')

    # Load in the correct analysis function
    if (not args.stokes):
        process_data = process_data_to_linear
    else:
        process_data = process_data_to_stokes

    # Go!
    for o in sorted(obsList.keys()):
        try:
            process_data(idf,
                         antennas,
                         obsList[o][0],
                         obsList[o][2],
                         obsList[o][3],
                         args,
                         ds,
                         obsID=o,
                         clip1=clip1)
        except RuntimeError as e:
            print("Observation #%i: %s, abandoning this observation" %
                  (o, str(e)))

    # Save the output to a HDF5 file
    f.close()

    # Close out the data file
    idf.close()
Exemple #6
0
def main(args):
    # Get the file names
    meta = args.metadata
    data = args.filename

    # Get all observations and their start times
    try:
        ## LWA-1
        sdf = metabundle.get_sdf(meta)
        ses = metabundle.get_session_spec(meta)
        obs = metabundle.get_observation_spec(meta)
    except:
        ## LWA-SV
        ### Try again
        sdf = metabundleADP.get_sdf(meta)
        ses = metabundleADP.get_session_spec(meta)
        obs = metabundleADP.get_observation_spec(meta)
    obs.sort(_obs_comp)
    tStart = []
    oDetails = []
    for i,o in enumerate(obs):
        tStart.append( mjdmpm_to_datetime(o['mjd'], o['mpm']) )
        oDetails.append( {'m': o['mode'], 'd': o['dur'] / 1000.0, 'f': o['bw'], 
                          'p': o['project_id'], 's': o['session_id'], 'o': o['obs_id'], 
                          't': sdf.sessions[0].observations[o['obs_id']-1].target} )

        print("Observation #%i" % (o['obs_id']))
        print(" Start: %i, %i -> %s" % (o['mjd'], o['mpm'], tStart[-1]))
        print(" Mode: %s" % mode_to_string(o['mode']))
        print(" BW: %i" % o['bw'])
        print(" Target: %s" % sdf.sessions[0].observations[o['obs_id']-1].target)
    print(" ")

    # Figure out where in the file the various bits are.
    fh = open(data, 'rb')
    lf = drx.read_frame(fh)
    beam, j, k = lf.id
    if beam != obs[0]['drx_beam']:
        print('ERROR: Beam mis-match, metadata is for #%i, file is for #%i' % (obs[0]['drx_beam'], beam))
        sys.exit()
    firstFrame = lf.time.datetime
    if abs(firstFrame - min(tStart)) > timedelta(seconds=30):
        print('ERROR: Time mis-match, metadata is for %s, file is for %s' % (min(tStart), firstFrame))
        sys.exit()
    fh.seek(0)

    for i in range(len(tStart)):
        eof = False

        ## Get observation properties
        oStart = tStart[i]
        oMode = mode_to_string(oDetails[i]['m'])
        oDur  = oDetails[i]['d']
        oBW   = oDetails[i]['f']
        print("Seeking %s observation of %.3f seconds at %s" % (oMode, oDur, oStart))

        ## Get the correct reader to use
        if oMode == 'TBW':
            reader = tbw
            bwKey = None
            bwMult = 520.0 / 400
            fCount = 400
        elif oMode == 'TBN':
            reader = tbn
            bwKey = tbn.FILTER_CODES
            bwMult = 520.0 / 512
            fCount = 512
        else:
            reader = drx
            bwKey = drx.FILTER_CODES
            bwMult = 4.0 / 4096
            fCount = 4096

        ## Jump ahead to where the next frame should be, if needed
        if i != 0:
            pDur  = oDetails[i-1]['d']
            pBW   = oDetails[i-1]['f']

            nFramesSkip = int(pDur*bwKey[pBW]*bwMult)
            fh.seek(nFramesSkip*reader.FRAME_SIZE, 1)
            if fh.tell() >= os.path.getsize(data):
                fh.seek(-10*reader.FRAME_SIZE, 2)
                
        ## Figure out where we are and make sure we line up on a frame
        ## NOTE: This should never be needed
        fail = True
        while fail:
            try:
                frame = reader.read_frame(fh)
                fail = False
            except errors.SyncError:
                fh.seek(1, 1)
            except errors.EOFError:
                break
        fh.seek(-reader.FRAME_SIZE, 1)	

        ## Go in search of the start of the observation
        if frame.time.datetime < oStart:
            ### We aren't at the beginning yet, seek fowards
            print("-> At byte %i, time is %s < %s" % (fh.tell(), frame.time.datetime, oStart))

            while frame.time.datetime < oStart:
                try:
                    frame = reader.read_frame(fh)
                except errors.SyncError:		
                    fh.seek(1, 1)
                except errors.EOFError:
                    break
                #print(frame.time.datetime, oStart)

        elif frame.time.datetime > oStart:
            ### We've gone too far, seek backwards
            print("-> At byte %i, time is %s > %s" % (fh.tell(), frame.time.datetime, oStart))

            while frame.time.datetime > oStart:
                if fh.tell() == 0:
                    break
                fh.seek(-2*reader.FRAME_SIZE, 1)
                try:
                    frame = reader.read_frame(fh)
                except errors.SyncError:		
                    fh.seek(-1, 1)
                except errors.EOFError:
                    break
                #print(frame.time.datetime, oStart)
                
        else:
            ### We're there already
            print("-> At byte %i, time is %s = %s" % (fh.tell(), frame.time.datetime, oStart))
            
        ## Jump back exactly one frame so that the filehandle is in a position 
        ## to read the first frame that is part of the observation
        try:
            frame = reader.read_frame(fh)
            print("-> At byte %i, time is %s = %s" % (fh.tell(), frame.time.datetime, oStart))
            fh.seek(-reader.FRAME_SIZE, 1)
        except errors.EOFError:
            pass
            
        ## Update the bytes ranges
        if fh.tell() < os.path.getsize(data):
            oDetails[i]['b'] = fh.tell()
            oDetails[i]['e'] = -1
        else:
            oDetails[i]['b'] = -1
            oDetails[i]['e'] = -1

        if i != 0:
            oDetails[i-1]['e'] = fh.tell()

        ## Progress report
        if oDetails[i]['b'] >= 0:
            print('-> Obs.', oDetails[i]['o'], 'starts at byte', oDetails[i]['b'])
        else:
            print('-> Obs.', oDetails[i]['o'], 'starts after the end of the file')
    print(" ")

    # Report
    for i in range(len(tStart)):
        if oDetails[i]['b'] < 0:
            print("%s, Session %i, Observation %i: not found" % (oDetails[i]['p'], oDetails[i]['s'], oDetails[i]['o']))

        else:
            print("%s, Session %i, Observation %i: %i to %i (%i bytes)" % (oDetails[i]['p'], oDetails[i]['s'], oDetails[i]['o'], oDetails[i]['b'], oDetails[i]['e'], (oDetails[i]['e'] - oDetails[i]['b'])))
    print(" ")

    # Split
    if not args.list:
        for i in range(len(tStart)):
            if oDetails[i]['b'] < 0:
                continue
                
            ## Report
            print("Working on Observation %i" % (i+1,))
            
            ## Create the output name
            if args.source:
                outname = '%s_%i_%s.dat' % (oDetails[i]['p'], oDetails[i]['s'], oDetails[i]['t'].replace(' ', '').replace('/','').replace('&','and'))
            else:
                outname = '%s_%i_%i.dat' % (oDetails[i]['p'], oDetails[i]['s'], oDetails[i]['o'])
                
            oMode = mode_to_string(oDetails[i]['m'])

            ## Get the correct reader to use
            if oMode == 'TBW':
                reader = tbw

            elif oMode == 'TBN':
                reader = tbn
            else:
                reader = drx

            ## Get the number of frames
            if oDetails[i]['e'] > 0:
                nFramesRead = (oDetails[i]['e'] - oDetails[i]['b']) // reader.FRAME_SIZE
            else:
                nFramesRead = (os.path.getsize(data) - oDetails[i]['b']) // reader.FRAME_SIZE

            ## Split
            if os.path.exists(outname):
                if not args.force:
                    yn = input("WARNING: '%s' exists, overwrite? [Y/n] " % outname)
                else:
                    yn = 'y'
                    
                if yn not in ('n', 'N'):
                    os.unlink(outname)
                else:
                    print("WARNING: output file '%s' already exists, skipping" % outname)
                    continue
                    
            fh.seek(oDetails[i]['b'])
            
            t0 = time.time()
            oh = open(outname, 'wb')
            for sl in [2**i for i in range(17)[::-1]]:
                while nFramesRead >= sl:
                    temp = fh.read(sl*reader.FRAME_SIZE)
                    oh.write(temp)
                    nFramesRead -= sl
            oh.close()
            t1 = time.time()
            print("  Copied %i bytes in %.3f s (%.3f MB/s)" % (os.path.getsize(outname), t1-t0, os.path.getsize(outname)/1024.0**2/(t1-t0)))
    print(" ")
Exemple #7
0
def main(args):
    # Length of the FFT and the window to use
    LFFT = args.fft_length
    if args.bartlett:
        window = numpy.bartlett
    elif args.blackman:
        window = numpy.blackman
    elif args.hanning:
        window = numpy.hanning
    else:
        window = fxc.null_window
    args.window = window

    # Open the file and find good data (not spectrometer data)
    fh = open(args.filename, "rb")

    try:
        for i in xrange(5):
            junkFrame = drspec.read_frame(fh)
        raise RuntimeError(
            "ERROR: '%s' appears to be a DR spectrometer file, not a raw DRX file"
            % args.filename)
    except errors.SyncError:
        fh.seek(0)

    # Good, we seem to have a real DRX file, switch over to the LDP interface
    fh.close()
    idf = LWA1DataFile(args.filename,
                       ignore_timetag_errors=args.ignore_time_errors)

    # Metadata
    nFramesFile = idf.get_info('nframe')
    beam = idf.get_info('beam')
    srate = idf.get_info('sample_rate')
    beampols = idf.get_info('nbeampol')
    beams = max([1, beampols // 4])

    # Number of frames to integrate over
    nFramesAvg = int(args.average * srate / 4096) * beampols
    nFramesAvg = int(1.0 * (nFramesAvg // beampols) * 4096 /
                     float(LFFT)) * LFFT / 4096 * beampols
    args.average = 1.0 * (nFramesAvg // beampols) * 4096 / srate
    maxFrames = nFramesAvg

    # Offset into the file, if needed
    offset = idf.offset(args.skip)

    # Number of remaining chunks (and the correction to the number of
    # frames to read in).
    if args.metadata is not None:
        args.duration = 0
    if args.duration == 0:
        args.duration = 1.0 * nFramesFile / beampols * 4096 / srate
        args.duration -= args.skip
    else:
        args.duration = int(
            round(args.duration * srate * beampols / 4096) / beampols * 4096 /
            srate)
    nChunks = int(round(args.duration / args.average))
    if nChunks == 0:
        nChunks = 1
    nFrames = nFramesAvg * nChunks

    # Date & Central Frequency
    t1 = idf.get_info('start_time')
    beginDate = t1.datetime
    central_freq1 = idf.get_info('freq1')
    central_freq2 = idf.get_info('freq2')

    # File summary
    print("Filename: %s" % args.filename)
    print("Date of First Frame: %s" % str(beginDate))
    print("Beams: %i" % beams)
    print("Tune/Pols: %i" % beampols)
    print("Sample Rate: %i Hz" % srate)
    print("Tuning Frequency: %.3f Hz (1); %.3f Hz (2)" %
          (central_freq1, central_freq2))
    print("Frames: %i (%.3f s)" %
          (nFramesFile, 1.0 * nFramesFile / beampols * 4096 / srate))
    print("---")
    print("Offset: %.3f s (%i frames)" % (args.skip, offset))
    print("Integration: %.3f s (%i frames; %i frames per beam/tune/pol)" %
          (args.average, nFramesAvg, nFramesAvg / beampols))
    print("Duration: %.3f s (%i frames; %i frames per beam/tune/pol)" %
          (args.average * nChunks, nFrames, nFrames / beampols))
    print("Chunks: %i" % nChunks)
    print(" ")

    # Estimate clip level (if needed)
    if args.estimate_clip_level:
        estimate = idf.estimate_levels(fh, sigma=5.0)
        clip1 = (estimate[0] + estimate[1]) / 2.0
        clip2 = (estimate[2] + estimate[3]) / 2.0
    else:
        clip1 = args.clip_level
        clip2 = args.clip_level

    # Make the pseudo-antennas for Stokes calculation
    antennas = []
    for i in xrange(4):
        if i // 2 == 0:
            newAnt = stations.Antenna(1)
        else:
            newAnt = stations.Antenna(2)

        if i % 2 == 0:
            newAnt.pol = 0
        else:
            newAnt.pol = 1

        antennas.append(newAnt)

    # Setup the output file
    outname = os.path.split(args.filename)[1]
    outname = os.path.splitext(outname)[0]
    outname = '%s-waterfall.hdf5' % outname

    if os.path.exists(outname):
        if not args.force:
            yn = raw_input("WARNING: '%s' exists, overwrite? [Y/n] " % outname)
        else:
            yn = 'y'

        if yn not in ('n', 'N'):
            os.unlink(outname)
        else:
            raise RuntimeError("Output file '%s' already exists" % outname)

    f = hdfData.create_new_file(outname)

    # Look at the metadata and come up with a list of observations.  If
    # there are no metadata, create a single "observation" that covers the
    # whole file.
    obsList = {}
    if args.metadata is not None:
        try:
            project = metabundle.get_sdf(args.metadata)
        except Exception as e:
            project = metabundleADP.get_sdf(args.metadata)

        sdfBeam = project.sessions[0].drx_beam
        spcSetup = project.sessions[0].spcSetup
        if sdfBeam != beam:
            raise RuntimeError(
                "Metadata is for beam #%i, but data is from beam #%i" %
                (sdfBeam, beam))

        for i, obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsDur = obs.dur / 1000.0
            obsSR = drx.FILTER_CODES[obs.filter]

            obsList[i + 1] = (sdfStart, sdfStop, obsDur, obsSR)

        print("Observations:")
        for i in sorted(obsList.keys()):
            obs = obsList[i]
            print(" #%i: %s to %s (%.3f s) at %.3f MHz" %
                  (i, obs[0], obs[1], obs[2], obs[3] / 1e6))
        print(" ")

        hdfData.fill_from_metabundle(f, args.metadata)

    elif args.sdf is not None:
        try:
            project = sdf.parse_sdf(args.sdf)
        except Exception as e:
            project = sdfADP.parse_sdf(args.sdf)

        sdfBeam = project.sessions[0].drx_beam
        spcSetup = project.sessions[0].spcSetup
        if sdfBeam != beam:
            raise RuntimeError(
                "Metadata is for beam #%i, but data is from beam #%i" %
                (sdfBeam, beam))

        for i, obs in enumerate(project.sessions[0].observations):
            sdfStart = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm)
            sdfStop = mcs.mjdmpm_to_datetime(obs.mjd, obs.mpm + obs.dur)
            obsDur = obs.dur / 1000.0
            obsSR = drx.FILTER_CODES[obs.filter]

            obsList[i + 1] = (sdfStart, sdfStop, obsDur, obsSR)

        site = 'lwa1'
        if args.lwasv:
            site = 'lwasv'
        hdfData.fill_from_sdf(f, args.sdf, station=site)

    else:
        obsList[1] = (datetime.utcfromtimestamp(t1),
                      datetime(2222, 12, 31, 23, 59, 59), args.duration, srate)

        site = 'lwa1'
        if args.lwasv:
            site = 'lwasv'
        hdfData.fill_minimum(f, 1, beam, srate, station=site)

    if (not args.stokes):
        data_products = ['XX', 'YY']
    else:
        data_products = ['I', 'Q', 'U', 'V']

    for o in sorted(obsList.keys()):
        for t in (1, 2):
            hdfData.create_observation_set(
                f, o, t, numpy.arange(LFFT, dtype=numpy.float64),
                int(round(obsList[o][2] / args.average)), data_products)

    f.attrs['FileGenerator'] = 'hdfWaterfall.py'
    f.attrs['InputData'] = os.path.basename(args.filename)

    # Create the various HDF group holders
    ds = {}
    for o in sorted(obsList.keys()):
        obs = hdfData.get_observation_set(f, o)

        ds['obs%i' % o] = obs
        ds['obs%i-time' % o] = hdfData.get_time(f, o)

        for t in (1, 2):
            ds['obs%i-freq%i' % (o, t)] = hdfData.get_data_set(f, o, t, 'freq')
            for p in data_products:
                ds["obs%i-%s%i" % (o, p, t)] = hdfData.get_data_set(f, o, t, p)
            ds['obs%i-Saturation%i' % (o, t)] = hdfData.get_data_set(
                f, o, t, 'Saturation')

    # Load in the correct analysis function
    if (not args.stokes):
        processDataBatch = processDataBatchLinear
    else:
        processDataBatch = processDataBatchStokes

    # Go!
    for o in sorted(obsList.keys()):
        try:
            processDataBatch(idf,
                             antennas,
                             obsList[o][0],
                             obsList[o][2],
                             obsList[o][3],
                             args,
                             ds,
                             obsID=o,
                             clip1=clip1,
                             clip2=clip2)
        except RuntimeError as e:
            print("Observation #%i: %s, abandoning this observation" %
                  (o, str(e)))

    # Save the output to a HDF5 file
    f.close()

    # Close out the data file
    idf.close()