def generate_UPS(param):
    """genere la matrice de covariance Ups dans le cadre du modèle RVoG en SB
    pour un jeu de paramètre donné   
     """
    ind_baseline = 0
    k_z = param.k_z[ind_baseline]
    theta = param.theta
    T_vol = param.T_vol
    T_ground = param.T_ground
    h_v = param.h_v
    z_g = param.z_g
    sigma_v = param.sigma_v

    alpha = 2 * sigma_v / np.cos(theta)
    a = np.exp(-alpha)
    I1 = (1 - a) / alpha
    I2=(np.exp(1j*k_z*h_v)-a)/ \
        (1j*k_z+alpha)
    #Construction matrice de covariance Upsilon
    Ups = bl.nans([6, 6], dtype=np.complex)
    T = bl.nans([3, 3], dtype=np.complex)
    Om = bl.nans([3, 3], dtype=np.complex)

    T = I1 * T_vol + a * T_ground
    Om = np.exp(1j * k_z * z_g) * (I2 * T_vol + a * T_ground)
    Ups = np.vstack([np.hstack([T, Om]), np.hstack([Om.T.conj(), T])])
    return Ups
    def get_fisher_sigma_v_gt_known(self):
        """Retourne la matricde de Fisher à sigma_v et {gt}_ij connu
        eta=(Tvol,Tground,zg,hv)."""

        Fisher = self.get_fisher()
        Fisher_sigma_v_gt_known = bl.nans([20, 20], dtype=float)
        Fisher_sigma_v_gt_known = Fisher[0:20, 0:20]  #
        return Fisher_sigma_v_gt_known
    def get_fisher_sigma_v_Tg_Tv_known(self):
        """Retourne la matricde de Fisher à sigma_v connu 
        à partir de la matrice de Fisher à sigma_v inconnu
        eta=(zg,hv)"""

        Fisher_sigma_v_Tg_Tv_known = bl.nans([2, 2], dtype=float)
        Fisher = self.get_fisher_sigma_v_known()
        Fisher_sigma_v_Tg_Tv_known = Fisher[18:20, 18:20]
        return Fisher_sigma_v_Tg_Tv_known
    def get_Om(self, i, j):
        """Renvoie la rep. interfero. entre les antennes ``i`` et ``j`` """

        Ups = self.get_upsilon()
        if i == j:
            print 'Warning : les rep interfero verifient i !=j'
            return bl.nans((3, 3))

        return Ups[3 * i:3 * (i + 1), 3 * j:3 * (j + 1)]
    def get_fisher_Tg_Tv_zg_known(self):
        """Retourne la matricde de Fisher à Tv,Tg,zg connu
        eta=(hv,sigv,{gt})
        eta=[Tvol,Tground,zg,hv,sigv,{gt}]"""

        Fisher = self.get_fisher()
        Ngt = get_Nb_from_Na(self.Na)
        Nbarg = 2 + Ngt
        Fisher_Tg_Tv_zg_known = bl.nans([Nbarg, Nbarg], dtype=float)
        #selectionne les Nbarg avant dernieres lignes et colonnes
        Fisher_Tg_Tv_zg_known = Fisher[-Nbarg:, -Nbarg:]
        return Fisher_Tg_Tv_zg_known
    def get_upsilon_gt(self):
        """
        Retourne la marice de covariance théorique upsilon 3Nax3Na du modèle RVog
        avec décohérence temporelle.
        """

        Na = self.Na
        k_z = self.k_z  #vecteur
        theta = self.theta
        T_vol = self.T_vol
        T_ground = self.T_ground
        h_v = self.h_v
        z_g = self.z_g
        sigma_v = self.sigma_v

        #Construction matrice de covariance Upsilon
        Ups = bl.nans([3 * Na, 3 * Na], dtype=np.complex)
        T = bl.nans([3, 3], dtype=np.complex)
        Om = np.ndarray((Na, Na, 3, 3), dtype=np.complex)

        a = self.get_a()
        alpha = 2 * sigma_v / np.cos(theta)
        I1 = (1 - a) / alpha
        T = I1 * T_vol + a * T_ground

        #Dupplication Na fois de la matrice T
        Ups = np.kron(np.eye(Na) + 0 * 1j, T)

        for i in range(Na - 1):
            for j in range(i + 1, Na):
                #parcours les i<j
                #print 'in get_upsilon' (i,j)
                I2gt = self.get_I2(i, j) * self.get_gamma_t(i, j)
                exp_iPhig = np.exp(1j * self.get_k_z(i, j, Na) * z_g)
                Om[i, j][:, :] = exp_iPhig * (I2gt * T_vol + a * T_ground)

                Ups[3 * i:3 * (i + 1), 3 * j:3 * (j + 1)] = Om[i, j][:, :]
                Ups[3 * j:3 * (j + 1),
                    3 * i:3 * (i + 1)] = Om[i, j][:, :].T.conj()
        return Ups
    def get_upsilon(self):
        """
        Retourne la matrice de covariance théorique upsilon 3Nax3Na du modèle RVog.
        """

        Na = self.Na
        theta = self.theta
        T_vol = self.T_vol
        T_ground = self.T_ground
        h_v = self.h_v
        z_g = self.z_g
        sigma_v = self.sigma_v
        if Na != len(self.k_z) + 1:
            print 'Erreur Na != nbre de kz_1n'
        #Construction matrice de covariance Upsilon
        Ups = bl.nans([3 * Na, 3 * Na], dtype=np.complex)
        T = bl.nans([3, 3], dtype=np.complex)
        Om_ij = np.zeros((3, 3), dtype=np.complex)

        alpha = 2 * sigma_v / np.cos(theta)
        a = np.exp(-alpha * h_v)
        I1 = (1 - a) / alpha
        T = I1 * T_vol + a * T_ground

        #Dupplication Na fois de la matrice T
        Ups = np.kron(np.eye(Na) + 0 * 1j, T)
        for i in range(Na - 1):
            for j in range(i + 1, Na):
                #parcours les i<j
                I2 = (np.exp(1j*self.get_k_z(i,j,Na)*h_v)-a)/ \
                (1j*self.get_k_z(i,j,Na)+alpha)
                exp_Phig = np.exp(1j * self.get_k_z(i, j, Na) * z_g)
                Om_ij = exp_Phig * (I2 * T_vol + a * T_ground)

                Ups[3 * (i):3 * (i + 1), 3 * (j):3 * (j + 1)] = Om_ij
                Ups[3 * (j):3 * (j + 1), 3 * (i):3 * (i + 1)] = Om_ij.T.conj()

        return Ups
    def get_fisher_sigma_v_known(self):
        """Retourne la matricde de Fisher à sigma_v connu         
        eta=(Tvol,Tground,zg,hv,{gt}_ij)."""

        Fisher = self.get_fisher()
        Ngt = get_Nb_from_Na(self.Na)
        #Fisher -> eta=(Tvol,Tground,zg,hv,sigv,{gt}ij)
        allidx = range(21 + Ngt)
        idx = allidx[:20] + allidx[20 + 1:]  #1,2,...,21+Ngt sauf idx 20->sigv
        #Suprresion de la ligne indice 20 et colonne indice 20
        Fisher_sigma_v_known = bl.nans([20 + Ngt, 20 + Ngt], dtype=float)
        Ftemp = Fisher[idx, :]
        Fisher_sigma_v_known = Ftemp[:, idx]
        return Fisher_sigma_v_known
    def get_fisher_z_c_known(self, Fisher):
        """Retourne la matricde de Fisher à z_c (haut de canopée) connue"""

        Fisher_z_c_known = bl.nans([20, 20], dtype=float)
        #changement de var (tvol,tground,z_g,h_v,sigma_v)->(tvol,tground,z_c,h_v,sigma_v)
        J = np.eye(21)
        J[18, 19] = 1
        #mat de fisher dans les coord données nouvelles
        Fisher_2 = npl.inv(J).T.dot(Fisher.dot(npl.inv(J)))
        #On supprime la 19e ligne et colonne (correspond à zc)
        Idx = range(0, 21)
        Idx.remove(18)  #19e composante
        Fisher_z_c_known = Fisher_2[Idx, :]  #suprresion 19e ligne
        Fisher_z_c_known = Fisher_z_c_known[:, Idx]  #supression 19e colonne
        return Fisher_z_c_known  #matrice 20x20
    def get_fisher_zg_known(self):
        """Retourne la matricde de Fisher à zg connu
        à partir de la matrice de Fisher         
        eta=(Tvol,Tground,hv,sigv,{gt}ij)."""

        Fisher = self.get_fisher()
        Ngt = get_Nb_from_Na(self.Na)
        #Fisher -> eta=(Tvol,Tground,zg,hv,sigv,{gt}ij)
        allidx = range(21 + Ngt)
        idx = allidx[:18] + allidx[18 + 1:]  #1,2,...,21+Ngt sauf idx 18->zg
        #Suprresion de la ligne indice 18 et colonne indice 18
        Fisher_zg_known = bl.nans([20 + Ngt, 20 + Ngt], dtype=float)
        Ftemp = Fisher[idx, :]
        Fisher_zg_known = Ftemp[:, idx]
        return Fisher_zg_known
    def get_fisher_z_c_known_sigma_v_known(self, Fisher):
        """Retourne la matricde de Fisher à z_c (hauteur de canopée)
        et sigma_v (atténuation) connue à partir de la matrice 
        de Fisher à sigma_v inconnu (obtenue avec get_fisher)"""

        Fisher_z_c_known_sigma_v_known = bl.nans([19, 19], dtype=float)
        Fisher_sigma_v_known = self.get_fisher_sigma_v_known(Fisher)
        #changement de var (tvol,tground,z_g,h_v)->(tvol,tground,z_c,h_v)
        J = np.eye(20)
        J[18, 19] = 1
        #mat de fisher dans les coord données nouvelles
        Fisher_2_sigma_v_known = npl.inv(J).T.dot(
            Fisher_sigma_v_known.dot(npl.inv(J)))
        #On supprime la 19e ligne et 19e colonne (correspond a zc)
        Idx = range(0, 20)
        Idx.remove(18)  #19e composante
        Fisher_z_c_known_sigma_v_known= \
                  Fisher_2_sigma_v_known[Idx,:] #supression 19e ligne
        Fisher_z_c_known_sigma_v_known= \
                  Fisher_z_c_known_sigma_v_known[:,Idx]#supression 19e colonne
        return Fisher_z_c_known_sigma_v_known
    def get_fisher(self):
        """Retourne la matrice de Fisher en fonction du jeu de paramètres param
        
        La matrice de Fisher est exprimé dans la base 
        (Tvol, Tground, h_v, z_g,sigma_v,{gammat}_ij (i<j))
        ex en dual baseline eta=(Tvol, Tground, h_v, z_g,sigma_v,gammat12,gammat13,gammat12)
        """

        Na = self.Na
        N = self.N
        k_z = self.k_z
        theta = self.theta
        T_vol = self.T_vol
        T_ground = self.T_ground
        h_v = self.h_v
        z_g = self.z_g
        sigma_v = self.sigma_v
        #var intermediaire
        alpha = 2 * sigma_v / np.cos(theta)
        a = np.exp(-alpha * h_v)
        I1 = (1 - a) / alpha

        Nbase = get_Nb_from_Na(
            self.Na)  #Nbre de baseline possibles pour Na acquis
        Nb_inc = 21 + Nbase  #Nbre d'inconnus
        Ups = self.get_upsilon_gt()  #version avec deco temporelle
        #Cacul des dérivés de Upsilon
        #derive de Ups : liste des derivées de Ups par rapport aux 21+Ngt paramètres
        # eta = (Tvol,Tgro,zg,hv,sigma_v,{gt}ij)
        Ups_der = np.ndarray((21 + Nbase, 3 * Na, 3 * Na), dtype=complex)
        E = []
        #Generation de la famille des Ei
        for i in range(0, 3):
            mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
            mat_E_tmp[i, i] = 1
            E.append(mat_E_tmp)

        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[1, 0] = 1
        mat_E_tmp[0, 1] = 1
        E.append(mat_E_tmp)
        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[1, 0] = -1j
        mat_E_tmp[0, 1] = 1j
        E.append(mat_E_tmp)
        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[2, 0] = 1
        mat_E_tmp[0, 2] = 1
        E.append(mat_E_tmp)
        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[2, 0] = -1j
        mat_E_tmp[0, 2] = 1j
        E.append(mat_E_tmp)
        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[2, 1] = 1
        mat_E_tmp[1, 2] = 1
        E.append(mat_E_tmp)
        mat_E_tmp = np.zeros([3, 3], dtype=np.complex)
        mat_E_tmp[2, 1] = -1j
        mat_E_tmp[1, 2] = 1j
        E.append(mat_E_tmp)

        #k numéro de la variable par rapport à laquelle on dérive
        for k in range(0, Nb_inc):  # 0,1,...,Nb_inc
            if k in range(0, 9):  # 0,1,...,8
                #derivée de T
                #Duplication de Na fois la matrice dT/dtvol,k
                T_der = I1 * E[k] + 0 * 1j
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)

                #derivée de Omega_ij
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        #parcours les i<j
                        I2gt_ij = self.get_I2(i, j) * self.get_gamma_t(i, j)
                        Om_der_ij = np.exp(
                            1j * self.get_k_z(i, j, Na) * z_g) * I2gt_ij * E[k]
                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

            elif k in range(9, 18):  #9,10,11,12,13,14,15,16,17
                #derivée de T
                #Duplication de Na fois la matrice dT/dtvol,k
                T_der = a * E[k - 9]
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)

                #derivée par de Omega_ij
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        #parcours les i<j
                        Om_der_ij = np.exp(
                            1j * self.get_k_z(i, j, Na) * z_g) * a * E[k - 9]
                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

            elif k == 18:
                #derivé par rapport à zg
                #derivée par de T
                #Duplication de Na fois la matrice dT/d(eta(k))
                T_der = np.zeros([3, 3], dtype=np.complex)
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)

                #derivée de Omega_ij
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        #print i,j
                        #parcours les i<j
                        Om_ij = Ups[3 * (i):3 * (i + 1), 3 * (j):3 * (j + 1)]
                        Om_der_ij = 1j * self.get_k_z(i, j, Na) * Om_ij
                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

            elif k == 19:
                #Derivée par rapport à h_v
                #derivée de T
                #Duplication de Na fois la matrice dT/dtvol,k
                T_der = a * T_vol - 2 * sigma_v / np.cos(theta) * a * T_ground
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)

                #derivée par rapport à Omega_ij
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        #parcours les i<j
                        I2gt_ij = self.get_I2(i, j) * self.get_gamma_t(i, j)
                        Om_der_ij = np.exp(1j*self.get_k_z(i,j,Na)*z_g)*(\
                                    ((-2*sigma_v/np.cos(theta))*I2gt_ij+\
                                    np.exp(1j*self.get_k_z(i,j,Na)*h_v))*T_vol-\
                                    2*sigma_v/np.cos(theta)*a*T_ground)

                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

            elif k == 20:
                #Dérivée par rapport à sigma_v
                der_I1 = (a * (1 + alpha * h_v) - 1) / (sigma_v * alpha)
                der_a = -2 * h_v * a / np.cos(theta)

                #derivée de T
                #Duplication de Na fois la matrice dT/dtvol,k
                T_der = der_I1 * T_vol + der_a * T_ground
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)

                #derivée de Omega_ij
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        #parcours les i<j
                        der_I2gt_ij=self.get_gamma_t(i,j)*(2/np.cos(theta)*\
                                    (a*(h_v*(1j*self.get_k_z(i,j,Na)+alpha)+1)\
                                    -np.exp(1j*self.get_k_z(i,j,Na)*h_v)))/\
                                    (1j*self.get_k_z(i,j,Na)+alpha)**2
                        Om_der_ij = np.exp(
                            1j * self.get_k_z(i, j, Na) *
                            z_g) * (der_I2gt_ij * T_vol + der_a * T_ground)
                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

            elif 21 <= k and k <= Nb_inc:
                T_der = np.zeros([3, 3], dtype=np.complex)
                Ups_der[k][:, :] = np.kron(np.eye(Na), T_der)
                #derivée par rapport à Omega_ij
                p, q = get_idx_dble_from_idx_mono(k - 21, Na)  #recup l'idx 2D
                for i in range(Na - 1):
                    for j in range(i + 1, Na):
                        Om_der_ij = self.get_I2(i,j)*T_vol*\
                                    bl.KroDelta(i,p)*bl.KroDelta(j,q)*\
                                    np.exp(1j*self.get_k_z(i,j,Na)*z_g)

                        Ups_der[k][3 * (i):3 * (i + 1),
                                   3 * (j):3 * (j + 1)] = Om_der_ij
                        Ups_der[k][3 * (j):3 * (j + 1),
                                   3 * (i):3 * (i + 1)] = Om_der_ij.T.conj()

        #Deduction de la M de Fisher par Slepian-Bang
        Fisher = bl.nans([Nb_inc, Nb_inc], dtype=float)
        for a in range(0, Nb_inc):
            for b in range(0, Nb_inc):
                Fisher[a, b] = N * np.real(
                    np.trace(
                        bl.inv_cond(Ups, 'Ups').dot(Ups_der[a][:, :].dot(
                            bl.inv_cond(Ups, 'Ups').dot(Ups_der[b][:, :])))))
        return Fisher