# **109-2通訊實驗期末報告--PAM與PCM的傳送與接收**
## 實驗目的
1.用Python實現信號處理。
2.使用“平頂PAM”(Fat-Top PAM)(脈衝幅度調製)發送和接收 ASCII文本字符串。
3.使用PCM(脈衝編碼調製)來傳輸模擬信號。
## 介紹PAM與PCM
### (a)名詞解釋
* 脈波編碼調變(簡稱PCM)是一種數位訊號的數位化方法。PCM將訊號的強度依照同樣的間距分成數段,然後用獨特的數位記號(通常是二進位)來量化。
* 脈波振幅調變(簡稱PAM)是一種訊號調變的方式,是將一連串的類比訊號用脈波訊號取樣調變,藉此將原始訊號之振幅擷取出來的調變方法。
### (b)原理
#### **1. Pulse-Amplitude Modulation (PAM)**
<div style="text-align: center">
<img src="https://i.imgur.com/Uy70g4F.png"/>
</div>
<center>圖1:PAM轉換示意圖 [1]</center>center><br></br>
由於我們要將二進位ASCII Code(dn),藉由PAM傳輸出去,但是PAM的輸入必須是連續信號(Continuous-Time Signal),因此要dn轉為CT Signal :s(t),再進行PAM。離散時間(DT)序列需要先轉換為(CT)後,才能通過波形通道傳輸。dn為經過ASCII Code轉換後的DT訊號,用0、1表示,此時可以將dn看成不同時間下脈衝函數的組合,值0為0,1的話代表一個脈衝。其中位元速率(Bit Rate) Fb=1/Tb。
* #### DT 轉 CT 過程
![](https://i.imgur.com/MMSm4O4.png)
<center>圖2:DT轉CT公式圖[2]</center>center><br></br>
利用一個矩形函數p(t)隨著n越大,p(t)向右平移並和dn相乘,可以看成在不同時間下,和單位脈衝的相乘的,利用Sifting Property(篩選定理):「任何任意函數p(t)和脈衝在時間nTb相乘時,其結果只保留原信號在該時間的值:p(t)*δ(t-nTb)=p(nTb)*δ(t-nTb),將這些結果相加起來,可以表示Σp(t)*δ(t-nTb)=p(nTb)」。用這定理即可把離散訊號依據振幅大小,將DT轉成CT,又保留矩形函數平頂的特性,符合 Flat-Top PAM的性質。
![](https://i.imgur.com/YYpfw1Q.png)
<center>圖3:矩形函數[3]</center>center><br></br>
而矩形和數的頻寬為Tb/2,其大小的用意是每一個0、1訊號間隔為Tb,為了在每次Sifting Property不去影響下一個(N+1)離散訊號的值,但又不能頻寬太窄而取不到值,所以矩形的頻寬為取樣周期的一半,才能完全將離散訊號轉成連續訊號。
![](https://i.imgur.com/ZcztWYz.png)
<center>圖4:DT Signal轉CT Signal結果圖[4]</center>center><br></br>
* #### CT轉DT
CT Signal轉成DT Signal就是要做取樣:
![](https://i.imgur.com/TdvZi9D.png)
<center>圖5:PAM訊號取樣原理圖[5]</center>center><br></br>
s(t)的取樣結果,是用上面得到連續的訊號,取樣頻率為位元速率Fb,對不同時間間隔nTb做取樣,得到圖中的結果:取樣的結果是對矩形函數dn的摺積(Coonvolution)。
下圖為一個對矩形函數脈衝響應的圖,有了下圖可以觀察,一個矩形函數可以由無數脈衝表示,而我們的情境的道理是一樣,只不過是對不同時間的脈衝響應摺積,因為摺積的概念就是疊加嘛!!!!
![](https://i.imgur.com/nXaX1mO.png)
<center>圖6:矩形函數脈衝響應圖[6]</center>center><br></br>
* #### Point:取樣頻率的問題
由於我們要將一個連續訊號轉成離散訊號,就需要用到取樣頻率,但是為了保持取完的訊號保有離散的特性,所以取樣頻率不能太大,但是依據取樣定理,也不能低於原信號頻率的兩倍。
#### **2.Pulse Code Modulation(PCM)**
![](https://i.imgur.com/cWcOrMx.png)
<center>圖7:PCM Flow Diagram[7]</center>center><br></br>
PCM有三個步驟:取樣(Sampling)、量化(Quantizing)、編碼(Encoding)。而我們這題因為是二進位ASCII Code,所以不太需要取樣,因為輸入訊號已經是離散的,只要完成後面兩個步驟即可完成PCM
* 量化(Quantizing)
將訊號的振幅依據等分大小做劃分,其量化的格數越多,其頻寬會越高。
* 編碼(Encoding)
將量化的格數依據二進位做編碼,例如:量化後有七格,其編碼會從第一量化格000,到第七格111,其結果如下圖:
![](https://i.imgur.com/8TwIGOz.png)
<center>圖8:PCM原理圖 [8]</center>center><br></br>
![](https://i.imgur.com/X7xRLuG.png)
<center>圖9:經PCM後的二進位字串 [9]</center>center><br></br>
## **ASCII Code**
* 有8 bits 其中 7bits顯示字元,1 bit當作parity bit
* parity bit:同位位元有兩種類型:偶核對位元與奇核對位元
以偶核對位元來說,如果一組給定資料位中1的個數是;奇數,補一個bit為1,使得總的1的個數是偶數。例:000001, 補一個bit為1, 變成00000011。
以奇核對位元來說,如果給定一組資料位中1的個數是奇數,補一個bit為0,使得總的1的個數是奇數。例:0000001, 補一個bit為0, 變成00000010。
* 其中ASCII Code分為兩大字元: (a)控制字元 (b)符號字元
* 表示二進位字串有2種:(a)MSB-First (b)LSB-First
### MSB&LSB
1. MSB (most significant bit): 最高有效位元
2. LSB (least significant bit):最低有效位元
![](https://i.imgur.com/h7UX9rl.png)
<center>圖10: MSB和LSB [10]</center>center><br></br>
### MSB-First & LSB-First
如果當位元序列是MSB-First,其表示方法為由左至右,從MSB排序,此時MSB在序列最左側,LSB在右側;如果當位元序列是LSB-First,其表示方法為由左至右,從LSB排序,此時LSB在序列最左側,MSB在右側如下圖所示:
![](https://i.imgur.com/06i81Vz.png)
<center>圖11: MSB-First和LSB-First [11]</center>center><br></br>
### ASCII Code Table
![](https://i.imgur.com/tYu8ylq.png)
<center>圖12: ASCII Code表 [12]</center>center><br></br>
## Python的環境
我們使用Jupyter Notebook,優點有:
1.極其適合資料分析
2.支援多語言
3.分享便捷
4.遠端執行
5.互動式展現
<div style="text-align: center">
<img src="https://i.imgur.com/NRWh1zY.png"/>
</div>
<center>圖13: Jupyter NoteBook Logo [13]</center>center><br></br>
## Pyrhon程式碼
### ASCII Code Coversion in Python
```python=
#File: ascfun.py
# Functions for conversion between ASCII and bits
from pylab import *
import numpy as np
def asc2bin(txt, bits=8):
"""
ASCII message to serial binary conversion
>>>>> dn = asc2bin(txt, bits) <<<<<
where txt ASCII message (text string)
abs(bits) bits per character, default: 8
bits > 0 LSB first parallel to serial conv
bits < 0 MSB first parallel to serial conv
dn binary output DT sequence
"""
txtnum = np.array([ord(c) for c in txt], np.int16)
if bits > 0:
# powers of 2: 2**0, 2**-1, 2**-2, ..., 2**-(bits-1)
p2 = np.array(np.power(2.0, -np.arange(bits)), np.float32)
else:
# powers of 2: 2**(bits+1), ..., 2**-2, 2**-1, 2**0
p2 = np.array(np.power(2.0, np.arange(bits+1,1)), np.float32)
# 2-dim array of bits, one row per character in txt
B = np.array(np.mod(np.floor(np.outer(txtnum,p2)),2),np.int8)
# parallel to serial conversion
return np.reshape(B, -1)
```
<center>程式1: ASCII Code To Bits [14]</center>center><br></br>
```python=
def bin2asc(dn, bits=8, flg=1):
"""
Serial binary to ASCII text conversion
>>>>> txt = bin2asc(dn, bits, flg) <<<<<
where dn binary input sequence
abs(bits) bits per char, default=8
bits > 0 LSB first parallel to serial
bits < 0 MSB first parallel to serial
flg != 0 limit range to [0...127]
txt output text string
"""
no_of_char = int(np.floor(size(dn)/abs(bits)))
#if(mod(len(dn),8)!=0): # If length of input data bits not a multiple of 8, add zeros to make it a multiple of 8
#no_of_zeros = len(dn) - 8*no_of_char
#if bits > 0: #Append the zeros
#np.lib.pad(dn,(0,no_of_zeros),'constant',constant_values = (0))
dec =[0]*(no_of_char) #Initializing array for decimal values with zeros
if bits > 0: # Pos powers of 2, increasing exp
p2 = np.power(2.0,arange(0,bits,1))
else: # Pos powers of 2, decreasing exp
p2 = np.power(2.0,-1+arange(-bits,0,-1))
for index in np.arange(no_of_char):# loop for converting 8 binary bits to decimal value sequenctially from the input bit sequence
dec[index] = int(inner(dn[abs(bits)*index+arange(0,abs(bits))],p2))
data_string = ''.join(chr(c) for c in dec) # Converting decimal value to ASCII for each element in the array
return data_string # string returned
```
<center>程式2: Bits To ASCII Code [15]</center><br>
### Flat-Top PAM in Python
```python=
import numpy as np
import ascfun as af
import matplotlib.pyplot as plt
def ftpam01(txt, Fb, Fs):
"""
Function that accepts an ASCII text string as input and
produces a corresponding binary unipolar flat-top PAM
signal s(t) with bit rate Fb and sampling rate Fs.
>>>>> tt, st = ftpam01(txt, Fb, Fs) <<<<<
where tt: time axis for PAM signal s(t) (starting at -Tb/2)
st: flat-top PAM signal s(t)
s(t) = dn, (n-1/2)*Tb <= t < (n+1/2)*Tb
txt: ASCII text string, 8 bits/symbol,
converted to LSB-first bitstream dn
Fb: bit rate of dn, Tb=1/Fb
Fs: sampling rate of s(t)
"""
bits = 8 # Bits per ASCII symbol
# >> Convert txt to bitstream dn here, LSB-first <<
dn = af.asc2bin(txt, bits = 8)
N = len(dn) # Total number of bits
Tb = 1/float(Fb) # Time per bit
ixL = round(-0.5*Fs*Tb) # Left index for time axis
ixR = round((N-0.5)*Fs*Tb) # Right index for time axis
tt = np.arange(ixL, ixR)/float(Fs) # Time axis for s(t)
# >> Generate flat-top PAM signal s(t) here <<
st = np.sign(dn)
return tt, st
```
<center>程式3: Bits To Flat-Top PAM[16]</center><br>
```python=
import numpy as np
def ftpam_rcvr01(tt, rt, Fbparms):
"""
Binary unipolar Flat-top PAM sampling receiver for input signal
r(t) with bitrate Fb and sampling rate Fs. The PAM signal rt
with associated time axis tt (starting at -0.5*Tb) is sampled
at times t = n*Tb + dly*Tb, where 0<=dly<1 is a delay parameter,
to obtain a DT sequence rn.
>>>>> rn, ixn = ftpam_rcvr01(tt, rt, Fbparms) <<<<<
where rn: sampled DT sequence
ixn: sampling time indexes
tt: time axis for r(t)
rt: received (noisy) PAM signal r(t)
Fbparms: = [Fb, dly]
Fb: Bit rate of PAM signal, Tb=1/Fb
dly: sampling delay for r(t) -> r_n as a fraction of Tb
sampling times are t=n*Tb+t0 where t0 = dly*Tb
"""
if type(Fbparms)==int:
Fb, dly = Fbparms, 0
else:
Fb, dly = Fbparms[0], 0
if len(Fbparms) > 1:
dly = Fbparms[1]
Fs = (len(tt)-1)/(tt[-1]-tt[0]) # Extract sampling rate
#>> Compute sampling indexes ixn and samples rn here <<
n = len(rt)
ixn =(n+dly)/float(Fb)
rn = np.sign(rt)
return rn, ixn
```
<center>程式4: Flat-Top PAM To Bits[17]</center><br>
### PCM in Python
```python=
#File: pcmfun.py
# Functions for conversion between m(t) and PCM representation
from pylab import *
import numpy as np
def mt2pcm(mt, bits=8):
"""
Message signal m(t) to binary PCM conversion
>>>>> dn = mt2pcm(mt, bits) <<<<<
where
mt normalized (A=1) "analog" message signal
bits number of bits used per sample
dn binary output sequence in sign-magnitude
form, MSB (sign) first
"""
step_size = 2/2**(bits)# total dynamic range/number of levels
qt = (mt*2**(bits-1)) #Scaling the signal by dividing step size to make step size 1
corr = array([int(sign(c)) for c in mt]) # Since we cannot represent 2**(bits-1) value, we apply different correction factor
# for positive values and negative values
dt = qt -corr/2
dt = array([int(floor(c)) for c in dt])
bit_seq = []
# Decimal to binay conversion - first MSB Rule
for index in arange(size(qt)):
if sign(dt[index]) <0:
sign_bit = 1
dt[index] = abs(dt[index]+1);
else:
sign_bit = 0
p2 = np.power(2.0,arange(-bits+1,0,1)+1)
B = array(mod(floor(outer(dt[index],p2)),2),int)
B= reshape(B,size(B))
C = insert(B,0,sign_bit)
bit_seq = insert(bit_seq,len(bit_seq),C)
return bit_seq
```
<center>程式5:PCM Modulation [18]</center><br>
```python=
def pcm2mt(dn, bits=8):
"""
Binary PCM to message signal m(t) conversion
>>>>> mt = pcm2mt(dn, bits) <<<<<
where
dn binary output sequence in sign-magnitude
form MSB (sign) first
bits number of bits used per sample
mt normalized (A=1) "analog" message signal
"""
# n-bit Binary to Decimal conversion - first MSB Rule
no_of_dec = int(floor(size(dn)/abs(bits)))
dec =[0]*(no_of_dec) #Initializing array for decimal values with zeros
# Pos powers of 2, decreasing exp
p2 = np.power(2.0,-1+arange(bits-1,0,-1))
for index in arange(no_of_dec):# loop for converting nbit binary value to decimal value sequenctially from the input bit sequence
dec_num = int(inner(dn[bits*index+arange(1,bits)],p2))
sign_bit = dn[bits*index]
if sign_bit>0:
dec[index] = -dec_num -1
else:
dec[index] = dec_num
dec = reshape(dec,size(dec))
corr_factor = array([int(sign(c)) for c in dec]) # Since we cannot represent 2**(bits-1) value, we apply different correction factor
# for positive values and negative values
mthat = dec
mthat = array([int(ceil(c)) for c in mthat])
mthat = mthat/2**(bits-1)
return mthat
```
<center>程式6:PCM Demodulation [19]</center><br>
### WAVE檔的燒錄和讀取
```python=
import numpy as np
import scipy.io.wavfile as sio_wav
def wavread(fname):
"""
Read N-channel 16-bit PCM wav-file
>>>>> Fs, rt = wavread(fname) <<<<<
where fname file name of wav-file
Fs sample rate of wav-file
rt data read from wav-file, N channels,
Nsamples data samples per channel,
(Nsamples x N) numpy array of type np.float32,
data samples normalized to range -1 ... +1
"""
Fs, rt = sio_wav.read(fname)
if rt.dtype is np.dtype(np.int16):
rt = rt/float(2**15-1) # convert to -1...+1 range
rt = np.array(rt, np.float32)
else:
print('not a 16-bit PCM wav-file')
rt = []
return Fs, rt
def wavwrite(fname, Fs, xt):
"""
Write N-channel 16-bit PCM wav-file
>>>>> wavwrite(fname, Fs, xt) <<<<<
where fname file name of wav-file
Fs sample rate of wav-file
xt data to be written to wav-file
(Nsamples x N) numpy array of floats
normalized to range -1...+1
N channels, Nsamples data samples per channel
"""
# convert to np.int16 data type
xt = np.array((2**15-1)*xt, np.int16)
sio_wav.write(fname, Fs, xt)
```
<center>程式7:wav檔燒錄和讀取</center><br>
## 實驗程式結果
### PAM
#### 繪圖程式碼
```python=
import numpy as np
import ascfun as af
import matplotlib.pyplot as pl
txt = 'C'
Fb=100
Fs = 44100
tt, st = ftpam01(txt, Fb, Fs)
data = np.repeat(st, 2)
t = 0.5 * np.arange(len(data))/Fb
t1 = t - 0.005
fsz = (10,2.5)
plt.figure(1,figsize=fsz)
plt.step(t1, data, 'b', linewidth = 2, where = 'post')
plt.plot(t[::2], st ,"rx",label='Samples at t=n$T_b$')
plt.ylim(-0.2,1.2)
str = 'Unipolar Binary Flap-top PAM for String \'C\' '
str = str + ',$F_b={}$ bits/sec,$F_s={}$ Hz'.format(Fb,Fs)
plt.title(str)
plt.ylabel('$s(t)$')
plt.xlabel('t [sec]')
plt.legend()
plt.grid()
plt.show()
```
<center>程式8:繪製PAM程式</center><br>
#### 繪圖結果
<div style="text-align: center">
<img src="https://i.imgur.com/MPbcJg7.pnghttps://i.imgur.com/NRWh1zY.png"/>
</div>
<center>圖14: 經Flap-top PAM調變後的字元 'C' </center>center><br></br>
```python=
import wavfun as wf
wf.wavwrite('MyTest.wav',Fs,0.99*st/float(max(abs(st)))) # Write wav-file
wf.wavread('MyTest.wav')
```
<center>程式9:建立wav檔</center><br>
```python=
import numpy as np
import ascfun as af
import wavfun as wf
Fb = 100 # Data bit rate
Fs, rt = wf.wavread('MyTest.wav')
tt = np.arange(rt.size)/float(Fs)-1/(2.0*Fb) # Time axis for rt
Fbparms = [Fb, 0]
rn, ixn = ftpam_rcvr01(tt, rt, Fbparms)
dnhat = np.floor(rn)
txthat = af.bin2asc(dnhat, bits=8, flg=1) # >>Convert bitstream dnhat to received text string<<
print(txthat) # Print result is 'C'
```
<center>程式10:PAM 解調 && Bits To ASCII Code結果</center><br>
![](https://i.imgur.com/9O2dhDS.png)
<center>圖15:程式9結果</center><br>
### PCM
```python=
import ascfun as af
sd = 'C'
st = af.asc2bin(sd,bits=8)
print("ACII Code of sd = %s" % st)
dd = mt2pcm(st,bits=8)
print("PCM of st = %s" % dd)
```
<center>程式11:將字元轉成ASCII Code,再PCM</center><br>
![](https://i.imgur.com/DYxC8Sw.png)
<center>圖16:程式10結果</center><br>
```python=
ff = pcm2mt(dd, bits= 8)
ww = np.sign(ff)
txt = af.bin2asc(ww,bits=8)
print("The Result is %s" % txt)
```
<center>程式12:PCM解調,再Bits To ASCII Code</center><br>
![](https://i.imgur.com/ua1XxTd.png)
<center>圖17:程式11結果</center><br>
## 實驗影片
[Flat Top PAM in Python](https://youtu.be/9mWwDngmNsE)
[PCM in Python](https://youtu.be/83Va1PGCq3U)
## 參考資料
[1]~[5] https://ecee.colorado.edu/~mathys/ecen4652/pdf/lab01.pdf
[6] https://www.researchgate.net/figure/a-The-impulse-response-and-b-frequency-response-of-a-rectangular-pulse-The-impulse_fig9_311758451
[7]~[9] https://ecee.colorado.edu/~mathys/ecen4652/pdf/lab01.pdf
[10] https://ytliu0.pixnet.net/blog/post/225927830-msb---lsb-%E6%98%AF%E4%BB%80%E9%BA%BC%E6%84%8F%E6%80%9D%EF%BC%9F%E6%98%AF%E4%BB%80%E9%BA%BC%E7%9A%84%E7%B8%AE%E5%AF%AB%EF%BC%9F
[11]~[12] https://ecee.colorado.edu/~mathys/ecen4652/pdf/lab01.pdf
[13] https://zh.wikipedia.org/wiki/Jupyter
[14]~[15] https://github.com/soumy94/Communication-Theory/blob/master/ascfun.py
[16]~[17] https://ecee.colorado.edu/~mathys/ecen4652/pdf/lab01.pdf
[18]~[19]https://github.com/soumy94/Communication-Theory/blob/master/pcmfun.py