Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Homepage mini simulation #57

Merged
merged 9 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ distribute-*
env/
build/
_build/
OLD_build/
dist/
Sphinx.egg-info/
doc/_build/
Expand Down
177 changes: 177 additions & 0 deletions _static/js/homepage_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
function homepage_app() {
function createSignal(N) {
const x = new Array(N * 2);

// complex AWGN
let noise_ampl_dB = document.getElementById("noise_ampl_dB").value;
document.getElementById("noise_value").innerHTML = noise_ampl_dB - 30;
let noise_ampl = Math.pow(10, noise_ampl_dB / 10);
for (let i = 0; i < N; i++) {
x[i * 2] = noise_ampl * Math.sqrt(-2.0 * Math.log(Math.random())) * Math.cos(2.0 * Math.PI * Math.random());
x[i * 2 + 1] = noise_ampl * Math.sqrt(-2.0 * Math.log(Math.random())) * Math.cos(2.0 * Math.PI * Math.random());
}

// Tone
let freq = document.getElementById("freq").value;
document.getElementById("freq_value").innerHTML = freq;
for (let i = 0; i < N; i++) {
x[i * 2] += Math.cos(2 * Math.PI * i * freq);
x[i * 2 + 1] += Math.sin(2 * Math.PI * i * freq);
}

return x;
}

const N = 1024;
let update_period = 50; // in ms, gets doubled every time refersh is too slow to keep up

function updatePlot() {
const start_t = performance.now();
const signal = createSignal(N);

const fft_obj = new FFT(N);
const signal_fft = fft_obj.createComplexArray();
fft_obj.transform(signal_fft, signal);

// Take magnitude of FFT
const signal_fft_mag = new Array(N);
for (let i = 0; i < N; i++) {
signal_fft_mag[i] = signal_fft[2 * i] * signal_fft[2 * i] + signal_fft[2 * i + 1] * signal_fft[2 * i + 1];
}
let signal_fft_mag_shifted = fftshift(signal_fft_mag);

// Convert to dB
const signal_fft_mag_shifted_dB = new Array(N);
for (let i = 0; i < N; i++) {
signal_fft_mag_shifted_dB[i] = 10 * Math.log10(signal_fft_mag_shifted[i]);
}

// Plot freq
const canvas = document.getElementById("freq_plot");
const ctx = canvas.getContext("2d", { alpha: false }); // apparently turning off transparency makes it faster

ctx.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx.fillStyle = "white";
ctx.lineWidth = 1;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.translate(0, 400); // move half of canvas height so y=0 in middle
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.moveTo(0, Math.floor(-7 * signal_fft_mag_shifted_dB[0] + 200));
for (let i = 1; i < N; i++) {
ctx.lineTo(i * 2, Math.floor(-7 * signal_fft_mag_shifted_dB[i] + 200)); // -1* to flip y-axis
}
ctx.stroke();

// Freq x-axis ticks and labels
ctx.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.font = "36px Arial";
ctx.fillStyle = "black";
// axis line
ctx.moveTo(0, ctx.canvas.height - 80);
ctx.lineTo(ctx.canvas.width, ctx.canvas.height - 80);
// ticks
for (let i = 0; i < 11; i++) {
ctx.moveTo((ctx.canvas.width / 10) * i, ctx.canvas.height - 100);
ctx.lineTo((ctx.canvas.width / 10) * i, ctx.canvas.height - 80);
ctx.fillText(Math.round((i - 5) * 0.1 * 100) / 100, ((ctx.canvas.width / 10) * i - 0) * 0.975, ctx.canvas.height - 35);
}
ctx.fillText("Hz", ctx.canvas.width / 2 + 5, ctx.canvas.height - 35);
ctx.fillText("Frequency", ctx.canvas.width / 2 - 70, ctx.canvas.height - 7);
ctx.stroke();

// Freq y-axis ticks and labels
ctx.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.font = "36px Arial";
ctx.fillStyle = "black";
// axis line
ctx.moveTo(0, 0);
ctx.lineTo(0, ctx.canvas.height - 80);
// ticks
for (let i = 1; i < 6; i++) {
ctx.moveTo(0, ((ctx.canvas.height - 80) / 6) * i);
ctx.lineTo(20, ((ctx.canvas.height - 80) / 6) * i);
ctx.fillText(i * -10, 30, ((ctx.canvas.height - 80) / 6) * i + 10);
}
ctx.fillText("dB", 20, 36);
ctx.stroke();

// Plot time
const canvas_time = document.getElementById("time_plot");
const ctx_time = canvas_time.getContext("2d", { alpha: false });

ctx_time.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx_time.fillStyle = "white";
ctx_time.lineWidth = 1;
ctx_time.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx_time.translate(0, 400); // move half of canvas height so y=0 in middle
ctx_time.beginPath();
ctx_time.strokeStyle = "blue";
ctx_time.moveTo(0, Math.floor(-20 * signal[0] - 40));
for (let i = 1; i < N; i++) {
ctx_time.lineTo(i * 2, Math.floor(-20 * signal[i * 2] - 40)); // -1* to flip y-axis
}
ctx_time.stroke();

// Time x-axis ticks and labels
ctx_time.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx_time.beginPath();
ctx_time.strokeStyle = "black";
ctx_time.lineWidth = 3;
ctx_time.font = "36px Arial";
ctx_time.fillStyle = "black";
// axis line
ctx_time.moveTo(0, ctx_time.canvas.height - 60);
ctx_time.lineTo(ctx_time.canvas.width, ctx_time.canvas.height - 60);
// ticks
for (let i = 0; i < 11; i++) {
ctx_time.moveTo((ctx_time.canvas.width / 10) * i, ctx_time.canvas.height - 80);
ctx_time.lineTo((ctx_time.canvas.width / 10) * i, ctx_time.canvas.height - 60);
//ctx_time.fillText(Math.round(i* 0.1 * 100) / 100, ((ctx_time.canvas.width / 10) * i - 0) * 0.98, ctx_time.canvas.height - 5);
}
ctx_time.fillText("Time", ctx_time.canvas.width / 2, ctx_time.canvas.height - 5);
ctx_time.stroke();

// Time y-axis ticks and labels
ctx_time.setTransform(1, 0, 0, 1, 0, 0); // resets transform
ctx_time.beginPath();
ctx_time.strokeStyle = "black";
ctx_time.lineWidth = 3;
ctx_time.font = "36px Arial";
ctx_time.fillStyle = "black";
// axis line
ctx_time.moveTo(0, 0);
ctx_time.lineTo(0, ctx_time.canvas.height - 80);
// ticks
for (let i = 1; i < 6; i++) {
ctx_time.moveTo(0, ((ctx_time.canvas.height - 80) / 6) * i);
ctx_time.lineTo(20, ((ctx_time.canvas.height - 80) / 6) * i);
}
ctx_time.fillText("1", 20, 36);
ctx_time.fillText("-1", 20, ctx_time.canvas.height - 80);
ctx_time.fillText("0", 30, ctx_time.canvas.height / 2 - 40);
ctx_time.stroke();
// y=0 line
ctx_time.strokeStyle = "grey";
ctx_time.lineWidth = 1;
ctx_time.moveTo(0, (ctx_time.canvas.height - 80) / 2);
ctx_time.lineTo(ctx_time.canvas.width, (ctx_time.canvas.height - 80) / 2);
ctx_time.stroke();

//console.log("Time taken to update frame: " + (performance.now() - start_t) + " ms");
if (performance.now() - start_t > update_period) {
console.log("Warning: browser is not able to keep up, doubling update period");
update_period = update_period * 2;
}
}

setInterval(function () {
updatePlot();
}, update_period); // in ms
}
61 changes: 36 additions & 25 deletions _templates/homepage.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,55 @@ <h1 style="text-align: center">PySDR: A Guide to SDR and DSP using Python</h1>
<span class="std std-ref">Dr. Marc Lichtman</span>
</a>
-
<a
class="reference external"
href="mailto:pysdr&#37;&#52;&#48;vt&#46;edu"
rel="noopener noreferrer"
target="_blank"
>
<a class="reference external" href="mailto:pysdr&#37;&#52;&#48;vt&#46;edu" rel="noopener noreferrer" target="_blank">
pysdr<span>&#64;</span>vt<span>&#46;</span>edu
</a>
</p>

<h4 style="line-height: 1.4">
Welcome to PySDR, a free online textbook (not a Python library!) that provides a gentle introduction
to wireless communications and software-defined radio (SDR) using an abundance
of diagrams, animations, and Python code examples. From FFTs to filters to
digital modulation to receiving and transmitting from SDRs in Python, PySDR
has you covered!
Welcome to PySDR, a free online textbook (not a Python library!) that provides a gentle introduction to wireless communications and software-defined
radio (SDR) using an abundance of diagrams, animations, and Python code examples. From FFTs to filters to digital modulation to receiving and
transmitting from SDRs in Python, PySDR has you covered!
</h4>

<h4 style="line-height: 1.4">
The goal of PySDR is to increase accessibility to topics traditionally covered
in a math-intensive manner and within a relatively small set of universities.
All content used to generate PySDR is open source, and can be found
<a
class="reference external"
href="https://github.com/777arc/PySDR"
rel="noopener noreferrer"
target="_blank"
>here</a
>.
The goal of PySDR is to increase accessibility to topics traditionally covered in a math-intensive manner and within a relatively small set of
universities. All content used to generate PySDR is open source, and can be found
<a class="reference external" href="https://github.com/777arc/PySDR" rel="noopener noreferrer" target="_blank">here</a>.
</h4>

<h4>
See
<a class="reference internal" href="content/intro.html#intro-chapter"
><span class="std std-ref">Chapter 1: Introduction</span></a
>
<a class="reference internal" href="content/intro.html#intro-chapter"><span class="std std-ref">Chapter 1: Introduction</span></a>
for the textbook's purpose and target audience.
</h4>

<h4>
To get a quick taste of RF signal processing, try playing with the simulation below which shows the frequency and time domain of a signal
consisting of a tone and white Gaussian noise.
</h4>

<div>
<input type="range" min="-0.4" max="0.4" step="0.0001" value="0.01" class="slider" id="freq" />
<span id="freq_value"></span>
<label>[Hz] - Tone Frequency</label>
</div>
<div>
<input type="range" min="-20" max="10" step="0.1" value="0" class="slider" id="noise_ampl_dB" />
<span id="noise_value"></span>
<label>[dB] - Noise Amplitude</label>
</div>
<div>
<canvas width="2048" height="800" id="freq_plot" style="width: 512px; height: 200px; border: 0px; image-rendering: auto"></canvas>
</div>
<br />
<br />
<div>
<canvas width="2048" height="800" id="time_plot" style="width: 512px; height: 200px; border: 0px; image-rendering: auto"></canvas>
</div>

<script>
homepage_app();
</script>

DEMO
<br />
3 changes: 2 additions & 1 deletion conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ def setup(app):
html_js_files = [
'js/beamforming_slider_app.js',
'js/FFT.js',
'js/cyclostationary_app.js'
'js/cyclostationary_app.js',
'js/homepage_app.js'
]

# Add any extra paths that contain custom files (such as robots.txt or
Expand Down
Loading