1
$\begingroup$

I am very new to DSP, and I am trying to implement a BFSK signal with python, but for some reason, the wave is not "clear", and there seems to be a lot of "discontinuities phases" in the transition between the two frequencies.

Here is the signal plotted with mathplot and you can clearly see that:

The code I used to generate the signal:

import numpy as np
import random
import matplotlib.pyplot as plt


sampling_rate = 44100
baud_rate = 300
samples_per_bit = 1.0 / baud_rate * sampling_rate

# tones representing bits, dummy data (0,1)
bits_in_tones = [1200, 2200] * 100
random.shuffle(bits_in_tones)
bit_arr = np.array(bits_in_tones)

symbols_freqs = np.repeat(bit_arr, samples_per_bit)

t = np.arange(0, len(symbols_freqs) / sampling_rate, 1.0 / sampling_rate)

signal = np.sin(2.0 * np.pi * symbols_freqs * (t))


plt.plot(signal)
plt.show()

Can't figure out what I am doing wrong. any help will be appreciated.

$\endgroup$
2
  • 2
    $\begingroup$ You've implemented the phase of the sin() function as switching between 2 independent oscillators of different frequencies both starting at time 0. Instead, implement the phase of the output sin() as the integral of the frequency value (aka the cumulative sum of the delta phi for the instantaneous frequency at the given sample rate), and you will get Continuous Phase Frequency Shift Keying (CPFSK). $\endgroup$
    – Andy Walls
    Commented Dec 28, 2021 at 0:01
  • $\begingroup$ The phase increment per sample is $\Delta \phi = \pi \dfrac{f_i}{F_s / 2}$ by the way. Where $f_i$ is the instantaneous frequency applicable to that sample, either 1200 or 2200 in your application. $\endgroup$
    – Andy Walls
    Commented Dec 28, 2021 at 0:25

1 Answer 1

5
$\begingroup$

The phase of your output signal is not continuous, because you have implemented the phase as the output of one of two independent frequency oscillators both starting at time $0$, so their phases are not synchronized when you switch frequencies.

We can get continuous phase by noting that the phase is the integral of instantaneous frequency over time:

$$\phi(t) = \int^t 2\pi f_i(\tau) d\tau = \int^t d\phi(\tau)$$

or the discrete time equivalent, normalizing $1$ sample of a Nyquist frequency wave to be equivalent to $\pi$ radians of phase increment (or, equivalently, setting $\Delta t = 1/F_s$, the duration of $1$ sample time):

$$ \phi[n] = \sum_{k=0}^{n} \dfrac{\pi}{\frac{F_s}{2}} f_i[k] = \sum_{k=0}^{n} 2\pi f_i[k]\Delta t =\sum_{k=0}^{n} \Delta\phi[k]$$

Demonstrating this with a small addition to your script:

import numpy as np
import random
import matplotlib.pyplot as plt


sampling_rate = 44100
baud_rate = 300
samples_per_bit = 1.0 / baud_rate * sampling_rate

# tones representing bits, dummy data (0,1)
bits_in_tones = [1200, 2200] * 100
random.shuffle(bits_in_tones)
bit_arr = np.array(bits_in_tones)

symbols_freqs = np.repeat(bit_arr, samples_per_bit)

t = np.arange(0, len(symbols_freqs) / sampling_rate, 1.0 / sampling_rate)

signal = np.sin(2.0 * np.pi * symbols_freqs * (t))


plt.plot(signal)
plt.show()

# New lines here demonstrating continuous phase FSK (CPFSK)
delta_phi = symbols_freqs * np.pi / (sampling_rate / 2.0)
phi = np.cumsum(delta_phi)
signal2 = np.sin(phi)

plt.plot(t, signal+1.0)
plt.plot(t, signal2-1.0)
plt.show()

Of course this implementation runs into numerical problems: as we keep incrementing phase, we will begin to lose precision. Also, it is bad practice to pass arguments to the $\sin()$ function that are far outside of the interval $[-2\pi,2\pi]$. You'll want to wrap the phase to keep it within $[0,2\pi)$ as you cumulatively sum it, as opposed to the naive, convenient cumulative sum I've done here.

Here is a comparison of the output of your original switched FSK implementation vs. the CPFSK implementation: Comparison of switched FSK vs. CPFSK output signals

$\endgroup$
1
  • 1
    $\begingroup$ Thank you very much for the detailed explanation! It helped a lot. $\endgroup$
    – hoshenk13
    Commented Dec 29, 2021 at 2:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.