Podstawy szerokopasmowej transmisji przewodowej

Autorzy: Jarosław Bułat

Pliki: rozdzial_23.zip

przykład 23.1

W przykładzie 23.1 (plik przyklad_23_1.m) przedstawiono ilustrację transmisji według schematu z rysunku 23.4. Jest to prosty symulator transmisji DMT, zestrojony dla usługi ADSL. Uwzględnia on modulacje i demodulację QAM, kanał transmisyjny w postaci filtra FIR, korektor czasowy TEQ, korektor częstotliwościowy FEQ, blok funkcyjny OFDM oraz zakłócenia AWGN. Program oblicza średni SNR w każdym podpaśmie częstotliwościowym dla przesłanych Ns ramek sygnału. Do poprawnego działania należy dostarczyć odpowiedź impulsową kanału komunikacyjnego – c, odpowiedź impulsową filtra TEQ – w oraz opóźnienie Dx, skojarzone ze skróconą odpowiedzią impulsową. Symulacja jest wykonywana poprzez zakodowanie sygnału pseudolosowego, przesłanie go w dziedzinie czasu, zdekodowanie i następnie porównanie z wysłanym sygnałem. Założono, że odbiornik jest zsynchronizowany z nadajnikiem.
% Przykład 23.1 - ilustracja transmisji ADSL zgodnie ze schematem przedstawionym
% na rysunku 23.4

clear;
fs = 2.208e6; 
codingGain = 4.2; 
margin = 6;

% inicjalizacja parametrów transmisji
N = 512; 
Ld = 32; 
Le = 16; 
Nd = N + Ld; 
Ns = 200;
inputPower = 23; 
inputImp = 100; 
lineNoise = -120;
load c_w_Dx;

% korektor TEQ, IR kanału transmisyjnego
c = [ c zeros( 1, N-length(c)) ];
sir = conv( w, c );

% skrócona odpowiedz impulsowa kanału SIR
sir = [ sir zeros(1,N-length(sir)) ];
SIR = fft( sir(1:N ) ).'; k = (0:N-1)';
FEQ = SIR .* (1/(2*sqrt(2))) .* exp( j*2*pi/N *k* Dx );

% wagi korektora FEQ
rb = zeros(2*Nd,1); 
tf_sig = zeros(N,1); 
tf_err = zeros(N,1); % inicjalizacja modemu
U = zeros(N,1); 
U0 = zeros(N,1); 
c_res = zeros( 1, length( c ) -1 );
w_res = zeros( 1, length( w ) -1 );
noiseAWGN = randn(Ns*Nd,1) * sqrt(inputImp*0.001*fs/2*10^(lineNoise/10)); % szumy
signalPower = inputImp * 0.001 * 10^( inputPower/10 ); % wzmocnienie sygnału
scalingFactor = sqrt( 10^( codingGain/10 ) ) * sqrt( signalPower / 1 );

for iter=1:Ns
    U0 = U;
    Ur=sqrt(3)*(2*rand(N/2-1, 1)-1);
    Ui=sqrt(3)*(2*rand(N/2-1, 1)-1);
    U = Ur+j*Ui;
    U = [ 0; U; 0; conj( flipud(U) ) ];
    x = ifft( U );
    x = ( real( x )*sqrt(N) ) / sqrt(2);
    x = [(x(N-Ld+1:N)); x];
    s = x * scalingFactor;
    
    [ y c_res ] = filter( c, 1, s, c_res );     % kanał
    y = y + noiseAWGN( (iter-1)*Nd+1:iter*Nd ); % dodanie szumu addytywnego
    y = y / scalingFactor;                      % normowanie mocy sygnału
    [ ye w_res ] = filter( w, 1, y, w_res );    % korektor TEQ
    rb(1:Nd) = rb(Nd+1:2*Nd);
    rb(Nd+1:2*Nd) = ye;
    y = rb(1+Dx+Ld : N+Dx+Ld);                  % usunięcie prefiksu
    Y= fft(y);                                  % blok funkcyjny OFDM
    Y = Y/(2*sqrt(N));                          % normowanie sygnału
    U_r = Y ./ FEQ;                             % korektor FEQ
    if( iter > 2 )                              % wyznaczanie SNR
        e = U0 - U_r;                           % błąd rekonstrukcji
        tf_sig = tf_sig + U0.*conj(U0);         % moc sygnału
        ce = e .* conj(e);                      
        tf_err = tf_err + ce;                   % moc zakłóceń
    end
end

idx = find( tf_err == 0 ); tf_err( idx ) = eps;
snr = tf_sig(1:N/2) ./ tf_err(1:N/2);           % stosunek mocy sygnału do szumu

Przykład 23.2

Przykład 23.2 (plik adsl.m) prezentuje implementację w języku MATLAB kodu źródłowego głównej pętli zbudowanego symulatora ADSL. Sposób użycia tej funkcji oraz inicjalizacja niezbędnych parametrów zostaną podane w przykładzie 23.3.
% Przykład 23.2 - plik adsl.m
function y = adsl( iter, trType, bitMask, teqIR, Dx, feq, noiseType )
% iter: liczba iteracji (ramek)
% trType: typ transmisji:
% 0 = sygnał treningowy {1,2,3}, bez prefiksu, bez TEQ, bez FEQ, bitMask ignorowany
% 1 = estymacja SNR: jak dla 0 plus prefiks, TEQ, FEQ, bitMask ignorowany
% 2 = transmisja danych: wszystkie elementy modemu działają
% bitMask: (N/2,1) liczba bitów transmitowany w podkanale, 0 - brak transmisji

Lp = 32;                    % długość prefiksu
N = 512;                    % długość ramki bez prefiksu
fp = 2.208e6;               % częstotliwość próbkowania
sigPower = 23;              % moc sygnału [dBm]
% noiseType = 'AWGN';       % %typ zakłóceń addytywnych (szumu): AWGN, NBI, impulsowy, etc...
noisePower = -100;          % moc szumu AWGN [dBm/Hz]
noisePowerNBI = -20;        % moc szumu NBI [dBm]
noiseChannelNBI = 100;      % numer podkanału w którym wystąpiło zakłócenie NBI
channelNr = 2;              % numer kanału z bazy csaloop: do wyboru {1...8, 10, 11}
H = zeros( 1, N/2 );        
sig = zeros( 1, N/2 );
noise = zeros( 1, N/2 );
ber = zeros( 1, N/2 );

% główna pętla programu - jedna iteracja = transmisja jednej ramki danych
for i = 1:iter
    X1 = genBinData( trType, bitMask );
    X2 = QAM( X1, trType, bitMask );
    x3 = Mod( X2, N );
    x4 = Prefix( x3, trType, Lp );
    x5 = NormEnergy( x4, sigPower );
    y1 = channel( x5, channelNr );
    y2 = AddNoise( y1, noiseType, noisePower, noisePowerNBI, noiseChannelNBI, fp, iter );
    y3 = DeNormEnergy( y2, sigPower );
    y4 = TEQ( y3, teqIR );
    y5 = DePrefix( y4, trType, N, Dx, Lp );
    Y6 = DeMod( y5, N );
    Y7 = FEQ( Y6, feq );
    Y8 = DeQAM( Y7, trType, bitMask );
    H = HEstim( i, trType, X2, Y6, H );
    [sig, noise] = SNR( i, trType, X2, Y7, sig, noise );
    ber = BER( i, trType, bitMask, X1, Y8, ber );
    %     err = cmp( X1, Y8, X2, Y7 );
end

if( trType == 0 )                               % estymacja knanału
    H( N/2+2:N ) = conj( fliplr( H(2:N/2) ) );
    H = H / (iter-1);
    h = ifft( H );
    h = real(h);
    y = h;
elseif( trType == 1 )                           % obliczanie SNR
    snr = sig./noise;
    y = snr;
elseif( trType == 2 )                           % transmisja bitów
    y = ber;
end

%===================== genBinData ====================
function y = genBinData( trType, mask )
% trType: typ transmisji:
%   0 = sygnał treningowy {1,2,3,4}, bez: prefiksu, TEQ, FEQ; bitMask ignorowany
%   1 = esytmacja SNR: wygnał jak w 0, uwzględniono: prefiks, TEQ, FEQ, bitMask ignorowany
%   2 = transmisja danych
% mask(N/2,1): ilość bitół w każdy podpaśmie
% y(N/2,1): wektor wyjściowy zawiarający losowe dane
persistent state;
if isempty( state )  % zapewnia brak korelacji pomiędzy szumem danymi (losowymi)
    rand( 'state', 111 );
    disp( 'initialize persistent random data generator' );
else
    rand( 'state', state );
end

if( trType == 0 || trType == 1 )  % ReverbSequence (ADSL norma C-REVERB1 s.92)
    N = 512;
    b = zeros( N, 1 );
    b( 1:9 ) = 1;
    for i =10:length(b)
        b(i) = xor( b(i-4), b(i-9) );
    end
    
    for i = 1:N/2
        y( i ) = ( 2 * b( 2*(i-1)+1 ) + b( 2*(i-1)+2 ) ) +1;
    end
elseif( trType == 2 )
    for s=1:length(mask)
        if( mask(s) == 0 )
            y(s) = 0;
        else
            y(s) = randint( 1, 1, 2^mask(s) );
        end
    end
else
    y = zeros( size(mask) );
end
state = rand( 'state' );


%===================== QAM ====================
function y = QAM( x, trType, mask )
% x(N/2,1): dane binarne {0, 1, 2, 3, 4, 5, ... 2^n-1}
% mask(N/2,1): liczba bitów w każdym podkanale
% y(N/2,1): QAM
y=zeros( size(mask) );
if( trType == 0 || trType == 1 )    % estymacja SNR i kanału, używane wyłącznie QAM4
    QAM4 = [ 1+j, 1-j; -1+j, -1-j ];
    y = zeros( size( mask ) );
    for i = 1:length( mask )
        if( mask(i) ~= 2 )
            y(i) = 0;
        else
            y(i) = QAM4( x(i) );
        end
    end
elseif( trType == 2 )       % QAM4, QAM8, ..., QAM32768
    qamMax = [ 0 1 3 3 5 7 11 15 23 31 47 63 95 127 191 ];
    for s=1:length(mask)
        M = 2^mask(s);
        if( mask( s ) == 0 )
            y(s) = 0;
        else
            y(s) = qammod( x(s), M ) / qamMax( mask(s) );
        end
    end
else
    y = zeros( size(mask) );
end


%===================== Mod ====================
function y = Mod( x, N )
% x(N/2,1): QAM
% N: długość ramki (512 dla adsl)
% y(N,1): dane zmodulowane (ifft dla adsl)
% uwaga: zmienna y nie może zawierać części urojonej
%        sygnał wyjściowy musi być unormowany do energii jednostkowej

x(1) = sqrt( 2 );
x( N/2+1 ) = sqrt( 2 );
% x(1) = 0;
% x( N/2+1 ) = 0;

x( N/2+2: N ) = conj( fliplr( x(2:N/2 ) ) );
y = ifft( x );
y = y.*sqrt(N/2);


%===================== prefix ====================
function y = Prefix( x, trType, Lp )
% x(N,1): dane zmodulowane (w dziedzinie czasu)
% trType: typ transmisji:
%   0: bez: prefiksu, TEQ, FEQ; bitMask ignorowany
%   1,2: transmisja, wymagany przefiks
if( trType == 0 )
    y = x;
else
    y = [ x(end-Lp+1:end), x ];
end


%===================== NormEnergy ===============
function y = NormEnergy( x, power )
% x(N+Lp,1): jedna ramka danych w dziedzinie czasu
% power: moc syngnału w dBm
inputImp = 100;
signalPower = inputImp * 0.001 * 10^( power/10 );
y = x .* sqrt( signalPower );


%===================== channel ==================
function y = channel( x, nr )
% x(N+Lp,1): jedna ramka sygnału przed kanałem
% nr: numer symulowanego kanału {1...8, 10, 11}
% y(N+Lp,1): jedna ramka sygnału po przejściu przez kanał
persistent ir_residue;
persistent ir;
if isempty( ir_residue )
    load ir;                % ładowanie odpowiedzi impulsowych różnych kanałów
    eval( sprintf( 'ir=ir%d;', nr ) );
    ir_residue = zeros( length( ir ) -1, 1 );
    disp( 'initialize persistent ir_residue array' );
end
[ y ir_residue ] = filter( ir, 1, x, ir_residue );


%===================== noise ===================
function y = AddNoise( x, type, power, powerNBI, channelNBI, fs, iter )
% x(N+Lp,1)
% type: typ szumu: {AWGN, NBI, Impulse}
% power: moc szumu w dBm/Hz
% fp: częstotliwość pró”kowania
persistent state;
persistent NBI;
if isempty( state )   % zapewnia brak korelacji pomiędzy szumem danymi (losowymi)
    rand( 'state', 112 );
    disp( 'initialize persistent random noise' );
    NBI = narrowNoiseGen( fs, iter*544, channelNBI/256/2*fs, 'AM-FIR',  fs/512, 12345 )';
else
    rand( 'state', state );
end
y = x;
if( strcmp(type,'AWGN') || strcmp( type, 'AWGN+NBI' ) )
    inputImp = 100;
    npower = inputImp * 0.001 * fs/2 * 10^( power/10 );
    xn = randn( size(x) );
    y = y + xn .* sqrt( npower );
end
if( strcmp( type, 'NBI' ) || strcmp ( type, 'AWGN+NBI' ) )
    inputImp = 100;
    noiseNotchGain = inputImp * 0.001 * 10^( powerNBI/10 );
    y = y + NBI( 1+(iter-1)*length(x) : iter*length(x) ) * sqrt( noiseNotchGain );
end
if( strcmp( type, 'Impulse' ) )
    error( 'AddNoise - Impulse noise: missing code' );
end
state = rand( 'state' );


%===================== NormEnergy ===============
function y = DeNormEnergy( x, power )
% x(N+Lp,1)
% y(N+Lp,1)
inputImp = 100;
signalPower = inputImp * 0.001 * 10^( power/10 );
y = x ./ sqrt( signalPower );


%===================== TEQ ====================
function y = TEQ( x, ir )
% x(N+Lp,1)
% ir(Le,1): odpowiedź impulsowa korektora TEQ
% y(N+Lp,1): ramka poddana korekcji TEQ w dziedzinie czasu
persistent eq_residue;
if isempty( eq_residue )
    eq_residue = zeros( length( ir ) -1, 1 );
    disp( 'initialize persistent eq_residue array' );
end
[ y eq_residue ] = filter( ir, 1, x, eq_residue );


%===================== DePrefix =================
function y = DePrefix( x, trType, N, D, Lp )
% x(N+Lp,1)
% type: typ transmisji
%     0: sekwencja treningowa - bez prefiksu
%     1: transmisjia - prefiks wymagany
% N: liczba próbek w ramce (długość ramki w dziedzinie czasu)
% D: opóźnienie transmisji powodowane przez kanał i TEQ
% Lp: długość prefiksu
% y(N,1): jedna ramka danych w dziedzinie czasu bez prefiksu
persistent lastFrame;
if isempty( lastFrame )
    lastFrame = zeros( size( x ) );
    disp( 'initialize last frame delay' );
end

cx = [ lastFrame, x ];
lastFrame = x;

if( trType == 0 )
    y = x;
else
    y = cx( D+Lp+1 : N+D+Lp );
end

%===================== DeMod ==================
function y = DeMod( x, length )
% x(N,1)
% length: długość ramki w dziedzinie czasu
% y(N/2,1): zespolone dane w dziedzinie częstotliwości (zdemodulowane)
y = fft( x );
y = y( 1:length/2 );
y = y ./ sqrt( length/2 );


%===================== FEQ ====================
function y = FEQ( x, feq )
% x(N/2,1): dane zespolone
% feq(N/2,1): korektor częstotliwościowy - FEQ
y = x .* feq;


%===================== DeQAM ==================
function y = DeQAM( x, trType, mask )
% x(N/2,1): dane po korekcji FEQ - dziedzina częstotliwości
% mask(N/2,1): liczba bitów w podkanałach
% y(N/2,1): dane binarne

if( trType == 0 || trType == 1 )
    %QAM4 = [ 1+j, 1-j; -1+j, -1-j ];
    x1 = round( x );
    for i = 1:length( x )
        rx = real( x1(i) );
        ix = imag( x1(i) );
        if( rx==1 && ix==1 )
            y(i) = 1;
        elseif( rx==1 && ix==-1 )
            y(i) = 2;
        elseif( rx==-1 && ix==1 )
            y(i) = 3;
        elseif( rx==-1 && ix==-1 )
            y(i) = 4;
        else
            y(i) = 0;
        end
    end
elseif( trType == 2 )
    qamMax = [ 0 1 3 3 5 7 11 15 23 31 47 63 95 127 191 ];
    for s=1:length(mask)
        M = 2^mask(s);
        if( mask( s ) == 0 )
            y(s) = 0;
        else
            y(s) = qamdemod( x(s)*qamMax( mask(s) ), M );
        end
    end
else
    y=zeros( size(mask) );
end


%===================== H estimation ===============
function H = HEstim( i, trType, X, Y, Hp )
% estymacja odpowiedzi impulsowej, pierwsza ramka jest błędna, należy ją zignorować
% iter: numer iteracji (przesłanej ramki)
% type: typ transmisji
%     0: sekwencja treningowa = identyfikacja kanału
%     1: estymacja SNR
%     2: transmisjia bitów
% X(N/2,1): transmitowane dane w dziedzinie częstotliwości
% Y(N/2,1): odebrane dane w dziedzinie częstotliwości
% Hp(N/2,1): odpowiedź częstotliwościowa kanału z poprzedniej ramki
% H(N/2,1): odpowiedź częstotliwościowa kanału - uśrednianie
if( trType ~= 0 )
    H = Hp;
    return;
else
    if( i == 1 )
        H = zeros( size(Hp) );
    else
        H = Hp + Y./X;
    end
end

function [sig, noise] = SNR( i, trType, X, Y, sigPrev, noisePrev )
% estymacja SNR (Signal-to-Noise-Ratio), pierwsza ramka jest błędna, należy ją zignorować
% iter: iteration number
% iter: numer iteracji (przesłanej ramki)
% type: typ transmisji
%     0: sekwencja treningowa = identyfikacja kanału
%     1: estymacja SNR
%     2: transmisjia bitów
% X(N/2,1): transmitowane dane w dziedzinie częstotliwości
% Y(N/2,1): odebrane dane w dziedzinie częstotliwości
% sigPrev: moc sygnału z poprzedniej ramki
% noisePrev: moc szumół z poprzedniej ramki
% sig: moc sygnału - uśrednianie
% noise: moc szumu - uśrednianie
if( trType ~=1 )
    sig = zeros( size( sigPrev ) );
    noise = zeros( size( noisePrev ) );
    return;
else
    if( i == 1)                         % pierwsza ramka
        sig = zeros( size( sigPrev ) );
        noise = zeros( size( noisePrev ) );
    else
        e = Y - X;
        e = e.*conj(e);
        
        sig = sigPrev + (X.*conj(X));
        noise = noisePrev + e;
    end
end


function ber = BER( i, trType, mask, X, Y, berp )
% estymacja BER, pierwsza ramka jest błędna, należy ją zignorować
% type: typ transmisji
%     0: sekwencja treningowa = identyfikacja kanału
%     1: estymacja SNR
%     2: transmisjia bitów
% mask: liczba bitów w podkanałach
% X(N/2,1): tansmitowane dane (binarnie)
% Y(N/2,1): odebrane dane (binarnie)
persistent lastX;
if isempty( lastX )
    lastX = zeros( size( X ) );
    disp( 'initialize last frame delay' );
end

if( trType ~=2 )
    ber = zeros( size(X) );
    return;
else
    %error( 'BER: missing code' );
    if( i>2 )
        ber = zeros( size(X) );
    else
        ber = berp + ( (lastX-Y) ~= 0 );
    end
end
lastX = X;

%===================== cmp ====================
function err = cmp( x1, y1, x2, y2 )
% porównywanie, wykresy, etc...
persistent lastX2;
if isempty( lastX2 )
    lastX2 = zeros( size( x2 ) );
    disp( 'initialize last frame delay' );
    err =  zeros( size(x2) );
    return;
end

figure(1);
t = 1:length(y2);
plot( t, real( lastX2(t)), 'b.', t, real(y2(t)), 'bo', t, imag( lastX2(t)), 'rx', t, imag(y2(t)), 'ro' );
err = zeros( size(x1) );
lastX2 = x2;

Przykład 23.3

Przykład 23.3 (plik przyklad_23_3.m) prezentuje implementację w języku MATLAB sposobu użycia symulatora.
% przyklad 23.3

clear all;
close all;

N = 512;                                % długość ramki
Le = 16;                                % długość filtru TEQ
Lp = 32;                                % długość prefiksu

% etap 1, identyfikacja kanału:
trType = 0;                             % estymacja kanału
mask = 2*ones( 1, N/2 );                % maska (estymowane wszystkie podkanały)
teqIR = [ 1, 0, 0, 0, 0, 0, 0, 0 ];     % ,,pusty'' TEQ - delta Kroneckera
D = 0;                                  % opóźnienie wprowadzane przez kanał
FEQ = ones( 1, N/2 );                   % ,,pusty'' FEQ
h = adsl( 500, trType, mask, teqIR, D, FEQ, 'AWGN' );   % estymacja IR kanału

