A few considerations for Practical 1. Your internet must be open for the maths to render properly. Hit the F5 button a few times if it still doesn't render properly.

Question 1 & 2:

As a result of an incorrect usage of np.linspace, some did not sample the signals at the correct sampling frequencies. Let's investigate.

40 samples at a sampling frequency of $f_s = 2000\,{\rm Hz}$ (with $T = 1/f_s = 0.0005$) do not have sampling timestamps at t = np.linspace(0, 40*T, 40) since the generate timestamps are then from $0$ upto (and including) $40 T$. Pass the argument endpoint=False, as in t = np.linspace(0, 40*T, 40, False) to exclude the endpoint $40T$.

Note the difference:
With t = np.linspace(0, 0.02, 40), we generate $t[n] = [0, 0.00051282,\ldots,0.02]$, which results in an incorrect $f_s = 1/(t[1]-t[0]) = 1950\, {\rm Hz}$.
With t = np.linspace(0, 0.02, 40, False), we generate $t[n] = [0, 0.0005,\ldots, 0.0195]$, which results in a correct $f_s = 1/(t[1]-t[0]) = 2000\, {\rm Hz}$.

1.4) For the $200\, {\rm Hz}$ signal, we have $f_\omega = f/f_s = 0.1$. We can therefore plot this as $20 \sin(0.1\cdot 2 \pi n)$, with $n = [0,1,\ldots,39]$.

The combined output for Question 1 and Question 2 would result in something like this:

In [50]:

Question 3:

We stumbled upon an ambiguity in the questions: Question 3.1 we referred to a time signal with $100\,{\rm Hz}$ and a $2100\,{\rm Hz}$ components, and Question 3.3 onwards referred to time signals of $100\,{\rm Hz}$ and $900\,{\rm Hz}$. I ask you to resolve this ambiguity by allowing Question 3.3 onwards to refer to the $100\,{\rm Hz}$ and $900\,{\rm Hz}$ signals of Question 1 & 2. I.e. use the frequencies as is stated in the question.

$T_p$ is straight-forward to calculate since we are only concerned with the continuous-time domain.

$N_p$ is a bit tricky to calculate. Look at it this way, for a discrete-time signal to have a period, we need a $N_p$ such that $x[n + N_p] = x[n], \forall n$. We can work this out by investigating the fraction of $T_p/T$, with $T$ as the sampling frequency. For what positive whole number $c$ do we have a whole number for $c \times \frac{T_p}{T}$? Go look at the graphs in Question 1 for a clear understanding of this. (*Cough*, *cough* 20 samples.)

Converting $N_p$ back to seconds requires just one multiplication.

Note that 3.6 is a trick Question taking advantage of the difference between Hz and rad/s.

In [54]:

Question 4:

If you have "complex casting" warnings this question, your fill-in variable that you are trying to in-place add to is not of type complex. You can set it as such with X = np.zeros(...)*1j, or alternatively you can use addition-and-assign X = X + ... instead of in-place addition X += ... in your for-loop.

Hint, you can use your $f_\omega$ arrays directly in the numpy functions, then you only need one for-loop: for i in range(len(x)).

We also had a typo, we want the plot over $$-\frac{1}{2} < f_\omega < \frac{3}{2}.$$ (We left out the minus sign in the original document by accident.)

In [52]:

Question 5:

Unfortunately it seems like the WinPython on the computers cannot play the audio at very low sampling frequencies. If you hit such a limit, just interpret what you can from the graphs.

In [53]: