def frame_sync(rx_signal,
               ref_symbs,
               os,
               frame_len=2**16,
               M_pilot=4,
               mu=1e-3,
               Ntaps=17,
               **eqargs):
    """
    Locate the pilot sequence frame
    
    Uses a CMA-based search scheme to located the initiial pilot sequence in
    the long data frame. 

    Parameters
    ----------
    rx_signal: array_like
        Received Rx signal
    ref_symbs: array_like
        Pilot sequence
    os: int
        Oversampling factor
    frame_len: int
        Total frame length including pilots and payload
    M_pilot: int, optional
        QAM-order for pilot symbols
    mu: float, optional
        CMA step size
    Ntaps: int, optional
        number of taps to use in the equaliser
    **eqargs:
        arguments to be passed to equaliser

        
    Returns
    -------
    shift_factor: array_like
        location of frame start index per polarization
    foe_corse:  array_like
        corse frequency offset
    mode_sync_order: array_like
        Synced descrambled reference pattern order
    """
    # If synchronization fails, then change sync_bool to 'False'
    sync_bool = True

    FRAME_SYNC_THRS = 120  # this is somewhat arbitrary but seems to work well
    rx_signal = np.atleast_2d(rx_signal)
    ref_symbs = np.atleast_2d(ref_symbs)
    pilot_seq_len = ref_symbs.shape[-1]
    nmodes = rx_signal.shape[0]
    assert rx_signal.shape[-1] >= (
        frame_len +
        2 * pilot_seq_len) * os, "Signal must be at least as long as frame"
    if "method" in eqargs.keys():
        if eqargs["method"] in equalisation.REAL_VALUED:
            if np.iscomplexobj(rx_signal):
                raise ValueError("Equaliser method is {}, but using a real-valued equaliser in frame sync is unsupported"\
                                 .format(eqargs["method"]))
        elif eqargs["method"] in equalisation.DATA_AIDED:
            raise ValueError("Equaliser method is {}, but using a data-aided equaliser in frame sync is unsupported"\
                             .format(eqargs["method"]))
    mode_sync_order = np.zeros(nmodes, dtype=int)
    not_found_modes = np.arange(0, nmodes)
    search_overlap = 2  # fraction of pilot_sequence to overlap
    search_window = pilot_seq_len * os
    step = search_window // search_overlap
    # we only need to search the length of one frame*os plus some buffer (the extra step)
    num_steps = (frame_len * os) // step + 1
    # Now search for every mode independent
    shift_factor = np.zeros(nmodes, dtype=int)
    # Search based on equalizer error. Avoid one pilot_seq_len part in the beginning and
    # end to ensure that sufficient symbols can be used for the search
    sub_vars = np.ones((nmodes, num_steps)) * 1e2
    wxys = np.zeros((num_steps, nmodes, nmodes, Ntaps), dtype=rx_signal.dtype)
    for i in np.arange(search_overlap,
                       num_steps):  # we avoid one step at the beginning
        tmp = rx_signal[:, i * step:i * step + search_window]
        wxy, err_out = equalisation.equalise_signal(tmp,
                                                    os,
                                                    mu,
                                                    M_pilot,
                                                    Ntaps=Ntaps,
                                                    **eqargs)
        wxys[i] = wxy
        sub_vars[:, i] = np.var(err_out, axis=-1)
    # Lowest variance of the CMA error for each pol
    min_range = np.argmin(sub_vars, axis=-1)
    wxy = wxys[min_range]
    for l in range(nmodes):
        idx_min = min_range[l]
        # Extract a longer sequence to ensure that the complete pilot sequence is found
        longSeq = rx_signal[:, (idx_min) * step -
                            search_window:(idx_min) * step + search_window]
        # Apply filter taps to the long sequence and remove coarse FO
        wx1 = wxy[l]
        symbs_out = equalisation.apply_filter(longSeq, os, wx1)
        foe_corse = phaserecovery.find_freq_offset(symbs_out)
        symbs_out = phaserecovery.comp_freq_offset(symbs_out, foe_corse)
        # Check for pi/2 ambiguties and verify all
        max_phase_rot = np.zeros(nmodes, dtype=np.float64)
        found_delay = np.zeros(nmodes, dtype=np.int)
        for ref_pol in not_found_modes:
            ix, dat, ii, ac = ber_functions.find_sequence_offset_complex(
                ref_symbs[ref_pol], symbs_out[l])
            found_delay[ref_pol] = -ix
            max_phase_rot[ref_pol] = ac
        # Check for which mode found and extract the reference delay
        max_sync_pol = np.argmax(max_phase_rot)
        if max_phase_rot[max_sync_pol] < FRAME_SYNC_THRS:  #
            warnings.warn(
                "Very low autocorrelation, likely the frame-sync failed")
            sync_bool = False
        mode_sync_order[l] = max_sync_pol
        symb_delay = found_delay[max_sync_pol]
        # Remove the found reference mode
        not_found_modes = not_found_modes[not_found_modes != max_sync_pol]
        # New starting sample
        shift_factor[l] = (idx_min) * step + os * symb_delay - search_window
    # Important: the shift factors are arranged in the order of the signal modes, but
    # the mode_sync_order specifies how the signal modes need to be rearranged to match the pilots
    # therefore shift factors also need to be "mode_aligned"
    return shift_factor, foe_corse, mode_sync_order, wx1, sync_bool
Beispiel #2
0
 def test_find_freq_offset(self, ndim):
     s = signals.SignalQAMGrayCoded(16, 2 ** 16, fb=20e9, nmodes=ndim)
     fo = cphaserecovery.find_freq_offset(s)
     assert ndim == fo.shape[0]
Beispiel #3
0
def frame_sync(rx_signal, ref_symbs, os, frame_length = 2**16, mu = 1e-3, M_pilot = 4, ntaps = 25, Niter = 10, adap_step = True, method='cma' ,search_overlap = 2):
    """
    Locate and extract the pilot starting frame.
    
    Uses a CMA-based search scheme to located the initiial pilot sequence in
    the long data frame. 
    
    Input:
        rx_signal: Received Rx signal
        ref_symbs: Pilot sequence
        os: Oversampling
        frame_length: Total frame length including pilots and payload
        mu: CMA step size. Tuple(Search, Convergence)
        M_pilot: Order for pilot symbols. Should normally be QPSK and M=4
        ntaps: Number of T/2-spaced taps for equalization
        Niter: Number of iterations for the equalizer.Tuple(Search, Convergence)
        adap_step: Use adaptive step size (bool). Tuple(Search, Convergence)
        method: Equalizer mehtods to be used. Tuple(Search, Convergence)
        search_overlap: Overlap of subsequences in the test 
        
        
    Output:
        eq_pilots: Found pilot sequence after equalization
        shift_factor: New starting point for initial equalization
        out_taps: Taps for equalization of the whole signal
        foe_course: Result of blind FOE used to sync the pilot sequence. 
    
    """
    # Inital settings
    rx_signal = np.atleast_2d(rx_signal)
    ref_symbs = np.atleast_2d(ref_symbs)
    npols = rx_signal.shape[0]
    
    # Find the length of the pilot frame
    pilot_seq_len = len(ref_symbs[0,:])
    
    symb_step_size = int(np.floor(pilot_seq_len * os / search_overlap))
    
    # Adapt signal length
    sig_len = (np.shape(rx_signal)[1])
    if (sig_len > (frame_length + (search_overlap*2 + 5)*pilot_seq_len)*os):
        num_steps = int(np.ceil(((frame_length + (search_overlap*2 + 5) *pilot_seq_len)*os)/symb_step_size))
    else:
        num_steps = int(np.ceil(np.shape(rx_signal)[1] / symb_step_size))
    
    # Now search for every mode independent
    shift_factor = np.zeros(npols,dtype = int)
    for l in range(npols):

        # Search based on equalizer error. Avoid certain part in the beginning and
        # end to ensure that sufficient symbols can be used for the search
        sub_var = np.ones(num_steps)*1e2
        for i in np.arange(2+(search_overlap),num_steps-3-(search_overlap)):
            err_out = equalisation.equalise_signal(rx_signal[:,(i)*symb_step_size:(i+1+(search_overlap-1))*symb_step_size], os, mu, M_pilot,Ntaps = ntaps, Niter = Niter, method = method,adaptive_stepsize = adap_step)[1]
            sub_var[i] = np.var(err_out[l,int(-symb_step_size/os+ntaps):])
                       
        # Lowest variance of the CMA error
        minPart = np.argmin(sub_var)
        
        # Corresponding sequence
        shortSeq = rx_signal[:,(minPart)*symb_step_size:(minPart+1+(search_overlap-1))*symb_step_size]
        
        # Extract a longer sequence to ensure that the complete pilot sequence is found
        longSeq = rx_signal[:,(minPart-2-search_overlap)*symb_step_size:(minPart+3+search_overlap)*symb_step_size]

        # Use the first estimate to get rid of any large FO and simplify alignment
        wx1, err = equalisation.equalise_signal(shortSeq, os, mu, M_pilot,Ntaps = ntaps, Niter = Niter, method = method,adaptive_stepsize = adap_step)    
        seq_foe = equalisation.apply_filter(longSeq,os,wx1)
        foe_corse = phaserecovery.find_freq_offset(seq_foe)        
         
        # Apply filter taps to the long sequence
        symbs_out= equalisation.apply_filter(longSeq,os,wx1)     
        symbs_out[l,:] = phaserecovery.comp_freq_offset(symbs_out[l, :], foe_corse[l, :])
        # Check for pi/2 ambiguties
        max_phase_rot = np.zeros([4])
        found_delay = np.zeros([4])
        for k in range(4):
            # Find correlation for all 4 possible pi/2 rotations
            xcov = np.correlate(np.angle(symbs_out[l,:]*1j**k),np.angle(ref_symbs[l,:]))
            max_phase_rot[k] = np.max(xcov)
            found_delay[k] = np.argmax(xcov)
    
        # Select the best one    
        symb_delay = int(found_delay[np.argmax(max_phase_rot)]) 
        
        # New starting sample
        shift_factor[l] = int((minPart-4)*symb_step_size + os*symb_delay)

    return  shift_factor, foe_corse
Beispiel #4
0
def equalize_pilot_sequence(rx_signal, ref_symbs, shift_factor, os, sh = False, process_frame_id = 0, frame_length = 2**16, mu = (1e-4,1e-4), M_pilot = 4, ntaps = (25,45), Niter = (10,30), adap_step = (True,True), method=('cma','cma'),avg_foe_modes = True, do_pilot_based_foe=True,foe_symbs = None,blind_foe_payload=False):
    
    # Inital settings
    rx_signal = np.atleast_2d(rx_signal)
    ref_symbs = np.atleast_2d(ref_symbs)
    npols = rx_signal.shape[0]    
    pilot_seq_len = len(ref_symbs[0,:])    
    eq_pilots = np.zeros([npols,pilot_seq_len],dtype = complex)
    tmp_pilots = np.zeros([npols,pilot_seq_len],dtype = complex)
    out_taps = []

    # Tap update and extract the propper pilot sequuence    
    if not((ntaps[1]-ntaps[0])%os  ==  0):
        raise ValueError("Taps for search and convergence impropper configured")
    tap_cor = int((ntaps[1]-ntaps[0])/2)
    
    # Run FOE and shift spectrum
    if not sh:    
        # First Eq, extract pilot sequence to do FOE
        for l in range(npols):
            pilot_seq = rx_signal[:,shift_factor[l]-tap_cor:shift_factor[l]-tap_cor+pilot_seq_len*os+ntaps[1]-1]
            wx, err = equalisation.equalise_signal(pilot_seq, os, mu[0], M_pilot,Ntaps = ntaps[1], Niter = Niter[1], method = method[0],adaptive_stepsize = adap_step[1])
            # wx, err = equalisation.equalise_signal(pilot_seq, os, mu[1], M_pilot, wxy = wx, Ntaps=ntaps[1], Niter=Niter[1],
            #                                        method=method[0], adaptive_stepsize=adap_step[1])
            symbs_out= equalisation.apply_filter(pilot_seq,os,wx)       
            tmp_pilots[l,:] = symbs_out[l,:]
            
        # FOE Estimation, several options available. Default is pilot aided
        if do_pilot_based_foe:
            # Use the pilot-based FOE. Requires a pilot sequence of sufficient length    
            if foe_symbs is None:
                foe, foePerMode, cond = pilot_based_foe(tmp_pilots, ref_symbs)
            else:
                num_symbs = int(foe_symbs)
                if num_symbs > pilot_seq_len:
                    raise ValueError("Required number of symbols for FOE is larger than availabe sequence length. Maximum length available is %d"%pilot_seq_len)
                foe, foePerMode, cond = pilot_based_foe(tmp_pilots[:,:num_symbs], ref_symbs[:,:num_symbs])
        # Equalize 1 frame and do blind 4:th power FFT-based estimation
        else:
            foe_est_symbs = np.zeros([npols, frame_length],dtype=complex)
            for l in range(npols):
                # If blind_foe_payload is true, use the payload for FOE estimation and not the pilot sequence. Aims at comparing blind power of 4 FOE to pilot-based
                if blind_foe_payload:
                    est_sig = rx_signal[:,
                              shift_factor[l] - tap_cor + pilot_seq_len*os:shift_factor[l] - tap_cor + frame_length * os + ntaps[1] - 1 + pilot_seq_len*os]
                else:
                    est_sig = rx_signal[:,shift_factor[l]-tap_cor:shift_factor[l]-tap_cor+frame_length*os+ntaps[1]-1]
                est_frame = equalisation.apply_filter(est_sig,os,wx)
                foe_est_symbs[l,:] = est_frame[l,:]
            if foe_symbs is None:
                foePerMode = phaserecovery.find_freq_offset(foe_est_symbs)
            else:
                foePerMode = phaserecovery.find_freq_offset(foe_est_symbs[:, :foe_symbs], fft_size=foe_symbs)
        if avg_foe_modes:
            foe = np.mean(foePerMode)
            foePerMode = np.ones(foePerMode.shape)*foe
        # Apply FO-compensation
        sig_dc_center = phaserecovery.comp_freq_offset(rx_signal, foePerMode, os=os)
    else:
        # Signal should be DC-centered. Do nothing. 
        sig_dc_center = rx_signal
        foePerMode = np.zeros([npols,1])
        
    # First step Eq
    for l in range(npols):
        pilot_seq = sig_dc_center[:,shift_factor[l]-tap_cor:+shift_factor[l]-tap_cor+pilot_seq_len*os+ntaps[1]-1]    
        wxy_init, err = equalisation.equalise_signal(pilot_seq, os, mu[1], 4, Ntaps = ntaps[1], Niter = Niter[1], method = method[0],adaptive_stepsize = adap_step[1]) 
        wx, err = equalisation.equalise_signal(pilot_seq, os, mu[1], 4,wxy=wxy_init,Ntaps = ntaps[1], Niter = Niter[1], method = method[1],adaptive_stepsize = adap_step[1]) 
        out_taps.append(wx)
        symbs_out = equalisation.apply_filter(pilot_seq,os,out_taps[l])
        eq_pilots[l,:] = symbs_out[l,:]

    # Equalize using additional frames if selected
    for frame_id in range(0,process_frame_id+1):
        for l in range(npols):
            pilot_seq = sig_dc_center[:,frame_length*os*frame_id+shift_factor[l]-tap_cor:frame_length*os*frame_id+shift_factor[l]-tap_cor+pilot_seq_len*os+ntaps[1]-1]    
            wx_1, err = equalisation.equalise_signal(pilot_seq, os, mu[1], 4,wxy=out_taps[l],Ntaps = ntaps[1], Niter = Niter[1], method = method[1],adaptive_stepsize = adap_step[1])
            wx, err = equalisation.equalise_signal(pilot_seq, os, mu[1], 4,wxy=wx_1,Ntaps = ntaps[1], Niter = Niter[1], method = method[1],adaptive_stepsize = adap_step[1]) 
            out_taps[l] = wx

            if frame_id == process_frame_id:
                symbs_out = equalisation.apply_filter(pilot_seq,os,out_taps[l])
                eq_pilots[l,:] = symbs_out[l,:]
                shift_factor[l] = shift_factor[l] + process_frame_id*os*frame_length  

    return eq_pilots, foePerMode, out_taps, shift_factor