% etap 2, obliczanie SNR
clear adsl                              % kasowanie zmiennych persistant
trType = 1;                             % estymacja SNR
[ teqIR, D ] = AdslTEQ( h, Le, Lp, 0 ); % wyznaczanie korektora TEQ
sir = conv( teqIR, h );                 % obliczanie skróconej odpowiedzi impulsowej
FEQ = fft( sir(1:N) );                  % wyznaczanie korekora FEQ
FEQ = 1./ ( FEQ .* exp( j*2*pi/N * (0:N-1)*(D) ) );
FEQ = FEQ( 1:N/2 );
snr = adsl( 512, trType, mask, teqIR, D, FEQ, 'AWGN' ); % estymacja wartości SNR

% wizualizacja wyników: SNR, przydział bitów
figure( 1 ); plot( 10*log10( snr ), 'k' );
axis( [0 255, 0, 50] ); title( 'SNR' );
xlabel( 'nr podkanału' ); ylabel( 'snr [dB]' );

bit = bitLoad( snr, 0 );                % przydział bitów - scenariusz ,,maksimum bitrate''
figure( 2 ), plot( 1:256, bit, 'k');
axis( [0, 255, 0, 15] ); title( 'przydział bitów' );
xlabel( 'numer podkanału' ); ylabel( 'ilość bitów' );

% etap 3, transmisja danych
clear adsl
trType = 2;                             % estymacja BER
[ teqIR, D ] = AdslTEQ( h, Le, Lp, 0 );
sir = conv( teqIR, h );
FEQ = fft( sir(1:N) );
FEQ = 1./ ( FEQ .* exp( j*2*pi/N * (0:N-1)*(D) ) );
FEQ = FEQ( 1:N/2 );
mask = bit;
ber = adsl( 16, trType, mask, teqIR, D, FEQ, 'AWGN' );

figure( 3 ); plot( ber );
title( 'liczba błędów w podkanałach' ); xlabel( 'nr podkanału' );
ylabel( 'liczba błędnie przesłanych symboli' );

Przykład 23.4

Przykład 23.4 (plik przyklad_23_4.m) prezentuje sposób estymacji odpowiedzi impulsowej dla modemu ADSL. Funkcja HEstim(...) jest częścią składową pliku adsl.m.
% Przykład 23.4
% funkcja obliczająca i uśredniająca iloraz widma sygnału wyjściowego i wejściowego
function H = HEstim( i, trType, X, Y, Hp )
% (...)
H = Hp + Y./X;

% obliczanie estymaty odpowiedzi impulsowej z uśrednionych danych
H( N/2+2:N ) = conj( fliplr( H(2:N/2) ) );
H = H / (iter-1);
h = ifft( H );
h = real( h );

Przykład 23.5

Program z przykładu 23.5 (przyklad_23_5.m) oblicza wagi korektora TEQ maksymalnie skracającego odpowiedź impulsową kanału.
% Przykład 23.5
function [ w ] = EigMinISI( c, Ld, Lw, D )
% c - odpowiedź impulsowa kanału
% Ld - długość prefiksu
% Lw - długość korektora TEQ
% D - opóźnienie skróconej odpowiedzi impulsowej
% w - obliczona odpowiedź impulsowa filtra TEQ
Lc = length( c );
Lz = Lc+Lw-1-Ld-D;
C = zeros( Lw, Lc+Lw-1 );
for i = 1:Lw
    C( i, i:i+Lc-1 ) = c;
end
Wd = zeros( Lc+Lw-1, Lc+Lw-1 ); Wr = Wd;
b = [ zeros( 1, D ), ones( 1, Ld ), zeros( 1, Lz ) ];
Wd = diag ( b ); Wr = diag ( 1-b );
Xd = C*Wd*C';
G = chol ( Xd );
Xr = C*Wr*C';
T = inv( G )'*Xr*inv( G );
[ V, D ] = eig( T );
LambdaMin = min( diag( D ) );
LambdaMinIdx = find( diag( D ) == LambdaMin );
w = V( :, LambdaMinIdx )'*inv( G )';