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.
- 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
- 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
- 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
- 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
- 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
- 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)
- 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
- 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
- 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);
- 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.