Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ttm4105_assignment1_2024/Assignment1_TTM4105_2024.ipynb
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
917 lines (917 sloc)
45.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "7a3f6a1a-8821-4a0d-8c42-e59af8fe1759", | |
"metadata": { | |
"tags": [] | |
}, | |
"source": [ | |
"# TTM4105 2024 Assignment 1\n", | |
"\n", | |
"The purpose of this assignment is to enhance your understanding of the concepts introduced in the lectures via *experience based learning*. The assignment has two parts where Part 1 focuses on how audio is converted into a signal and Part 2 focuses on the main elements of transmitting this signal over a medium. \n", | |
"\n", | |
"\n", | |
"Submission guidelines:<br>\n", | |
"You should export this notebook with solved tasks as a PDF. Your submission should then contain two items: <br>\n", | |
"a.) A zip archive of the complete folder named *TTM4105_Assignment1_GroupX*, <br>\n", | |
"b.) Exported PDF of the notebook *including the group number* in the file name. In case you need help with jupyter notebook and exporting your results, there is a file called *instructions_assignment1_TTM4105.pdf* on Blackboard. \n", | |
"One delivery per group is sufficient.<br>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "60df0505-724a-4d98-a41d-e0818449d384", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"!pip install librosa" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"id": "abdee77a-a66d-419f-93ad-f7974394adb3", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"### NB! ###\n", | |
"# Run this box to import all nessecary libraries.\n", | |
"# Remember to fill in your own student number below before starting on your assignment.\n", | |
"\n", | |
"%run -i 'scripts/assignment1-init.py'\n", | |
"\n", | |
"# TODO: Fill in your student number here\n", | |
"seed = " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f13cb85f-20b7-4c85-b0b2-065532a21150", | |
"metadata": {}, | |
"source": [ | |
"# <a id='part1'>Part 1: Filtering, Sampling and Quantization [40 points]</a>\n", | |
"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "c01ef92f-9f17-4025-8d68-937adb054840", | |
"metadata": {}, | |
"source": [ | |
"# <a id='1.1-filtering'>1.1 Filtering </a>\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/filtering.png\" alt=\"Signal Filtering Techniques\" width=\"600\"/> |\n", | |
"|*Source: [Wikipedia](https://en.wikipedia.org/wiki/Filter_%28signal_processing%29)* |\n", | |
"<a id=\"filtering\"><p style=\"text-align: center;\"><b>Figure 1.1: Different signal filtering methods that can be used to remove unwanted frequencies from the signal.</b></p></a>\n", | |
"\n", | |
"In sound and telecommunications, filtering is the process of selectively modifying the frequency content of a signal. This manipulation allows engineers to remove unwanted noise, isolate specific frequency bands, and enhance the overall quality of audio or data transmission. Filtering techniques find applications in everything from noise reduction in headphones to ensuring clear conversations across vast cellular networks.\n", | |
"\n", | |
"- **Importance of Filtering:**<br>\n", | |
"Filtering is essential in the age of Big Data to focus analysis, save storage space, and optimize processing power by targeting only relevant information.\n", | |
"<br>\n", | |
"- **Filtering in Telecommunications:**<br>\n", | |
"Filtering takes a central role in telecommunications for several reasons:\n", | |
" - **Noise Reduction:** Filters remove unwanted background noise and interference, improving the efficiency and clarity of the data transmissions.\n", | |
" - **Channel Separation:** In wireless communications, filters isolate specific frequency bands for different channels. This prevents signals from overlapping and causing interference." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "ddf808b3-fd63-422e-aebd-918e8832bf47", | |
"metadata": { | |
"tags": [] | |
}, | |
"source": [ | |
"## <a id='1.1.1-experimenting_filtering'>Experimenting with Filter Cutoff</a>\n", | |
"\n", | |
"A cutoff frequency is where a filter starts reducing a signal, determining which frequencies pass and which are blocked. By adjusting the cutoff, you can hear how filters shape the audio. Start by plotting the frequency domain of the provided audio clip to see which frequencies are present and their amplitudes.\n", | |
"\n", | |
"## <a id='1.1.2-Coding_Task'>Plot the Frequency Domain</a>\n", | |
"Listen to audio/run-forrest.wav. Below you will find a code block with some template code for creating a frequency domain for the provided audio clip, **your first task** will be to finish the code for the template code provided, or create your own function to plot the frequency domain of the audio clip." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "751e03a8-fc11-431e-bc68-6a39082bf7f7", | |
"metadata": { | |
"tags": [] | |
}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Plots the frequency domain representation of an audio file.\n", | |
"\n", | |
"Args:\n", | |
" audio_file (str): Path to the audio file.\n", | |
" sampling_rate (float, optional): Sampling rate to use. If not provided, try to extract it from the audio file.\n", | |
"\"\"\"\n", | |
"\n", | |
"# Imports\n", | |
"import numpy as np\n", | |
"import matplotlib.pyplot as plt\n", | |
"import librosa \n", | |
"\n", | |
"\n", | |
"def plot_frequency_domain_audio(audio_file, sampling_rate=None):\n", | |
"\n", | |
" # Load the audio file using librosa\n", | |
" signal, sr = librosa.load(audio_file, sr=sampling_rate)\n", | |
"\n", | |
" # If the sampling rate wasn't provided, use the one from the audio file\n", | |
" if sampling_rate is None:\n", | |
" sampling_rate = sr\n", | |
"\n", | |
" # Compute the FFT, frequencies, and magnitude (same as before)\n", | |
" fft_spectrum = np.fft.fft(signal)\n", | |
" fft_length = len(fft_spectrum)\n", | |
" frequencies = np.fft.fftfreq(fft_length, d=1 / sampling_rate)\n", | |
" magnitude_spectrum = np.abs(fft_spectrum)\n", | |
"\n", | |
" # Plot the magnitude spectrum\n", | |
" plt.figure()\n", | |
" plt.plot(frequencies, magnitude_spectrum)\n", | |
" plt.xlabel(\"Frequency (Hz)\")\n", | |
" plt.ylabel(\"Magnitude\")\n", | |
" plt.title(\"Frequency Domain Representation\")\n", | |
" plt.grid(True)\n", | |
" plt.show()\n", | |
"\n", | |
"\n", | |
"plot_frequency_domain_audio(\"audio/run-forrest.wav\") \n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "6cb8adb8-b130-42d6-bfec-8e17ade989f8", | |
"metadata": {}, | |
"source": [ | |
"Now that you have plotted the frequency domain for the audio signal, you can see the frequencies the audio clip consists of. This will be useful for the upcoming questions. Use the frequency domain aquired to answer the following questions." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5c8f2c45-c75c-4521-b804-4aae9d088a0c", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.1.3-filtering_questions'>Filtering Questions [12 points]</a>\n", | |
"\n", | |
"**Q1.1.1:** Think back to the birds chirping in the \"Run Forrest Run\" sound clip and remember the frequency plot you generated earlier. In which part of the frequency domain would you expect the birds chirping to be located in? <br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q1.1.2:** Based on **Figure 1.1**, which filter type would you use to isolate just the birdsong? <br>\n", | |
"*Answer:*\n", | |
"\n", | |
"**Q1.1.3:** What are the advantages of filtering? What happens if the signal is filtered too much or too little? Use the code provided below to filter the audio signal with a cutoff frequency of 3000Hz and 8000Hz. What can you observe? How does the signal sound with a lower/higher cutoff? <br>\n", | |
"*Note: It can be beneficial to also filter at higher/lower frequencies to aid yourself with answering this question.*<br>\n", | |
"*Answer:* \n", | |
"\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "a4fb5e90-7b91-4402-a0a7-d022058f2253", | |
"metadata": { | |
"tags": [] | |
}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Args:\n", | |
" input_file (str): Path to the input WAV file.\n", | |
" output_file (str): Path to the output filtered WAV file.\n", | |
" cutoff (int, optional): Cutoff frequency (Hz). Defaults to 1000.\n", | |
" numtaps (int, optional): Number of filter taps. Defaults to 201.\n", | |
"\"\"\"\n", | |
"\n", | |
"# Args (You can use your own audio file if you wish)\n", | |
"input_file = 'audio/run-forrest.wav'\n", | |
"output_file = 'audio/run-forrest-filtered.wav'\n", | |
"\n", | |
"# Apply Filter\n", | |
"apply_fir_filter(input_file, output_file, cutoff=7000)\n", | |
"\n", | |
"# Import and Display Audio\n", | |
"sampling_rate, freq = sp.io.wavfile.read('audio/run-forrest-filtered.wav')\n", | |
"ipd.Audio(freq, rate=sampling_rate)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "69167cdd-f10c-411f-8fa6-fcf3e4fd4772", | |
"metadata": { | |
"tags": [] | |
}, | |
"source": [ | |
"# <a id='1.2-sampling'>1.2 Sampling</a>\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/signal_sampling.png\" alt=\"Signal Sampling\" width=\"600\"/> |\n", | |
"|*Source: [Wikipedia](https://en.wikipedia.org/wiki/Sampling_%28signal_processing%29)* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>Figure 2.1: Signal sampling representation. The continuous signal S(t) is represented with a green colored line while the discrete samples are indicated by the blue vertical lines.</b></p></a>\n", | |
"\n", | |
"The image shows signal sampling, where blue vertical lines represent discrete data points from a continuous signal. Sampling discretizes the signal along the x-axis, crucial for efficient processing in digital signal processing and telecommunications, enabling analog-to-digital conversion.</b>\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "3af984e3-3332-49df-807a-5722c410e60d", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.1-nyquist_sampling'>Nyquist–Shannon Sampling Theorem</a>\n", | |
"\n", | |
"In an ideal scenario, we would sample a signal at an infinitely small time interval, but this is impractical due to excessive storage and computational demands. The key question is: what is the minimum sampling frequency to ensure accurate signal reconstruction and avoid aliasing?\n", | |
"\n", | |
"The Nyquist–Shannon sampling theorem addresses this, stating that the sample rate must be at least twice the signal’s bandwidth to avoid aliasing (covered in Section 1.2.2). For example, sound is often sampled at 44.1 kHz (0.023 milliseconds per sample). Excessive sampling is inefficient as it increases storage and computational load without adding useful information.\n", | |
"\n", | |
"For hardware with a maximum sampling rate of 6,000 Hz, only signals up to 3,000 Hz can be accurately captured, requiring at least two samples per period. This is the Nyquist rate or Nyquist limit, which is $ \\frac{1}{2} $ the sampling rate $f_s$.</p>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "be8e1a8a-2e1b-4a25-9f09-8102bc4b0ebe", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.2-aliasing'>Aliasing</a>\n", | |
"\n", | |
"<p style='text-align: justify;'>\n", | |
"When a signal with frequency components above the Nyquist limit is sampled, aliasing occurs, causing higher frequencies to appear as lower frequencies due to insufficient sampling. This is shown in the figure below.\n", | |
"<p/>\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/aliasing.jpg\" alt=\"Aliasing\" width=\"600\"/> |\n", | |
"|*Source: [ni.com docs](https://www.ni.com/docs/en-US/bundle/labwindows-cvi/page/advancedanalysisconcepts/aliasing.html)* |\n", | |
"<a id=\"fig2\"><p style=\"text-align: center;\"><b>Figure 2.2: Adequate sampling can be seen in the first graph, and aliasing due to sampling frequency lower than required by Nyquist theorem can be seen in the second graph.</b></p></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "aa595cad-c79a-443b-ac20-01acca796a48", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.2-aliasing'>1.2 Experimenting with Aliasing (plotting) [4 points]</a>\n", | |
"\n", | |
"In this section you will directly observe the effects of aliasing and understand why the Nyquist limit is so important in digital signal processing.\n", | |
"\n", | |
"**Use the code block below to try the following experiments:**\n", | |
"- Sample different frequencies below the Nyquist limit\n", | |
"- Sample different frequencies at or above the Nyquist limit\n", | |
"- Adjust the phase shift <br>\n", | |
"\n", | |
"*Note: Make sure to document results and findings in the final delivery.* <br>\n", | |
"*Note: The code might result in OptimizeWarning but that does not change the outcome and can be safely ignored.*" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "55c91bf9-cd7a-4458-ab08-54d54dab3d14", | |
"metadata": {}, | |
"source": [ | |
"*Observations*:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "eef28674-b675-4e64-9b82-6b78dbd1c5d6", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Args:\n", | |
" frequency (int): Initial frequency (Hz).\n", | |
" sample_rate (int): The sample rate to sample the inital frequency at (Hz).\n", | |
" shift_wave (int): Amount of phase shift to add to the initial wave (How many samples to shift the wave to the left).\n", | |
"\"\"\"\n", | |
"\n", | |
"sample_frequency(frequency=20, sample_rate=20, shift_wave=150)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "9b4e84e4-8dec-45fd-b0b4-4e412ff24177", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.4-aliasing_questions'> Aliasing Questions [8 points]</a>\n", | |
"\n", | |
"**Q1.2.1:** Starting with a frequency of 20Hz, how does the reconstructed signal behave as the sample rate approaches the Nyquist limit? When does aliasing occur, and can you provide an example of an aliased frequency that occurs when the sample rate is lower than 40Hz? <br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q1.2.2:** Why is it that sampling at exactly the Nyquist limit is sometimes insufficient, and what information about the signal would be necessary to properly reconstruct it? <br>\n", | |
"*Answer:* \n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "8e28b211-ff48-45ae-a911-4928adb4cd2f", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.5-aliasing'> Aliasing: Sound Data </a>\n", | |
"In this task, we will will look at how sampling works in *practice* by the use of an audio file. We will use an audio file that *sweeps* through the range of frequencies that humans can hear, approximately 0-22,050 Hz.\n", | |
"\n", | |
"**Starting Point**<br>\n", | |
"The audio file has already been filtered and has a starting sampling rate of 44,100 Hz. This means there are 44,100 samples taken every second to represent the sound.\n", | |
"\n", | |
"**Change the Sample Rate**<br>\n", | |
"You will experiment by changing the sample rate. This will be done by providing a resample rate. The resample rate determines how often the signal's values are recorded with respect to the original sample rate. A resample rate of 2 means that the signal is sampled every two data points. The general formula is:\n", | |
"\n", | |
"$ New Sample Rate = \\frac{Original Sample Rate}{Resample Rate} $\n", | |
"\n", | |
"**What to look for:**<br>\n", | |
"- *Reduce the sample rate:* If the resample rate is 2, the number of samples will be halved. If the resample rate is 4, the number of samples will be halved twice. \n", | |
"- **Observe** with a spectrogram and listen to the audio: A spectrogram is a visual way to see how frequencies in a sound change over time. We'll create spectrograms of the audio file at different sample rates.\n", | |
"- As you change the resample rate and look at the spectrograms, pay attention to how the visual representation of the sound changes. The code can be found under the questions.\n", | |
"\n", | |
"*Note: The code has a UserWarning but that does not change the outcome and can be safely ignored.*" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "4dfa0a56-911f-41da-ac37-135837bc347b", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.2.6-more_aliasing_questions'>More Aliasing Questions [4 points]</a>\n", | |
"Use the code below to answer the upcoming questions.\n", | |
"\n", | |
"**Q1.2.3:** Using the code cell below, what do you observe when you change the resample rate of the audio clip? How does the sample rate affect the frequency, can this be heard? <br>\n", | |
"*Answer:* \n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "bd066ffc-a2bc-4ea3-9fbe-a23d3867012e", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Run this code block to display the original audio\n", | |
"\"\"\"\n", | |
"\n", | |
"# Import Audio\n", | |
"sampling_rate, freq_sweep_44100 = sp.io.wavfile.read('audio/sweep-linear.wav')\n", | |
"\n", | |
"# Plot Spectrogram\n", | |
"scripts.signal.plot_spectrogram(freq_sweep_44100, sampling_rate)\n", | |
"\n", | |
"# Display Audio\n", | |
"ipd.Audio(freq_sweep_44100, rate=sampling_rate)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "501ce661-b23f-42fd-8664-1e579c4875d5", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Change the resample factor to sample the frequency sweep at lower sample rates.\n", | |
"\"\"\"\n", | |
"# Change this\n", | |
"resample_factor = 4\n", | |
"\n", | |
"# Resample Audio\n", | |
"freq_sweep_new, new_sampling_rate = scripts.signal.resample_audio(resample_factor, sampling_rate, freq_sweep_44100)\n", | |
"\n", | |
"# Plot Spectrogram\n", | |
"scripts.signal.plot_spectrogram(freq_sweep_new, new_sampling_rate)\n", | |
"\n", | |
"# Display Audio\n", | |
"ipd.Audio(freq_sweep_new, rate=new_sampling_rate)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "70e77676-e19c-4fd5-a203-720b2af5ba3b", | |
"metadata": {}, | |
"source": [ | |
"# <a id='1.3-Quantization'>1.3 Quantization</a>\n", | |
"\n", | |
"\n", | |
"Quantization is the process of converting a continuous range of signal amplitudes into discrete levels, similar to rounding a curve to the nearest horizontal line. This reduces precision but enables digital representation. Unlike sampling, which discretizes the x-axis, quantization discretizes the y-axis. An example is shown in the figure below.\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/quantization.png\" alt=\"Quantization\" width=\"600\"/> |\n", | |
"|*Source: [Tech](https://julie-tech.tistory.com/142)* |\n", | |
"<a id=\"fig5\"><p style=\"text-align: center;\"><b>Figure 3.1: Signal quantization representation of a 3 bit resolution. The continuous signal S(t) is represented with a red colored line while the discrete levels are indicated by the blue line.</b></p></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "47ba4a3b-30cf-4652-be4d-db0d5c805cc2", | |
"metadata": {}, | |
"source": [ | |
"The original analog signal has an infinite range of amplitude values. In quantization, we map these amplitudes to a set number of levels, such as 256 levels for an 8-bit representation ($2^8=256$). Each sample is rounded to the nearest level, introducing some quantization error since the rounded value typically won't match the original exactly.\n", | |
"\n", | |
"There are different quantization methods:\n", | |
"\n", | |
"**Linear Quantization:** Levels are evenly spaced across the amplitude range.\n", | |
"\n", | |
"**Non-linear Quantization:** Levels are unevenly spaced, with more concentrated where the signal often resides, reducing the effect of quantization noise, especially for signals with a wide dynamic range like speech or audio." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "d34f5729-a594-421e-aa48-924c52c2cb45", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.3.1-Quantization-Errors'>Quantization Errors [6 points]</a>\n", | |
"\n", | |
"**Q1.3.1:** Calculate the maximum possible quantization error if the signal range is 10V and the resolution is 12 bits? <br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q1.3.2:** Derive a formula for maximum quantization error given the signal's peak-to-peak voltage range (Vpp) and the number of bits used in quantization (N), given linear quantization? <br>\n", | |
"*Answer:* \n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "2cc069e5-b27f-43f9-85c8-72b73878bd1f", | |
"metadata": {}, | |
"source": [ | |
"## <a id='1.3.2-Quantization-Effects'>How does quantization affect audio signals? [6 points]</a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f3cb4587-3dda-495d-a301-2868e8c9a3c9", | |
"metadata": {}, | |
"source": [ | |
"Use the code block below to answer the upcoming questions. \n", | |
"\n", | |
"*Note:* In the cell below, you can run code to adjust the number of quantization bits for an audio clip. The output will include the new audio clip and two graphs. The first graph shows samples on the x-axis and their values on the y-axis. With many samples, zooming in on the x-axis can help. You can adjust the variable xlim_zoom to see a zoomed-in second graph.\n", | |
"\n", | |
"**Q1.3.3:** How does the signal change when you lower the amount of quantization levels? <br>\n", | |
"*Answer:*\n", | |
"\n", | |
"**Q1.3.4:** Mention a few factors that are relevant when choosing the amount of quantization bits needed for a certain wave? <br>\n", | |
"*Answer:*" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "2db5a850-7a2a-43d4-9919-e2d132da5c87", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"\"\"\"\n", | |
"Args:\n", | |
" input_file (str): Path to the input WAV file.\n", | |
" output_file (str): Path to the output filtered WAV file.\n", | |
" quantization_bits (int): Amount of quantization bits.\n", | |
" original_quantization (int, optional): Amount of quantization bits in the original audio file. Defaults to 16.\n", | |
"\"\"\"\n", | |
"\n", | |
"# Args (You can use your own audio file if you wish)\n", | |
"input_file = 'audio/run-forrest.wav'\n", | |
"output_file = 'audio/run-forrest-filtered.wav'\n", | |
"quantization_bits = 7 # Change this to change the quantization bits\n", | |
"\n", | |
"# Quantize Signal\n", | |
"audio_data, sampling_rate = signal_quantization(input_file, output_file, quantization_bits=quantization_bits)\n", | |
"\n", | |
"\n", | |
"# Plot quantizaiton graph\n", | |
"xlim_zoom = (11000, 11200) # You may want to change this depending on what audio file you have loaded\n", | |
"scripts.signal.plot_signal(audio_data, sampling_rate, quantization_bits, xlim_zoom = xlim_zoom)\n", | |
"\n", | |
"\n", | |
"# Import and Display Audio\n", | |
"ipd.Audio(audio_data, rate=sampling_rate)\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "6f52e932-6be9-4f5d-9114-dfcf52515567", | |
"metadata": {}, | |
"source": [ | |
"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", | |
"# <a id='part2'>Part 2: Digital Signal processing and modulation [60 points]</a>\n", | |
"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", | |
"\n", | |
"In this section, we'll explore digital modulation and wireless symbols, focusing on using schemes like ASK, PSK, FSK, and QAM to transmit information (1's and 0's). We'll also cover IQ plots, constellations, and use Python to simulate transmissions.\n", | |
"\n", | |
"## 2.1 Symbols\n", | |
"A symbol represents certain bits of information. The transmitted signal consists of these symbols, allowing us to convey multiple bits per symbol, thus enabling faster data transmission by manipulating wireless signals. This is why both data rate and symbol rate are used to describe communication speeds.\n", | |
"\n", | |
"## 2.2 Modulation and Transmitting Bits\n", | |
"Modulation converts data into electrical signals for transmission. For example, binary data can be transmitted over a wire, where high and low voltage levels represent 1's and 0's, with each being a symbol. An alternating symbol signal would look like this:\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/voltage_levels.png\" alt=\"Voltage Levels\" width=\"800\"/> |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.1:* Electrical signal representing binary information through varying voltage levels. In the given illustration, each symbol has a set duration of 0.1 seconds, which corresponds to a single bit.</b></p></a>\n", | |
" \n", | |
"Now let's picture that we want to transmit more information per symbol. To transmit more than one bit per symbol, an intuitive approach involves introducing additional levels to represent symbols. For instance, employing a 4-level amplitude modulation enables the transmission of 2 bits per symbol. _Figure 2.2_ shows how this can be done in practice. Note that the data conveyed in the two figures (_Figure 2.1_ and _Figure 2.2_) are not the same.\n", | |
"\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/4_level_shift_keying.png\" alt=\"Several Voltage Levels\" width=\"800\"/> |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.2:* 4-level amplitude modulation scheme. This modulation scheme enables the transmission of 2 bits per symbol with a symbol duration of 0.1 seconds. This illustrates the concept of conveying multiple bits within a single symbol.</b></p></a>\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "0c3b1f89-f800-453a-a9ee-a588ecf1d4c4", | |
"metadata": {}, | |
"source": [ | |
"## <a id='2.2-Shift keying questions'>2.2 Questions [12 Points]</a>\n", | |
"\n", | |
"Note: Symbol duration is 0.1s\n", | |
"\n", | |
"**Q2.2.1:** How many bits per second are transmitted in Figure 2.2? <br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q2.2.2:** What throughput can be achieved with 16 different amplitude levels and 8 ns symbols? <br>\n", | |
"*Answer:*\n", | |
"\n", | |
"**Q2.2.3:** Can you think of a general formula of how to calculate bits per symbol based on the amount of symbols used? <br>\n", | |
"*Answer:*\n", | |
"\n", | |
"**Q2.2.4:** When calculating the respective throughputs we have neglected an important phenomenon that would occur in real life. What important real life factor is left out in the calculations above? <br>\n", | |
"*Answer:*\n", | |
"\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "bcfefd91-4325-4a60-849d-fc2d91bc16c6", | |
"metadata": {}, | |
"source": [ | |
"## 2.3 Wireless Symbols and Modulation\n", | |
"Now that we know how to send symbols over a wired medium, let's explore transmitting data wirelessly using radio waves. The principles of modulation are similar for both wired and wireless transmission, but we can't directly send the wired signal for two reasons:\n", | |
"\n", | |
"1. **Low frequencies:** Signals below 300 kHz need impractically large antennas for transmission.\n", | |
"2. **Square waves:** These waves are inefficient as they consume too much bandwidth.\n", | |
"\n", | |
"To transmit wirelessly, we use a \"carrier wave,\" a continuous wave that carries no information on its own. We modify it, like how we adjusted voltage levels for wired transmission, to convey data.\n", | |
"\n", | |
"How do we modify the carrier wave? We alter the sinusoidal carrier wave's parameters:\n", | |
"1. Amplitude, $A$\n", | |
"2. Frequency, $f$\n", | |
"3. Phase, $\\phi$\n", | |
"\n", | |
"By adjusting these, we modulate data onto the carrier wave, creating a wireless signal.\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "ec602cf1-4151-4adc-bf37-3e4fccaa7fda", | |
"metadata": {}, | |
"source": [ | |
"## 2.4 Amplitude Shift Keying (ASK)\n", | |
"Amplitude Shift Keying (ASK) is a simple digital modulation scheme where the amplitude of the carrier wave is varied to represent data. In ASK, a fixed-amplitude carrier wave at a fixed frequency is transmitted for a specific time to represent a symbol. For instance, in a 2-ASK system, the carrier is transmitted at one amplitude for a 1 and a reduced amplitude for a 0. Since only two amplitude levels are used, this is called 2-ASK. An example of 2-ASK is shown below:\n", | |
"\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://pysdr.org/_images/ASK.svg\" alt=\"2-ASK example\" width=\"800\"/> |\n", | |
"|*Source: https://pysdr.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.3:* An electrical signal and its corresponding translation into a 2-ASK scheme.</b></p></a>\n", | |
"\n", | |
"\n", | |
"But how do we create this signal digitally? Through code, we generate a vector with _N_ samples per symbol, then multiply that vector by a sinusoid. This modulates the signal onto a carrier wave (the sinusoid). The example below demonstrates 2-ASK with 10 samples per symbol:\n", | |
"\n", | |
"\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://pysdr.org/_images/ask3.svg\" alt=\"Sampliong for 2-ASK\" width=\"800\"/> |\n", | |
"|*Source: https://pysdr.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.4:* Representation of sampling for the 2-ASK signal.</b></p></a>\n", | |
"---" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "be2fcd5a-c25b-448d-95a1-1eb0b604ebce", | |
"metadata": {}, | |
"source": [ | |
"## 2.4 Coding Task: Generating an ASK Signal [12 points]\n", | |
"Let's create our own ASK signal. We have provided default parameters for configuring the signal and a function that generates a binary stream of data (symbols). Your task is to create a 2-ASK signal by mixing the carrier with the symbols: when a 0 occurs, the sinusoid should have an amplitude of 0, and when a 1 occurs, the amplitude should be 1.\n", | |
"\n", | |
"**NB!:** Remember to input your student number as the seed and run both code blocks to generate a random set of symbols.\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"id": "70cd4469-c205-4584-ba30-3ac5c5e43c91", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Carrier wave and binary waveform configuration parameters\n", | |
"sample_rate = 1000 # (Hz)\n", | |
"symbol_duration = 0.1 # (sec)\n", | |
"carrier_frequency = 30 # (Hz)\n", | |
"simulation_time = 1 # (sec)\n", | |
"t = np.arange(0, simulation_time, 1/sample_rate)\n", | |
"carrier_signal = np.sin(2*pi*carrier_frequency*t) # signal = A * sin(2πft + φ)\n", | |
"\n", | |
"# Create binary stream of data\n", | |
"num_of_samples = sample_rate*symbol_duration\n", | |
"num_of_symbols = int(np.floor(np.size(t)/num_of_samples))\n", | |
"symbols = gen_binary_array(num_of_symbols, num_of_samples, seed)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4e3a9356-6e9f-4e29-955d-99b6c3ecb6c0", | |
"metadata": { | |
"tags": [] | |
}, | |
"outputs": [], | |
"source": [ | |
"# Code for ASK\n", | |
"\n", | |
"# ASK waveform generation\n", | |
"# Modulate the generated symbol on to the carrier signal. Your signal should resemble the one shown in the picture above. \n", | |
"# Before starting, remember to run the code block above. This will generate a stream of data - symbols to transmit.\n", | |
"\n", | |
"ask_signal = #TODO: write code here \n", | |
"\n", | |
"# Binary waveform and ASK waveform Plots\n", | |
"figure, axis = plt.subplots(2)\n", | |
"# Set y-axis labels to '0' and '1'\n", | |
"axis[0].set_yticks([0, 1])\n", | |
"axis[0].set_yticklabels(['0', '1'])\n", | |
"axis[0].plot(t,symbols)\n", | |
"axis[0].set_title(\"Binary digital data\")\n", | |
"axis[1].plot(t, ask_signal)\n", | |
"axis[1].set_title(\"ASK modulated signal\")\n", | |
"plt.tight_layout()\n", | |
"plt.show()\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "4d35369c-8dae-49f6-83f6-7e50a072664c", | |
"metadata": {}, | |
"source": [ | |
"## 2.5 Frequency Shift Keying (FSK)\n", | |
"Next, let's explore Frequency Shift Keying (FSK). Similar to ASK, but instead of adjusting the amplitude, we adjust the frequency. We shift between _N_ frequencies, each representing a symbol. Since we are modulating a carrier, we shift the carrier frequency by +/- these _N_ values to derive the frequencies.\n", | |
"\n", | |
"For example, a 4-FSK signal in the frequency domain might look like this:\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://pysdr.org/_images/fsk.svg\" alt=\"4-FSK spectral density\" width=\"800\"/> |\n", | |
"|*Source: https://pysdr.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.5:* Power spectral density map of a 4-FSK signal.</b></p></a>\n", | |
"\n", | |
"\n", | |
"The figure above is a power spectral density map, showing how the power of a signal is distributed across different frequencies. The x-axis represents frequency, and the y-axis represents power density. Each peak corresponds to a specific frequency, with 4 peaks indicating a 4-FSK scheme. Variations in the rest of the spectrum represent \"noise,\" which is electromagnetic interference from external sources.\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "d9ef5854-e053-49bc-929d-aee0456e4908", | |
"metadata": {}, | |
"source": [ | |
"## <a id='2.5-ASK task'>2.5 Coding Task: Generating a FSK signal [12 points]</a>\n", | |
"\n", | |
"Your task now is to generate a 2-FSK signal. Fill out the code below to generate a 2-FSK signal from the same binary stream of data. \n", | |
"\n", | |
"Start with creating a function for the frequency, mixing the binary stream of data with the carrier frequency. Then, use this to generate a sinusoid. Remember that this creates a representation of changes in the signal, and not a power spectral density. Your figure will not resemble the figure used above.\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5207b041-1dd4-46b7-b3b2-c2a995af2c95", | |
"metadata": {}, | |
"source": [ | |
"**Want a hint?**\n", | |
"1. Create a variable representing the different frequencies: \n", | |
" - Whenever transmitting 1's the frequency of the signal should double the carrier frequency.\n", | |
" - Whenever transmitting 0's the frequency of the signal should be the same as the carrier frequency. <br><br>\n", | |
"2. Take the binary stream of data we generated for 2-ASK earlier. Now, instead of modulating it with amplitude like before, we want to modulate it differently. Mix this data stream with the carrier frrequency to get a 2-FSK signal.\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"id": "5cbaa56f-c071-4c43-aba9-bb108bb56186", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Carrier wave and binary waveform configuration parameters\n", | |
"sample_rate = 1000 # (Hz)\n", | |
"symbol_duration = 0.1 # (sec)\n", | |
"carrier_frequency = 30 # (Hz)\n", | |
"simulation_time = 1 # (sec)\n", | |
"t = np.arange(0, simulation_time, 1/sample_rate)\n", | |
"carrier_signal = np.sin(2*pi*carrier_frequency*t) # signal = A * sin(2πft + φ)\n", | |
"\n", | |
"# Create binary stream of data\n", | |
"num_of_samples = sample_rate*symbol_duration\n", | |
"num_of_symbols = int(np.floor(np.size(t)/num_of_samples))\n", | |
"symbols = gen_binary_array(num_of_symbols, num_of_samples, seed)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "0bd642f8-3a79-4d90-b847-97e4d6c0eebf", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# FSK task\n", | |
"f = #TODO: write code here \n", | |
"fsk_signal = #TODO: write code here \n", | |
"\n", | |
"# Binary waveform and FSK modulation waveform Plots\n", | |
"figure, axis = plt.subplots(2)\n", | |
"axis[0].plot(t, symbols)\n", | |
"axis[0].set_title(\"Binary digital data\")\n", | |
"axis[1].plot(t, fsk_signal, 'r')\n", | |
"axis[1].set_title(\"FSK modulated signal\")\n", | |
"plt.tight_layout()\n", | |
"plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "014d2030-ab00-4bf1-88d0-656401831680", | |
"metadata": {}, | |
"source": [ | |
"# 2.6 Phase Shift Keying (PSK)\n", | |
"Now let’s consider modulating the phase. We do this in a similar manner as we did with the amplitude. Again, we start with the simplest form is Binary PSK, a.k.a. BPSK. BPSK shifts the phase of the signal between two levels of phase:\n", | |
"\n", | |
"1. $\\phi = 0$, leading to no phase shift of the carrier.\n", | |
"2. $\\phi = \\pi$ leading to a 180 degree phase shift of the carrier.\n", | |
"\n", | |
"Below you can see an example of a BPSK scheme. Notice how the changes going from one type of symbol to another correspond to the phase shifts:\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/bpsk_signal.png\" alt=\"BPSK signal\" width=\"500\"/> |\n", | |
"|*Source: [Nikola Jovanovic, MathWorks](https://www.mathworks.com/matlabcentral/fileexchange/79224-bpsk-system-modeled-and-benchmarked-against-ber-snr)* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.6:* Phase shifts in a BPSK signal.</b></p></a>\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "8ab2dde3-18bf-485b-8bf7-710a0242d584", | |
"metadata": {}, | |
"source": [ | |
"## <a id='2.6-PSK task'>2.6 Coding Task: Generating a PSK signal [12 points]</a>\n", | |
"Generate a PSK signal. This time, mix the symbols with the phase in such a way that it aligns with the description of BPSK above. That is:\n", | |
"- Whenever transmitting 1's the signal wave should have no shift in phase. \n", | |
"- Whenever transmitting 0's the signal wave should have a phase shift of 180 degrees.\n", | |
"\n", | |
"Start by creating a formula for representing the phase. Then, use this as a variable when creating the BPSK-signal." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"id": "744ac3aa-ac73-4750-b6cb-0249dfb7a438", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Carrier wave and binary waveform configuration parameters\n", | |
"sample_rate = 1000 # (Hz)\n", | |
"symbol_duration = 0.1 # (sec)\n", | |
"carrier_frequency = 30 # (Hz)\n", | |
"simulation_time = 1 # (sec)\n", | |
"t = np.arange(0, simulation_time, 1/sample_rate)\n", | |
"carrier_signal = np.sin(2*pi*carrier_frequency*t) # signal = A * sin(2πft + φ)\n", | |
"\n", | |
"# Create binary stream of data\n", | |
"num_of_samples = sample_rate*symbol_duration\n", | |
"num_of_symbols = int(np.floor(np.size(t)/num_of_samples))\n", | |
"symbols = gen_binary_array(num_of_symbols, num_of_samples, seed)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "b82fc5b0-7a7b-4cfa-ab45-7f4e250fd1ed", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# PSK waveform generation\n", | |
"phase = 0 #TODO: write code here\n", | |
"bpsk_signal = 0 #TODO: write code here\n", | |
"\n", | |
"# Binary waveform and PSK modulation waveform Plots\n", | |
"figure, axis = plt.subplots(2)\n", | |
"axis[0].plot(t, symbols)\n", | |
"axis[0].set_title(\"Binary digital data\")\n", | |
"axis[1].plot(t, bpsk_signal, 'r')\n", | |
"axis[1].set_title(\"PSK modulated signal\")\n", | |
"plt.tight_layout()\n", | |
"plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "49ea3a0f-5177-45a4-ae12-4a4e14b9c671", | |
"metadata": {}, | |
"source": [ | |
"## 2.7 IQ-plots, constellation diagrams and QAM\n", | |
"Plotting a PSK signal like we did in Part 2.6 can give us an intitutive way of visualizing the phase shifts, but it can be hard to interpret such plots, especially when we use a higher level PSK modulation scheme (i.e., more than just two phase shifts). Because of this, we usually represent the phase in the complex plane, using what is called a *IQ-plot*. In a IQ-plot, each point represents a unique symbol, based on amplitude and phase. We then plot the respective symbols on a complex plane. Remember that the phase of a symbol can be measured relative to a reference angle (typically 0° relative to carrier signal). A modulation scheme that alters the phase, like Binary Phase Shift Keying (BPSK), could therefore be mapped on to a complex plane, as seen in Figure 2.7.1.\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://pysdr.org/_images/bpsk_iq.png\" alt=\"BPSK on an IQ-plot\" width=\"300\"/> |\n", | |
"|*Source: https://pysdr.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.7:* BPSK scheme mapped to a complex plane.</b></p></a>\n", | |
"\n", | |
"\n", | |
"When we show the set of possible symbols for a given modulation scheme, we call it the “constellation”. Many modulation schemes can be defined by their constellation. Modulation schemes with greater numbers of constellation points are able to transmit more information per symbol, as the more symbols there are in a given modulation scheme, the greater number of bits a single symbol can represent. \n", | |
"\n", | |
"For PSK we always have N different phases. We space them evenly around for best results. When mapping PSK schemes, we often show the unit circle to emphasize that all points have the same magnitude (amplitude). Take for example the modulation scheme QPSK, which has 4 levels of phase. In QPSK, the constellation diagram would consist of four equally spaced points around the unit circle in the complex plane. Each point represents a unique symbol, and the distance between adjacent points corresponds to a phase shift of 90°. The four symbols could correspond to bit patterns like 00, 01, 10, and 11. In 8-PSK, there would be eight equally spaced points, each corresponding to a phase shift of 45°. \n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://pysdr.org/_images/psk_set.png\" alt=\"BPSK, QPSK and 8-PSK\" width=\"500\"/> |\n", | |
"|*Source: https://pysdr.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.8:* BPSK scheme mapped to a complex plane.</b></p></a>\n", | |
"\n", | |
"\n", | |
"Theoretically QPSK can transmit twice as much data using the same amount of bandwidth as BPSK, and 8-PSK could transmit twice as much data using the same amount of bandwidth as QPSK. Another way of phrasing it is that QPSK could transmit the same amount of data using half the bandwidth as BPSK. The tradeoff, however, is that there is less tolerance in the system for error. To illustrate this, consider that BPSK utilizes only two constellation points, effectively dividing the entire I/Q plane into two sections with the decision boundary positioned along the Q-axis. Consequently, a received I/Q value could deviate by 89° in phase, yet the intended symbol could still be accurately interpreted since it falls within the correct decision boundary (as depicted in Fig. 5). However, in QPSK, the I/Q plane is divided into four sections, with decision boundaries present along both the I- and Q-axes, allowing less room for error. Therefore, in QPSK, a symbol received with a phase deviation of 89° would be misinterpreted by the receiver, resulting in a symbol error.\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"figures/BPSK_vs_QPSK.png\" alt=\"Margins of error\" width=\"500\"/> |\n", | |
"|*Source: [NuWaves Engineering](https://nuwaves.com/wp-content/uploads/AN-005-Constellation-Diagrams-and-How-They-Are-Used1.pdf)* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.9:* Comparing symbol errors .</b></p></a>\n", | |
"\n", | |
"A modulation scheme that alters both the amplitude and phase is called Quadrature Amplitude Modulation (QAM). This makes QAM signal able to transmit more information per symbol. In QAM, the constellation diagram represents both amplitude and phase information. The points in the constellation are typically arranged in a rectangular grid pattern, with signal points located at various combinations of amplitude and phase. For example, in 16-QAM, there are 16 signal points arranged in a 4x4 grid. Each point in the grid represents a unique combination of both amplitude and phase, allowing for the transmission of four bits per symbol.\n", | |
"\n", | |
"| |\n", | |
"|---|\n", | |
"| <img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/16QAM_Gray_Coded.svg/1942px-16QAM_Gray_Coded.svg.png\" alt=\"16QAM\" width=\"300\"/> |\n", | |
"|*Source: https://wikipedia.org/* |\n", | |
"<a id=\"fig1\"><p style=\"text-align: center;\"><b>*Figure 2.10:* 16-QAM Constellation diagram.</b></p></a>\n", | |
"\n", | |
"\n", | |
"By adjusting both the amplitude and phase of the signal, QAM modulation schemes can achieve higher data rates and spectral efficiency compared to simpler modulation schemes like PSK or ASK. However, QAM schemes are also more susceptible to noise and interference due to the increased complexity of decoding the received signal." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "6fc0d05a-7aa5-46f0-bde1-8af487e4b02a", | |
"metadata": {}, | |
"source": [ | |
"## <a id='2.7-IQ-plot questions'>2.7 Questions [12 points]</a>\n", | |
"\n", | |
"**Q2.7.1:** Given one specific scheme, do all points on a constellation diagram have to be equally spaced? Why or why not? <br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q2.7.2:** Explain how noise and interference impact the distribution of constellation points in modulation scheme. How does this affect the receiver's ability to correctly demodulate the received signal?<br>\n", | |
"*Answer:* \n", | |
"\n", | |
"**Q2.7.3:** Discuss the trade-offs involved in selecting a higher order QAM scheme, such as 256-QAM or 64-QAM, compared to lower order schemes like QPSK or 8-PSK. Consider factors such as sensitivity to noise and spectral efficiency. <br>\n", | |
"*Answer:* \n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f82690d5-dbb7-47b8-995b-20e461f68ecc", | |
"metadata": { | |
"tags": [] | |
}, | |
"source": [ | |
"\n", | |
" " | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.10.12" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |