"""ESP32 8KB CSI embedding v2 (2026-05-31) — 8 -> 64 -> 128 contrastive encoder. Drop-in compatible with the v1 feature contract: input is the same pre-extracted 8-dim CSI feature vector the RuView sensing-server already emits (`type:"feature"`). v2 fixes the v1 trainer's broken (flat-loss) convergence; held-out temporal-triplet accuracy improves 66.4% (raw features) -> 82.3% (this encoder), time-disjoint split. from safetensors.torch import load_file net = Enc(); net.load_state_dict(load_file("csi-embed-v2.safetensors")); net.eval() z = net(torch.tensor(feat8).float()[None]) # feat8: standardized 8-dim CSI feature -> 128-dim L2 embedding """ import torch, torch.nn as nn, torch.nn.functional as F class Enc(nn.Module): def __init__(s): super().__init__() s.w1 = nn.Linear(8, 64); s.bn1 = nn.BatchNorm1d(64) s.w2 = nn.Linear(64, 128); s.bn2 = nn.BatchNorm1d(128) def forward(s, x): # x: [B, 8] standardized CSI features h = F.gelu(s.bn1(s.w1(x))) return F.normalize(s.bn2(s.w2(h)), dim=-1) # -> [B, 128] L2-normalized embedding