Restructure package: descriptive study documentation and clean layout
This commit is contained in:
Executable
+94
@@ -0,0 +1,94 @@
|
||||
"""E4 — Symbol-timing offsets with block-wise receiver realignment.
|
||||
|
||||
The BS knows the per-user timing estimates (pilot-based) and realigns each
|
||||
user's block region individually inside the single received frame:
|
||||
y_al[b_w + t] = y[b_w + t + delta_w], t = 0..d/U-1
|
||||
Energy that crossed block boundaries is lost or appears as residual
|
||||
interference, exactly as in a real system with per-user timing advance error.
|
||||
"""
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
|
||||
import lib
|
||||
from lib import (SCENARIOS, UWCA, DEVICE, block_masks, channel, gen_embeddings,
|
||||
mean_cos, ofdma_decode, save_json, ser, set_seed)
|
||||
|
||||
rng = set_seed(42)
|
||||
d, U, H = 64, 4, 4
|
||||
dpu = d // U
|
||||
masks = block_masks(U, d)
|
||||
scen = SCENARIOS["HIGH"]
|
||||
off_rng = np.random.default_rng(5)
|
||||
|
||||
|
||||
def gen(n):
|
||||
return gen_embeddings(n, d, U, rng, scen).to(DEVICE)
|
||||
|
||||
|
||||
model = UWCA(d, U, H).to(DEVICE)
|
||||
model.load_state_dict(torch.load(lib.DATA / "e1_uwca_HIGH.pt",
|
||||
map_location=DEVICE))
|
||||
model.eval()
|
||||
|
||||
|
||||
def realign(y, offs_hat):
|
||||
"""Block-wise realignment: shift each user's block back by its offset."""
|
||||
n, dd = y.shape
|
||||
out = torch.zeros_like(y)
|
||||
for u in range(U):
|
||||
b0 = u * dpu
|
||||
for o in offs_hat[:, u].unique():
|
||||
o = int(o.item())
|
||||
idx = offs_hat[:, u] == o
|
||||
src_end = min(b0 + dpu + o, dd)
|
||||
ln = src_end - (b0 + o)
|
||||
if ln > 0:
|
||||
out[idx, b0:b0 + ln] = y[idx, b0 + o:src_end]
|
||||
return out
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def evaluate(mode, dmax, snr, err_p=0.0, n_mc=200):
|
||||
s_acc = c_acc = 0.0
|
||||
for _ in range(n_mc):
|
||||
E = gen(64)
|
||||
offs = None
|
||||
if dmax > 0:
|
||||
offs = torch.from_numpy(
|
||||
off_rng.integers(0, dmax + 1, size=(64, U))).to(DEVICE)
|
||||
ch = channel(E, snr_db=snr, offsets=offs)
|
||||
y = ch["yI"]
|
||||
if dmax > 0 and mode.endswith("cor"):
|
||||
offs_hat = offs.clone()
|
||||
if err_p > 0:
|
||||
flip = torch.from_numpy(
|
||||
off_rng.random((64, U)) < err_p).to(DEVICE)
|
||||
pm = torch.from_numpy(
|
||||
off_rng.choice([-1, 1], size=(64, U))).to(DEVICE)
|
||||
offs_hat = (offs_hat + flip.long() * pm).clamp(min=0)
|
||||
y = realign(y, offs_hat)
|
||||
if mode.startswith("uwca"):
|
||||
Eh = model(y, ch["yQ"])
|
||||
else:
|
||||
Eh = ofdma_decode(y, masks)
|
||||
s_acc += ser(Eh, E)
|
||||
c_acc += mean_cos(Eh, E)
|
||||
return s_acc / n_mc, c_acc / n_mc
|
||||
|
||||
|
||||
dgrid = [0, 1, 2, 4, 8]
|
||||
out = {"dmax": dgrid, "snr_eval": [10.0, 20.0], "curves": {}}
|
||||
for label, mode, ep in [("uwca_uncorrected", "uwca_unc", 0.0),
|
||||
("ofdma_uncorrected", "ofdma_unc", 0.0),
|
||||
("uwca_corrected", "uwca_cor", 0.0),
|
||||
("ofdma_corrected", "ofdma_cor", 0.0),
|
||||
("uwca_corrected_err20", "uwca_cor", 0.2)]:
|
||||
cur = {}
|
||||
for snr in out["snr_eval"]:
|
||||
cur[str(snr)] = [evaluate(mode, dm, snr, ep) for dm in dgrid]
|
||||
out["curves"][label] = cur
|
||||
print(f"[E4] {label} 10dB SER: "
|
||||
f"{[round(a[0],3) for a in cur['10.0']]}", flush=True)
|
||||
|
||||
save_json("e4_async.json", out)
|
||||
Reference in New Issue
Block a user