MATLAB仿真過程中,編寫MATLAB代碼的時候犯了很多錯誤,做了很多蠢事。記錄下自己犯錯的點點滴滴,並引以為戒。使用MATLAB版本為2014a,以下內容如有不當還請指正。
1. 仿真開始前清理工作區
工作區存在的變量可能會對腳本運行產生影響,故代碼(腳本)開頭需要添加如下命令
clc;clear all;close all;
2. 養成良好的變量、函數命名習慣
MATLAB中有很多內置的常量、函數等。寫代碼的時候不能夠隨意命名,以防造成不必要的麻煩。譬如在循環時不應該使用i,j變量,在MATLAB中這多用於表示虛數符號。更多的內容可以參考《MATLAB 編程風格指南》。
3. 測試完成的代碼再寫出函數形式
有時候會把相對獨立、固定的內容寫成一個函數。但往往直接寫成函數后是不利於測試的,即使添加斷點后進入函數內部查看各變量取值,此時不能夠觀測到函數之外的變量。因此測試好代碼無誤再封裝成函數是必要的。
4. 浮點數的精度
舉個例子,下面代碼運行結果是什么呢?
a=10^-17;b=1; if(a+b==1) fprintf('a+b=1'); else fprintf('a+b!=1'); end
答案應該是輸出 a+b=1,雖然很明顯這是不成立的,但我們要知道MATLAB不是萬能的,其表示的數的精度是有限的。對於精度問題不做具體的討論,意識到這個問題后,我們必須將理論和時間區分開。
譬如在做除法的時候,有些變量依理論而言是不為0的,但實際上可能因為運算過程中的精度損失而使得計算結果產生Inf或是NaN,進而導致程序出現偏差。就拿上述例子來說,運行結果表明a+b-b=0。為避免這些情況的發生,必須對程序做相應的處理。在MATLAB中鍵入
help eps
This MATLAB function returns the distance from 1.0 to the next largest double-precision number, that is eps = 2^(-52).
可以在幫助文檔內閱讀詳細描述,在實際代碼中運用eps防止出現異常。(由其是在具有循環迭代的代碼中更要注意這一點)
5. 寫好注釋,用好發布功能
我有個腳本試着發布了一下,如這里所示,實際上要好看些。這部分內容可以在幫助-MATLAB-Programming Scripts and Functions-Scripts中找到相關的介紹。個人覺得還是不錯的,注釋是肯定要寫的,既然要寫,為何不好好按照格式寫呢?
當然,MATLAB提供的功能還是比較簡單的,具體的功能大概也就這些。使用發布功能后代碼會運行一遍,之后將結果也添加到發布的文檔中去。
%%文檔標題 % 具體描述 % % 有格式文本 % 最后修改日期:2015-03-07 % 軟件版本:MATLAB(R) 2014a % % *加粗* % % # 編號列表 % # 編號列表 %%有標題節 % %% %
6. 用好幫助,更要看清幫助
為什么很多仿真都用MATLAB?因為MATLAB很強大,很全面,可以做好很多事情。了解其強大功能的一個很好的途徑是看其自身的幫助。當然,我想強調的是看清幫助。
前幾天就犯了個錯誤,sgn是取符號的函數,MATLAB里面有個函數叫做sign實現了這個功能。然后我就用了,沒有看清sign(0)=0……然而我希望的結果是sign(0)=1。所以在實際使用過程中還是要看清楚才行。
7. 適時保存運行結果
有的仿真要跑1天、兩天、很多天,而且往往是可以中斷的。為了防止電腦死機、斷點,適時保存運行結果是必要的。我們可以讓程序在命令行窗口實時輸出信息以查看代碼運行狀態,並利用diary將這些信息保存起來。譬如
diary; fprintf('\n-------------------------------\n'); fprintf('Eb/No = %e \n',Var1); fprintf('總幀數 = %d, ',Var2); fprintf('誤幀數 = %d, ',Var3);
但我還是覺得新建文件自由一些,命令行窗口實時輸出信息的格式還是不太好看。譬如我們可以將運行結果采用表格的形式存儲起來,之后用readtable讀取。
fid = fopen(FILE_NAME,'at+'); fprintf(fid,'日期 %s\n',datestr(now,'yyyymmdd')); fprintf(fid,'EbN0\t T_Frame\t E_Frames\t E_Bits\t A_IterNums\t BER\t FER\n'); fprintf(fid,'%3.2g\t %5d\t %4d\t ',EbN0_dB(nEbN0),nF,frameError(nEbN0)); fprintf(fid,'%8d\t %8.6g\t ',bitError(nEbN0),iterNumTotal(nEbN0)/nF); fprintf(fid,'%e\t %e\n',BER(nEbN0),frameError(nEbN0)/nF); readtable(FILE_NAME,'HeaderLines',1,'Delimiter','\t')
讀取的結果大概是這個樣子,還是很好看的(雖然歪了……),而且可以輕松獲取每列數據。
EbN0 T_Frame E_Frames E_Bits A_IterNums BER FER ____ _______ ________ ______ __________ ___ ___ 1 1 0 0 27 0 0
8.待續
……