All checks were successful
Build & Deploy Shannon / 🏗️ Build & Deploy Shannon (push) Successful in 1m3s
270 lines
8.2 KiB
Python
270 lines
8.2 KiB
Python
"""
|
|
Page 1: Shannon's Equation — Theoretical Exploration
|
|
|
|
Interactive exploration of the Shannon capacity formula with Plotly graphs.
|
|
"""
|
|
|
|
import streamlit as st
|
|
import numpy as np
|
|
import plotly.graph_objects as go
|
|
from math import log
|
|
|
|
from core.calculations import (
|
|
combine_cnr,
|
|
shannon_capacity,
|
|
shannon_points,
|
|
br_multiplier,
|
|
fmt_br,
|
|
)
|
|
from core.help_texts import THEORY_HELP
|
|
|
|
|
|
def _make_bw_sensitivity_plot(cnr_nyq: float, bw_nyq: float, c_n0: float) -> go.Figure:
|
|
"""Bandwidth sensitivity at constant power."""
|
|
n = 40
|
|
bw = np.zeros(n)
|
|
br = np.zeros(n)
|
|
cnr = np.zeros(n)
|
|
cnr[0] = cnr_nyq + 10 * log(8, 10)
|
|
bw[0] = bw_nyq / 8
|
|
br[0] = shannon_capacity(bw[0], cnr[0])
|
|
for i in range(1, n):
|
|
bw[i] = bw[i - 1] * 2 ** (1 / 6)
|
|
cnr[i] = cnr[i - 1] - 10 * log(bw[i] / bw[i - 1], 10)
|
|
br[i] = shannon_capacity(bw[i], cnr[i])
|
|
|
|
fig = go.Figure()
|
|
fig.add_trace(go.Scatter(
|
|
x=bw, y=br, mode="lines",
|
|
name="Shannon Capacity",
|
|
line=dict(color="#4FC3F7", width=3),
|
|
))
|
|
|
|
# Mark reference point
|
|
ref_br = shannon_capacity(bw_nyq, cnr_nyq)
|
|
fig.add_trace(go.Scatter(
|
|
x=[bw_nyq], y=[ref_br], mode="markers+text",
|
|
name=f"Reference: {bw_nyq:.1f} MHz, {ref_br:.1f} Mbps",
|
|
marker=dict(size=12, color="#FF7043", symbol="diamond"),
|
|
text=[f"{ref_br:.1f} Mbps"],
|
|
textposition="top center",
|
|
))
|
|
|
|
fig.update_layout(
|
|
title=f"Theoretical Bit Rate at Constant Power<br><sub>C/N₀ = {c_n0:.1f} MHz</sub>",
|
|
xaxis_title="Bandwidth [MHz]",
|
|
yaxis_title="Bit Rate [Mbps]",
|
|
template="plotly_dark",
|
|
height=500,
|
|
showlegend=True,
|
|
legend=dict(yanchor="bottom", y=0.02, xanchor="right", x=0.98),
|
|
)
|
|
return fig
|
|
|
|
|
|
def _make_power_sensitivity_plot(cnr_nyq: float, bw_nyq: float, cnr_linear: float) -> go.Figure:
|
|
"""Power sensitivity at constant bandwidth."""
|
|
n = 40
|
|
p_mul = np.zeros(n)
|
|
br = np.zeros(n)
|
|
cnr = np.zeros(n)
|
|
p_mul[0] = 1 / 8
|
|
cnr[0] = cnr_nyq - 10 * log(8, 10)
|
|
br[0] = shannon_capacity(bw_nyq, cnr[0])
|
|
for i in range(1, n):
|
|
p_mul[i] = p_mul[i - 1] * 2 ** (1 / 6)
|
|
cnr[i] = cnr[i - 1] + 10 * log(2 ** (1 / 6), 10)
|
|
br[i] = shannon_capacity(bw_nyq, cnr[i])
|
|
|
|
fig = go.Figure()
|
|
fig.add_trace(go.Scatter(
|
|
x=p_mul, y=br, mode="lines",
|
|
name="Shannon Capacity",
|
|
line=dict(color="#81C784", width=3),
|
|
))
|
|
|
|
# Reference point (multiplier = 1)
|
|
ref_br = shannon_capacity(bw_nyq, cnr_nyq)
|
|
fig.add_trace(go.Scatter(
|
|
x=[1.0], y=[ref_br], mode="markers+text",
|
|
name=f"Reference: 1x, {ref_br:.1f} Mbps",
|
|
marker=dict(size=12, color="#FF7043", symbol="diamond"),
|
|
text=[f"{ref_br:.1f} Mbps"],
|
|
textposition="top center",
|
|
))
|
|
|
|
fig.update_layout(
|
|
title=f"Theoretical Bit Rate at Constant Bandwidth: {bw_nyq:.1f} MHz<br>"
|
|
f"<sub>Reference: C/N = {cnr_linear:.1f} [Linear]</sub>",
|
|
xaxis_title="Power Multiplying Factor",
|
|
yaxis_title="Bit Rate [Mbps]",
|
|
template="plotly_dark",
|
|
height=500,
|
|
showlegend=True,
|
|
legend=dict(yanchor="bottom", y=0.02, xanchor="right", x=0.98),
|
|
)
|
|
return fig
|
|
|
|
|
|
def _make_br_factor_map(cnr_nyq: float, bw_nyq: float, c_n0: float, br_bw: float) -> go.Figure:
|
|
"""Contour map of BR multiplying factors."""
|
|
n = 41
|
|
bw_mul = np.zeros((n, n))
|
|
p_mul = np.zeros((n, n))
|
|
br_mul = np.zeros((n, n))
|
|
for i in range(n):
|
|
for j in range(n):
|
|
bw_mul[i, j] = (i + 1) / 8
|
|
p_mul[i, j] = (j + 1) / 8
|
|
br_mul[i, j] = br_multiplier(bw_mul[i, j], p_mul[i, j], cnr_nyq)
|
|
|
|
fig = go.Figure(data=go.Contour(
|
|
z=br_mul,
|
|
x=bw_mul[:, 0],
|
|
y=p_mul[0, :],
|
|
colorscale="Viridis",
|
|
contours=dict(showlabels=True, labelfont=dict(size=10, color="white")),
|
|
colorbar=dict(title="BR Factor"),
|
|
))
|
|
fig.update_layout(
|
|
title=f"Bit Rate Multiplying Factor<br><sub>Ref: C/N = {cnr_nyq:.1f} dB, "
|
|
f"BW = {bw_nyq:.1f} MHz, C/N₀ = {c_n0:.1f} MHz, "
|
|
f"BR = {br_bw:.1f} Mbps</sub>",
|
|
xaxis_title="Bandwidth Multiplying Factor",
|
|
yaxis_title="Power Multiplying Factor",
|
|
template="plotly_dark",
|
|
height=550,
|
|
)
|
|
return fig
|
|
|
|
|
|
def render():
|
|
"""Render the Theoretical Exploration page."""
|
|
|
|
# ── Header ──
|
|
col_img, col_title = st.columns([1, 3])
|
|
with col_img:
|
|
st.image("Shannon.png", width=200)
|
|
with col_title:
|
|
st.markdown("# Shannon's Equation for Dummies")
|
|
st.markdown(
|
|
"Exploration of Claude Shannon's channel capacity theorem — "
|
|
"the fundamental limit of digital communications."
|
|
)
|
|
st.link_button("Wiki: Claude Shannon", "https://en.wikipedia.org/wiki/Claude_Shannon")
|
|
|
|
st.divider()
|
|
|
|
# ── Input Parameters ──
|
|
st.markdown("### Input Parameters")
|
|
|
|
col_in1, col_in2 = st.columns(2)
|
|
with col_in1:
|
|
cnr_input = st.text_input(
|
|
"Reference C/N [dB]",
|
|
value="12",
|
|
help=THEORY_HELP["cnr"],
|
|
)
|
|
with col_in2:
|
|
bw_input = st.number_input(
|
|
"Reference BW [MHz]",
|
|
value=36.0, min_value=0.1, step=1.0,
|
|
help=THEORY_HELP["bw"],
|
|
)
|
|
|
|
# Parse CNR (supports comma-separated combinations)
|
|
try:
|
|
cnr_values = [float(v.strip()) for v in cnr_input.split(",")]
|
|
cnr_nyq = combine_cnr(*cnr_values)
|
|
except (ValueError, ZeroDivisionError):
|
|
st.error("Invalid C/N values. Use comma-separated numbers (e.g., '12' or '12, 15').")
|
|
return
|
|
|
|
# ── Computed Results ──
|
|
cnr_linear, br_inf, c_n0, br_bw = shannon_points(bw_input, cnr_nyq)
|
|
br_unit = c_n0 # Spectral efficiency = 1
|
|
|
|
st.markdown("### Results")
|
|
st.info(THEORY_HELP["c_n0"], icon=":material/info:") if st.checkbox("Show C/N₀ explanation", value=False) else None
|
|
|
|
m1, m2, m3, m4 = st.columns(4)
|
|
m1.metric("C/N₀", f"{c_n0:.1f} MHz", help=THEORY_HELP["c_n0"])
|
|
m2.metric("BR at ∞ BW", fmt_br(br_inf), help=THEORY_HELP["br_inf"])
|
|
m3.metric("BR at SpEff=1", fmt_br(br_unit), help=THEORY_HELP["br_unit"])
|
|
m4.metric("BR at Ref BW", fmt_br(br_bw), help=THEORY_HELP["br_bw"])
|
|
|
|
st.metric(
|
|
"C/N Ratio",
|
|
f"{cnr_nyq:.1f} dB · {cnr_linear:.1f} linear",
|
|
help=THEORY_HELP["cnr_lin"],
|
|
)
|
|
|
|
st.divider()
|
|
|
|
# ── Sensitivity Analysis ──
|
|
st.markdown("### Sensitivity Analysis")
|
|
|
|
col_s1, col_s2, col_s3 = st.columns(3)
|
|
with col_s1:
|
|
bw_mul_val = st.number_input(
|
|
"BW Increase Factor",
|
|
value=1.0, min_value=0.01, step=0.25,
|
|
help=THEORY_HELP["bw_mul"],
|
|
)
|
|
with col_s2:
|
|
p_mul_val = st.number_input(
|
|
"Power Increase Factor",
|
|
value=2.0, min_value=0.01, step=0.25,
|
|
help=THEORY_HELP["p_mul"],
|
|
)
|
|
with col_s3:
|
|
br_mul_val = br_multiplier(bw_mul_val, p_mul_val, cnr_nyq)
|
|
st.metric(
|
|
"Bit Rate Factor",
|
|
f"{br_mul_val:.3f}",
|
|
delta=f"{(br_mul_val - 1) * 100:+.1f}%",
|
|
help=THEORY_HELP["br_mul"],
|
|
)
|
|
|
|
st.divider()
|
|
|
|
# ── Graphs ──
|
|
st.markdown("### Interactive Graphs")
|
|
|
|
tab_bw, tab_pow, tab_map = st.tabs([
|
|
"Bandwidth Sensitivity",
|
|
"Power Sensitivity",
|
|
"BR Factor Map",
|
|
])
|
|
|
|
with tab_bw:
|
|
st.plotly_chart(
|
|
_make_bw_sensitivity_plot(cnr_nyq, bw_input, c_n0),
|
|
width="stretch",
|
|
)
|
|
|
|
with tab_pow:
|
|
st.plotly_chart(
|
|
_make_power_sensitivity_plot(cnr_nyq, bw_input, cnr_linear),
|
|
width="stretch",
|
|
)
|
|
|
|
with tab_map:
|
|
st.plotly_chart(
|
|
_make_br_factor_map(cnr_nyq, bw_input, c_n0, br_bw),
|
|
width="stretch",
|
|
)
|
|
|
|
# ── Help Section ──
|
|
with st.expander("Background Information"):
|
|
help_topic = st.selectbox(
|
|
"Choose a topic:",
|
|
options=["shannon", "advanced", "help"],
|
|
format_func=lambda x: {
|
|
"shannon": "Shannon's Equation",
|
|
"advanced": "Advanced (AWGN Model)",
|
|
"help": "How to use this tool",
|
|
}[x],
|
|
)
|
|
st.markdown(THEORY_HELP[help_topic])
|