In this part of the lab we are going to experiment with Digital modulation and communication. Network Communication systems have layered architechture. The bottom layer is the physical which implements the modulation. Here we will use AFSK, which is a form of BFSK in the audio range (hence the 'A'). We will write a modulator/demodulator for AFSK. In the following part of lab we will leverage AX.25, which is an amateur-radio data-link layer protocol. AX.25 is a packet based protocol that will help us transmit data using packets. It implements basic synchronization, addressing, data encapsulation and some error detection. In the ham world, an implementation of AFSK and AX.25 together is also called a TNC ( Terminal Node Controller ). In the past TNC's were separate boxes that hams used to attach to their radios to communicate with packet-based-communication. Today, it is easy to implement TNC's in software using the computer's soundcard.... as you will see here!
%pylab
# Import functions and libraries
import numpy as np
import matplotlib.pyplot as plt
import pyaudio
import Queue
import threading,time
import sys
from numpy import pi
from numpy import sin
from numpy import zeros
from numpy import r_
from scipy import signal
from scipy import integrate
import threading,time
import multiprocessing
from rtlsdr import RtlSdr
from numpy import mean
from numpy import power
from numpy.fft import fft
from numpy.fft import fftshift
from numpy.fft import ifft
from numpy.fft import ifftshift
import bitarray
from scipy.io.wavfile import read as wavread
import serial
%matplotlib inline
AFSK1200 encodes digital binary data at a data-rate of 1200b/s. It uses the frequencies 1200Hz and 2200Hz ( center frequency of $1700$Hz $\pm 500$ Hz) to encode the '0's and '1's (also known as space and mark) bits. Even though it has a relatively low bit-rate it is still the dominant standard for amature packet radio over VHF. It is a common physical layer for the AX.25 packet protocol and hence a physical layer for the Automatic Packet Reporting System (APRS), which we will describe later.
The exact frequency spectrum of a general FSK signal is difficult to obtain. But, when the mark and space frequency difference $\Delta f$ is much larger than the bit-rate, $B$, then the bandwidth of FSK is approximately $2\Delta f + B$. This is not exactly the case for AFSK1200 where the spacing between the frequencies is 1000Hz and the bit-rate is 1200 baud.
Note, that for the (poor) choice of 1200/2200Hz for frequencies, a synchronous phase (starting each bit with the same phase) is not going to be continuous. For the Bandwidth to be narrow, it is important that the phase in the modulated signal is continuous. For this reason, AFSK1200 has to be generated in the following way: $$ s(t) = cos\left(2\pi f_c t + 2\pi \Delta f \int_{\infty}^t m(\tau)d\tau \right),$$ where $m(t)$ has the value =1 for a duration of a mark bit, and a value =-1 for a duration of a space bit. Such $m(t)$ signal is called an Non-Return-to-Zero (NRZ) signal in the digital communication jargon. Here's a link to some notes provided by Fred Nicolls from the university of Cape Town
The integration guarentees that the phase in continuous. In addition, the instantaneous frequency of $s(t)$ is the derivative of its phase, which is $2\pi f_c + 2\pi \Delta f m(t)$, which is exactly what we need.
Write a function sig = afsk1200(bits)
the function will take a bitarray of bits and will output an AFSK1200 modulated signal of them, sampled at 44100Hz. Note that Mark frequency is 1200Hz and Space Frequency is 2200Hz. If your USB device prefers 48000Hz, then use 48KHz for the sampling rate.
Note, that 44100 does not divide by 1200 so each "bit" will have non-integer length in samples. If you are not careful, this would lead to deviation from the right rate over time. To make sure that you produce signals that have the right rate over time generate the signal first at a rate of 44100*4 (which does divide by 1200) for the entire bit sequence and then downsample by 4 at the end. You don't need to low-pass filter, since the signal is narrow banded anyways.
For integration, use the function integrate.cumtrapz, which implements the trapezoid method.
def afsk1200(bits):
#the function will take a bitarray of bits and will output an AFSK1200 modulated signal of them, sampled at 44100Hz
fs = 44100*4
#your code here
sig = real(exp(1j*ph[::4]))
return sig
bits=bitarray.bitarray((rand(64)>0.5).tolist())
sig = afsk1200(bits)
fig = figure(figsize(16,4))
plot(linspace(0,22050/4,20000/4),abs(np.fft.fft(sig,n=40000))[:20000/4])
xlabel('Hz')
title('Spectrum of AFSK1200')
AFSK is a form of digital frequency modulation. As such it can be demodulated like FM. Because AFSK alternates between two frequencies, we can place two bandpass filters around the frequency of the Mark and Space and use envelope detection to determine which frequency is active in a bit period. This is a non-coherent AFSK demodulation, because the receiver phase does not need to be synced to the transmitter phase in order to demodulate the signal. The implementation we will use here is loosly based on on the one by Sivan Tolede (4X6IZ), a CS faculty in Tel-Aviv university who has written a nice article on a high-performance AX.25 modem. You can find it Here.
Here's a diagram of a non-coherent AFSK1200 demodulator that returns an NRZ signal:
# Generate filters
TBW = 2
N =
BW =
h_space =
h_mark =
print(BW)
# plot the magitude impulse response of the space filter
figure
plot(abs(h_space))
# Filter the signal and compute the difference between the envelopes of the results use signal.fftconvolve
# plot the resulting difference NRZa
# your code here:
NRZa =
sign
) function of the difference. # your code here.
We can also demodulate AFSK using FM demodulation. Recall that the bandwidth of AFSK is approximately $2\Delta f + B$ where $B$ is the bitrate (1200Hz for AFSK1200). The afsk signal is $s(t) = cos\left(2\pi f_c t + 2\pi \Delta f \int_{\infty}^t m(\tau)d\tau \right)$. If we filter the signal by passing only the positive frequencies we will get $s(t) = \frac{1}{2}e^{2\pi j f_c t + 2\pi j \Delta f \int_{\infty}^t m(\tau)d\tau}$. This is the analytic signal and we can FM demodulate it in a similar way as we did for the FM radio Lab by taking the derivative of the phase. After appropriate scaling and offset we can extract the NRZ signal.
# your code here
def nc_afskDemod(sig, TBW=2.0, N=74):
# non-coherent demodulation of afsk1200
# function returns the NRZI (without rectifying it)
# your code here
return NRZI
def fm_afskDemod(sig, TBW=4, N=74):
# non-coherent demodulation of afsk1200
# function returns the NRZI (without rectifying it)
# your code here
return NRZI
One of the ways to evaluate the properties of a digital modulation scheme is to compute the bit-error-rate (BER) curves with as a function of signal-to-noise ratio (SNR). The BER is the number of bit errors (received bits that have been altered due to decoding error) divided by the total number of transmitted bits.
Let's calculate the BER for AFSK with both the non-coherent and FM demodulations:
# your code here
fig = figure(figsize=(16,4))
plot(NRZa[:3000])
title('result of non-coherent demodulation')
fig = figure(figsize=(16,4))
plot(NRZ_fma[:3000])
axis((0,3000,-2,2))
title('analog NRZ of fm demodulation')
# compute bit error rate
Your Bit error rate will depend also on the quality of the reconstruction. You can try to repeat the experiment for different choices of filters if you like.
BER curves are usually displayed in log log of the BER vs SNR. SNR is measured by energy per bit over noise power spectral density. Since we are just interested in the trend, we will plot the BER vs 1/noise standard deviation
# your code here: