Added some comments

This commit is contained in:
David Lenfesty 2019-09-04 21:25:32 -06:00
parent 1eede7d8c4
commit 38f866cd1b
3 changed files with 63 additions and 1 deletions

View File

@ -16,6 +16,12 @@ pilot_value = 1 + 1j
snr_db = 300
def sim(in_data):
"""
Simulate effects of communication channel.
Convolves the channel response, and adds random noise to the signal.
"""
out_data = np.ndarray((len(in_data), len(in_data[0])), dtype=np.csingle)
# noise stuff is straight copied from the DSP illustrations article
@ -34,6 +40,32 @@ def sim(in_data):
# Again, most of this is stolen from the guide
# this sort of stuff I had no idea about before I read the guide
def estimate(in_data, pilots=0):
"""
Estimate channel effects by some cool properties.
Since the effects of the channel medium is a convolution of the transmitted signal,
we can abuse this for some easy math.
We take the time domain input signal and turn that into a frequency domain signal.
If we had a perfect channel, this frequency domain signal would exactly equal the signal
transmitted. But it doesn't. However, we are in the frequency domain, which means
convolution turns into multiplication, and to find the effect of the channel on each
subcarrier, we can simply use division.
Unfortunately, we don't know what the original data was, so we use "pilot" subchannels, which transmit
known information. We can use this to get estimates for each of the pilot carriers, and finally, we can interpolate
these values to get an estimate for everything.
There are a few issues with this method:
1: It is very estimate-ey. We have to interpolate from a subset of the carriers.
2: It is quite inefficient. We are sending useless information on every symbol.
I think a better solution is to send a known symbol (or a set of known symbols)
at the beginnning of each transmission instead. This means we get a full channel estimate
every time. This also has the advantage of being able to synchronise the symbols. Since I
will be implementing some sort of protocol anyways, I think this will be a good idea. As well,
we move slow enough that the channel will not likely change significantly over a single packet.
"""
all_carriers = np.arange(len(in_data[0]))

24
main.py
View File

@ -7,7 +7,6 @@ Hopefully eventually this modem design makes it onto an fpga.
TODO:
Change channel estimation to pre-amble symbols
Add comments for functions
Explain what the main function is doing
Add more errors, like a shifted signal
Add support for 16-QAM, 64-QAM, etc...
Add some sort of payload support, i.e. be able to drop the padding at the end
@ -29,6 +28,14 @@ import qam
from serpar import parallelise, serialise
def cp_add(in_data, prefix_len):
"""
Adds a cyclic prefix to an array of symbols, with a specified length.
This changes the linear convolution of the data into a circular convolution,
allowing easier equalization.
As well, it helps remove inter-symbol interference.
"""
out_data = np.ndarray((len(in_data), len(in_data[0]) + prefix_len), dtype=np.csingle)
for i in range(len(in_data)):
@ -38,6 +45,10 @@ def cp_add(in_data, prefix_len):
return out_data
def cp_remove(in_data, prefix_len):
"""
Removes cyclic prefix from retrieved data. Naively assumes that data is correctly aligned.
"""
out_data = np.ndarray((len(in_data), len(in_data[0]) - prefix_len), dtype=np.csingle)
for i in range(len(in_data)):
@ -54,25 +65,36 @@ if __name__ == '__main__':
bytes = bytearray(data, 'utf8')
# Turn data into a parallelised form, able to be QAM-modulated
parallel = parallelise(64, bytes)
# modulate data with a QAM scheme
modulated = qam.modulate(parallel, pilots=20)
# Run IFFT to get a time-domain signal to send
ofdm_time = np.fft.ifft(modulated)
# Add cyclic prefix to each symbol
tx = cp_add(ofdm_time, 16)
# Simulate effects of a multipath channel
rx = channel.sim(tx)
# Remove cyclic prefix from incoming symbols
ofdm_cp_removed = cp_remove(rx, 16)
# Bring symbols back into frequency domain to get carrier channels
to_equalize = np.fft.fft(ofdm_cp_removed)
# Find an estimate for channel effect
H_est = channel.estimate(to_equalize, pilots=20)
# Equalise based on estimated channel
to_decode = channel.equalize(to_equalize, H_est)
# Demodulate symbol into output data
to_serialise = qam.demodulate(to_decode, pilots=20)
# Turn data back into string
data = serialise(64, to_serialise)
print(data)

8
qam.py
View File

@ -16,6 +16,10 @@ def modulate(in_data, pilots=0):
"""
Modulates into 4-QAM encoding, might change that number later.
This turns input data into a "constellation" of complex numbers,
ready to be fed into an IFFT. This constellation could also be used
directly with an IQ modulator.
Parameters:
in_data - m X n array, m symbols to run on
pilots (optional) - number of pilot signals to intersperse into carriers
@ -54,6 +58,10 @@ def modulate(in_data, pilots=0):
return out_data
def demodulate(in_data, pilots=0):
"""
Demodulates incoming signal.
"""
all_carriers = np.arange(len(in_data[0]), dtype=int)
if pilots > 0: