refactor: remove emojis from titles and buttons for a cleaner UI
All checks were successful
Build & Deploy Shannon / 🏗️ Build & Deploy Shannon (push) Successful in 1m3s
All checks were successful
Build & Deploy Shannon / 🏗️ Build & Deploy Shannon (push) Successful in 1m3s
This commit is contained in:
16
app.py
16
app.py
@@ -17,7 +17,7 @@ import streamlit as st
|
||||
|
||||
st.set_page_config(
|
||||
page_title="Shannon's Equation for Dummies",
|
||||
page_icon="📡",
|
||||
page_icon="antenna_bars",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded",
|
||||
)
|
||||
@@ -104,7 +104,7 @@ st.markdown("""
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
with st.sidebar:
|
||||
st.markdown("## 📡 Shannon for Dummies")
|
||||
st.markdown("## Shannon for Dummies")
|
||||
st.caption("Educational Application — AP Feb 2021")
|
||||
st.divider()
|
||||
|
||||
@@ -112,12 +112,12 @@ with st.sidebar:
|
||||
"Navigation",
|
||||
options=["animation", "orbits", "sat_types", "theory", "real_world", "contributions"],
|
||||
format_func=lambda x: {
|
||||
"animation": "📡 Satellite Link Animation",
|
||||
"orbits": "🌍 GEO / MEO / LEO Orbits",
|
||||
"sat_types": "🛰️ Satellite Missions & Types",
|
||||
"theory": "🧮 Theoretical Exploration",
|
||||
"real_world": "🛰️ Real World Link Budget",
|
||||
"contributions": "💬 Community Contributions",
|
||||
"animation": "Satellite Link Animation",
|
||||
"orbits": "GEO / MEO / LEO Orbits",
|
||||
"sat_types": "Satellite Missions & Types",
|
||||
"theory": "Theoretical Exploration",
|
||||
"real_world": "Real World Link Budget",
|
||||
"contributions": "Community Contributions",
|
||||
}[x],
|
||||
label_visibility="collapsed",
|
||||
)
|
||||
|
||||
@@ -101,7 +101,7 @@ THEORY_HELP = {
|
||||
"- Try multiple values in all the fields one by one\n"
|
||||
"- Explore the graphs and try to understand the underlying physics\n"
|
||||
"- The units are as explicit as possible to facilitate the exploration\n"
|
||||
"- Click on the ℹ️ icons to get information about each parameter"
|
||||
"- Click on the info icons to get information about each parameter"
|
||||
),
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ REAL_WORLD_HELP = {
|
||||
"The position of the ground station affects link availability due to weather statistics "
|
||||
"(tropical regions have very heavy rains attenuating signals at high frequencies). "
|
||||
"It also impacts the elevation angle and overall path length.\n\n"
|
||||
"🔗 [Find coordinates](https://www.gps-coordinates.net)"
|
||||
"[Find coordinates](https://www.gps-coordinates.net)"
|
||||
),
|
||||
"availability": (
|
||||
"**Link Availability [%]**\n\n"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -11,17 +11,17 @@ from core.database import write_contribution, search_contributions, delete_contr
|
||||
def render():
|
||||
"""Render the Contributions page."""
|
||||
|
||||
st.markdown("# 💬 Community Contributions")
|
||||
st.markdown("# Community Contributions")
|
||||
st.markdown(
|
||||
"Share your observations about Shannon's theorem, satellite communications, "
|
||||
"or suggest improvements. Contributions are stored locally and shared with all users."
|
||||
)
|
||||
|
||||
tab_read, tab_write = st.tabs(["📖 Read Contributions", "✍️ Write Contribution"])
|
||||
tab_read, tab_write = st.tabs(["Read Contributions", "Write Contribution"])
|
||||
|
||||
# ── Read Contributions ──
|
||||
with tab_read:
|
||||
st.markdown("### 🔍 Search Contributions")
|
||||
st.markdown("### Search Contributions")
|
||||
|
||||
db_choice = st.radio(
|
||||
"Database",
|
||||
@@ -39,7 +39,7 @@ def render():
|
||||
kw_filter = st.text_input("Filter by Keywords", key="filter_kw")
|
||||
content_filter = st.text_input("Filter by Content", key="filter_content")
|
||||
|
||||
if st.button("🔍 Search", type="primary", key="btn_search"):
|
||||
if st.button("Search", type="primary", key="btn_search"):
|
||||
results = search_contributions(
|
||||
db_name,
|
||||
name_filter=name_filter,
|
||||
@@ -60,10 +60,10 @@ def render():
|
||||
):
|
||||
st.markdown(contrib["text"])
|
||||
if contrib["keywords"]:
|
||||
st.caption(f"🏷️ Keywords: {contrib['keywords']}")
|
||||
st.caption(f"Keywords: {contrib['keywords']}")
|
||||
|
||||
# Delete functionality
|
||||
with st.popover("🗑️ Delete"):
|
||||
with st.popover("Delete"):
|
||||
st.warning("This action cannot be undone.")
|
||||
del_password = st.text_input(
|
||||
"Enter contribution password",
|
||||
@@ -72,7 +72,7 @@ def render():
|
||||
)
|
||||
if st.button("Confirm Delete", key=f"del_btn_{contrib['num']}"):
|
||||
if delete_contribution(db_name, contrib["num"], del_password):
|
||||
st.success(f"✅ Contribution #{contrib['num']} deleted.")
|
||||
st.success(f"Contribution #{contrib['num']} deleted.")
|
||||
# Refresh results
|
||||
st.session_state["contrib_results"] = search_contributions(
|
||||
db_name,
|
||||
@@ -83,13 +83,13 @@ def render():
|
||||
)
|
||||
st.rerun()
|
||||
else:
|
||||
st.error("❌ Incorrect password or contribution not found.")
|
||||
st.error("Incorrect password or contribution not found.")
|
||||
elif "contrib_results" in st.session_state:
|
||||
st.info("No contributions found matching your filters.")
|
||||
|
||||
# ── Write Contribution ──
|
||||
with tab_write:
|
||||
st.markdown("### ✍️ New Contribution")
|
||||
st.markdown("### New Contribution")
|
||||
|
||||
db_choice_w = st.radio(
|
||||
"Database",
|
||||
@@ -109,17 +109,16 @@ def render():
|
||||
type="password",
|
||||
)
|
||||
|
||||
submitted = st.form_submit_button("📤 Submit", type="primary")
|
||||
submitted = st.form_submit_button("Submit", type="primary")
|
||||
|
||||
if submitted:
|
||||
if not name or not title or not text:
|
||||
st.error("❌ Please fill in all required fields (marked with *).")
|
||||
st.error("Please fill in all required fields (marked with *).")
|
||||
else:
|
||||
new_id = write_contribution(db_name_w, name, title, keywords, text, password)
|
||||
st.success(f"✅ Thank you! Your contribution has been stored with ID #{new_id}.")
|
||||
st.balloons()
|
||||
st.success(f"Thank you. Your contribution has been stored with ID #{new_id}.")
|
||||
|
||||
with st.expander("❓ Help"):
|
||||
with st.expander("Help"):
|
||||
st.markdown(
|
||||
"Write your contribution as a free text. Contributions should be:\n\n"
|
||||
"- Candid observations about the technical subject\n"
|
||||
|
||||
@@ -66,7 +66,7 @@ _ORBITS_HTML = """
|
||||
<canvas id="c"></canvas>
|
||||
|
||||
<div id="info">
|
||||
<h3>🛰️ Orbit Comparison</h3>
|
||||
<h3>Orbit Comparison</h3>
|
||||
<div id="infoContent">
|
||||
<p style="color:#94a3b8; text-align:center;">
|
||||
Hover over a satellite to see its details.
|
||||
@@ -226,8 +226,8 @@ function showInfo(o) {
|
||||
<div class="stat"><span class="label">FSPL</span><span class="value">${o.fsplDb}</span></div>
|
||||
<div class="stat"><span class="label">Coverage</span><span class="value">${o.coverage}</span></div>
|
||||
<hr>
|
||||
<div class="stat"><span class="label">✅ Pros</span><span class="value" style="text-align:right; max-width:160px">${o.pros}</span></div>
|
||||
<div class="stat"><span class="label">⚠️ Cons</span><span class="value" style="text-align:right; max-width:160px">${o.cons}</span></div>
|
||||
<div class="stat"><span class="label">Pros</span><span class="value" style="text-align:right; max-width:160px">${o.pros}</span></div>
|
||||
<div class="stat"><span class="label">Cons</span><span class="value" style="text-align:right; max-width:160px">${o.cons}</span></div>
|
||||
<hr>
|
||||
<div class="stat"><span class="label">Examples</span><span class="value" style="color:#4FC3F7">${o.examples}</span></div>
|
||||
`;
|
||||
@@ -489,7 +489,7 @@ draw();
|
||||
|
||||
def render():
|
||||
"""Render the GEO/MEO/LEO orbit comparison animation."""
|
||||
st.markdown("## 🌍 Satellite Orbits — GEO vs MEO vs LEO")
|
||||
st.markdown("## Satellite Orbits — GEO vs MEO vs LEO")
|
||||
st.markdown(
|
||||
"Compare the three main satellite orbit types. "
|
||||
"Hover over satellites or legend items to explore their characteristics."
|
||||
@@ -500,50 +500,50 @@ def render():
|
||||
|
||||
# ── Educational content below ──
|
||||
st.divider()
|
||||
st.markdown("### 📊 Orbit Comparison at a Glance")
|
||||
st.markdown("### Orbit Comparison at a Glance")
|
||||
|
||||
col1, col2, col3 = st.columns(3)
|
||||
|
||||
with col1:
|
||||
st.markdown("""
|
||||
#### 🟢 LEO — Low Earth Orbit
|
||||
#### LEO — Low Earth Orbit
|
||||
**160 – 2 000 km**
|
||||
|
||||
- ⚡ Latency: **4 – 20 ms** (RTT)
|
||||
- 📉 FSPL: ~155 dB (Ku-band)
|
||||
- 🔄 Period: ~90 – 120 min
|
||||
- 📡 Small footprint → **large constellations** needed (hundreds to thousands)
|
||||
- 🤝 Requires **handover** between satellites
|
||||
- 🛰️ *Starlink (550 km), OneWeb (1 200 km), Iridium (780 km)*
|
||||
- Latency: **4 – 20 ms** (RTT)
|
||||
- FSPL: ~155 dB (Ku-band)
|
||||
- Period: ~90 – 120 min
|
||||
- Small footprint → **large constellations** needed (hundreds to thousands)
|
||||
- Requires **handover** between satellites
|
||||
- *Starlink (550 km), OneWeb (1 200 km), Iridium (780 km)*
|
||||
""")
|
||||
|
||||
with col2:
|
||||
st.markdown("""
|
||||
#### 🟡 MEO — Medium Earth Orbit
|
||||
#### MEO — Medium Earth Orbit
|
||||
**2 000 – 35 786 km**
|
||||
|
||||
- ⚡ Latency: **40 – 80 ms** (RTT)
|
||||
- 📉 FSPL: ~186 dB (Ku-band)
|
||||
- 🔄 Period: ~2 – 12 h
|
||||
- 📡 Medium footprint → **medium constellations** (20 – 50 sats)
|
||||
- 🌍 Good balance between coverage and latency
|
||||
- 🛰️ *GPS (20 200 km), Galileo, O3b/SES (8 000 km)*
|
||||
- Latency: **40 – 80 ms** (RTT)
|
||||
- FSPL: ~186 dB (Ku-band)
|
||||
- Period: ~2 – 12 h
|
||||
- Medium footprint → **medium constellations** (20 – 50 sats)
|
||||
- Good balance between coverage and latency
|
||||
- *GPS (20 200 km), Galileo, O3b/SES (8 000 km)*
|
||||
""")
|
||||
|
||||
with col3:
|
||||
st.markdown("""
|
||||
#### 🔴 GEO — Geostationary Orbit
|
||||
#### GEO — Geostationary Orbit
|
||||
**35 786 km (fixed)**
|
||||
|
||||
- ⚡ Latency: **240 – 280 ms** (RTT)
|
||||
- 📉 FSPL: ~205 dB (Ku-band)
|
||||
- 🔄 Period: 23 h 56 min (= 1 sidereal day)
|
||||
- 📡 Huge footprint → **3 sats = global** coverage
|
||||
- 📌 **Fixed position** in the sky — no tracking needed
|
||||
- 🛰️ *Intelsat, SES, Eutelsat, ViaSat*
|
||||
- Latency: **240 – 280 ms** (RTT)
|
||||
- FSPL: ~205 dB (Ku-band)
|
||||
- Period: 23 h 56 min (= 1 sidereal day)
|
||||
- Huge footprint → **3 sats = global** coverage
|
||||
- **Fixed position** in the sky — no tracking needed
|
||||
- *Intelsat, SES, Eutelsat, ViaSat*
|
||||
""")
|
||||
|
||||
with st.expander("🔬 Key Trade-offs in Detail"):
|
||||
with st.expander("Key Trade-offs in Detail"):
|
||||
st.markdown(r"""
|
||||
| Parameter | LEO | MEO | GEO |
|
||||
|:---|:---:|:---:|:---:|
|
||||
@@ -569,7 +569,7 @@ for the same bandwidth and transmit power. LEO compensates with lower FSPL
|
||||
but requires more satellites and complex handover.
|
||||
""")
|
||||
|
||||
with st.expander("🛰️ Notable Constellations"):
|
||||
with st.expander("Notable Constellations"):
|
||||
st.markdown("""
|
||||
| Constellation | Orbit | Altitude | # Satellites | Use Case |
|
||||
|:---|:---:|:---:|:---:|:---|
|
||||
|
||||
@@ -159,21 +159,21 @@ def render():
|
||||
with col_img:
|
||||
st.image("Satellite.png", width=200)
|
||||
with col_title:
|
||||
st.markdown("# 🛰️ Shannon & Friends in the Real World")
|
||||
st.markdown("# Shannon & Friends in the Real World")
|
||||
st.markdown("From theory to satellite communication link budget.")
|
||||
|
||||
wiki_cols = st.columns(4)
|
||||
wiki_cols[0].link_button("📖 Harry Nyquist", "https://en.wikipedia.org/wiki/Harry_Nyquist")
|
||||
wiki_cols[1].link_button("📖 Richard Hamming", "https://en.wikipedia.org/wiki/Richard_Hamming")
|
||||
wiki_cols[2].link_button("📖 Andrew Viterbi", "https://en.wikipedia.org/wiki/Andrew_Viterbi")
|
||||
wiki_cols[3].link_button("📖 Claude Berrou", "https://en.wikipedia.org/wiki/Claude_Berrou")
|
||||
wiki_cols[0].link_button("Harry Nyquist", "https://en.wikipedia.org/wiki/Harry_Nyquist")
|
||||
wiki_cols[1].link_button("Richard Hamming", "https://en.wikipedia.org/wiki/Richard_Hamming")
|
||||
wiki_cols[2].link_button("Andrew Viterbi", "https://en.wikipedia.org/wiki/Andrew_Viterbi")
|
||||
wiki_cols[3].link_button("Claude Berrou", "https://en.wikipedia.org/wiki/Claude_Berrou")
|
||||
|
||||
st.divider()
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 1: Satellite Link
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
st.markdown("## 📡 Satellite Link")
|
||||
st.markdown("## Satellite Link")
|
||||
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
@@ -213,7 +213,7 @@ def render():
|
||||
try:
|
||||
sat_cir_list = [float(v.strip()) for v in sat_cir_input.split(",")]
|
||||
except ValueError:
|
||||
st.error("❌ Invalid C/I values.")
|
||||
st.error("Invalid C/I values.")
|
||||
return
|
||||
|
||||
# Compute satellite link
|
||||
@@ -224,11 +224,11 @@ def render():
|
||||
sat_alt, sat_lat, sat_lon, gs_lat, gs_lon, availability,
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"❌ Satellite link computation error: {e}")
|
||||
st.error(f"Satellite link computation error: {e}")
|
||||
return
|
||||
|
||||
# Display satellite link results
|
||||
st.markdown("#### 📊 Satellite Link Results")
|
||||
st.markdown("#### Satellite Link Results")
|
||||
r1, r2, r3 = st.columns(3)
|
||||
r1.metric("Output Power", fmt_power(sat["sig_power"]), help=REAL_WORLD_HELP["output_power"])
|
||||
r2.metric("Antenna Gain", fmt_gain(sat["sat_gain_linear"]), help=REAL_WORLD_HELP["sat_gain"])
|
||||
@@ -245,14 +245,14 @@ def render():
|
||||
st.metric("Power Flux Density", fmt_pfd(sat["pfd_linear"]), help=REAL_WORLD_HELP["pfd"])
|
||||
|
||||
if sat["elevation"] <= 0:
|
||||
st.warning("⚠️ Satellite is below the horizon (negative elevation). Results may not be meaningful.")
|
||||
st.warning("Satellite is below the horizon (negative elevation). Results may not be meaningful.")
|
||||
|
||||
st.divider()
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 2: Radio Front End
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
st.markdown("## 📻 Radio Front End")
|
||||
st.markdown("## Radio Front End")
|
||||
|
||||
col_r1, col_r2 = st.columns(2)
|
||||
with col_r1:
|
||||
@@ -268,10 +268,10 @@ def render():
|
||||
cpe_ant_d, cpe_t_clear,
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"❌ Receiver computation error: {e}")
|
||||
st.error(f"Receiver computation error: {e}")
|
||||
return
|
||||
|
||||
st.markdown("#### 📊 Receiver Results")
|
||||
st.markdown("#### Receiver Results")
|
||||
rx1, rx2 = st.columns(2)
|
||||
rx1.metric("Antenna Area · G/T", f"{rcv['cpe_ae']:.2f} m² · {rcv['cpe_g_t']:.1f} dB/K",
|
||||
help=REAL_WORLD_HELP["cpe_gain"])
|
||||
@@ -290,7 +290,7 @@ def render():
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 3: Baseband Unit
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
st.markdown("## 💻 Baseband Unit")
|
||||
st.markdown("## Baseband Unit")
|
||||
|
||||
col_b1, col_b2, col_b3 = st.columns(3)
|
||||
with col_b1:
|
||||
@@ -310,7 +310,7 @@ def render():
|
||||
try:
|
||||
cnr_imp_list = [float(v.strip()) for v in cir_input.split(",")]
|
||||
except ValueError:
|
||||
st.error("❌ Invalid C/I values.")
|
||||
st.error("Invalid C/I values.")
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -319,10 +319,10 @@ def render():
|
||||
sat["sat_cir"], bandwidth, rolloff, overheads, cnr_imp_list, penalties,
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"❌ Baseband computation error: {e}")
|
||||
st.error(f"Baseband computation error: {e}")
|
||||
return
|
||||
|
||||
st.markdown("#### 📊 Baseband Results")
|
||||
st.markdown("#### Baseband Results")
|
||||
|
||||
b1, b2, b3 = st.columns(3)
|
||||
b1.metric("SNR in Available BW", f"{bb['cnr_bw']:.1f} dB in {bandwidth:.1f} MHz",
|
||||
@@ -336,7 +336,7 @@ def render():
|
||||
|
||||
b4, b5 = st.columns(2)
|
||||
with b4:
|
||||
st.markdown("##### 🎯 Theoretical")
|
||||
st.markdown("##### Theoretical")
|
||||
st.metric(
|
||||
"Theoretical BR",
|
||||
f"{fmt_br(bb['br_nyq'])} · {bb['br_nyq_norm']:.0%}",
|
||||
@@ -344,7 +344,7 @@ def render():
|
||||
)
|
||||
st.caption(f"Spectral Eff: {bb['spe_nyq']:.2f} bps/Hz · {bb['bits_per_symbol']:.2f} b/Symbol")
|
||||
with b5:
|
||||
st.markdown("##### 🏭 Practical")
|
||||
st.markdown("##### Practical")
|
||||
st.metric(
|
||||
"Physical Layer BR",
|
||||
f"{fmt_br(bb['br_rcv'])} · {bb['br_rcv_norm']:.0%}",
|
||||
@@ -360,12 +360,12 @@ def render():
|
||||
st.divider()
|
||||
|
||||
# ── Graphs ──
|
||||
st.markdown("### 📈 Interactive Graphs")
|
||||
st.markdown("### Interactive Graphs")
|
||||
|
||||
tab_bw, tab_pow, tab_map = st.tabs([
|
||||
"📶 BW Sensitivity",
|
||||
"⚡ Power Sensitivity",
|
||||
"🗺️ BR Factor Map",
|
||||
"BW Sensitivity",
|
||||
"Power Sensitivity",
|
||||
"BR Factor Map",
|
||||
])
|
||||
|
||||
cnr_imp = combine_cnr(*cnr_imp_list)
|
||||
|
||||
@@ -78,7 +78,7 @@ body{background:transparent;overflow:hidden;font-family:'Segoe UI',system-ui,san
|
||||
<canvas id="c"></canvas>
|
||||
|
||||
<div id="tabs"></div>
|
||||
<div id="info"><h3>🛰️ Satellite Missions</h3><p style="color:#64748b;text-align:center;padding:12px 0">Select a mission type above<br>to explore its animation.</p></div>
|
||||
<div id="info"><h3>Satellite Missions</h3><p style="color:#64748b;text-align:center;padding:12px 0">Select a mission type above<br>to explore its animation.</p></div>
|
||||
<div id="sceneLabel"><span>Mission Control</span> — choose a category</div>
|
||||
|
||||
<script>
|
||||
@@ -86,37 +86,37 @@ body{background:transparent;overflow:hidden;font-family:'Segoe UI',system-ui,san
|
||||
// DATA
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const cats=[
|
||||
{id:'nav',icon:'🧭',name:'Navigation',color:'#34d399',rgb:'52,211,153',
|
||||
{id:'nav',icon:'NAV',name:'Navigation',color:'#34d399',rgb:'52,211,153',
|
||||
orbit:'MEO',alt:'20 200 km',band:'L-band (1.2–1.6 GHz)',power:'~50 W/signal',
|
||||
precision:'< 1 m (dual-freq)',lifetime:'12–15 yrs',
|
||||
examples:['GPS (USA)','Galileo (EU)','GLONASS (RU)','BeiDou (CN)'],
|
||||
fact:'Each GPS satellite carries 4 atomic clocks accurate to ~1 ns. Relativity corrections of 38 μs/day are applied.',
|
||||
sceneHint:'Triangulation — 3 satellites fix your position'},
|
||||
{id:'com',icon:'📡',name:'Communication',color:'#4FC3F7',rgb:'79,195,247',
|
||||
{id:'com',icon:'COM',name:'Communication',color:'#4FC3F7',rgb:'79,195,247',
|
||||
orbit:'GEO + LEO',alt:'550–36 000 km',band:'C / Ku / Ka-band',power:'2–20 kW',
|
||||
precision:'100+ Gbps (HTS)',lifetime:'15–20 yrs',
|
||||
examples:['Starlink','Intelsat','SES','OneWeb'],
|
||||
fact:'A modern HTS can deliver 500+ Gbps — equivalent to 100 000 HD streams simultaneously.',
|
||||
sceneHint:'Data flowing between ground stations'},
|
||||
{id:'eo',icon:'📸',name:'Earth Observation',color:'#a78bfa',rgb:'167,139,250',
|
||||
{id:'eo',icon:'EO',name:'Earth Observation',color:'#a78bfa',rgb:'167,139,250',
|
||||
orbit:'LEO (SSO)',alt:'500–800 km',band:'X-band (downlink)',power:'SAR + optical',
|
||||
precision:'0.3–30 m resolution',lifetime:'5–7 yrs',
|
||||
examples:['Sentinel','Landsat','Planet Doves','Pléiades'],
|
||||
fact:'Planet Labs\' 200+ Doves image the entire land surface every single day at 3 m resolution.',
|
||||
sceneHint:'Satellite scanning the terrain below'},
|
||||
{id:'wx',icon:'🌦️',name:'Weather',color:'#fbbf24',rgb:'251,191,36',
|
||||
{id:'wx',icon:'WX',name:'Weather',color:'#fbbf24',rgb:'251,191,36',
|
||||
orbit:'GEO + LEO',alt:'800–36 000 km',band:'L / S / Ka-band',power:'Imager + Sounder',
|
||||
precision:'Full disk every 10 min',lifetime:'10–15 yrs',
|
||||
examples:['Meteosat','GOES','Himawari','FengYun'],
|
||||
fact:'GOES-16 produces 3.6 TB of weather imagery per day across 16 spectral bands.',
|
||||
sceneHint:'Atmospheric monitoring & cloud tracking'},
|
||||
{id:'sci',icon:'🔭',name:'Science',color:'#f472b6',rgb:'244,114,182',
|
||||
{id:'sci',icon:'SCI',name:'Science',color:'#f472b6',rgb:'244,114,182',
|
||||
orbit:'Various (L2, LEO, HEO)',alt:'540 km – 1.5M km',band:'S / X / Ka-band',power:'Telescope + Spectrometer',
|
||||
precision:'28 Mbps (JWST)',lifetime:'5–20+ yrs',
|
||||
examples:['JWST','Hubble','Gaia','SOHO','Chandra'],
|
||||
fact:'JWST\'s 6.5 m gold mirror operates at −233 °C to see infrared light from the first galaxies, 13.5 billion years ago.',
|
||||
sceneHint:'Deep-space telescope view'},
|
||||
{id:'def',icon:'🛡️',name:'Defense',color:'#fb923c',rgb:'251,146,60',
|
||||
{id:'def',icon:'DEF',name:'Defense',color:'#fb923c',rgb:'251,146,60',
|
||||
orbit:'LEO / GEO / HEO',alt:'Variable',band:'UHF / SHF / EHF',power:'Anti-jam, encrypted',
|
||||
precision:'SIGINT + IMINT',lifetime:'7–15 yrs',
|
||||
examples:['SBIRS','WGS','Syracuse (FR)','Skynet (UK)'],
|
||||
@@ -155,7 +155,7 @@ cats.forEach(c=>{
|
||||
});
|
||||
|
||||
function resetInfo(){
|
||||
document.getElementById('info').innerHTML='<h3>🛰️ Satellite Missions</h3><p style="color:#64748b;text-align:center;padding:12px 0">Select a mission type above<br>to explore its animation.</p>';
|
||||
document.getElementById('info').innerHTML='<h3>Satellite Missions</h3><p style="color:#64748b;text-align:center;padding:12px 0">Select a mission type above<br>to explore its animation.</p>';
|
||||
}
|
||||
function showInfo(c){
|
||||
document.getElementById('info').innerHTML=
|
||||
@@ -170,7 +170,7 @@ function showInfo(c){
|
||||
'<div style="color:#64748b;font-size:10.5px;margin-bottom:4px">Notable systems:</div>'+
|
||||
'<div>'+c.examples.map(function(e){return '<span class="tag">'+e+'</span>'}).join('')+'</div>'+
|
||||
'<hr class="sep">'+
|
||||
'<div class="fact"><b>💡 Did you know?</b><p>'+c.fact+'</p></div>';
|
||||
'<div class="fact"><b>Key fact:</b><p>'+c.fact+'</p></div>';
|
||||
}
|
||||
function updateLabel(){
|
||||
var el=document.getElementById('sceneLabel');
|
||||
@@ -791,7 +791,7 @@ frame();
|
||||
|
||||
def render():
|
||||
"""Render the satellite missions / types dashboard page."""
|
||||
st.markdown("## 🛰️ Satellite Missions — Types & Applications")
|
||||
st.markdown("## Satellite Missions — Types & Applications")
|
||||
st.markdown(
|
||||
"Explore **six categories** of satellite missions through unique animated scenes. "
|
||||
"Click a tab to switch between navigation, communication, observation, weather, "
|
||||
@@ -803,23 +803,23 @@ def render():
|
||||
|
||||
# ── Educational content below ──
|
||||
st.divider()
|
||||
st.markdown("### 📋 Mission Comparison")
|
||||
st.markdown("### Mission Comparison")
|
||||
|
||||
st.markdown("""
|
||||
| Category | Orbit | Altitude | Key Band | Examples |
|
||||
|:---|:---:|:---:|:---:|:---|
|
||||
| 🧭 **Navigation** | MEO | 20 200 km | L-band | GPS, Galileo, GLONASS, BeiDou |
|
||||
| 📡 **Communication** | GEO / LEO | 550 – 36 000 km | C / Ku / Ka | Starlink, Intelsat, SES |
|
||||
| 📸 **Earth Observation** | LEO (SSO) | 500 – 800 km | X-band | Sentinel, Landsat, Planet |
|
||||
| 🌦️ **Weather** | GEO / LEO | 800 – 36 000 km | L / Ka | Meteosat, GOES, Himawari |
|
||||
| 🔭 **Science** | Various | Variable | S / X / Ka | JWST, Hubble, Gaia |
|
||||
| 🛡️ **Defense** | LEO / GEO / HEO | Variable | UHF / EHF | SBIRS, WGS, Syracuse |
|
||||
| **Navigation** | MEO | 20 200 km | L-band | GPS, Galileo, GLONASS, BeiDou |
|
||||
| **Communication** | GEO / LEO | 550 – 36 000 km | C / Ku / Ka | Starlink, Intelsat, SES |
|
||||
| **Earth Observation** | LEO (SSO) | 500 – 800 km | X-band | Sentinel, Landsat, Planet |
|
||||
| **Weather** | GEO / LEO | 800 – 36 000 km | L / Ka | Meteosat, GOES, Himawari |
|
||||
| **Science** | Various | Variable | S / X / Ka | JWST, Hubble, Gaia |
|
||||
| **Defense** | LEO / GEO / HEO | Variable | UHF / EHF | SBIRS, WGS, Syracuse |
|
||||
""")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
with st.expander("🧭 Navigation Satellites"):
|
||||
with st.expander("Navigation Satellites"):
|
||||
st.markdown("""
|
||||
**How GNSS works:**
|
||||
|
||||
@@ -842,7 +842,7 @@ With 4+ satellites the receiver solves for $(x, y, z, \\Delta t)$ —
|
||||
- **BeiDou** (China) — 35+ sats
|
||||
""")
|
||||
|
||||
with st.expander("📡 Communication Satellites"):
|
||||
with st.expander("Communication Satellites"):
|
||||
st.markdown("""
|
||||
**Evolution:**
|
||||
|
||||
@@ -860,7 +860,7 @@ LEO at 550 km, but compensates with larger antennas, higher power
|
||||
and advanced modulation (DVB-S2X, 256-APSK).
|
||||
""")
|
||||
|
||||
with st.expander("🌦️ Weather Satellites"):
|
||||
with st.expander("Weather Satellites"):
|
||||
st.markdown("""
|
||||
**Two approaches:**
|
||||
|
||||
@@ -873,7 +873,7 @@ and advanced modulation (DVB-S2X, 256-APSK).
|
||||
""")
|
||||
|
||||
with col2:
|
||||
with st.expander("📸 Earth Observation Satellites"):
|
||||
with st.expander("Earth Observation Satellites"):
|
||||
st.markdown("""
|
||||
**Imaging technologies:**
|
||||
|
||||
@@ -889,7 +889,7 @@ and advanced modulation (DVB-S2X, 256-APSK).
|
||||
consistent solar illumination for temporal comparison.
|
||||
""")
|
||||
|
||||
with st.expander("🔭 Science & Exploration"):
|
||||
with st.expander("Science & Exploration"):
|
||||
st.markdown("""
|
||||
| Mission | Location | Purpose |
|
||||
|:---|:---:|:---|
|
||||
@@ -902,7 +902,7 @@ consistent solar illumination for temporal comparison.
|
||||
at ~160 **bits**/s with 23 W through NASA's 70 m DSN antennas.
|
||||
""")
|
||||
|
||||
with st.expander("🛡️ Defense & Intelligence"):
|
||||
with st.expander("Defense & Intelligence"):
|
||||
st.markdown("""
|
||||
**Categories:**
|
||||
|
||||
|
||||
@@ -146,17 +146,17 @@ def render():
|
||||
with col_img:
|
||||
st.image("Shannon.png", width=200)
|
||||
with col_title:
|
||||
st.markdown("# 📡 Shannon's Equation for Dummies")
|
||||
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.link_button("Wiki: Claude Shannon", "https://en.wikipedia.org/wiki/Claude_Shannon")
|
||||
|
||||
st.divider()
|
||||
|
||||
# ── Input Parameters ──
|
||||
st.markdown("### ⚙️ Input Parameters")
|
||||
st.markdown("### Input Parameters")
|
||||
|
||||
col_in1, col_in2 = st.columns(2)
|
||||
with col_in1:
|
||||
@@ -177,15 +177,15 @@ def render():
|
||||
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').")
|
||||
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="ℹ️") if st.checkbox("Show C/N₀ explanation", value=False) else None
|
||||
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"])
|
||||
@@ -202,7 +202,7 @@ def render():
|
||||
st.divider()
|
||||
|
||||
# ── Sensitivity Analysis ──
|
||||
st.markdown("### 🔬 Sensitivity Analysis")
|
||||
st.markdown("### Sensitivity Analysis")
|
||||
|
||||
col_s1, col_s2, col_s3 = st.columns(3)
|
||||
with col_s1:
|
||||
@@ -229,12 +229,12 @@ def render():
|
||||
st.divider()
|
||||
|
||||
# ── Graphs ──
|
||||
st.markdown("### 📈 Interactive Graphs")
|
||||
st.markdown("### Interactive Graphs")
|
||||
|
||||
tab_bw, tab_pow, tab_map = st.tabs([
|
||||
"📶 Bandwidth Sensitivity",
|
||||
"⚡ Power Sensitivity",
|
||||
"🗺️ BR Factor Map",
|
||||
"Bandwidth Sensitivity",
|
||||
"Power Sensitivity",
|
||||
"BR Factor Map",
|
||||
])
|
||||
|
||||
with tab_bw:
|
||||
@@ -256,14 +256,14 @@ def render():
|
||||
)
|
||||
|
||||
# ── Help Section ──
|
||||
with st.expander("📘 Background Information"):
|
||||
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",
|
||||
"shannon": "Shannon's Equation",
|
||||
"advanced": "Advanced (AWGN Model)",
|
||||
"help": "How to use this tool",
|
||||
}[x],
|
||||
)
|
||||
st.markdown(THEORY_HELP[help_topic])
|
||||
|
||||
Reference in New Issue
Block a user