def pilot_based_cpe2(rec_symbs, pilot_symbs, num_average=1, use_pilot_ratio=1):
    """
    Carrier phase recovery using periodically inserted symbols.

    Performs a linear interpolation with averaging over n symbols to estimate
    the phase drift from laser phase noise to compensate for this.

    Input:
        rec_symbs: Received symbols in block (first of each block is the pilot)
        pilot_symbs: Corresponding pilot symbols.
            Index N is the first symbol in transmitted block N.
        pilot_ins_ratio: Length of each block. Ex. 16 -> 1 pilot symbol followed
            by 15 data symbols
        num_average: Number of pilot symbols to average over to avoid noise.
        use_pilot_ratio: Use ever n pilots. Can be used to sweep required rate.
        max_num_blocks: Maximum number of blocks to process
        remove_phase_pilots: Remove phase pilots after CPE. Default: True

    Output:
        data_symbs: Complex symbols after pilot-aided CPE. Pilot symbols removed
        phase_trace: Resulting phase trace of the CPE
    """

    #rec_symbs = np.atleast_2d(rec_symbs)
    #pilot_symbs = np.atleast_2d(pilot_symbs)
    pilots_symbs = rec_symbs.extract_pilots()
    npols = rec_symbs.shape[0]

    # Extract the pilot symbols
    #numBlocks = np.floor(np.shape(rec_symbs)[1]/pilot_ins_ratio)
    # If selected, only process a limited number of blocks.
    #if (max_num_blocks is not None) and numBlocks > max_num_blocks:
    #    numBlocks = max_num_blocks

    # Make sure that a given number of pilots can be used
    if (numBlocks % use_pilot_ratio):
        numBlocks -= (numBlocks % use_pilot_ratio)

    # Adapt for number of blocks
    #rec_pilots = rec_symbs[:,::int(pilot_ins_ratio)]
    #rec_pilots = rec_pilots[:,:int(numBlocks)]
    #rec_symbs = rec_symbs[:,:int(pilot_ins_ratio*numBlocks)]

    # Check that the number of blocks are equal and is valid
    numRefPilots = np.shape(pilot_symbs)[1]
    if numBlocks > numRefPilots:
        numBlocks = numRefPilots
        rec_symbs = rec_symbs[:, :int(numBlocks * pilot_ins_ratio)]
        rec_pilots = rec_pilots[:, :int(numBlocks)]
    elif numRefPilots > numBlocks:
        pilot_symbs = pilot_symbs[:, :int(numBlocks)]

    # Remove every X pilot symbol if selected
    if use_pilot_ratio >= pilot_symbs.shape[1]:
        raise ValueError(
            "Can not use every %d pilots since only %d pilot symbols are present"
            % (use_pilot_ratio, pilot_symbs.shape[1]))
    rec_pilots = rec_pilots[:, ::int(use_pilot_ratio)]
    pilot_symbs = pilot_symbs[:, ::int(use_pilot_ratio)]

    # Check for a out of bounch error
    if pilot_symbs.shape[1] <= num_average:
        raise ValueError(
            "Inpropper pilot symbol configuration. Larger averaging block size than total number of pilot symbols"
        )

    # Should be an odd number to keey symmetry in averaging
    if not (num_average % 2):
        num_average += 1

    # Allocate output memory and process modes
    data_symbs = np.zeros([npols, np.shape(rec_symbs)[1]], dtype=complex)
    phase_trace = np.zeros([npols, np.shape(rec_symbs)[1]])
    res_phase = pilot_symbs.conjugate() * rec_pilots
    pilot_phase_base = np.unwrap(np.angle(res_phase), axis=-1)
    pilot_phase_average = filter.moving_average(pilot_phase_base, num_average)
    pp1 = pilot_phase_base[:, :int((num_average - 1) / 2)]
    pp2 = pilot_phase_average[:]
    t = pilot_phase_base[:, :int((num_average - 1) / 2)]
    t2 = pp2[:, -1].reshape(2, 1)
    pp3 = np.ones(t.shape) * t2
    pilot_phase = np.hstack([pp1, pp2, pp3])
    pilot_pos = np.arange(
        0, pilot_phase.shape[-1] * pilot_ins_ratio * use_pilot_ratio,
        pilot_ins_ratio * use_pilot_ratio)
    pilot_pos_new = np.arange(
        0, pilot_phase.shape[-1] * pilot_ins_ratio * use_pilot_ratio)

    for l in range(npols):
        phase_trace[l, :] = np.interp(pilot_pos_new, pilot_pos,
                                      pilot_phaseN[l])
    data_symbs = rec_symbs * np.exp(-1j * phase_trace)

    # Remove the phase pilots after compensation. This is an option since they can be used for SNR estimation e.g.
    if remove_phase_pilots:
        pilot_pos = np.arange(0, np.shape(data_symbs)[1], pilot_ins_ratio)
        data_symbs = np.delete(data_symbs, pilot_pos, axis=1)

    return data_symbs, phase_trace
Esempio n. 2
0
 def test_lengths(self):
     for i in range(100, 131):
         x = np.arange(i)
         for j in range(5, 30):
             assert len(cfilter.moving_average(x, j)) == i - j + 1
