# **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