Beispiel #5
0
X = analog_frontend.comp_IQ_inbalance(X)
Y = analog_frontend.comp_IQbalance(Y)
print(X.shape)
print(Y.shape)
SS = np.vstack([X[5000:-5000],Y[5000:-5000]])

SS = SS[:,:int(2e5)]

E, wxy, err_both = equalisation.dual_mode_equalisation(SS, os, M, ntaps, Niter=(5, 5), methods=("mcma", "sbd"),
                                                       adaptive_stepsize=(True, True))

X = signal_quality.norm_to_s0(E[0, :], M)
Y = signal_quality.norm_to_s0(E[1, :], M)
E = np.vstack([X,Y])

foe = phaserecovery.find_freq_offset(E, fft_size =2 ** 10)


E = phaserecovery.comp_freq_offset(E, foe)

#Ec = E[:,2e4:-2e4]
wx, err_both = equalisation.equalise_signal(E, 1, M, Ntaps=ntaps, Niter=4, method="sbd", adaptive_stepsize=False)
Ec = equalisation.apply_filter(E, 1, wx)
E = Ec


print("X pol phase")
Ex, phx = phaserecovery.bps(E[0], 32, QAM.symbols, 8)
print("X pol phase done")
print("Y pol phase")
Ey, phy = phaserecovery.bps(E[1], 32, QAM.symbols, 8)