Exemplo n.º 1
0
def m5time(fn, fmt, offset):
    """Reports first timestamp in file and verifies additional timestamps are consistent."""
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
    except:
        print("Error: problem opening or decoding %s\n" % (fn))
        return EXIT_FAILURE

    if not (dms.format in fmt_supported):
        print('File format is not supported by m5ime.py')
        return EXIT_FAILURE

    mjd = dms.mjd
    sec = dms.sec + 1e-9 * dms.ns
    now = datetime.utcnow()
    now_mjd = date_to_mjd(now)

    if dms.format in [m5lib.MK5_FORMAT_VLBA, m5lib.MK5_FORMAT_MARK5B]:
        mjd = mjd + (now_mjd - (now_mjd % 1000))
        if (mjd > now_mjd):
            mjd = mjd - 1000

    (r, ss) = divmod(sec, 60)
    (hh, mm) = divmod(r, 60)

    mjd_curr = ms.contents.mjd
    for ii in range(Ncheck):
        m5lib.mark5_stream_next_frame(ms)
        mjd_next = ms.contents.mjd
        if ((mjd_next - mjd_curr) >= 2) or (mjd_curr > now_mjd):
            print(
                'Possibly wrong file format specified : MJD in frame %d jumped from %d to %d.'
                % (ii, mjd_curr, mjd_next))
            return EXIT_FAILURE
        mjd_curr = mjd_next

    print('MJD = %d/%02d:%02d:%05.2f' % (mjd, hh, mm, ss))
    return 0