def pilot_based_cpe_new(signal,
                        pilot_symbs,
                        pilot_idx,
                        frame_len,
                        seq_len=None,
                        num_average=1,
                        use_pilot_ratio=1,
                        max_num_blocks=None,
                        nframes=1):
    """
    Carrier phase recovery using periodically inserted symbols.
    
    Performs a linear interpolation with averaging over n symbols to estimate
    the phase drift from laser phase noise to compensate for this.
    
    Parameters
    ----------
    signal : array_like
        input signal containing pilots
    pilot_symbs : array_like
        the transmitter pilot symbols
    pilot_idx : array_like
        the position indices of the pilots in the signal
    frame_len : int
        length of a frame
    seq_len : int (optional)
        length of the pilot sequence, if None (default) do not use the pilot sequence for CPE,
        else use pilot sequence for CPE as well
    num_average : int (optional)
        length of the moving average filter
    use_pilot_ratio : int (optional)
        use only every nth pilot symbol
    max_num_blocks : int (optional)
        maximum number of phase pilot blocks, if None use maximum number of blocks that fit into data
    nframes : int (optional)
        number of frames to do phase recovery on

    Returns
    -------
    data_symbs : array_like
        compensated output signal, truncated to nframes*frame_len or max_num_blocks*block_length
    phase_trace : array_like
        trace of the phase
    """
    assert num_average > 1, "need to take average over at least 3"
    # Should be an odd number to keey symmetry in averaging
    if not (num_average % 2):
        num_average += 1
        warnings.warn(
            "Number of averages should be odd, adding one average, num_average={}"
            .format(num_average))
    signal = np.atleast_2d(signal)
    pilot_symbs = np.atleast_2d(pilot_symbs)
    # select idx based on insertion ratio and pilot sequence
    pilot_idx_new = pilot_idx[:max_num_blocks:use_pilot_ratio]
    nlen = min(frame_len * nframes, signal.shape[-1])
    frl = np.arange(nframes) * frame_len
    pilot_idx2 = np.broadcast_to(pilot_idx_new,
                                 (nframes, pilot_idx_new.shape[-1]))
    pilot_idx_full = np.ravel(pilot_idx2 + frl[:, None])
    ilim = pilot_idx_full < nlen
    pilot_idx_full = pilot_idx_full[ilim]
    rec_pilots = signal[:, pilot_idx_full]
    pilot_symbs = np.tile(pilot_symbs[:, ::use_pilot_ratio],
                          nframes)[:, :rec_pilots.shape[-1]]
    assert rec_pilots.shape == pilot_symbs.shape, "Inproper pilot configuration, the number of"\
            +" received pilots differs from reference ones"
    # Check for a out of bounch error
    assert pilot_symbs.shape[
        -1] >= num_average, "Inpropper pilot symbol configuration. Larger averaging block size than total number of pilot symbols"
    res_phase = np.unwrap(np.angle(pilot_symbs.conjugate() * rec_pilots),
                          axis=-1)
    res_phase_avg = filter.moving_average(res_phase, num_average)
    i_filt_adj = int((num_average - 1) / 2)
    idx_avg = pilot_idx_full[i_filt_adj:-i_filt_adj]
    assert idx_avg.shape[-1] == res_phase_avg.shape[
        -1], "averaged phase and new indices are not the same shape"
    nmodes = pilot_symbs.shape[0]
    phase_trace = np.zeros((nmodes, nlen), dtype=pilot_symbs.dtype)
    idxnew = np.arange(0, nlen)
    for i in range(nmodes):
        phase_trace[i] = np.interp(idxnew, idx_avg, res_phase_avg[i])
    sig_out = signal[:, :nlen] * np.exp(-1j * phase_trace)
    return sig_out[:, :nframes * frame_len], phase_trace[:, :nframes *
                                                         frame_len]
Esempio n. 4
0
 def test_moving_avg(self, ndim, N):
     x = np.random.randn(ndim, 2**15) + 0.j
     y = cfilter.moving_average(x, N=N)
     assert x.shape[0] == y.shape[0]
     assert x.shape[1] - N + 1 == y.shape[1]
Esempio n. 5
0
 def test_moving_avg_1d(self, N):
     x = np.random.randn(2**15) + 0.j
     y = cfilter.moving_average(x, N=N)
     assert x.shape[0] - N + 1 == y.shape[0]
Esempio n. 6
0
 def test_numbers3(self):
     npt.assert_allclose(np.array([6,9,12,15]) / 3, cfilter.moving_average(np.arange(1, 7), 3))
Esempio n. 7
0
 def test_numbers2(self):
     npt.assert_allclose(np.array([1.,1., 1.]), cfilter.moving_average(np.ones(5), 3))
Esempio n. 8
0
 def test_mvg_avg_attr(self, attr):
     s2 = cfilter.moving_average(self.s)
     assert getattr(self.s, attr) is getattr(s2, attr)
Esempio n. 9
0
 def test_mvg_avg(self):
     s2 = cfilter.moving_average(self.s)
     assert type(self.s) is type(s2)
Esempio n. 10
0
 def test_mvg_avg(self, dtype):
     s = self.s.astype(dtype)
     s2 = cfilter.moving_average(s)
     assert type(self.s) is type(s2)