Leakage, semantic and robustness experiments from the revision

This commit is contained in:
KiHoLee
2026-08-22 12:24:39 +09:00
parent e4da750812
commit 24019e901a
15 changed files with 514 additions and 10 deletions
+149
View File
@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
"""Information-theoretic security metrics for the main configuration.
The evaluation so far reported only the eavesdropper SER. This stage adds
the quantities a physical-layer-security reader expects, all computed
from the SAME Monte Carlo the SER curves use, so no new modelling
assumption enters.
Every metric is derived from the empirical joint law of the transmitted
digit and the DECISION each receiver makes. That decision is a
deterministic function of the received frame, so the data-processing
inequality makes each leakage number a LOWER bound on the true
I(s_u; y_E): what the modelled correlation eavesdropper actually
extracts. Reported per frame, an index carries P digits, so the frame
quantities are P times the per-digit ones under the independent-digit
source the evaluation uses.
I(s;s_hat) mutual information between the digit and the decision
H(s|s_hat) equivocation, and its ratio to log2(V)
TV distinguishing advantage, the average total variation
between the decision law given a digit and its marginal
R_s secrecy rate, the legitimate information rate minus the
eavesdropper one, per frame
Run under WSL. Writes data/infotheory.csv.
"""
from __future__ import annotations
import csv
import math
import sys
from pathlib import Path
import torch
sys.path.insert(0, str(Path(__file__).resolve().parent))
import sse_lib as L
from sse_lib import DATA, DEVICE, rayleigh_gain, snr_to_sigma2
from exp_full import main_model, eve_wrong_mask
from exp_refresh import kdf_invariant, install
FRAMES = 400_000
CHUNK = 40_000
SNRS = [0.0, 5.0, 10.0, 15.0, 20.0]
@torch.no_grad()
def confusion_refreshed(m, snr_db, sub_key, base_keys, base_book,
blocks=64, frames=FRAMES, seed=4242):
"""The same joint counts when the key is redrawn from the invariance
group every block, against an eavesdropper holding one fixed
substitute. Each block contributes frames/blocks frames."""
torch.manual_seed(seed + int(10 * snr_db))
C = torch.zeros(m.vu, m.vu, dtype=torch.float64, device=DEVICE)
per = max(CHUNK // 4, frames // blocks)
for b in range(blocks):
sg, cp, up = kdf_invariant(5150, b, m.users, m.L)
install(m, sg * base_keys[up], base_book, colperm=cp)
C += confusion(m, snr_db, sub_key=sub_key, frames=per,
seed=seed + 97 * b)
install(m, base_keys, base_book)
return C
@torch.no_grad()
def confusion(m, snr_db, sub_key=None, frames=FRAMES, seed=777):
"""Empirical joint counts of (transmitted digit, decided digit) for
user 0, pooled over the P periods. sub_key None means the legitimate
receiver; otherwise the eavesdropper substitutes that key."""
torch.manual_seed(seed + int(10 * snr_db))
C = torch.zeros(m.vu, m.vu, dtype=torch.float64, device=DEVICE)
keys = m.masks() if sub_key is None else sub_key.to(DEVICE)
done = 0
while done < frames:
n = min(CHUNK, frames - done)
dig = torch.randint(m.vu, (n, m.users, m.P), device=DEVICE)
Bn = m.unit_codebook()
e = Bn[dig] / math.sqrt(m.P)
y = (e * m.masks()[None, :, None, :]).sum(dim=1) / m.c
h = rayleigh_gain((n, 1), device=DEVICE)
sig = snr_to_sigma2(torch.full((n,), snr_db), m.d).to(DEVICE).sqrt()
rx = h[:, :, None, None] * y[:, None] \
+ sig[:, None, None, None] * torch.randn(n, 1, m.P, m.L,
device=DEVICE)
r = rx / h[:, :, None, None].clamp_min(1e-6)
cand = Bn[None, :, :] * keys[:1, None, :]
dec = torch.einsum("nupl,uvl->nupv", r, cand).argmax(-1)[:, 0]
idx = dig[:, 0].reshape(-1) * m.vu + dec.reshape(-1)
C += torch.bincount(idx, minlength=m.vu * m.vu).reshape(
m.vu, m.vu).to(torch.float64)
done += n
return C
def metrics(C, P, V):
"""Mutual information, equivocation and distinguishing advantage from
a joint count matrix, all in bits."""
J = C / C.sum()
px, py = J.sum(1), J.sum(0)
nz = J > 0
mi = float((J[nz] * (J[nz] / (px[:, None] * py[None, :])[nz]).log2()).sum())
hx = float(-(px[px > 0] * px[px > 0].log2()).sum())
equiv = hx - mi # H(digit | decision)
# distinguishing advantage: E_s || p(dec|s) - p(dec) ||_TV
cond = J / px[:, None].clamp_min(1e-300)
tv = float((px * 0.5 * (cond - py[None, :]).abs().sum(1)).sum())
return {"mi_digit": mi, "equiv_digit": equiv,
"mi_frame": P * mi, "equiv_frame": P * equiv,
"equiv_ratio": P * equiv / math.log2(V), "tv": tv}
def main():
m = main_model()
m.eval()
ew = eve_wrong_mask(m.users, m.L, seed=20260813)
base_keys = m.W.detach().clone().cpu()
base_book = m.B.detach().clone().cpu()
rows = []
for snr in SNRS:
lg = metrics(confusion(m, snr), m.P, m.V)
ev = metrics(confusion(m, snr, sub_key=ew), m.P, m.V)
rf = metrics(confusion_refreshed(m, snr, ew, base_keys, base_book),
m.P, m.V)
rs = max(0.0, lg["mi_frame"] - ev["mi_frame"])
rs_r = max(0.0, lg["mi_frame"] - rf["mi_frame"])
rows.append((snr,
"%.4f" % lg["mi_frame"], "%.6f" % ev["mi_frame"],
"%.6f" % ev["equiv_ratio"], "%.6f" % ev["tv"],
"%.4f" % rs,
"%.6f" % rf["mi_frame"], "%.6f" % rf["equiv_ratio"],
"%.6f" % rf["tv"], "%.4f" % rs_r))
print("%5.1f dB legit %6.3f | fixed key: MI %.3f TV %.3f Rs %6.3f "
"| refreshed: MI %.4f TV %.4f Rs %6.3f"
% (snr, lg["mi_frame"], ev["mi_frame"], ev["tv"], rs,
rf["mi_frame"], rf["tv"], rs_r), flush=True)
out = DATA / "infotheory.csv"
with open(out, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["snr_db", "mi_legit_bits",
"mi_eve_fixed_bits", "equiv_ratio_fixed", "tv_fixed",
"secrecy_rate_fixed_bits",
"mi_eve_refresh_bits", "equiv_ratio_refresh",
"tv_refresh", "secrecy_rate_refresh_bits"])
w.writerows(rows)
print("[csv]", out)
if __name__ == "__main__":
main()
+175
View File
@@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
"""Semantic leakage: does a wrong index still carry the meaning?
The symbol error rate counts any wrong index as a total failure, which
is the right accounting for a bit pipe and the wrong one for a semantic
pipe: a token decoded as a near synonym has leaked the meaning even
though the index is wrong. This stage measures what the SER cannot see,
on two semantic scales.
codeword cosine cos(e_shat, e_s) between the embedding a receiver
reconstructs and the transmitted one, uniform indices
BERT cosine cos of the BERT input embeddings of the decoded and
the transmitted token, on the AG News stream, which
is semantic similarity in the space the vocabulary
was built for
Each is reported for the legitimate receiver, the outsider and the
insider, against the chance level of two independently drawn tokens.
A scheme leaks semantically if the adversary's similarity sits above
that chance level.
Run under WSL. Writes data/semantic.csv.
"""
from __future__ import annotations
import csv
import math
import sys
from pathlib import Path
import torch
import torch.nn.functional as F
sys.path.insert(0, str(Path(__file__).resolve().parent))
import sse_lib as L
from sse_lib import DATA, DEVICE, rayleigh_gain, snr_to_sigma2
from exp_full import main_model, eve_wrong_mask
FRAMES = 200_000
CHUNK = 20_000
SNRS = [0.0, 10.0, 20.0]
REAL_SNRS = [0.0, 10.0, 20.0, 28.0]
@torch.no_grad()
def decide(m, dig, snr_db, keys, gen=None):
"""Decisions of a receiver holding `keys`, for the frames carrying
`dig`. Returns the decided digits of user 0."""
n = dig.shape[0]
Bn = m.unit_codebook()
e = Bn[dig] / math.sqrt(m.P)
y = (e * m.masks()[None, :, None, :]).sum(dim=1) / m.c
h = rayleigh_gain((n, 1), device=DEVICE)
sig = snr_to_sigma2(torch.full((n,), snr_db), m.d).to(DEVICE).sqrt()
rx = h[:, :, None, None] * y[:, None] \
+ sig[:, None, None, None] * torch.randn(n, 1, m.P, m.L,
device=DEVICE)
r = rx / h[:, :, None, None].clamp_min(1e-6)
cand = Bn[None, :, :] * keys[:1, None, :]
return torch.einsum("nupl,uvl->nupv", r, cand).argmax(-1)[:, 0]
def frame_embedding(m, digits):
"""The d-dimensional embedding an index maps to, digits (N,P)."""
Bn = m.unit_codebook()
return (Bn[digits] / math.sqrt(m.P)).reshape(digits.shape[0], -1)
@torch.no_grad()
def codeword_cosine(m, snr_db, keys, seed):
"""Mean cosine between the reconstructed and the true embedding."""
torch.manual_seed(seed + int(10 * snr_db))
tot, done = 0.0, 0
while done < FRAMES:
n = min(CHUNK, FRAMES - done)
dig = torch.randint(m.vu, (n, m.users, m.P), device=DEVICE)
dec = decide(m, dig, snr_db, keys)
c = F.cosine_similarity(frame_embedding(m, dec),
frame_embedding(m, dig[:, 0]), dim=1)
tot += float(c.sum())
done += n
return tot / done
@torch.no_grad()
def codeword_chance(m, seed=99):
"""Cosine between two independently drawn indices."""
torch.manual_seed(seed)
a = torch.randint(m.vu, (FRAMES, m.P), device=DEVICE)
b = torch.randint(m.vu, (FRAMES, m.P), device=DEVICE)
return float(F.cosine_similarity(frame_embedding(m, a),
frame_embedding(m, b), dim=1).mean())
def load_bert_embeddings():
"""BERT input embedding matrix, the space AG News tokens live in."""
from transformers import AutoModel
mdl = AutoModel.from_pretrained("bert-base-uncased")
return mdl.get_input_embeddings().weight.detach().to(DEVICE)
@torch.no_grad()
def real_semantic(m, emb, ids_all, snr_db, keys, seed):
"""Mean BERT cosine between the decoded and the transmitted token of
user 0. ids_all is (N,U): every user carries its OWN stream, so an
insider decoding user 0 gains nothing from its own traffic."""
torch.manual_seed(seed + int(10 * snr_db))
n = ids_all.shape[0]
dig = torch.stack([(ids_all // (m.vu ** p)) % m.vu
for p in range(m.P)], -1).to(DEVICE) # (N,U,P)
tot, done = 0.0, 0
while done < n:
k = min(CHUNK, n - done)
dec = decide(m, dig[done:done + k], snr_db, keys)
rec = sum(dec[:, p] * (m.vu ** p) for p in range(m.P))
true = ids_all[done:done + k, 0].to(DEVICE)
rec = rec.clamp(max=emb.shape[0] - 1)
tot += float(F.cosine_similarity(emb[rec], emb[true], dim=1).sum())
done += k
return tot / n
def main():
m = main_model()
m.eval()
ew = eve_wrong_mask(m.users, m.L, seed=20260813)
insider = m.masks()[1:2].detach() # user 2 attacking user 1
rows = []
chance = codeword_chance(m)
print("codeword chance cosine %.4f" % chance, flush=True)
for snr in SNRS:
lg = codeword_cosine(m, snr, m.masks(), 5150)
ev = codeword_cosine(m, snr, ew.to(DEVICE), 5151)
ins = codeword_cosine(m, snr, insider, 5152)
rows.append(("codeword", snr, "%.4f" % lg, "%.4f" % ev,
"%.4f" % ins, "%.4f" % chance))
print("codeword %4.0f dB legit %.4f outsider %.4f insider %.4f"
% (snr, lg, ev, ins), flush=True)
# real token streams in the BERT embedding space
try:
from exp_real_sec import load_streams
streams, _bounds, _vocab = load_streams()
nmin = min(len(x) for x in streams)
ids = torch.stack([torch.as_tensor(x[:nmin], dtype=torch.long)
for x in streams], dim=1)[:100_000] # (N,U)
emb = load_bert_embeddings()
rnd = torch.randint(0, emb.shape[0], (ids.shape[0],))
ch = float(F.cosine_similarity(emb[ids[:, 0].to(DEVICE)],
emb[rnd.to(DEVICE)], dim=1).mean())
print("BERT chance cosine %.4f" % ch, flush=True)
for snr in REAL_SNRS:
lg = real_semantic(m, emb, ids, snr, m.masks(), 5160)
ev = real_semantic(m, emb, ids, snr, ew.to(DEVICE), 5161)
ins = real_semantic(m, emb, ids, snr, insider, 5162)
rows.append(("bert", snr, "%.4f" % lg, "%.4f" % ev,
"%.4f" % ins, "%.4f" % ch))
print("bert %4.0f dB legit %.4f outsider %.4f insider %.4f"
% (snr, lg, ev, ins), flush=True)
except Exception as exc: # keep the codeword rows
print("[skip] real-token semantic stage: %s: %s"
% (type(exc).__name__, exc), flush=True)
out = DATA / "semantic.csv"
with open(out, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["space", "snr_db", "legit", "outsider", "insider",
"chance"])
w.writerows(rows)
print("[csv]", out)
if __name__ == "__main__":
main()
+148
View File
@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
"""Two robustness sweeps the evaluation was missing.
Users. Every other stage fixes U=4. The structured family admits U up to
L-1, and as U approaches L the frame fills with cross-user patterns, so
this sweep asks what the load costs the legitimate users and whether the
confidentiality survives it.
Channel estimation. Every other stage equalizes with the exact gain.
Here the receiver divides by an estimate h+e with e zero mean and
variance sigma_e^2 relative to the gain, so the residual phase and
amplitude error enters the correlation the same way a key mismatch
would, and the question is how much of the legitimate margin it costs.
Run under WSL. Writes data/users.csv and data/csi.csv.
"""
from __future__ import annotations
import csv
import math
import sys
from pathlib import Path
import torch
sys.path.insert(0, str(Path(__file__).resolve().parent))
import sse_lib as L
from sse_lib import DATA, DEVICE, rayleigh_gain, snr_to_sigma2, eval_ser_sse
from exp_full import (main_model, base_keys, get_model, eve_wrong_mask,
eval_ser_eve, mean_abs_xcorr, MAIN_D)
SNR = 10.0
FRAMES = 400_000
CHUNK = 40_000
USERS = [2, 4, 8, 16, 32, 48]
CSI = [0.0, 1e-3, 1e-2, 3e-2, 1e-1]
PHASE = [0.0, 0.02, 0.05, 0.10, 0.20] # residual phase error, radians rms
@torch.no_grad()
def ser_with_csi_error(m, snr_db, nmse, frames=FRAMES, seed=606):
"""Legitimate SER when the receiver equalizes with a noisy estimate."""
torch.manual_seed(seed + int(1e4 * nmse))
wrong = tot = 0
while tot < frames:
n = min(CHUNK, frames - tot)
dig = torch.randint(m.vu, (n, m.users, m.P), device=DEVICE)
Bn = m.unit_codebook()
e = Bn[dig] / math.sqrt(m.P)
y = (e * m.masks()[None, :, None, :]).sum(dim=1) / m.c
h = rayleigh_gain((n, m.users), device=DEVICE)
sig = snr_to_sigma2(torch.full((n,), snr_db), m.d).to(DEVICE).sqrt()
rx = h[:, :, None, None] * y[:, None] \
+ sig[:, None, None, None] * torch.randn(n, m.users, m.P, m.L,
device=DEVICE)
# estimate with a zero-mean error of the stated relative variance
hhat = h + math.sqrt(nmse) * h.abs() * torch.randn_like(h)
r = rx / hhat[:, :, None, None].clamp_min(1e-6)
cand = Bn[None, :, :] * m.masks()[:, None, :]
dec = torch.einsum("nupl,uvl->nupv", r, cand).argmax(-1)
wrong += int((dec != dig).any(dim=-1).sum())
tot += n * m.users
return wrong / tot
@torch.no_grad()
def ser_with_phase_error(m, snr_db, rms, frames=FRAMES, seed=707):
"""Legitimate SER under a residual phase error. Entries 2n-1 and 2n
are the I and Q of one complex channel use, so an uncompensated
phase rotates that pair. Unlike an amplitude error, this is not a
common scale and the argmax is not invariant to it."""
torch.manual_seed(seed + int(1e3 * rms))
wrong = tot = 0
half = m.L // 2
while tot < frames:
n = min(CHUNK, frames - tot)
dig = torch.randint(m.vu, (n, m.users, m.P), device=DEVICE)
Bn = m.unit_codebook()
e = Bn[dig] / math.sqrt(m.P)
y = (e * m.masks()[None, :, None, :]).sum(dim=1) / m.c
h = rayleigh_gain((n, m.users), device=DEVICE)
sig = snr_to_sigma2(torch.full((n,), snr_db), m.d).to(DEVICE).sqrt()
noise = torch.randn(n, m.users, m.P, m.L, device=DEVICE)
rx = h[:, :, None, None] * y[:, None] + sig[:, None, None, None] * noise
r = rx / h[:, :, None, None].clamp_min(1e-6)
if rms > 0: # rotate each I/Q pair
th = rms * torch.randn(n, m.users, 1, half, device=DEVICE)
v = r.reshape(n, m.users, m.P, half, 2)
i, q = v[..., 0], v[..., 1]
c_, s_ = th.cos(), th.sin() # broadcast over periods
r = torch.stack([i * c_ - q * s_, i * s_ + q * c_],
dim=-1).reshape(n, m.users, m.P, m.L)
cand = Bn[None, :, :] * m.masks()[:, None, :]
dec = torch.einsum("nupl,uvl->nupv", r, cand).argmax(-1)
wrong += int((dec != dig).any(dim=-1).sum())
tot += n * m.users
return wrong / tot
def main():
# --- users -------------------------------------------------------
rows = []
print("user load at %g dB, L=%d" % (SNR, MAIN_D // 4), flush=True)
for U in USERS:
Lp = MAIN_D // 4
if U > Lp - 1:
print(" U=%d exceeds L-1, skipped" % U, flush=True)
continue
m = get_model(d=MAIN_D, U=U, iters=4000, freeze_W=base_keys(U, Lp))
m.eval()
lg = eval_ser_sse(m, [SNR], frames=FRAMES)[0]
ew = eve_wrong_mask(U, Lp, seed=20260813)
ev = eval_ser_eve(m, ew, [SNR], frames=FRAMES)[0]
xc = mean_abs_xcorr(m.masks().detach())
rows.append((U, "%.6f" % lg, "%.6f" % ev, "%.6f" % xc))
print(" U=%2d legit %.4f eve %.5f xcorr %.2e"
% (U, lg, ev, xc), flush=True)
with open(DATA / "users.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["users", "legit_ser", "eve_ser", "mask_xcorr"])
w.writerows(rows)
print("[csv]", DATA / "users.csv", flush=True)
# --- channel estimation error ------------------------------------
m = main_model()
m.eval()
rows = []
print("channel estimation error at %g dB" % SNR, flush=True)
for nmse in CSI:
s = ser_with_csi_error(m, SNR, nmse)
rows.append(("%g" % nmse, "%.6f" % s))
print(" nmse %-6g legit %.4f" % (nmse, s), flush=True)
print("residual phase error at %g dB" % SNR, flush=True)
prows = []
for rms in PHASE:
s_ = ser_with_phase_error(m, SNR, rms)
prows.append(("%g" % rms, "%.6f" % s_))
print(" phase rms %-5g legit %.4f" % (rms, s_), flush=True)
with open(DATA / "csi.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["impairment", "level", "legit_ser"])
w.writerows([("amplitude_nmse",) + r for r in rows]
+ [("phase_rms_rad",) + r for r in prows])
print("[csv]", DATA / "csi.csv")
if __name__ == "__main__":
main()
+10 -10
View File
@@ -34,16 +34,16 @@ FIG.mkdir(exist_ok=True)
plt.rcParams.update({
"font.family": "serif",
"font.serif": ["DejaVu Serif", "Times New Roman"],
# The manuscript includes each result figure at 0.85 of a 3.455 in
# column while the canvas is 3.15 in, a printed scale of 0.933. Every
# The manuscript includes each result figure at 0.70 of a 3.455 in
# column while the canvas is 3.15 in, a printed scale of 0.768. Every
# size below is therefore pre-divided by that scale so the PRINTED
# sizes are 9 pt labels, 8 pt ticks and a 6.6 pt legend. Change the
# sizes are 8 pt labels, 7 pt ticks and a 5.8 pt legend. Change the
# include width and these must change with it.
"font.size": 9.7,
"axes.labelsize": 9.7,
"legend.fontsize": 7.1,
"xtick.labelsize": 8.6,
"ytick.labelsize": 8.6,
"font.size": 10.4,
"axes.labelsize": 10.4,
"legend.fontsize": 7.6,
"xtick.labelsize": 9.2,
"ytick.labelsize": 9.2,
"axes.grid": True,
"grid.linestyle": "--",
"grid.linewidth": 0.4,
@@ -53,7 +53,7 @@ plt.rcParams.update({
"figure.figsize": (3.15, 2.25), # shorter canvas: same printed width and font size, less page height
"pdf.fonttype": 42,
})
AXES_RECT = dict(left=0.205, right=0.970, top=0.955, bottom=0.215)
AXES_RECT = dict(left=0.215, right=0.970, top=0.955, bottom=0.225)
C_LEGIT = "#c0392b"
C_EVE = "#2c5fa8"
@@ -204,7 +204,7 @@ def main_legit(snr_db="10"):
def place_legend(ax, cands=("lower left", "upper left", "center left",
"center right", "lower center", "upper right",
"upper center", "center", "lower right"),
sizes=(7.1, 6.8, 6.6), ncol=1):
sizes=(7.6, 7.2, 6.8, 6.4, 6.0), ncol=1):
"""Choose the location and font size whose box the fewest curve points
fall inside, scored on rendered geometry rather than guessed from the
data. The size sweep is what makes a long label set placeable: a
+11
View File
@@ -0,0 +1,11 @@
impairment,level,legit_ser
amplitude_nmse,0,0.053300
amplitude_nmse,0.001,0.052325
amplitude_nmse,0.01,0.053213
amplitude_nmse,0.03,0.052790
amplitude_nmse,0.1,0.052969
phase_rms_rad,0,0.053000
phase_rms_rad,0.02,0.052800
phase_rms_rad,0.05,0.052500
phase_rms_rad,0.1,0.053800
phase_rms_rad,0.2,0.056600
1 impairment level legit_ser
2 amplitude_nmse 0 0.053300
3 amplitude_nmse 0.001 0.052325
4 amplitude_nmse 0.01 0.053213
5 amplitude_nmse 0.03 0.052790
6 amplitude_nmse 0.1 0.052969
7 phase_rms_rad 0 0.053000
8 phase_rms_rad 0.02 0.052800
9 phase_rms_rad 0.05 0.052500
10 phase_rms_rad 0.1 0.053800
11 phase_rms_rad 0.2 0.056600
+6
View File
@@ -0,0 +1,6 @@
snr_db,mi_legit_bits,mi_eve_fixed_bits,equiv_ratio_fixed,tv_fixed,secrecy_rate_fixed_bits,mi_eve_refresh_bits,equiv_ratio_refresh,tv_refresh,secrecy_rate_refresh_bits
0.0,9.9561,0.653799,0.959136,0.186184,9.3023,0.022776,0.998576,0.036130,9.9334
5.0,13.3042,1.036897,0.935192,0.234795,12.2673,0.039079,0.997557,0.047628,13.2651
10.0,14.9300,1.344006,0.915999,0.266709,13.5860,0.055213,0.996548,0.056616,14.8748
15.0,15.6143,1.531415,0.904285,0.284074,14.0829,0.066127,0.995866,0.061766,15.5482
20.0,15.8604,1.631648,0.898021,0.292605,14.2287,0.070091,0.995618,0.063096,15.7903
1 snr_db mi_legit_bits mi_eve_fixed_bits equiv_ratio_fixed tv_fixed secrecy_rate_fixed_bits mi_eve_refresh_bits equiv_ratio_refresh tv_refresh secrecy_rate_refresh_bits
2 0.0 9.9561 0.653799 0.959136 0.186184 9.3023 0.022776 0.998576 0.036130 9.9334
3 5.0 13.3042 1.036897 0.935192 0.234795 12.2673 0.039079 0.997557 0.047628 13.2651
4 10.0 14.9300 1.344006 0.915999 0.266709 13.5860 0.055213 0.996548 0.056616 14.8748
5 15.0 15.6143 1.531415 0.904285 0.284074 14.0829 0.066127 0.995866 0.061766 15.5482
6 20.0 15.8604 1.631648 0.898021 0.292605 14.2287 0.070091 0.995618 0.063096 15.7903
+8
View File
@@ -0,0 +1,8 @@
space,snr_db,legit,outsider,insider,chance
codeword,0.0,0.7866,-0.0164,0.0008,-0.0000
codeword,10.0,0.9730,-0.0238,0.0000,-0.0000
codeword,20.0,0.9972,-0.0262,-0.0001,-0.0000
bert,0.0,0.7051,0.2576,0.2482,0.2579
bert,10.0,0.9619,0.2498,0.2451,0.2579
bert,20.0,0.9962,0.2471,0.2447,0.2579
bert,28.0,0.9991,0.2466,0.2447,0.2579
1 space snr_db legit outsider insider chance
2 codeword 0.0 0.7866 -0.0164 0.0008 -0.0000
3 codeword 10.0 0.9730 -0.0238 0.0000 -0.0000
4 codeword 20.0 0.9972 -0.0262 -0.0001 -0.0000
5 bert 0.0 0.7051 0.2576 0.2482 0.2579
6 bert 10.0 0.9619 0.2498 0.2451 0.2579
7 bert 20.0 0.9962 0.2471 0.2447 0.2579
8 bert 28.0 0.9991 0.2466 0.2447 0.2579
+7
View File
@@ -0,0 +1,7 @@
users,legit_ser,eve_ser,mask_xcorr
2,0.026755,0.999999,0.000000
4,0.053062,0.999707,0.000000
8,0.106523,0.999414,0.000000
16,0.256325,0.999948,0.000000
32,0.946055,0.999983,0.000000
48,0.997737,0.999983,0.000000
1 users legit_ser eve_ser mask_xcorr
2 2 0.026755 0.999999 0.000000
3 4 0.053062 0.999707 0.000000
4 8 0.106523 0.999414 0.000000
5 16 0.256325 0.999948 0.000000
6 32 0.946055 0.999983 0.000000
7 48 0.997737 0.999983 0.000000
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.