Exemplo n.º 2
0
def m5subband(fn, fmt, fout, if_nr, factor, Ldft, start_bin, stop_bin, offset):
    """Extracts narrow-band signal out from file"""

    # Derived settings
    nin = Ldft
    nout = stop_bin - start_bin + 1
    #Lout = next_pow2(2*(nout-nout%2)) # time-domain output data will be somewhat oversampled
    Lout = next_even(
        2 * (nout - nout % 2)
    )  # time-domain output data will be closer to critically sampled
    iter = 0

    # Open file
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
        m5lib.mark5_stream_fix_mjd(ms, refMJD_Mark5B)
        (mjd, sec, ns) = m5lib.helpers.get_sample_time(ms)
    except:
        print('Error: problem opening or decoding %s\n' % (fn))
        return 1

    # Safety checks
    if (if_nr < 0) or (if_nr >= dms.nchan) or (factor < 0) or (
            factor > 32) or (Ldft < 2) or (start_bin > stop_bin) or (stop_bin
                                                                     >= Ldft):
        print('Error: invalid command line arguments')
        return 1
    if (Ldft % factor) > 0:
        print(
            'Error: length of DFT (Ldft=%u) must be divisible by overlap-add factor (factor=%u)'
            % (Ldft, factor))
        return 1
    if (Lout % factor) > 0:
        print(
            'Error: length derived for output IDFT (Lout=%u) does not divide the overlap-add factor (factor=%u)'
            % (Lout, factor))
        return 1

    # Get storage for raw sample data from m5lib.mark5_stream_decode()
    pdata = m5lib.helpers.make_decoder_array(ms, nin, dtype=ctypes.c_float)
    if_data = ctypes.cast(pdata[if_nr], ctypes.POINTER(ctypes.c_float * nin))

    # Numpy 2D arrays for processed data
    fp = 'float32'
    cp = 'complex64'  # complex64 is 2 x float32
    flt_in = numpy.zeros(shape=(factor, nin), dtype=fp)
    flt_out = numpy.zeros(shape=(factor, Lout), dtype=cp)
    iconcat = numpy.array([0.0 for x in range(2 * nin)], dtype=fp)
    oconcat = numpy.array([0.0 + 0.0j for x in range(2 * Lout)], dtype=cp)

    # Coefficient for coherent phase connection between overlapped input segments
    r = float(start_bin) / float(factor)
    rfrac = r - numpy.floor(r)
    rot_f0 = numpy.exp(2j * numpy.pi * rfrac)
    if (abs(numpy.imag(rot_f0)) < 1e-5):
        # set near-zero values to zero
        rot_f0 = numpy.real(rot_f0) + 0.0j
    rot_f = rot_f0**0.0

    # Window functions for DFT and IDFT
    win_in = numpy.cos(
        (numpy.pi / nin) * (numpy.linspace(0, nin - 1, nin) - 0.5 * (nin - 1)))
    win_in = numpy.resize(win_in.astype(fp), new_shape=(factor, nin))
    win_out = numpy.cos((numpy.pi / Lout) *
                        (numpy.linspace(0, Lout - 1, Lout) - 0.5 * (Lout - 1)))
    win_out = numpy.resize(win_out.astype(fp), new_shape=(factor, Lout))

    # Prepare VDIF output file with reduced data rate and same starting timestamp
    bwout = float(dms.samprate) * (nout / float(nin))
    fsout = 2 * bwout
    outMbps = fsout * 1e-6 * 32  # 32 for real-valued data, 64 for complex data
    vdiffmt = 'VDIF_8192-%u-1-32' % (outMbps)
    if not (int(outMbps) == outMbps):
        print('*** Warning: output rate is non-integer (%e Ms/s)! ***' %
              (outMbps))

    (vdifref,
     vdifsec) = m5lib.helpers.get_VDIF_time_from_MJD(mjd, sec + 1e-9 * ns)

    vdif = m5lib.writers.VDIFEncapsulator()
    vdif.open(fout, format=vdiffmt, complex=False, station='SB')
    vdif.set_time(vdifref, vdifsec, framenr=0)
    vdiffmt = vdif.get_format()

    # Report
    bw = float(dms.samprate) * 0.5
    print('Input file   : start MJD %u/%.6f sec' % (mjd, sec + ns * 1e-9))
    print(
        'Bandwidth    : %u kHz in, %.2f kHz out, bandwidth reduction of ~%.2f:1'
        % (1e-3 * bw, nout * 1e-3 * bw / nin, float(nin) / nout))
    print('Input side   : %u-point DFT with %u bins (%u...%u) extracted' %
          (nin, nout, start_bin, stop_bin))
    print('Output side  : %u-point IDFT with %u-point zero padding' %
          (Lout, Lout - nout))
    print('Overlap      : %u samples on input, %u samples on output' %
          (nin - nin / factor, Lout - Lout / factor))
    print('Phasors      : %s^t : %s ...' %
          (str(rot_f0), str([rot_f0**t for t in range(factor + 2)])))
    print('Output file  : rate %.3f Mbps, %u fps, format %s' %
          (outMbps, vdif.get_fps(), vdif.get_format()))

    # Do filtering
    print('Filtering...')
    while True:

        # Get next full slice of data
        rc = m5lib.mark5_stream_decode(ms, nin, pdata)
        if (rc < 0):
            print('\n<EOF> status=%d' % (rc))
            return 0
        in_new = numpy.frombuffer(if_data.contents, dtype='float32')

        # Debug: replace data with noise + tone
        if False:
            t = iter * nin + numpy.array(range(nin))
            f = (start_bin + numpy.floor(nout / 2.0)) / float(nin)
            in_new = numpy.random.standard_normal(
                size=in_new.size) + 10 * numpy.sin(2 * numpy.pi * f * t)
            in_new = in_new.astype('float32')

        # Feed the window-overlap-DFT processing input stage
        iconcat = numpy.concatenate([iconcat[0:nin], in_new])  # [old,new]
        for ii in range(factor):
            iconcat = numpy.roll(iconcat, -nin / factor)
            flt_in[ii] = iconcat[0:nin]

        # Window and do 1D DFT of 2D array
        flt_in = numpy.multiply(flt_in, win_in)
        F = numpy.fft.fft(flt_in)

        # Copy the desired bins and fix DC/Nyquist bins
        for ii in range(factor):
            flt_out[ii][0:nout] = F[ii][start_bin:(start_bin + nout)]
            flt_out[ii][0] = 0.0  # numpy.real(flt_out[ii][0])
            flt_out[ii][nout - 1] = 0.0  # numpy.real(flt_out[ii][nout-1])

        # Do inverse 1D DFT and window the result
        F = numpy.fft.ifft(flt_out)
        F = numpy.multiply(F, win_out)

        # Reconstruct time domain signal by shifting and stacking overlapped segments coherently
        for ii in range(factor):
            oconcat[Lout:] = oconcat[Lout:] + F[ii] * rot_f
            rot_f = rot_f * rot_f0
            oconcat = numpy.roll(oconcat, -Lout / factor)
            # note: numpy has a circular shift (numpy.roll), but no "shift array left/right" function,
            # so we need to zero out the undesired values shifted back in by the circular shift:
            oconcat[(-Lout / factor):] = 0

        # Output real part of complex time domain data
        # (If suppression of upper Nyquist is zone desired, should write out both real&imag)
        vdif.write(numpy.real(oconcat[0:Lout]).view('float32').tostring())

        # Reporting
        if (iter % 100) == 0:
            (mjd, sec, ns) = m5lib.helpers.get_sample_time(ms)
            T_abs = sec + 1e-9 * ns
            T_count = 1e-9 * dms.framens * dms.nvalidatepass
            print('Iter %7d : %u/%f : %u : %f sec\r' %
                  (iter, mjd, T_abs, dms.nvalidatepass, T_count)),
        iter = iter + 1

    vdif.close()
    return 0
Exemplo n.º 3
0
def m5selfcorr(fn, fmt):

        Nint = 500
        nfft = 1024
        offset = 0

        # Open file
        try:
                m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
                m5fmt  = m5lib.new_mark5_format_generic_from_string(fmt)
                ms     = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
                dms    = ms.contents
        except:
                print ('Error: problem opening or decoding %s using format %s\n' % (fn,fmt))
                return 1
        basename = os.path.basename(fn)
        basename = os.path.splitext(basename)[0]

        # Collection of vectors for mark5access decode() raw sample output data
        pdata = m5lib.helpers.make_decoder_array(ms, nfft, dtype=ctypes.c_float)

        # Result arrays
        ch_data = [ctypes.cast(pdata[ii], ctypes.POINTER(ctypes.c_float*nfft)) for ii in range(dms.nchan)]
        xspecs = numpy.zeros(shape=(dms.nchan*dms.nchan,nfft), dtype='complex128')

        # Process the recorded data
        iter = 0
        while (iter < Nint):
                iter = iter + 1

                # Read data
                rc = m5lib.mark5_stream_decode(ms, nfft, pdata)
                if (rc < 0):
                        print ('\n<EOF> status=%d' % (rc))
                        return 0

                # Calculate normalized cross-correlations
                for ii in range(dms.nchan):
                    for jj in range(dms.nchan):
                        # print ('Computing pair %d--%d...' % (ii,jj))
                        k = ii*dms.nchan + jj
                        a = numpy.frombuffer(ch_data[ii].contents, dtype='float32')
                        b = numpy.frombuffer(ch_data[jj].contents, dtype='float32')
                        A = numpy.fft.fft(a)
                        B = numpy.fft.fft(b)
			xspecs[k] = numpy.add(xspecs[k], A*numpy.conj(B))

        # Calculate lag spectra
        xspecs   /= Nint*nfft
        lagspecs  = numpy.zeros_like(xspecs)
        normspecs = numpy.zeros_like(xspecs)
        for ii in range(dms.nchan):
            for jj in range(dms.nchan):

                k = ii*dms.nchan + jj
                xc = numpy.fft.ifft(xspecs[k])
                lagspecs[k] = numpy.fft.fftshift(xc)

                xc = xspecs[k]
                ac1 = xspecs[ii*dms.nchan + ii]
                ac2 = xspecs[jj*dms.nchan + jj]
                norm = numpy.sqrt(numpy.multiply(ac1,ac2))
                normspecs[k] = numpy.divide(xc, norm)

        figsize = (5*dms.nchan,5*dms.nchan)

        # Show lag spectra
        ymin = numpy.amin(numpy.abs(lagspecs))
        ymax = numpy.amax(numpy.abs(lagspecs))
        pylab.figure(figsize=figsize,dpi=75)
        for ii in range(dms.nchan):
            for jj in range(dms.nchan):
                if (jj < ii):
                    continue
                print ('Plotting lag spec. %d--%d' % (ii,jj))
                if (ii == jj):
                    linespec = 'kx-'
                else:
                    linespec = 'bx-'
                k = ii*dms.nchan + jj
                pylab.subplot(dms.nchan, dms.nchan, k+1)
                pylab.ylim([ymin,ymax])
                pylab.xlim([0,nfft-1])
                pylab.plot(numpy.abs(lagspecs[k]), linespec)
                pylab.title('IF %d x %d' % (ii+1,jj+1))
                if (ii==jj):
                    pylab.xlabel('Lag')
                    pylab.ylabel('Cross-corr.')
        pylab.savefig(basename + '.lag.png')

        # Show normalized xcorr spectra: phase
        pylab.figure(figsize=figsize,dpi=75)
        for ii in range(dms.nchan):
            for jj in range(dms.nchan):
                if (jj < ii):
                    continue
                print ('Plotting xcorr spec. phase %d--%d' % (ii,jj))
                if (ii == jj):
                    linespec = 'kx'
                else:
                    linespec = 'bx'
                k = ii*dms.nchan + jj
                pylab.subplot(dms.nchan, dms.nchan, k+1)
                pylab.plot(numpy.angle(normspecs[k],deg=True), linespec)
                #pylab.ylim([-numpy.pi,+numpy.pi])
                pylab.ylim([-180.0,+180.0])
                pylab.xlim([0,nfft-1])
                pylab.title('IF %d x %d' % (ii+1,jj+1))
                if (ii==jj):
                    pylab.xlabel('FFT bin')
                    pylab.ylabel('Phase (deg)')
        pylab.savefig(basename + '.phase.png')

        # Show normalized xcorr spectra: amp
        ymin = numpy.amin(numpy.abs(normspecs))
        ymax = numpy.amax(numpy.abs(normspecs))
        pylab.figure(figsize=figsize,dpi=75)
        for ii in range(dms.nchan):
            for jj in range(dms.nchan):
                if (jj < ii):
                    continue
                print ('Plotting xcorr spec. amp %d--%d' % (ii,jj))
                if (ii == jj):
                    linespec = 'kx:'
                else:
                    linespec = 'bx:'
                k = ii*dms.nchan + jj
                pylab.subplot(dms.nchan, dms.nchan, k+1)
                pylab.plot(numpy.abs(normspecs[k]), linespec)
                pylab.ylim([ymin,ymax])
                pylab.xlim([0,nfft-1])
                pylab.title('IF %d x %d' % (ii+1,jj+1))
                if (ii==jj):
                    pylab.xlabel('FFT bin')
                    pylab.ylabel('Coherence')
        pylab.savefig(basename + '.coherence.png')
 
        # Show autocorrelations (without normalization)
        pylab.figure(figsize=figsize,dpi=75)
        Nnyq = int(nfft/2+1)
        for ii in range(dms.nchan):
                k = ii*dms.nchan + ii
                print ('Plotting auto power spec. %d' % (ii))
                pylab.subplot(dms.nchan,1, ii+1)
                ac = numpy.abs(xspecs[k])
                pylab.semilogy(ac[0:Nnyq], 'k-')
                pylab.xlim([0,Nnyq-1])
                pylab.title('Auto power spectrum IF %d' % (ii+1))
                if ii==(dms.nchan-1):
                    pylab.xlabel('FFT bin')
        pylab.savefig(basename + '.autospec.png')

        pylab.show()
Exemplo n.º 4
0
def m5stat(fn, fmt, nframes, offset):
    """Reports statistics for file"""

    # Open file
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
    except:
        print("Error: problem opening or decoding %s\n" % (fn))
        return 1

    # Read sample data
    nsamples = dms.framesamples * nframes
    pdata = m5lib.helpers.make_decoder_array(ms,
                                             nsamples,
                                             dtype=ctypes.c_float)
    m5lib.mark5_stream_decode(ms, nsamples, pdata)

    # Statistics
    A = 8.0 * (numpy.pi - 3.0) / (3.0 * numpy.pi * (4.0 - numpy.pi))
    eps = 0.1
    hist_limits = [
        [],  # histogram ranges for
        [-1.0 - eps, 0, +1.0 + eps],  # 1-bit data
        [-3.3359 - eps, -1.0 - eps, 0, +1.0 + eps,
         +3.3359 + eps],  # 2-bit data
        [-1.0 - eps, 0 - eps, 0 + eps, +1.0 - eps, +1.0 + eps],  # 3-bit data
        [x / 2.95 - eps for x in range(-8, 8 + 1)],  # 4-bit data
        [],
        [],
        [],
        [(x * 2 - 255) / 71.0 - eps for x in range(0, 255 + 1)]
    ]  # 8-bit data

    print(
        ' Ch     mean    std    skew  kurt    gfact   state counts from -HiMag to +HiMag'
    )
    for i in range(dms.nchan):
        d = pdata[i][0:nsamples]
        m0 = numpy.mean(d)
        m1 = numpy.std(d)
        m2 = stats.skew(d)
        m3 = stats.kurtosis(d, fisher=False, bias=False)
        (hcounts, hlimits) = numpy.histogram(d, bins=hist_limits[dms.nbit])
        hpdf = [x / float(sum(hcounts)) for x in hcounts]

        if dms.nbit == 2:
            x = hpdf[1] + hpdf[2]
        else:
            x = hpdf[0] + hpdf[1]
        if dms.nbit == 2 or dms.nbit == 1:
            k = numpy.log(1.0 - x * x)
            g = numpy.sqrt(-4.0 / (A * numpy.pi) - k +
                           2.0 * numpy.sqrt(2.0**(2.0 /
                                                  (A * numpy.pi) + 0.5 * k) -
                                            k / A)) / 0.91
        else:
            g = float('NaN')

        hstr = ' / '.join(['%4.1f' % (100.0 * x) for x in hpdf])
        print('%3d   %+6.2f %+6.2f   %+5.2f %+5.2f   %+6.2f   %s' %
              (i, m0, m1, m2, m3, g, hstr))

    print 'based on %u samples (%u invalid)' % (
        dms.framesamples * (dms.nvalidatepass + dms.nvalidatefail),
        dms.framesamples * dms.nvalidatefail)
    return 0
Exemplo n.º 5
0
def m5spec(fn, fmt, fout, T_int_ms, nfft, offset):
    """Form time-averaged autocorrelation spectra"""

    # Open file
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
    except:
        print('Error: problem opening or decoding %s\n' % (fn))
        return 1

    # Settings
    nint = numpy.round(float(dms.samprate) * T_int_ms * 1e-3 / float(nfft))
    Tint = float(nint * nfft) / float(dms.samprate)
    df = float(dms.samprate) / float(nfft)
    iter = 0
    print(
        'Averaging a total of %u DFTs, each with %u points, for a %f millisecond time average.'
        % (nint, nfft, Tint * 1e3))

    # Collection of vectors for mark5access decode() raw sample output data
    pdata = m5lib.helpers.make_decoder_array(ms, nfft, dtype=ctypes.c_float)

    # Result arrays
    ch_data = [
        ctypes.cast(pdata[ii], ctypes.POINTER(ctypes.c_float * nfft))
        for ii in range(dms.nchan)
    ]
    freqs = numpy.linspace(0.0, dms.samprate * 1e-6, num=nfft)
    specs = numpy.zeros(shape=(dms.nchan, nfft), dtype='float64')

    # Process the recorded data
    while True:

        # Read data
        rc = m5lib.mark5_stream_decode(ms, nfft, pdata)
        if (rc < 0):
            print('\n<EOF> status=%d' % (rc))
            return 0

        # Averaging of 'Abs(FFT(x))'
        for ii in range(dms.nchan):
            x = numpy.frombuffer(ch_data[ii].contents, dtype='float32')
            specs[ii] += numpy.abs(numpy.fft.fft(x))

        # Save data and plot at the end of an averaging period
        iter = iter + 1
        if (iter % nint) == 0:

            layouts = [
                (None, None),
                (1, 1),
                (1, 2),
                (1, 3),
                (2, 2),
                (1, 5),  # 1 to 5 channels
                (2, 3),
                (2, 4),
                (2, 4),
                (3, 3),
                (2, 5),
                (3, 4),
                (2, 6),  # 6 to 12 channels
                (3, 5),
                (3, 5),
                (3, 5),
                (4, 4)
            ]  # 13 to 16 channels

            N = int(numpy.floor(float(nfft) / 2 + 1))
            M = int(numpy.min([16, dms.nchan]))
            rows, cols = layouts[M]

            f = freqs[0:N]
            s = specs[:, 0:N]
            s /= float(nint)
            s[:, 0] = s[:, 1]
            s[:, -1] = s[:, -2]

            writeOut(fout, f, s)

            a_min = numpy.amin(s)
            a_max = numpy.amax(s)

            pylab.gcf().set_facecolor('white')
            for ch in range(M):
                pylab.subplot(rows, cols, ch + 1)
                pylab.plot(f, abs(s[ch]), 'k-')
                pylab.title('Channel %d' % (ch + 1))
                pylab.axis('tight')
                pylab.ylim([a_min, a_max])
                if ch >= (M - cols):
                    pylab.xlabel('Frequency (MHz)')
                else:
                    pylab.gca().set_xticklabels([])
                if (ch % rows) == 0:
                    pylab.ylabel('Amplitude (Sum|FFT(x)|)')
                else:
                    pylab.gca().set_yticklabels([])
            pylab.show()

            # Current version: stop after 1 integration period
            break

    return 0
