Sync with the audited manuscript

The jammed OMA model now matches the transmit chain the paper
describes, the key-length sweep averages the eavesdropper over eight
substitute-key draws, and verify_math gains the coded-OMA outage
reference, symbolic checks of the three algebraic identities, and the
cross-period remainder of Proposition 2. The README records the energy
and channel conventions.
This commit is contained in:
KiHoLee
2026-08-22 14:47:17 +09:00
parent 0b0796df55
commit 58a85c30f6
17 changed files with 250 additions and 111 deletions
+14
View File
@@ -62,6 +62,15 @@ Seeds are fixed: training 1, evaluation 777, attacker key guess
comparison 11, key refresh 5150. Re-running reproduces the released CSV comparison 11, key refresh 5150. Re-running reproduces the released CSV
files. files.
## Conventions
Flat Rayleigh fading, one gain per user per frame, with unit mean power.
A frame carries unit energy, so the SNR in decibels is the frame energy
over the total noise across all `d` real dimensions, and every scheme in
a comparison spends the same energy, bandwidth and rate. The
jamming-to-signal ratio is the jammer energy over the same frame energy.
Logarithms in an entropy or an information rate are base two.
## Figure and table map ## Figure and table map
| Artifact | Script | Data | | Artifact | Script | Data |
@@ -77,6 +86,11 @@ files.
| Key family table | `exp_full.stage_D` | `sec_maskfam.csv`, `sec_regjam.csv` | | Key family table | `exp_full.stage_D` | `sec_maskfam.csv`, `sec_regjam.csv` |
| Headline recovery table | `exp_real_sec` | `real_sec_stats.json` | | Headline recovery table | `exp_real_sec` | `real_sec_stats.json` |
| Key refresh tables | `exp_refresh` | `refresh_summary.csv`, `refresh_kpa.csv` | | Key refresh tables | `exp_refresh` | `refresh_summary.csv`, `refresh_kpa.csv` |
| Information-theoretic leakage | `exp_infotheory` | `infotheory.csv` |
| Semantic similarity | `exp_semantic` | `semantic.csv` |
| Load and channel-estimate sweeps | `exp_users_csi` | `users.csv`, `csi.csv` |
| Permutation-variant check | `exp_full.stage_M` | `perm_variant.csv` |
| Closed-form and symbolic checks | `verify_math` | `verify_math.csv` |
## Security scope ## Security scope
+36 -2
View File
@@ -212,10 +212,44 @@ chk("learned support overlap 0.10",
round(float(md["learned"]["mean_overlap"]), 2) == 0.10, round(float(md["learned"]["mean_overlap"]), 2) == 0.10,
md["learned"]["mean_overlap"]) md["learned"]["mean_overlap"])
chk("degeneracy numbers in tex", chk("degeneracy numbers in tex",
"$5$ to $8$ of the $64$ entries" in tex and "overlap of\n$0.10$" in tex "$5$ to $8$ of the $64$ entries" in tex
or "$5$ to $8$ of the $64$ entries" in tex and "overlap of $0.10$" in tex, and "overlapping by $0.10$ on average over user pairs" in " ".join(tex.split()),
"searched tex", needs_tex=True) "searched tex", needs_tex=True)
# --- key-length sweep floor ------------------------------------------
# The eavesdropper column is an average over eight substitute-key draws,
# so the quoted floor must track the data and not one lucky draw.
kl = rows("sec_keylen.csv")
floor = min(float(r["eve_ser"]) for r in kl)
chk("eavesdropper floor over key length", abs(floor - 0.9984) < 5e-4,
"%.6f" % floor)
if HAVE_TEX:
chk("quoted eavesdropper floor in tex", "$0.9984$" in tex,
"searched tex", needs_tex=True)
# --- tables against their generator -----------------------------------
# Every printed table cell must be the one make_tables.py derives from
# data/, so a rerun that moves a number cannot leave the manuscript behind.
if HAVE_TEX:
import io
import contextlib
import make_tables
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
make_tables.compare_table()
make_tables.maskfam_table()
make_tables.refresh_tables()
rows = [r.strip() for r in buf.getvalue().split("\n")
if r.rstrip().endswith(r"\\")]
flat = " ".join(tex.split())
lost = [r for r in rows if " ".join(r.split()) not in flat]
chk("table rows match the generator", not lost,
"%d rows, %d missing" % (len(rows), len(lost)), needs_tex=True)
for r in lost:
print(" missing:", r[:78])
# --- abstract --------------------------------------------------------- # --- abstract ---------------------------------------------------------
a = (tex.split(r"\begin{abstract}")[1].split(r"\end{abstract}")[0].strip() a = (tex.split(r"\begin{abstract}")[1].split(r"\end{abstract}")[0].strip()
if HAVE_TEX else "") if HAVE_TEX else "")
-49
View File
@@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
"""Guard against mangled TeX control sequences.
Shell heredocs silently turn a backslash escape into the control
character it names, so \times becomes a tab followed by "imes" and \ref
becomes a carriage return followed by "ef". LaTeX compiles both without
an error and prints the wreckage, so the build log cannot catch this.
This scan can.
"""
import re
import sys
from pathlib import Path
TEX = Path(__file__).resolve().parents[1] / "main.tex"
CTRL = {"\t": "TAB", "\r": "CR", "\x08": "BS", "\x0c": "FF",
"\x07": "BEL", "\x0b": "VT", "\x00": "NUL"}
# a macro name shorn of its first letter, which is what the escape ate
STUBS = ["ef{", "abel{", "ite{", "extbf{", "extit{", "ext{", "imes",
"rac{", "eft(", "ight)", "ho_", "elta", "psilon", "ambda",
"igma", "ewline", "otag", "uad", "nderline", "ag{", "egin{",
"nd{", "aption{", "ilde{", "ar{", "at{", "ec{"]
PAT = re.compile("(?<![" + chr(92)*2 + "A-Za-z0-9])(" +
"|".join(re.escape(x) for x in STUBS) + ")")
def main():
if not TEX.exists():
print(" SKIP tex health :: main.tex not in this package")
return 0
lines = TEX.read_text(encoding="utf-8").split("\n")
hits = []
for i, line in enumerate(lines, 1):
for ch, name in CTRL.items():
if ch in line:
hits.append("CTRL %s line %d: %r" % (name, i, line[:90]))
for m in PAT.finditer(line):
seg = line[max(0, m.start() - 30):m.start() + 30]
hits.append("STUB %r line %d: %r" % (m.group(1), i, seg))
for h in hits:
print(" FAIL " + h)
if hits:
print("tex health: %d suspicious sequences" % len(hits))
return 1
print(" PASS tex health :: no mangled control sequences")
return 0
if __name__ == "__main__":
sys.exit(main())
+41 -9
View File
@@ -288,8 +288,15 @@ def stage_B():
for d in [32, 48, 64, 80, 96, 128, 192, 256]: for d in [32, 48, 64, 80, 96, 128, 192, 256]:
m = main_model(d=d) # same structured family as Fig. 2 m = main_model(d=d) # same structured family as Fig. 2
lg = eval_ser_sse(m, [10.0], frames=500_000)[0] lg = eval_ser_sse(m, [10.0], frames=500_000)[0]
ew = eve_wrong_mask(m.users, m.L, seed=20260813).to(DEVICE) # Proposition 1 is a statement about the substitute-key ENSEMBLE, so
ev = eval_ser_eve(m, ew, [10.0], frames=500_000)[0] # the eavesdropper is averaged over eight draws. A single draw makes
# the curve jump wherever one key happens to land luckily, which is
# a property of that draw and not of the key length.
ev = sum(eval_ser_eve(
m, eve_wrong_mask(m.users, m.L,
seed=20260813 + 101 * k).to(DEVICE),
[10.0], frames=500_000 // 8)[0]
for k in range(8)) / 8.0
xc = mean_abs_xcorr(m.masks().detach()) xc = mean_abs_xcorr(m.masks().detach())
oma = oma_ser_keylen(m.L, 10.0) oma = oma_ser_keylen(m.L, 10.0)
rows.append((m.L, d, lg, ev, xc, oma)) rows.append((m.L, d, lg, ev, xc, oma))
@@ -735,12 +742,15 @@ def oma_ser_jammed(snr_db, jsr_db_list, bits=16, U=4, d=256, n_grid=4096):
"""OMA under a jammer that concentrates on the victim's slots. """OMA under a jammer that concentrates on the victim's slots.
An OMA user occupies L = d/U exclusive real dimensions that are An OMA user occupies L = d/U exclusive real dimensions that are
public, and drives its 16 index bits on 16 of them with the whole public, and repeats each of its 16 index bits over L/bits of them,
allocation energy, an amplitude gain of sqrt(L/bits) per bit. A combining coherently for an amplitude gain of sqrt(L/bits) per bit.
jammer needs no key to put all of its power on those same public This spread allocation is the configuration that serves the OMA user
dimensions. With unit energy per real dimension and a total jammer best under a jammer, so it is the one the comparison grants it. A
energy of rho times the frame energy, concentrating on bits of the d jammer needs no key to find those public dimensions, but it must
dimensions gives a per-dimension jammer variance of (d/bits)*rho. cover all L of them. With unit energy per real dimension and a total
jammer energy of rho times the frame energy, spreading over L of the
d dimensions gives a per-dimension jammer variance of (d/L)*rho,
which is U*rho.
The jammer reaches the victim through its own Rayleigh channel, the The jammer reaches the victim through its own Rayleigh channel, the
same convention eval_scheme uses for every simulated scheme, so the same convention eval_scheme uses for every simulated scheme, so the
@@ -757,13 +767,35 @@ def oma_ser_jammed(snr_db, jsr_db_list, bits=16, U=4, d=256, n_grid=4096):
out = [] out = []
for jsr_db in jsr_db_list: for jsr_db in jsr_db_list:
rho = 10.0 ** (jsr_db / 10.0) rho = 10.0 ** (jsr_db / 10.0)
var = (1.0 / snr + (d / bits) * rho * hj2)[None, :] # (1,n) var = (1.0 / snr + U * rho * hj2)[None, :] # (1,n)
arg = (h * gain / var.sqrt()).clamp(0, 38) arg = (h * gain / var.sqrt()).clamp(0, 38)
pe = 0.5 * torch.erfc(arg / math.sqrt(2.0)) # per-bit error pe = 0.5 * torch.erfc(arg / math.sqrt(2.0)) # per-bit error
out.append(float((1.0 - (1.0 - pe) ** bits).mean())) out.append(float((1.0 - (1.0 - pe) ** bits).mean()))
return out return out
def stage_M():
"""Why the permutation-key scheme shares one permutation.
The manuscript asserts that a per-user permutation breaks the trained
separation, which is the reason the compared scheme is granted a
shared one. That assertion needs a measurement of its own.
"""
print("[M] shared against per-user permutation ...")
m = main_model()
d = m.P * m.L
F = 200_000
g = torch.Generator().manual_seed(11)
shared = torch.randperm(d, generator=g)[None].repeat(m.users, 1)
peruser = torch.stack([torch.randperm(d, generator=g)
for _ in range(m.users)])
rows = [("shared", eval_scheme(m, 10.0, F, perms=shared)),
("per_user", eval_scheme(m, 10.0, F, perms=peruser))]
for k, v in rows:
print(" %-9s legit=%.4f" % (k, v))
write_csv(DATA / "perm_variant.csv", ["variant", "legit_ser"], rows)
def stage_L(): def stage_L():
"""Jamming comparison across schemes at 10 dB. """Jamming comparison across schemes at 10 dB.
+28 -20
View File
@@ -34,16 +34,17 @@ FIG.mkdir(exist_ok=True)
plt.rcParams.update({ plt.rcParams.update({
"font.family": "serif", "font.family": "serif",
"font.serif": ["DejaVu Serif", "Times New Roman"], "font.serif": ["DejaVu Serif", "Times New Roman"],
# The manuscript includes each result figure at 0.70 of a 3.455 in # The manuscript includes each result figure at 0.74 of a 3.455 in
# column while the canvas is 3.15 in, a printed scale of 0.768. Every # column while the canvas is 3.15 in, a printed scale of 0.812. Every
# size below is therefore pre-divided by that scale so the PRINTED # size below is therefore pre-divided by that scale so the PRINTED
# sizes are 8 pt labels, 7 pt ticks and a 5.8 pt legend. Change the # sizes are 8 pt labels, 7.6 pt ticks and a 6 pt legend at the
# include width and these must change with it. # smallest rung. Change the include width and these must change
"font.size": 10.4, # with it.
"axes.labelsize": 10.4, "font.size": 9.9,
"legend.fontsize": 7.6, "axes.labelsize": 9.9,
"xtick.labelsize": 9.2, "legend.fontsize": 9.2,
"ytick.labelsize": 9.2, "xtick.labelsize": 9.4,
"ytick.labelsize": 9.4,
"axes.grid": True, "axes.grid": True,
"grid.linestyle": "--", "grid.linestyle": "--",
"grid.linewidth": 0.4, "grid.linewidth": 0.4,
@@ -67,7 +68,7 @@ LBL = {
"legit": "Legitimate", "legit": "Legitimate",
"oma": "OMA", "oma": "OMA",
"eve_pub": "Eavesdropper, public masks", "eve_pub": "Eavesdropper, public masks",
"eve_key": "Eavesdropper, wrong key", "eve_key": "Eavesdropper", # the wrong-key condition is in the caption
"chance": "Random guess", "chance": "Random guess",
"nojam": "No jammer", "nojam": "No jammer",
"mask": "Keyed masking", "mask": "Keyed masking",
@@ -204,7 +205,7 @@ def main_legit(snr_db="10"):
def place_legend(ax, cands=("lower left", "upper left", "center left", def place_legend(ax, cands=("lower left", "upper left", "center left",
"center right", "lower center", "upper right", "center right", "lower center", "upper right",
"upper center", "center", "lower right"), "upper center", "center", "lower right"),
sizes=(7.6, 7.2, 6.8, 6.4, 6.0), ncol=1): sizes=(9.2, 8.8, 8.4, 8.0, 7.6, 7.2), ncol=1):
"""Choose the location and font size whose box the fewest curve points """Choose the location and font size whose box the fewest curve points
fall inside, scored on rendered geometry rather than guessed from the fall inside, scored on rendered geometry rather than guessed from the
data. The size sweep is what makes a long label set placeable: a data. The size sweep is what makes a long label set placeable: a
@@ -227,7 +228,8 @@ def place_legend(ax, cands=("lower left", "upper left", "center left",
for loc in cands: for loc in cands:
leg = ax.legend(loc=loc, prop={"size": size}, ncol=ncol, leg = ax.legend(loc=loc, prop={"size": size}, ncol=ncol,
handlelength=1.4, columnspacing=0.9, handlelength=1.4, columnspacing=0.9,
handletextpad=0.5, borderaxespad=0.55) handletextpad=0.5, borderaxespad=0.55,
framealpha=1.0)
ax.figure.canvas.draw() ax.figure.canvas.draw()
lb = _inflate(leg.get_window_extent(), ax.figure) lb = _inflate(leg.get_window_extent(), ax.figure)
hits = 0 hits = 0
@@ -249,12 +251,13 @@ def place_legend(ax, cands=("lower left", "upper left", "center left",
if hits == 0: if hits == 0:
ax.legend(loc=loc, prop={"size": size}, ncol=ncol, ax.legend(loc=loc, prop={"size": size}, ncol=ncol,
handlelength=1.4, columnspacing=0.9, handlelength=1.4, columnspacing=0.9,
handletextpad=0.5, borderaxespad=0.55) handletextpad=0.5, borderaxespad=0.55,
framealpha=1.0)
PL_CHOSEN.append(size) PL_CHOSEN.append(size)
return best return best
ax.legend(loc=best[0], prop={"size": best[1]}, ncol=ncol, ax.legend(loc=best[0], prop={"size": best[1]}, ncol=ncol,
handlelength=1.4, columnspacing=0.9, handletextpad=0.5, handlelength=1.4, columnspacing=0.9, handletextpad=0.5,
borderaxespad=0.55) borderaxespad=0.55, framealpha=1.0)
PL_CHOSEN.append(best[1]) PL_CHOSEN.append(best[1])
return best return best
@@ -303,7 +306,7 @@ def fig_keylen():
marker="^", ls=":", label=LBL["oma"]) marker="^", ls=":", label=LBL["oma"])
ax.semilogy(x, col(r, "eve_ser"), color=C_EVE, marker="s", ls="--", ax.semilogy(x, col(r, "eve_ser"), color=C_EVE, marker="s", ls="--",
label=LBL["eve_key"]) label=LBL["eve_key"])
ax.set_ylim(top=6.0) # headroom above the flat eavesdropper curve ax.set_ylim(top=22.0) # headroom above the flat eavesdropper curve
ax.set_xlabel("Key length $L$") ax.set_xlabel("Key length $L$")
ax.set_ylabel("SER") ax.set_ylabel("SER")
ax.set_xscale("log", base=2) ax.set_xscale("log", base=2)
@@ -322,18 +325,23 @@ def fig_jam():
me = max(1, len(x) // 8) me = max(1, len(x) // 8)
fig, ax = plt.subplots() fig, ax = plt.subplots()
ax.plot(x, col(r, "matched"), color=C_MATCH, marker="P", ls="--", ax.plot(x, col(r, "matched"), color=C_MATCH, marker="P", ls="--",
markevery=me, label=LBL["mask"] + ", matched") markevery=me, label="Public masks")
ax.plot(x, col(r, "oma_targeted"), color=C_PUB, marker="^", ls=":", ax.plot(x, col(r, "oma_targeted"), color=C_PUB, marker="^", ls=":",
markevery=me, label=LBL["oma"] + ", targeted") markevery=me, label=LBL["oma"])
# the two blind curves agree to 0.002; deliberate layering # the two blind curves agree to 0.002; deliberate layering
ax.plot(x, col(r, "blind"), color=C_LEGIT, marker="o", ls="-", ax.plot(x, col(r, "blind"), color=C_LEGIT, marker="o", ls="-",
markevery=(0, me), label=LBL["mask"] + ", blind", **UNDER) markevery=(0, me), label=LBL["mask"], **UNDER)
ax.plot(x, col(r, "perm_blind"), color=C_EVE, marker="s", ls="-.", ax.plot(x, col(r, "perm_blind"), color=C_EVE, marker="s", ls="-.",
markevery=(me // 2, me), label=LBL["perm"] + ", blind", **OVER) markevery=(me // 2, me), label=LBL["perm"], **OVER)
nojam = float(load("sec_jam.csv")[0]["nojam"]) nojam = float(load("sec_jam.csv")[0]["nojam"])
# the unjammed reference is named in the caption rather than in the # the unjammed reference is named in the caption rather than in the
# legend, which keeps the folded legend two rows tall # legend, which keeps the folded legend two rows tall
ax.axhline(nojam, color=C_OMA, ls=(0, (1, 3)), lw=0.9) # Behind the legend rather than through it. The placement guard skips
# axis-spanning lines, so it cannot move the legend off this one, and
# a reference drawn along the legend frame reads as part of the box.
ax.axhline(nojam, color=C_OMA, ls=(0, (1, 3)), lw=0.9, zorder=0)
ax.set_ylim(-0.42, 1.05)
ax.set_yticks([0.0, 0.2, 0.4, 0.6, 0.8, 1.0])
ax.set_xlabel("JSR (dB)") ax.set_xlabel("JSR (dB)")
ax.set_ylabel("SER") ax.set_ylabel("SER")
ax.set_xlim(min(x), max(x)) ax.set_xlim(min(x), max(x))
+97
View File
@@ -188,6 +188,100 @@ def v5_matched_jammer_concentrates():
return ok return ok
def v6_coded_oma_outage():
"""The genie reference the manuscript concedes: an OMA user coding at
the Rayleigh outage limit of its own allocation.
The user owns L = d/U real dimensions, so d/(2U) complex uses, and
carries log2(V) bits. Its per-dimension SNR is the frame SNR, the
same convention snr_to_sigma2 sets. Outage is the probability that
the instantaneous mutual information falls below that rate."""
import math
U, d, V = 4, 256, 65536
for snr_db in (10.0,):
g = 10.0 ** (snr_db / 10.0)
uses = d / (2.0 * U) # complex channel uses
rate = math.log2(V) / uses # bits per complex use
thr = (2.0 ** rate - 1.0) / g # |h|^2 threshold
pout = 1.0 - math.exp(-thr) # |h|^2 ~ Exp(1)
print(f"V6 coded-OMA outage @ {snr_db:.0f} dB: {pout:.4f} "
f"(rate {rate:.3f} bit/use)")
ROWS.append(("V6 coded-OMA outage @ %.0f dB" % snr_db,
"%.4f" % pout, "%.6f" % pout, "0", "0", "REFERENCE"))
return True
def v7_symbolic_identities():
"""Symbolic verification of the three algebraic identities. Monte
Carlo cannot check an identity, only an instance of it."""
try:
import sympy as sp
except ImportError:
print("V7 symbolic: sympy not installed, SKIPPED")
ROWS.append(("V7 symbolic identities", "-", "-", "-", "-", "SKIPPED"))
return True
ok = True
# refresh entropy: L sign bits, an entry permutation, a user permutation
L, Uu = 64, 4
ent = sp.Integer(L) + sp.log(sp.factorial(L), 2) + sp.log(sp.factorial(Uu), 2)
ok &= abs(float(ent) - 364.6) < 0.05
print("V7a refresh entropy %.3f bits/block" % float(ent))
# fixed-key entropy: ordered choices of U of the L-1 non-constant rows
fixed = sp.log(sp.factorial(L - 1) / sp.factorial(L - 1 - Uu), 2)
ok &= abs(float(fixed) - 23.8) < 0.05
ok &= sp.factorial(L - 1) / sp.factorial(L - 1 - Uu) == 14295960
print("V7b fixed-key entropy %.3f bits, %d choices"
% (float(fixed), sp.factorial(L - 1) / sp.factorial(L - 1 - Uu)))
# the termwise Hadamard identity behind eq:hadamard
n = 4
e = sp.Matrix(sp.symbols("e1:%d" % (n + 1)))
m = sp.Matrix(sp.symbols("m1:%d" % (n + 1)))
f = sp.Matrix(sp.symbols("f1:%d" % (n + 1)))
lhs = sum((sp.matrix_multiply_elementwise(e, m))[k] *
(sp.matrix_multiply_elementwise(f, m))[k] for k in range(n))
rhs = (e.T * sp.diag(*[m[k] ** 2 for k in range(n)]) * f)[0]
ok &= sp.simplify(lhs - rhs) == 0
print("V7c (e*m).(f*m) == e^T diag(m^2) f :",
sp.simplify(lhs - rhs) == 0)
ROWS.append(("V7 symbolic identities", "exact", "exact", "0", "0",
"PASS" if ok else "FAIL"))
return ok
def v8_cross_period_terms():
"""Proposition 2 keeps only the diagonal of the jammer projection.
The periodic key makes entries one period apart identical, so the
cross-period terms do not vanish termwise. They are zero mean over
the codebook, which is the claim the proof rests on."""
import math
import torch
from exp_full import main_model
torch.manual_seed(7)
m = main_model()
Bn = m.unit_codebook().detach().cpu()
pat = m.masks().detach().cpu()[0]
L, P, d = m.L, m.P, m.d
rel = []
for _ in range(300):
w = torch.randn(d)
w /= w.norm()
i = torch.randint(m.vu, (P,))
e = (Bn[i] / math.sqrt(P)).reshape(-1)
a = (w * e).reshape(P, L) * pat[None, :]
diag = float((a ** 2).sum())
rel.append((float((a.sum(0) ** 2).sum()) - diag) / diag)
mean = sum(rel) / len(rel)
ok = abs(mean) < 0.01
print("V8 cross-period remainder, mean %+.4f of the retained term" % mean)
ROWS.append(("V8 cross-period remainder", "0.0", "%.6f" % mean,
"%.6f" % abs(mean), "0.01", "PASS" if ok else "FAIL"))
return ok
def main(): def main():
print(f"config d={D} U={U} V={V}\n") print(f"config d={D} U={U} V={V}\n")
results = { results = {
@@ -196,6 +290,9 @@ def main():
"V3": v3_leakage_vs_correlation(), "V3": v3_leakage_vs_correlation(),
"V4": v4_blind_jammer_spread(), "V4": v4_blind_jammer_spread(),
"V5": v5_matched_jammer_concentrates(), "V5": v5_matched_jammer_concentrates(),
"V6": v6_coded_oma_outage(),
"V7": v7_symbolic_identities(),
"V8": v8_cross_period_terms(),
} }
print("\nsummary:", {k: ("PASS" if v else "FAIL") for k, v in results.items()}) print("\nsummary:", {k: ("PASS" if v else "FAIL") for k, v in results.items()})
print("ALL PASS" if all(results.values()) else "SOME FAILED") print("ALL PASS" if all(results.values()) else "SOME FAILED")
+16 -16
View File
@@ -1,17 +1,17 @@
jsr_db,blind,matched,perm_blind,oma_targeted jsr_db,blind,matched,perm_blind,oma_targeted
-10,0.10064,0.37023,0.10146,0.5614379461 -10,0.10064,0.37023,0.10146,0.2950044518
-8,0.1249233333,0.4708266667,0.12691,0.65563829 -8,0.1249233333,0.4708266667,0.12691,0.3736020055
-6,0.1634766667,0.5763733333,0.16262,0.7407016397 -6,0.1634766667,0.5763733333,0.16262,0.4641613508
-4,0.214,0.6803433333,0.21294,0.8120662111 -4,0.214,0.6803433333,0.21294,0.5604432983
-2,0.2825866667,0.76744,0.2808,0.8681995366 -2,0.2825866667,0.76744,0.2808,0.6547042122
0,0.3646733333,0.8388366667,0.36478,0.9100289945 0,0.3646733333,0.8388366667,0.36478,0.7398901068
2,0.4608633333,0.8904066667,0.4600566667,0.9398716824 2,0.4608633333,0.8904066667,0.4600566667,0.8114085956
4,0.5610033333,0.92756,0.5599433333,0.9604546595 4,0.5610033333,0.92756,0.5599433333,0.8676974349
6,0.65865,0.9529333333,0.6570033333,0.9742944207 6,0.65865,0.9529333333,0.6570033333,0.9096638579
8,0.7445133333,0.96962,0.74325,0.9834284377 8,0.7445133333,0.96962,0.74325,0.9396161468
10,0.8155633333,0.9808933333,0.8162233333,0.9893770607 10,0.8155633333,0.9808933333,0.8162233333,0.9602809786
12,0.8720266667,0.98766,0.8727233333,0.9932152779 12,0.8720266667,0.98766,0.8727233333,0.9741788992
14,0.9134533333,0.99211,0.9136866667,0.9956760816 14,0.9134533333,0.99211,0.9136866667,0.9833527887
16,0.94315,0.9950333333,0.9424633333,0.9972471319 16,0.94315,0.9950333333,0.9424633333,0.989328064
18,0.96219,0.9969166667,0.96196,0.9982475299 18,0.96219,0.9969166667,0.96196,0.9931837836
20,0.9756633333,0.99808,0.97557,0.9988837869 20,0.9756633333,0.99808,0.97557,0.995655941
1 jsr_db blind matched perm_blind oma_targeted
2 -10 0.10064 0.37023 0.10146 0.5614379461 0.2950044518
3 -8 0.1249233333 0.4708266667 0.12691 0.65563829 0.3736020055
4 -6 0.1634766667 0.5763733333 0.16262 0.7407016397 0.4641613508
5 -4 0.214 0.6803433333 0.21294 0.8120662111 0.5604432983
6 -2 0.2825866667 0.76744 0.2808 0.8681995366 0.6547042122
7 0 0.3646733333 0.8388366667 0.36478 0.9100289945 0.7398901068
8 2 0.4608633333 0.8904066667 0.4600566667 0.9398716824 0.8114085956
9 4 0.5610033333 0.92756 0.5599433333 0.9604546595 0.8676974349
10 6 0.65865 0.9529333333 0.6570033333 0.9742944207 0.9096638579
11 8 0.7445133333 0.96962 0.74325 0.9834284377 0.9396161468
12 10 0.8155633333 0.9808933333 0.8162233333 0.9893770607 0.9602809786
13 12 0.8720266667 0.98766 0.8727233333 0.9932152779 0.9741788992
14 14 0.9134533333 0.99211 0.9136866667 0.9956760816 0.9833527887
15 16 0.94315 0.9950333333 0.9424633333 0.9972471319 0.989328064
16 18 0.96219 0.9969166667 0.96196 0.9982475299 0.9931837836
17 20 0.9756633333 0.99808 0.97557 0.9988837869 0.995655941
+8 -8
View File
@@ -1,9 +1,9 @@
L,d,legit_ser,eve_ser,mask_xcorr,oma L,d,legit_ser,eve_ser,mask_xcorr,oma
8,32,0.948557,0.997348,0,0.6849191155 8,32,0.948557,0.999577,0,0.6849191155
12,48,0.413714,0.999937,0,nan 12,48,0.413714,0.999816,0,nan
16,64,0.257299,0.9999905,0,0.2747696909 16,64,0.257299,0.999865,0,0.2747696909
20,80,0.1874125,0.9998835,0,0.2289444229 20,80,0.1874125,0.999368,0,0.2289444229
24,96,0.1522385,0.99938,0,0.1961714033 24,96,0.1522385,0.9997115,0,0.1961714033
32,128,0.107608,0.9997135,0,0.1524639978 32,128,0.107608,0.9997615,0,0.1524639978
48,192,0.0719285,0.9897345,0,0.1054308944 48,192,0.0719285,0.998383,0,0.1054308944
64,256,0.0530375,0.9997025,0,0.08056383667 64,256,0.0530375,0.9996495,0,0.08056383667
1 L d legit_ser eve_ser mask_xcorr oma
2 8 32 0.948557 0.997348 0.999577 0 0.6849191155
3 12 48 0.413714 0.999937 0.999816 0 nan
4 16 64 0.257299 0.9999905 0.999865 0 0.2747696909
5 20 80 0.1874125 0.9998835 0.999368 0 0.2289444229
6 24 96 0.1522385 0.99938 0.9997115 0 0.1961714033
7 32 128 0.107608 0.9997135 0.9997615 0 0.1524639978
8 48 192 0.0719285 0.9897345 0.998383 0 0.1054308944
9 64 256 0.0530375 0.9997025 0.9996495 0 0.08056383667
+7 -7
View File
@@ -1,7 +1,7 @@
users,legit_ser,eve_ser,mask_xcorr users,legit_ser,eve_ser,mask_xcorr,oma
2,0.026755,0.999999,0.000000 2,0.026755,0.999999,0.000000,0.041447
4,0.053062,0.999707,0.000000 4,0.053062,0.999707,0.000000,0.080564
8,0.106523,0.999414,0.000000 8,0.106523,0.999414,0.000000,0.152464
16,0.256325,0.999948,0.000000 16,0.256325,0.999948,0.000000,0.274770
32,0.946055,0.999983,0.000000 32,0.946055,0.999983,0.000000,0.684919
48,0.997737,0.999983,0.000000 48,0.997737,0.999983,0.000000,nan
1 users legit_ser eve_ser mask_xcorr oma
2 2 0.026755 0.999999 0.000000 0.041447
3 4 0.053062 0.999707 0.000000 0.080564
4 8 0.106523 0.999414 0.000000 0.152464
5 16 0.256325 0.999948 0.000000 0.274770
6 32 0.946055 0.999983 0.000000 0.684919
7 48 0.997737 0.999983 0.000000 nan
+3
View File
@@ -8,3 +8,6 @@ V2b eve SER @ 80dB,0.99609375,0.9896666666666667,0.0064270833333333055,0.015,PAS
V3b random mask E|corr|,0.09973557010035818,0.10187042771408686,0.002134857613728683,0.009973557010035819,PASS V3b random mask E|corr|,0.09973557010035818,0.10187042771408686,0.002134857613728683,0.009973557010035819,PASS
V4a blind jammer projection mean,0.0,0.00026396107284673695,0.00026396107284673695,0.003,PASS V4a blind jammer projection mean,0.0,0.00026396107284673695,0.00026396107284673695,0.003,PASS
V4b blind jammer projection variance,0.01558576233568703,0.015476787953278994,0.00010897438240803532,0.0007792881167843516,PASS V4b blind jammer projection variance,0.01558576233568703,0.015476787953278994,0.00010897438240803532,0.0007792881167843516,PASS
V6 coded-OMA outage @ 10 dB,0.0406,0.040575,0,0,REFERENCE
V7 symbolic identities,exact,exact,0,0,PASS
V8 cross-period remainder,0.0,-0.001285,0.001285,0.01,PASS
1 check claim empirical abs_err tol verdict
8 V3b random mask E|corr| 0.09973557010035818 0.10187042771408686 0.002134857613728683 0.009973557010035819 PASS
9 V4a blind jammer projection mean 0.0 0.00026396107284673695 0.00026396107284673695 0.003 PASS
10 V4b blind jammer projection variance 0.01558576233568703 0.015476787953278994 0.00010897438240803532 0.0007792881167843516 PASS
11 V6 coded-OMA outage @ 10 dB 0.0406 0.040575 0 0 REFERENCE
12 V7 symbolic identities exact exact 0 0 PASS
13 V8 cross-period remainder 0.0 -0.001285 0.001285 0.01 PASS
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.