-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
170 lines (132 loc) · 7.2 KB
/
Copy pathapp.py
File metadata and controls
170 lines (132 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import streamlit as st
import numpy as np
import plotly.graph_objects as go
import ai_inference
# --- PAGE CONFIG ---
st.set_page_config(page_title="AMC Deep Learning Simulator", page_icon="📡", layout="wide")
# --- SIDEBAR: PROJECT INFO ---
with st.sidebar:
st.image("https://cdn-icons-png.flaticon.com/512/2852/2852103.png", width=70)
st.markdown("## About the Simulator")
st.write(
"Think of this as a virtual testing ground. We are simulating a real radio environment where digital messages are beamed through thin air and get corrupted by heavy static. Instead of using traditional math to decode the static, we trained a deep neural network to 'look' at the raw, messy radio waves and instantly recognize the hidden physical patterns.")
st.divider()
st.markdown("## How it works")
st.write("**1. Generate:** We mathematically create 1,000 clean radio signal frames.")
st.write(
"**2. Corrupt:** We inject real-world static (Additive White Gaussian Noise) to simulate a bad connection.")
st.write("**3. Analyze:** The AI scans the noisy data to find hidden phase and amplitude patterns.")
st.write("**4. Vote:** The system tallies up 1,000 separate AI guesses to make one highly accurate final decision.")
st.divider()
st.caption("Developed by Shreyansh Yadav and Shrinidhi Walvekar | IIT Dharwad")
# --- SIGNAL PROCESSING MATH ---
@st.cache_data
def get_rrc(num_taps=33, alpha=0.35, sps=8):
t = np.arange(-num_taps // 2, num_taps // 2) / sps
rrc = np.zeros(num_taps)
for i in range(num_taps):
if t[i] == 0.0:
rrc[i] = 1.0 - alpha + (4 * alpha / np.pi)
elif alpha != 0 and (t[i] == 1 / (4 * alpha) or t[i] == -1 / (4 * alpha)):
rrc[i] = (alpha / np.sqrt(2)) * (((1 + 2 / np.pi) * np.sin(np.pi / (4 * alpha))) + (
(1 - 2 / np.pi) * np.cos(np.pi / (4 * alpha))))
else:
rrc[i] = (np.sin(np.pi * t[i] * (1 - alpha)) + 4 * alpha * t[i] * np.cos(np.pi * t[i] * (1 + alpha))) / (
np.pi * t[i] * (1 - (4 * alpha * t[i]) ** 2))
return rrc / np.sqrt(np.sum(rrc ** 2))
def get_constellation(name):
if name == 'BPSK': phases = np.arange(2) * 2 * np.pi / 2; return np.exp(1j * phases)
if name == 'QPSK': phases = np.arange(4) * 2 * np.pi / 4; return np.exp(1j * phases)
if name == '8-PSK': phases = np.arange(8) * 2 * np.pi / 8; return np.exp(1j * phases)
def square_qam(M):
dim = int(np.sqrt(M))
axis = np.arange(-dim + 1, dim, 2)
X, Y = np.meshgrid(axis, axis)
c = X.flatten() + 1j * Y.flatten()
return c / np.sqrt(np.mean(np.abs(c) ** 2))
if name == '16-QAM': return square_qam(16)
if name == '64-QAM': return square_qam(64)
if name == '256-QAM': return square_qam(256)
if name == '128-QAM':
axis = np.arange(-11, 12, 2)
X, Y = np.meshgrid(axis, axis)
mask = ~((np.abs(X) > 7) & (np.abs(Y) > 7))
c = (X[mask] + 1j * Y[mask]).flatten()
return c / np.sqrt(np.mean(np.abs(c) ** 2))
def generate_live_signal(scheme_name, snr_db, num_frames=1000):
n_samples = 128
sps = 8
n_symbols = n_samples // sps
rrc_taps = get_rrc()
const_points = get_constellation(scheme_name)
num_points = len(const_points)
snr_linear = 10 ** (snr_db / 10)
X_batch = np.zeros((num_frames, 2, n_samples), dtype=np.float32)
for idx in range(num_frames):
raw_symbols = np.random.randint(0, num_points, n_symbols)
mapped_iq = const_points[raw_symbols]
upsampled = np.zeros(n_symbols * sps, dtype=complex)
upsampled[::sps] = mapped_iq
tx_signal = np.convolve(upsampled, rrc_taps, mode='same')
clean_frame = tx_signal[:n_samples]
sig_power = np.mean(np.abs(clean_frame) ** 2)
noise_power = sig_power / snr_linear
n_real = np.random.normal(0, 1, n_samples)
n_imag = np.random.normal(0, 1, n_samples)
noise = np.sqrt(noise_power / 2) * (n_real + 1j * n_imag)
rx_frame = clean_frame + noise
X_batch[idx, 0, :] = np.real(rx_frame)
X_batch[idx, 1, :] = np.imag(rx_frame)
return X_batch
# --- MAIN DASHBOARD ---
st.markdown("<h1 style='text-align: center;'>📡 Cognitive Radio AMC Simulator</h1>", unsafe_allow_html=True)
st.markdown("<p style='text-align: center; font-size: 1.1rem; margin-bottom: 2rem; opacity: 0.8;'>A live playground to see how an AI identifies different radio signals hidden in heavy static.</p>", unsafe_allow_html=True)
st.divider()
col1, col2 = st.columns([1, 3])
with col1:
st.header("🎛️ Parameters")
selected_scheme = st.selectbox("Select Target Scheme",
['BPSK', 'QPSK', '8-PSK', '16-QAM', '64-QAM', '128-QAM', '256-QAM'])
selected_snr = st.slider("Channel SNR (dB)", min_value=-20, max_value=20, value=10, step=2)
st.markdown("### 🧠 Engine Config")
st.info("Batch Size: 1,000 Frames\n\nObservation Window: 16 Symbols (128 Samples)\n\nInference: Majority Voting")
fire_button = st.button("Transmit & Analyze", type="primary", use_container_width=True)
with col2:
if fire_button:
with st.spinner("Generating 1,000 synthetic RF frames in memory..."):
X_batch = generate_live_signal(selected_scheme, selected_snr, num_frames=1000)
plot_i = X_batch[0, 0, :]
plot_q = X_batch[0, 1, :]
with st.spinner("Passing tensor batch to Deep CNN..."):
results = ai_inference.get_predictions(X_batch)
st.subheader("🎯 System Inference Results")
res_col1, res_col2 = st.columns(2)
with res_col1:
if results['majority_vote'] == selected_scheme:
st.success(f"**Network Prediction:** {results['majority_vote']} ✅")
else:
st.error(f"**Network Prediction:** {results['majority_vote']} ❌ (Ground Truth: {selected_scheme})")
with res_col2:
st.metric(label="Model Confidence (Time-Averaged)", value=f"{results['top_1_conf']:.2f}%")
st.caption(f"Secondary Guess: {results['top_2_guess']} ({results['top_2_conf']:.2f}%)")
st.divider()
st.subheader("🌌 Received Signal Geometry (Frame 1 of 1000)")
fig = go.Figure()
fig.add_trace(go.Scatter(
x=plot_i, y=plot_q,
mode='markers',
marker=dict(size=6, color='#0088FF', opacity=0.8),
name="I/Q Samples"
))
fig.update_layout(
xaxis_title="In-Phase (I)",
yaxis_title="Quadrature (Q)",
xaxis=dict(range=[-2, 2], zeroline=True, zerolinewidth=2, zerolinecolor='gray'),
yaxis=dict(range=[-2, 2], zeroline=True, zerolinewidth=2, zerolinecolor='gray'),
width=700, height=500,
margin=dict(l=20, r=20, t=30, b=20)
)
# The theme="streamlit" argument tells Plotly to strictly follow the Light/Dark mode
st.plotly_chart(fig, use_container_width=True, theme="streamlit")
else:
st.info(" Adjust your channel parameters and click 'Transmit & Analyze' to initiate the simulation.")