Exemplo n.º 6
0
def m5subband(fn, fmt, fout, if_nr, factor, start_MHz, stop_MHz, offset):
    """Extracts narrow-band signal out from file"""

    # Hard-coded settings
    refMJD_Mark5B = 57000  # reference MJD for Mark5B input data
    Ldft_out = 64  # number of DFT points for output transform
    m5_t = ctypes.c_float  # mark5access output type for decoded samples
    fp_t = 'float32'  # numpy array type for DFT input
    cp_t = 'complex64'  # numpy array type for DFT output, complex64 is 2 x float32
    nbits_out = 8  # quantization (32 or 8 bits) to use in output VDIF file

    stats_done = False
    out_std = 1.0

    # Open the VLBI recording
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
        m5lib.mark5_stream_fix_mjd(ms, refMJD_Mark5B)
        (mjd, sec, ns) = m5lib.helpers.get_sample_time(ms)
    except:
        print('Error: problem opening or decoding %s\n' % (fn))
        return 1

    if (factor < 0) or (factor > 16) or (if_nr < 0) or (if_nr >= dms.nchan):
        print('Error: invalid arguments')
        return 1

    # Derive input-side DFT length
    bw_in = float(dms.samprate) * 0.5 * 1e-6
    bw_out = numpy.abs(stop_MHz - start_MHz)
    R = bw_in / bw_out
    Ldft_in = int(R * Ldft_out + 0.5)
    radix = [2, 3, 5, 7, 11]
    while not (any([(Ldft_in % rx) == 0
                    for rx in radix])) and not ((Ldft_in % factor) == 0):
        Ldft_in = Ldft_in + 1
    R = Ldft_in / float(Ldft_out)
    start_bin = int(Ldft_in * start_MHz / bw_in)
    stop_bin = int(Ldft_in * stop_MHz / bw_in)
    start_MHz = (bw_in * start_bin) / Ldft_in
    stop_MHz = (bw_in * stop_bin) / Ldft_in
    bw_out = numpy.abs(stop_MHz - start_MHz)
    Ldft_in = 2 * Ldft_in  # due to r2c DFT rather than c2c DFT

    # Increase output-side DFT's input length, i.e., use zero padding
    Ldft_copy = Ldft_out
    #Ldft_out = next_pow2(2*(Ldft_out-Ldft_out%2)) # time-domain output data will be somewhat oversampled
    Ldft_out = next_even(
        2 * (Ldft_out - Ldft_out % 2)
    )  # time-domain output data will be closer to critically sampled

    print(
        'Derived settings : actual extraction range %.3f--%.3f MHz, %d-point DFT bins %d--%d (%d bins) padded to %d-point IDFT\n'
        % (start_MHz, stop_MHz, Ldft_in, start_bin, stop_bin, Ldft_copy,
           Ldft_out))

    # Safety checks
    if (Ldft_in < Ldft_out) or (start_bin > stop_bin) or (stop_bin >= Ldft_in):
        print('Error: invalid derived settings')
        return 1
    if (Ldft_in % factor) > 0:
        print(
            'Error: length of input-side DFT (%u) must be divisible by overlap-add factor (quality factor %u)'
            % (Ldft_in, factor))
        return 1
    if (Ldft_out % factor) > 0:
        print(
            'Error: length derived for output-side IDFT (%u) does not divide the overlap-add factor (quality factor %u)'
            % (Ldft_out, factor))
        return 1

    # Get storage for raw sample data from m5lib.mark5_stream_decode()
    pdata = m5lib.helpers.make_decoder_array(ms, Ldft_in, dtype=m5_t)
    if_data = ctypes.cast(pdata[if_nr], ctypes.POINTER(m5_t * Ldft_in))

    # Numpy 2D arrays for processed data
    flt_in = numpy.zeros(shape=(factor, Ldft_in), dtype=fp_t)
    flt_out = numpy.zeros(shape=(factor, Ldft_out), dtype=cp_t)
    iconcat = numpy.array([0.0 for x in range(2 * Ldft_in)], dtype=fp_t)
    oconcat = numpy.array([0.0 + 0.0j for x in range(2 * Ldft_out)],
                          dtype=cp_t)

    # Coefficient for coherent phase connection between overlapped input segments
    r = float(start_bin) / float(factor)
    rfrac = r - numpy.floor(r)
    rot_f0 = numpy.exp(2j * numpy.pi * rfrac)
    if (abs(numpy.imag(rot_f0)) < 1e-5):
        # set near-zero values to zero
        rot_f0 = numpy.real(rot_f0) + 0.0j
    rot_f = rot_f0**0.0

    # Window functions for DFT and IDFT
    win_in = numpy.cos(
        (numpy.pi / Ldft_in) * (numpy.linspace(0, Ldft_in - 1, Ldft_in) - 0.5 *
                                (Ldft_in - 1)))
    win_in = numpy.resize(win_in.astype(fp_t), new_shape=(factor, Ldft_in))
    win_out = numpy.cos(
        (numpy.pi / Ldft_out) *
        (numpy.linspace(0, Ldft_out - 1, Ldft_out) - 0.5 * (Ldft_out - 1)))
    win_out = numpy.resize(win_out.astype(fp_t), new_shape=(factor, Ldft_out))

    # Prepare VDIF output file with reduced data rate and same starting timestamp
    bwout = float(dms.samprate) * (Ldft_copy / float(Ldft_in))
    fsout = 2 * bwout
    outMbps = fsout * 1e-6 * nbits_out
    vdiffmt = 'VDIF_8192-%u-1-%d' % (outMbps, nbits_out)
    #if not(int(outMbps) == outMbps):
    #	print ('*** Warning: output rate is non-integer (%e Ms/s)! ***' % (outMbps))

    (vdifref,
     vdifsec) = m5lib.helpers.get_VDIF_time_from_MJD(mjd, sec + 1e-9 * ns)

    vdif = m5lib.writers.VDIFEncapsulator()
    vdif.open(fout, format=vdiffmt, complex=False, station='SB')
    vdif.set_time(vdifref, vdifsec, framenr=0)
    vdiffmt = vdif.get_format()

    # Report
    bw = float(dms.samprate) * 0.5
    print('Input file   : start MJD %u/%.6f sec' % (mjd, sec + ns * 1e-9))
    print(
        'Bandwidth    : %u kHz in, %.2f kHz out, bandwidth reduction of ~%.2f:1'
        % (1e-3 * bw, Ldft_copy * 1e-3 * bw / Ldft_in,
           float(Ldft_in) / Ldft_copy))
    print('Input side   : %u-point DFT with %u bins (%u...%u) extracted' %
          (Ldft_in, Ldft_copy, start_bin, stop_bin))
    print('Output side  : %u-point IDFT with %u-point zero padding' %
          (Ldft_out, Ldft_out - Ldft_copy))
    print('Overlap      : %u samples on input, %u samples on output' %
          (Ldft_in - Ldft_in / factor, Ldft_out - Ldft_out / factor))
    print('Phasors      : %s^t : %s ...' %
          (str(rot_f0), str([rot_f0**t for t in range(factor + 2)])))
    print('Output file  : rate %.3f Mbps, %u fps, format %s' %
          (outMbps, vdif.get_fps(), vdif.get_format()))

    # Do filtering
    print('Filtering...')
    iter = 0
    while True:

        # Get next full slice of data
        rc = m5lib.mark5_stream_decode(ms, Ldft_in, pdata)
        if (rc < 0):
            print('\n<EOF> status=%d' % (rc))
            return 0
        in_new = numpy.frombuffer(if_data.contents, dtype='float32')

        # Debug: replace data with noise + tone
        if False:
            t = iter * Ldft_in + numpy.array(range(Ldft_in))
            f = (start_bin + numpy.floor(Ldft_copy / 2.0)) / float(Ldft_in)
            in_new = numpy.random.standard_normal(
                size=in_new.size) + 10 * numpy.sin(2 * numpy.pi * f * t)
            in_new = in_new.astype('float32')

        # Feed the window-overlap input-side DFT processing input stage
        iconcat = numpy.concatenate([iconcat[0:Ldft_in], in_new])  # [old,new]
        for ii in range(factor):
            iconcat = numpy.roll(iconcat, -Ldft_in / factor)
            flt_in[ii] = iconcat[0:Ldft_in]

        # Window and do 1D DFT of 2D array
        flt_in = numpy.multiply(flt_in, win_in)
        F = numpy.fft.fft(flt_in)

        # Copy the desired bins and fix DC/Nyquist bins
        for ii in range(factor):
            flt_out[ii][0:Ldft_copy] = F[ii][start_bin:(start_bin + Ldft_copy)]
            flt_out[ii][0] = 0.0  # numpy.real(flt_out[ii][0])
            flt_out[ii][Ldft_copy -
                        1] = 0.0  # numpy.real(flt_out[ii][Ldft_copy-1])

        # Do inverse 1D DFT of 2D array and window the result
        F = numpy.fft.ifft(flt_out)
        F = numpy.multiply(F, win_out)

        # Reconstruct time domain signal by shifting and stacking overlapped segments coherently
        for ii in range(factor):
            oconcat[Ldft_out:] = oconcat[Ldft_out:] + F[ii] * rot_f
            rot_f = rot_f * rot_f0
            oconcat = numpy.roll(oconcat, -Ldft_out / factor)
            # note: numpy has a circular shift (numpy.roll), but no "shift array left/right" function,
            # so we need to zero out the undesired values shifted back in by the circular shift:
            oconcat[(-Ldft_out / factor):] = 0

        # Output real part of complex time domain data
        # (If suppression of upper Nyquist is zone desired, should write out both real&imag)
        if not stats_done:
            out_std = numpy.std(numpy.real(oconcat[0:Ldft_out]))
            stats_done = True
        if nbits_out == 32:
            raw = numpy.real(oconcat[0:Ldft_out]).view('float32').tostring()
            vdif.write(raw)
        elif nbits_out == 8:
            a = numpy.real(oconcat[0:Ldft_out]) * (127.5 / (8 * out_std))
            a8 = a.astype(numpy.int8)
            vdif.write(a8.view('int8').tostring())
        elif nbits_out == 2:
            # TODO: add actual conversion to 4-level (first float to int8, then divide by some integer?)
            # TODO: add some "fast" means of packing four 2-bit samples into a byte in Python
            # TODO: ensure Ldft_out * 2-bit is still an 8-byte multiple to meet VDIF requirements
            pass

        # Reporting
        if (iter % 100) == 0:
            (mjd, sec, ns) = m5lib.helpers.get_sample_time(ms)
            T_abs = sec + 1e-9 * ns
            T_count = 1e-9 * dms.framens * dms.nvalidatepass
            print('Iter %7d : %u/%f : %u : %f sec\r' %
                  (iter, mjd, T_abs, dms.nvalidatepass, T_count)),
        iter = iter + 1

    vdif.close()
    return 0
Exemplo n.º 7
0
def m5tone(fn,
           fmt,
           fout,
           if_nr,
           Tint_sec,
           tonefreq_Hz,
           Ldft,
           offset,
           doPlot=False,
           doFast=True):
    """Extracts a single tone from the desired channel in a VLBI recording"""

    # Open file
    try:
        m5file = m5lib.new_mark5_stream_file(fn, ctypes.c_longlong(offset))
        m5fmt = m5lib.new_mark5_format_generic_from_string(fmt)
        ms = m5lib.new_mark5_stream_absorb(m5file, m5fmt)
        dms = ms.contents
    except:
        print('Error: problem opening or decoding %s\n' % (fn))
        return 1

    # Get storage for raw sample data from m5lib.mark5_stream_decode()
    pdata = m5lib.helpers.make_decoder_array(ms, Ldft, dtype=ctypes.c_float)
    if_data = ctypes.cast(pdata[if_nr - 1],
                          ctypes.POINTER(ctypes.c_float * Ldft))

    # Derived settings
    Lnyq = numpy.floor(Ldft / 2 - 1)
    nint = numpy.round(float(dms.samprate) * Tint_sec / float(Ldft))
    Tint = float(nint * Ldft) / float(dms.samprate)
    pcbin = Ldft * float(tonefreq_Hz) / float(dms.samprate)
    iter = 0

    # Safety checks
    if (if_nr < 1) or (if_nr > dms.nchan):
        print(
            'Error: requested nonexistent channel %d (file has channels 1...%d).'
            % (if_nr, dms.nchan))
        return 1
    if (tonefreq_Hz <= 0) or (tonefreq_Hz >= 0.5 * dms.samprate):
        print(
            'Error: tone frequency of %u Hz not within channel bandwidth of %.1f Hz.'
            % (tonefreq_Hz, 0.5 * dms.samprate))
        return 1
    if (Ldft < 16):
        print('Error: length of DFT (Ldft=%d) must be >=16 points.' % (Ldft))
        return 1
    if (pcbin != int(pcbin)):
        print(
            'Error: tone bin is not an integer (bin=%f). Adjust Ldft or frequency.'
            % (pcbin))
        return 1

    # Spectral data
    winf = numpy.kaiser(Ldft, 7.0)  # Kaiser window function
    spec = numpy.zeros(shape=(Ldft), dtype='complex64')  # Accumulated spectrum
    tavg = numpy.zeros(shape=(Ldft),
                       dtype='float32')  # Accumulated time domain data
    pcamp = 0.0  # Total abs amplitude
    history = {'amp': [], 'phase': [], 'coh': [], 'T': [], 'MJD': []}

    # Plotting
    Lsgram = 8
    specgram = numpy.zeros(shape=(Lsgram, Lnyq), dtype='complex64')
    if doPlot:
        pylab.ion()
        pylab.figure()
        pylab.gcf().set_facecolor('white')

    # Report
    print(
        'Tone at %.3f kHz in the %.3f MHz wide band lands in %.3f kHz-wide bin %u.'
        % (tonefreq_Hz * 1e-3, dms.samprate * 0.5e-6,
           1e-3 * dms.samprate / float(Ldft), pcbin))
    print(
        'Integrating for %.2f milliseconds with %u spectra per integration.' %
        (Tint * 1e3, nint))
    if doFast:
        print(
            "Note: Using fast ingration, coherence 'r' will not actually be calculated."
        )

    # Detect tone phase and amplitude
    (mjd0, sec0, ns0) = m5lib.helpers.get_frame_time(ms)
    while True:

        # Get next full slice of data
        rc = m5lib.mark5_stream_decode(ms, Ldft, pdata)
        if (rc < 0):
            print('\n<EOF> status=%d' % (rc))
            return 0
        dd = numpy.frombuffer(if_data.contents, dtype='float32')

        # Extract the tone
        if not (doFast):
            # Brute force method, benefit is that coherence can be measured
            ddwin = numpy.multiply(dd, winf)
            F = numpy.fft.fft(ddwin)
            spec = numpy.add(spec, F)
            pcamp = pcamp + numpy.abs(F[pcbin])
        else:
            # Faster method, but cannot measure coherence
            tavg = numpy.add(tavg, dd)

        # Report the result at end of each averaging period
        iter = iter + 1
        if (iter % nint) == 0:

            # Timestamp at mid-point of integration
            T_count = Tint * (
                (iter / nint) - 0.5)  # data-second, relative time
            (mjd1, sec1, ns1) = m5lib.helpers.get_sample_time(ms)
            T_stamp = mjd1 + (sec1 + ns1 * 1e-9 - Tint /
                              2.0) / 86400.0  # fractional MJD, absolute time

            # Extract final tone amp, phase, coherence
            if doFast:
                spec = numpy.fft.fft(numpy.multiply(tavg, winf))
            pctone = spec[pcbin] / float(nint * Ldft)
            pcamp = pcamp / float(nint * Ldft)
            pctone_A = numpy.abs(pctone)
            pctone_ph = numpy.angle(pctone, deg=True)
            if doFast:
                pctone_C = 1.0
            else:
                pctone_C = pctone_A / pcamp

            # Store results
            print('%.9f mjd : %.6f sec : %e /_ %+.2f deg : r=%.3f' %
                  (T_stamp, T_count, pctone_A, pctone_ph, pctone_C))
            history['amp'].append(pctone_A)
            history['phase'].append(pctone_ph)
            history['coh'].append(pctone_C)
            history['T'].append(T_count)
            history['MJD'].append(T_stamp)
            line = '%.9f %.6f %e %+.2f %.3f\n' % (T_stamp, T_count, pctone_A,
                                                  pctone_ph, pctone_C)
            fout.write(line)

            # Plotting
            if doPlot:
                specgram[1:] = specgram[0:-1]
                specgram[0] = numpy.abs(spec[0:Lnyq])
            if doPlot and ((iter / nint) % Lsgram) == 0:
                pylab.clf()
                pylab.subplot(311)
                pylab.plot(history['T'], history['amp'], 'rx')
                # pylab.plot(numpy.real(pcvec))
                pylab.xlabel('Time (s)')
                pylab.ylabel('Amplitude')

                pylab.subplot(312)
                pylab.plot(history['T'], history['phase'], 'gx')
                pylab.ylim(-180.0, 180.0)
                pylab.xlabel('Time (s)')
                pylab.ylabel('Phase (deg)')

                pylab.subplot(313)
                xyextent = [
                    0, 0.5e-6 * dms.samprate, T_count * 1e3,
                    (T_count + Lsgram) * Tint * 1e3
                ]
                pylab.imshow(numpy.abs(specgram),
                             aspect='auto',
                             extent=xyextent)
                Fpeak = 1e-3 * dms.samprate * float(numpy.argmax(
                    specgram[0])) / Ldft
                pylab.text(Fpeak, Lsgram / 2, 'Peak at %.3f kHz' % (Fpeak))
                pylab.xlabel('Frequency (MHz)')
                pylab.ylabel('Time (ms)')
                pylab.draw()  # non-blocking
                pylab.draw()

            # Clear accumulated values
            spec = numpy.zeros_like(spec)
            tavg = numpy.zeros_like(tavg)
            pcamp = 0.0

    return 0