Implementação de Algoritmos de Detecção para Sistemas de Modulação Espacial

A modulação espacial (Spatial Modulation, SM) representa uma técnica inovadora para ssitemas MIMO, onde a seleção da antena ativa transmite informação adicional além dos símbolos modulados. Este artigo apresenta uma implementação completa dos principais algoritmos de detecção, com exemplos práticos em MATLAB e Python.

  1. Modelagem do Sistema e Geração de Sinais

% sm_system_simulation.m
% Simulação completa de sistema SM com múltiplos algoritmos de detecção

clear; clc; close all;

%% Parâmetros de Configuração
Ntx = 4;              % Número de antenas transmissoras
Nrx = 4;              % Número de antenas receptoras
ordem_mod = 4;        % Ordem da modulação (QPSK)
total_bits = 10000;   % Bits a serem transmitidos
faixa_snr = 0:5:30;   % Valores de SNR em dB
num_simulacoes = 500; % Número de realizações por ponto

%% Mapeamento de Constelação
simbolos_qpsk = (1/sqrt(2)) * [1+1i, 1-1i, -1+1i, -1-1i];
mapeamento_bits = [0 0; 0 1; 1 0; 1 1];

%% Inicialização de Resultados
resultados_ml = zeros(size(faixa_snr));
resultados_zf = zeros(size(faixa_snr));
resultados_mmse = zeros(size(faixa_snr));
resultados_sd = zeros(size(faixa_snr));

%% Função de Geração de Sinal SM
function [sinal_tx, antena_ativa, idx_simbolo] = gerar_sinal_sm(bits_entrada, Ntx, ordem_mod, simbolos)
    bits_por_simbolo = log2(Ntx) + log2(ordem_mod);
    num_simbolos = length(bits_entrada) / bits_por_simbolo;
    
    sinal_tx = zeros(Ntx, num_simbolos);
    antena_ativa = zeros(1, num_simbolos);
    idx_simbolo = zeros(1, num_simbolos);
    
    for sym = 1:num_simbolos
        idx_inicio = (sym-1)*bits_por_simbolo + 1;
        idx_fim = sym*bits_por_simbolo;
        bits_atuais = bits_entrada(idx_inicio:idx_fim);
        
        % Decodificação de bits para seleção de antena
        bits_antena = bits_atuais(1:log2(Ntx));
        idx_antena = bin2dec(num2str(bits_antena)) + 1;
        
        % Decodificação de bits para seleção de símbolo
        bits_simbolo = bits_atuais(log2(Ntx)+1:end);
        idx_sym = bin2dec(num2str(bits_simbolo)) + 1;
        
        % Construção do vetor de transmissão
        vetor_tx = zeros(Ntx, 1);
        vetor_tx(idx_antena) = simbolos(idx_sym);
        
        sinal_tx(:, sym) = vetor_tx;
        antena_ativa(sym) = idx_antena;
        idx_simbolo(sym) = idx_sym;
    end
end

  1. Algoritmo de Máxima Verossimilhança Otimizado

%% Detecção por Máxima Verossimilhança (ML)
function [antena_detectada, simbolo_detectado] = detector_ml_otimizado(recebido, canal, Ntx, ordem_mod, simbolos)
    melhor_metrica = inf;
    antena_escolhida = 1;
    simbolo_escolhido = 1;
    
    % Busca exaustiva por todas as combinações
    for ant = 1:Ntx
        for sym = 1:ordem_mod
            % Vetor candidato
            candidato = zeros(Ntx, 1);
            candidato(ant) = simbolos(sym);
            
            % Cálculo da distância euclidiana
            distancia = norm(recebido - canal * candidato)^2;
            
            if distancia < melhor_metrica
                melhor_metrica = distancia;
                antena_escolhida = ant;
                simbolo_escolhido = sym;
            end
        end
    end
    
    antena_detectada = antena_escolhida;
    simbolo_detectado = simbolo_escolhido;
end

%% Versão Simplificada de Baixa Complexidade
function [antena_det, simbolo_det] = detector_ml_simplificado(rx, H, Ntx, ordem_mod, simbolos)
    [Qmat, Rmat] = qr(H);
    y_processado = Qmat' * rx;
    
    menor_dist = inf;
    antena_idx = 1;
    
    for ant = 1:Ntx
        % Estimação inicial via projeção
        coluna_canal = H(:, ant);
        estimativa_zf = (coluna_canal' * rx) / (coluna_canal' * coluna_canal);
        
        % Quantização ao símbolo mais próximo
        [~, idx_sym] = min(abs(estimativa_zf - simbolos));
        
        % Avaliação da métrica ML
        vetor_teste = zeros(Ntx, 1);
        vetor_teste(ant) = simbolos(idx_sym);
        metrica = norm(rx - H * vetor_teste)^2;
        
        if metrica < menor_dist
            menor_dist = metrica;
            antena_idx = ant;
            simbolo_idx = idx_sym;
        end
    end
    
    antena_det = antena_idx;
    simbolo_det = simbolo_idx;
end

  1. Métodos de Detecção Linear

%% Detector de Zero-Forcing (ZF)
function [antena_zf, simbolo_zf] = detector_zero_forcing(sinal_rx, matriz_canal, Ntx, ordem_mod, simbolos)
    % Cálculo da matriz de equalização ZF
    W_zf = pinv(matriz_canal); % Pseudo-inversa de Moore-Penrose
    
    % Equalização
    sinal_equalizado = W_zf * sinal_rx;
    
    % Detecção de antena (maior magnitude)
    [~, idx_antena] = max(abs(sinal_equalizado));
    
    % Detecção de símbolo
    simbolo_recebido = sinal_equalizado(idx_antena);
    [~, idx_simbolo] = min(abs(simbolo_recebido - simbolos));
    
    antena_zf = idx_antena;
    simbolo_zf = idx_simbolo;
end

%% Detector de Mínimo Erro Quadrático Médio (MMSE)
function [antena_mmse, simbolo_mmse] = detector_mmse(sinal_rx, matriz_canal, Ntx, ordem_mod, simbolos, potencia_ruido)
    % Matriz de identidade
    I_mat = eye(Ntx);
    
    % Matriz de equalização MMSE
    W_mmse = (matriz_canal' * matriz_canal + potencia_ruido * I_mat) \ matriz_canal';
    
    % Processamento do sinal
    sinal_processado = W_mmse * sinal_rx;
    
    % Detecção
    [~, idx_antena] = max(abs(sinal_processado));
    simbolo_estimado = sinal_processado(idx_antena);
    [~, idx_simbolo] = min(abs(simbolo_estimado - simbolos));
    
    antena_mmse = idx_antena;
    simbolo_mmse = idx_simbolo;
end

  1. Decodificação por Esfera (Sphere Decoding)

%% Decodificador por Esfera
function [antena_sd, simbolo_sd] = sphere_decoder_custom(rx, H, Ntx, ordem_mod, simbolos, raio_busca)
    [Qmat, Rmat] = qr(H);
    y_hat = Qmat' * rx;
    
    % Inicialização da busca
    nivel = Ntx;
    metrica_atual = 0;
    melhor_metrica = inf;
    caminho_otimo = zeros(Ntx, 1);
    caminho_atual = zeros(Ntx, 1);
    
    % Busca recursiva
    [caminho_otimo, ~] = busca_recursiva(nivel, y_hat, Rmat, Ntx, ordem_mod, simbolos, raio_busca, metrica_atual, caminho_atual, melhor_metrica, caminho_otimo);
    
    % Extração dos resultados
    [~, antena_sd] = max(abs(caminho_otimo));
    valor_simbolo = caminho_otimo(antena_sd);
    [~, simbolo_sd] = min(abs(valor_simbolo - simbolos));
end

function [caminho_final, metrica_final] = busca_recursiva(nivel, y_proc, Rmat, Ntx, ordem_mod, simbolos, raio, metrica_parcial, caminho_parcial, melhor_metrica, caminho_melhor)
    if nivel == 0
        if metrica_parcial < melhor_metrica
            metrica_final = metrica_parcial;
            caminho_final = caminho_parcial;
        else
            metrica_final = melhor_metrica;
            caminho_final = caminho_melhor;
        end
        return;
    end
    
    % Cálculo da distância parcial
    if nivel == Ntx
        termo_independente = 0;
    else
        termo_independente = y_proc(nivel) - Rmat(nivel, nivel+1:Ntx) * caminho_parcial(nivel+1:Ntx);
    end
    
    % Verificação do raio de busca
    distancia_restante = raio^2 - metrica_parcial;
    if distancia_restante < 0
        metrica_final = melhor_metrica;
        caminho_final = caminho_melhor;
        return;
    end
    
    % Limites de busca
    limite_inferior = (real(termo_independente) - sqrt(distancia_restante)) / Rmat(nivel, nivel);
    limite_superior = (real(termo_independente) + sqrt(distancia_restante)) / Rmat(nivel, nivel);
    
    % Busca nos símbolos válidos
    for s = 1:ordem_mod
        simb = simbolos(s);
        if real(simb) >= limite_inferior && real(simb) <= limite_superior
            caminho_parcial(nivel) = simb;
            
            % Atualização da métrica
            delta_metrica = abs(y_proc(nivel) - Rmat(nivel, nivel) * simb)^2;
            nova_metrica = metrica_parcial + delta_metrica;
            
            % Chamada recursiva
            [caminho_final, metrica_final] = busca_recursiva(nivel-1, y_proc, Rmat, Ntx, ordem_mod, simbolos, raio, nova_metrica, caminho_parcial, melhor_metrica, caminho_melhor);
        end
    end
end

  1. Detecção Baseada em Compressive Sensing

%% Detector CS com Reconstrução LASSO
function [antena_cs, simbolo_cs] = detector_compressive_sensing(rx, H, Ntx, ordem_mod, simbolos, param_lambda)
    % Construção do dicionário sobredeterminado
    dicionario = zeros(Nrx, Ntx * ordem_mod);
    for ant = 1:Ntx
        for sym = 1:ordem_mod
            idx_coluna = (ant-1)*ordem_mod + sym;
            dicionario(:, idx_coluna) = H(:, ant) * simbolos(sym);
        end
    end
    
    % Reconstrução via LASSO
    sinal_estimado = lasso_reconstrucao(rx, dicionario, param_lambda);
    
    % Identificação do componente dominante
    [~, idx_max] = max(abs(sinal_estimado));
    
    % Mapeamento para antena e símbolo
    antena_cs = ceil(idx_max / ordem_mod);
    simbolo_cs = mod(idx_max-1, ordem_mod) + 1;
end

function x_est = lasso_reconstrucao(y, A, lambda_reg, max_iter)
    if nargin < 4
        max_iter = 800;
    end
    
    [~, n_dim] = size(A);
    x_est = zeros(n_dim, 1);
    L_const = max(eig(A' * A));
    
    for iter = 1:max_iter
        % Passo de gradiente
        grad = A' * (A * x_est - y);
        x_temp = x_est - (1/L_const) * grad;
        
        % Operador de soft-thresholding
        x_est = sign(x_temp) .* max(abs(x_temp) - lambda_reg/L_const, 0);
        
        % Critério de parada
        if norm(grad) < 1e-5
            break;
        end
    end
end

  1. Abordagem com Redes Neurais Profundas

# neural_network_sm_detector.py
# Implementação de detector neural para modulação espacial

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models

class DetectorNeuralSM:
    def __init__(self, ntx, nrx, ordem_mod, taxa_aprendizado=0.0005):
        self.ntx = ntx
        self.nrx = nrx
        self.ordem_mod = ordem_mod
        self.modelo = self._construir_arquitetura()
        self.otimizador = keras.optimizers.Adam(learning_rate=taxa_aprendizado)
        
    def _construir_arquitetura(self):
        modelo = models.Sequential([
            layers.Input(shape=(2*self.nrx + 2*self.ntx*self.nrx,)),
            
            layers.Dense(512, activation='relu', kernel_regularizer='l2'),
            layers.BatchNormalization(),
            layers.Dropout(0.25),
            
            layers.Dense(256, activation='relu', kernel_regularizer='l2'),
            layers.BatchNormalization(),
            layers.Dropout(0.25),
            
            layers.Dense(128, activation='relu'),
            layers.BatchNormalization(),
            
            layers.Dense(self.ntx + self.ordem_mod, activation='softmax')
        ])
        return modelo
    
    def preprocessar_dados(self, y_recebido, H_canal):
        y_real = np.concatenate([np.real(y_recebido), np.imag(y_recebido)], axis=1)
        H_real = np.concatenate([np.real(H_canal), np.imag(H_canal)], axis=1)
        H_flat = H_real.reshape(H_real.shape[0], -1)
        return np.concatenate([y_real, H_flat], axis=1)
    
    def treinar(self, X_treino, y_antena, y_simbolo, epocas=40, batch_size=64):
        y_antena_onehot = keras.utils.to_categorical(y_antena, self.ntx)
        y_simbolo_onehot = keras.utils.to_categorical(y_simbolo, self.ordem_mod)
        y_treino = np.concatenate([y_antena_onehot, y_simbolo_onehot], axis=1)
        
        self.modelo.compile(
            optimizer=self.otimizador,
            loss='categorical_crossentropy',
            metrics=['accuracy']
        )
        
        historico = self.modelo.fit(
            X_treino, y_treino,
            epochs=epocas,
            batch_size=batch_size,
            validation_split=0.15,
            verbose=0
        )
        return historico
    
    def detectar(self, y, H):
        entrada = self.preprocessar_dados(y.reshape(1, -1), H.reshape(1, -1))
        predicao = self.modelo.predict(entrada, verbose=0)
        
        prob_antena = predicao[0, :self.ntx]
        prob_simbolo = predicao[0, self.ntx:]
        
        return np.argmax(prob_antena), np.argmax(prob_simbolo)

  1. Algoritmo de Passagem de Mensagens Aproximado (AMP)

%% Detector AMP Avançado
function [antena_amp, simbolo_amp] = detector_approximate_message_passing(rx, H, Ntx, ordem_mod, simbolos, max_iter, tolerancia)
    if nargin < 6
        max_iter = 40;
    end
    if nargin < 7
        tolerancia = 1e-5;
    end
    
    N_total = Ntx * ordem_mod;
    estimativa = zeros(N_total, 1);
    residuo = rx;
    variancia_noise = 0.01;
    
    for iter = 1:max_iter
        estimativa_anterior = estimativa;
        
        % Cálculo do termo de Onsager
        if iter > 1
            tau_sq = norm(residuo)^2 / size(H, 1);
            onsager_correction = tau_sq * (estimativa - gamma);
        else
            gamma = zeros(N_total, 1);
            onsager_correction = zeros(N_total, 1);
        end
        
        % Atualização da variável auxiliar
        gamma = estimativa + H' * residuo - onsager_correction;
        
        % Estimador não-linear
        estimativa = estimador_nao_linear(gamma, variancia_noise, simbolos, ordem_mod);
        
        % Atualização do resíduo
        derivada = calculo_derivada(gamma, variancia_noise, simbolos);
        residuo = rx - H * estimativa + (size(H,1)/N_total) * residuo * mean(derivada);
        
        % Verificação de convergência
        if norm(estimativa - estimativa_anterior) / norm(estimativa_anterior) < tolerancia
            break;
        end
    end
    
    % Decisão final
    [~, idx_max] = max(abs(estimativa));
    antena_amp = ceil(idx_max / ordem_mod);
    simbolo_amp = mod(idx_max-1, ordem_mod) + 1;
end

function x_est = estimador_nao_linear(gamma_val, var_ruido, simbolos, ordem_mod)
    x_est = zeros(size(gamma_val));
    for i = 1:length(gamma_val)
        probabilidades = exp(-abs(gamma_val(i) - simbolos).^2 / (2*var_ruido));
        probabilidades = probabilidades / sum(probabilidades);
        x_est(i) = sum(probabilidades .* simbolos);
    end
end

function deriv = calculo_derivada(gamma_val, var_ruido, simbolos)
    deriv = zeros(size(gamma_val));
    for i = 1:length(gamma_val)
        probs = exp(-abs(gamma_val(i) - simbolos).^2 / (2*var_ruido));
        probs = probs / sum(probs);
        media = sum(probs .* simbolos);
        segundo_momento = sum(probs .* abs(simbolos).^2);
        deriv(i) = segundo_momento - abs(media)^2;
    end
end

  1. Análise de Complexidade Computacional

%% Análise Comparativa de Complexidade
function analisar_complexidade(Ntx, Nrx, ordem_mod)
    fprintf('\n=== ANÁLISE DE COMPLEXIDADE COMPUTACIONAL ===\n');
    fprintf('Configuração: %dTx × %dRx, Modulação %d-QAM\n\n', Ntx, Nrx, ordem_mod);
    
    % Complexidade ML
    complex_ml = Ntx * ordem_mod;
    fprintf('ML Otimizado: O(%d) operações de distância\n', complex_ml);
    
    % Complexidade ZF
    complex_zf = Nrx^3 + Ntx^2 * Nrx;
    fprintf('Zero-Forcing: O(Nrx³ + Ntx²Nrx) ≈ O(%d) FLOPs\n', complex_zf);
    
    % Complexidade MMSE
    complex_mmse = Nrx^3 + Ntx^3 + Ntx*Nrx;
    fprintf('MMSE: O(Nrx³ + Ntx³) ≈ O(%d) FLOPs\n', complex_mmse);
    
    % Complexidade Sphere Decoding
    complex_sd = ordem_mod * sqrt(Ntx);
    fprintf('Sphere Decoding: O(M√Nt) ≈ O(%d) nós visitados\n', round(complex_sd));
    
    % Complexidade AMP
    complex_amp = max_iter * (Ntx*Nrx + Ntx*ordem_mod);
    fprintf('AMP (40 iterações): O(%d) FLOPs\n', complex_amp);
    
    % Complexidade Neural Network
    complex_nn = 512*(2*Nrx + 2*Ntx*Nrx) + 256*512 + 128*256 + (Ntx+ordem_mod)*128;
    fprintf('Rede Neural: ≈ O(%d) MACs\n', complex_nn);
end

  1. Programa Principal de Avaliação de Desempenho

%% Loop de Simulação Principal
for idx_snr = 1:length(faixa_snr)
    snr_linear = 10^(faixa_snr(idx_snr)/10);
    potencia_ruido = 1 / snr_linear;
    
    erros_ml = 0; erros_zf = 0; erros_mmse = 0; erros_sd = 0;
    
    for trial = 1:num_simulacoes
        % Geração de bits aleatórios
        bits_tx = randi([0 1], 1, total_bits);
        
        % Geração do sinal SM
        [sinal_enviado, antena_real, simbolo_real] = gerar_sinal_sm(bits_tx, Ntx, ordem_mod, simbolos_qpsk);
        
        % Geração do canal Rayleigh
        canal = (randn(Nrx, Ntx) + 1i*randn(Nrx, Ntx)) / sqrt(2);
        
        % Geração de ruído AWGN
        ruido = sqrt(potencia_ruido/2) * (randn(Nrx, 1) + 1i*randn(Nrx, 1));
        
        % Sinal recebido
        sinal_recebido = canal * sinal_enviado + ruido;
        
        % Detecção ML
        [ant_ml, sym_ml] = detector_ml_otimizado(sinal_recebido, canal, Ntx, ordem_mod, simbolos_qpsk);
        if (ant_ml ~= antena_real) || (sym_ml ~= simbolo_real)
            erros_ml = erros_ml + 1;
        end
        
        % Detecção ZF
        [ant_zf, sym_zf] = detector_zero_forcing(sinal_recebido, canal, Ntx, ordem_mod, simbolos_qpsk);
        if (ant_zf ~= antena_real) || (sym_zf ~= simbolo_real)
            erros_zf = erros_zf + 1;
        end
        
        % Detecção MMSE
        [ant_mmse, sym_mmse] = detector_mmse(sinal_recebido, canal, Ntx, ordem_mod, simbolos_qpsk, potencia_ruido);
        if (ant_mmse ~= antena_real) || (sym_mmse ~= simbolo_real)
            erros_mmse = erros_mmse + 1;
        end
        
        % Sphere Decoding
        [ant_sd, sym_sd] = sphere_decoder_custom(sinal_recebido, canal, Ntx, ordem_mod, simbolos_qpsk, 1.5);
        if (ant_sd ~= antena_real) || (sym_sd ~= simbolo_real)
            erros_sd = erros_sd + 1;
        end
    end
    
    % Cálculo das taxas de erro
    resultados_ml(idx_snr) = erros_ml / num_simulacoes;
    resultados_zf(idx_snr) = erros_zf / num_simulacoes;
    resultados_mmse(idx_snr) = erros_mmse / num_simulacoes;
    resultados_sd(idx_snr) = erros_sd / num_simulacoes;
    
    fprintf('SNR = %d dB | ML: %.4f | ZF: %.4f | MMSE: %.4f | SD: %.4f\n', ...
        faixa_snr(idx_snr), resultados_ml(idx_snr), resultados_zf(idx_snr), ...
        resultados_mmse(idx_snr), resultados_sd(idx_snr));
end

%% Visualização dos Resultados
figure('Position', [100 100 800 600]);
semilogy(faixa_snr, resultados_ml, 'b-o', 'LineWidth', 2, 'MarkerSize', 8);
hold on;
semilogy(faixa_snr, resultados_zf, 'r-s', 'LineWidth', 2, 'MarkerSize', 8);
semilogy(faixa_snr, resultados_mmse, 'g-^', 'LineWidth', 2, 'MarkerSize', 8);
semilogy(faixa_snr, resultados_sd, 'm-d', 'LineWidth', 2, 'MarkerSize', 8);

grid on; grid minor;
xlabel('SNR (dB)', 'FontSize', 12);
ylabel('Taxa de Erro de Bit (BER)', 'FontSize', 12);
title(['Performance de Detecção SM (' num2str(Ntx) '×' num2str(Nrx) ', QPSK)'], 'FontSize', 14);
legend('ML Otimizado', 'Zero-Forcing', 'MMSE', 'Sphere Decoding', 'Location', 'northeast');
set(gca, 'FontSize', 11, 'LineWidth', 1.5);

  1. Recomendações para Implementação Prática

Seleção de Algoritmo por Escala:

  • Sistemas pequenos (Ntx ≤ 4): ML otimizado oferece performance máxima
  • Sistemas médios (4 < Ntx ≤ 8): Sphere Decoding ou AMP equilibram performance e complexidade
  • Sistemas grandes (Ntx > 8): Abordagens baseadas em aprendizado profundo ou detectores lineares

Otimizações de Desempenho:

  • Vetorização de operações matriciais para aceleração
  • Implementação de tabelas de consulta para funções não-lineares
  • Exploração de paralelismo em GPUs para treinamento neural
  • Pré-processamento de canal para condicionamento

Considerações de Hardware:

  • FPGAs: Ideais para implementação de Sphere Decoding e AMP devido ao paralelismo
  • ASICs: Detectores lineares (ZF/MMSE) beneficiam-se de arquiteturas fixas
  • GPUs: Processamento neural e operações matrciiais massivas
  • DSPs: Soluções de baixa potência para ML simplificado

**Trade-offs Principais:**A escolha do algoritmo envolve balancear três fatores: proximidade ao desempenho ótimo, recursos computacionais disponíveis, e latência máxima tolerável. Sistemas em tempo real podem preferir ZF/MMSE, enquanto aplicações de alta confiabilidade devem utilizar ML ou SD.

Tags: modulação-espacial mimo detecção-ml algoritmo-zf mmse

Publicado em 8-19 06:15