250 likes | 464 Views
Per Zetterberg. USRP Lecture. Tools. We use ubuntu 12.04 (UI: gnome or unity) Google for tutorials etc. Editing textfiles in Emacs: Tutorial: http://www2.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html Coding language C++ “C++ primer” by Lippman and Lajoie, cplusplus.com
E N D
Per Zetterberg USRP Lecture
Tools • We use ubuntu 12.04 (UI: gnome or unity) • Google for tutorials etc. • Editing textfiles in Emacs: Tutorial:http://www2.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html • Coding language C++ • “C++ primer” by Lippman and Lajoie, cplusplus.com • The KTH USRP skeleton code and Matlab interface • Command documentation (“help”) and source code. • Compiling source code: • g++ (http://gcc.gnu.org/), • make (https://www.gnu.org/software/make/) • Debugging • printf, cout, save/load from/to file • ddd (data display debugger) (http://www.gnu.org/software/ddd/manual/html_mono/ddd.html) • Signal processing & communications library • IT++ (http://itpp.sourceforge.net//) • Code repository and versioning • git (http://toroid.org/ams/git-central-repo-howto) • USRP N210 (www.ettus.com) AND THESE SLIDES!
Skeleton Code • Download the skeleton code from repository using • Register a (free) account on github, email me your username • Wait for confirmation from me. • git clone https://github.com/pzett/kth_usrp_utilities.git • Skeleton code files: • rx_60GHz.cpp, rx_60GHz.m • tx_60GHz.cpp, tx_60GHz.m • harness.cpp, harness.m, square_all_elements_of_array.m, square_all_elements_of_array.cpp • Makefile • Etc. • How build the code • Make all • Creates: rx_60GHz, tx_GHz, harness (executable files)
Using (our) Matlab interface for USRP • On TX computer: • Check that USRP is properly connected (uhd_find_devices). • Start matlab in a directory where the skeleton code is located. • Create a vector to transmit e.g y=1000*exp(j*0.1*2*pi*(1:10000)); • Elements of X should be complex with real and imag [-2^15,2^15-1]. • Transmit the data tx.m or tx_60GHz.m <----- Repeats the data until interrupted • Eg: tx(length(y),5.5e9,y,0,10,25e6,10e6); On RX computer: • Check that USRP is properly connected (uhd_find_devices). • Start matlab in a directory where the skeleton code is located. • Receive the signal rx.m or rx_60GHz.m. • E.g X=rx(10000,5.5e9,0,15,25e6,10e6);
tx.m,tx_60GHz.m tx( Nsamples,RF_freq, X, ref_clk, gain, tx_rate, LOoffset, low_res) Recommendations: Nsamples=length(X) or Nsamples=length(X)+guard RF_freq=5.5e9 or 5.6e9 X: Signal to be transmitted (complex). RMS <= 5000. ref_clk=0; gain 0-20 tx_rate=25e6 LOoffset=10e6 tx_60GHz.m I will give you info later.
rx.m, rx_60GHz.m X=rx(Nsamples,RFfreq,ref_clk,gain,rx_rate,LOoffset); Recommendation: Nsamples: Two times longer than transmit signal RFfreq: 5.5e9 or 5.6e9 ref_clk=0 gain=0,15 or 30. tx_rate=25e6 LOoffset=10e6 rx_60GHz.m: I will give you info later. To remove LO leakage: t=(-10:10)+1e-5; hlp=sin(2*pi*0.3*t)./t; X=conv(X,hlp);
Compiling Parameters, compilation flags, libraries to link width • Compile source without Makefile: • g++ -o test test.cpp • In our Makefile: CXXFLAGS = -o3 -Wall -DNDEBUG LDLIBS = -luhd -lboost_program_options ... tx_60GHz: tx_60GHz.o $(CXX) -o $@ $^ $(LDLIBS) • Compiling with Makefile >make tx_60GHz g++ -o3 -Wall -DNDEBUG -c -o tx_60GHz.o tx_60GHz.cpp g++ -o tx_60GHz tx_60GHz.o -luhd -lboost_program_options > make tx_60GHz make: 'tx_60GHx' is up to date The executable tx_60GHz depends on tx_60GHz.o. Since there is a file tx_60GHz.cpp in the folder, “make” understands that it should compile it obtain tx_60GHz.o.
Passing command line parameters to c++-program: classic way #include <iostream> #include <stdlib.h> int main(int argc, char **argv) { double input=atof(argv[1]); std::cout << “input=” << input << “\n”; return 0; }; > ./main 23.23 input=23.23 > Program Execution
Passing command line parameters to c++program: boost::program_options po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") /* command-line perfix, type, help string */ ("no_elements", po::value<int>(&number_of_elements)->default_value(10), "number of elements in vector") ("dummy_str",po::value<std::string>(&dummy_string)->default_value("Default string"), "dummy string") ("dummy_fl",po::value<float>(&dummy_float)->default_value(3.14), "dummy float") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); //print the help message if (vm.count("help")){ std::cout << boost::format("harness: %s") % desc << std::endl; return ~0; } std::cout<< "dummy_string=" << dummy_string << std::endl; std::cout<< "dummy_float=" << dummy_float << std::endl; std::cout << "number_of_elements=" << number_of_elements << std::endl; ./harness ---dummy_fl=2.12 dummy_string=Default string dummy_float=2.12 number_of_elements=10 Program Execution
Calling program from Matlab • ./harness ---dummy_fl=2.12 • dummy_float=2.12; • system(['./harness –dummy_fl=',num2str(dummy_float),';']); • Transfering larger amount of data. In Matlab: • fid=fopen('data_to_harness.dat','w'); • fwrite(fid,data_to_harness,'float'); • fclose(fid); • Reading the data from C++ program: • std::ifstream ifs( "data_to_harness.dat" , std::ifstream::in ); • ifs.read((char * )data_to_harness,number_of_elements*sizeof(float)); • ifs.close(); • Write data from C++ program: Datatype float data_to_harness[number_of_elements]; std::ofstream ofs( "data_from_harness.dat" , std::ifstream::out ); ofs.write((char * )data_to_harness,number_of_elements*sizeof(float)); ofs.close(); Read the fata
Tab Creating an implementation >cp kth_usrp_utilities/rx_60GHz.cpp green/rx_green.cpp >cp kth_usrp_utilities/rx_60GHz.cpp green/tx_green.cpp >cp kth_usrp_utilities/Makefile green/Makefile green_signal_processing.cpp green_signal_processing.hpp Modify Makefile: rx_green: rx_green.o green_signal_processing.o green_signal_processing.hpp $(CXX) -o $@ $^ $(LDLIBS) Modify rx_green.cpp: #include “green_signal_processing.hpp” Add parameters, Array to hold detected data? save received data on disk. Your signal processing implementation functions (objects?)
Modify rx_green.cpp while (num_rx_samps<total_num_samps) { num_rx_samps_latest_call=0; while (num_rx_samps_latest_call==0) { num_rx_samps_latest_call= rx_stream->recv(&buff_short[0],buffer_size, md, 3.0); }; if (num_rx_samps_latest_call!=buffer_size) { std::cerr << "I expect the buffer size to be always the same!"; exit(1); }; /* Process the just received buffer */ }; green_process_received_power(buff_short,detected_bits); int i1=2*num_rx_samps; int i2=0; while ((i1<(int) (2*total_num_samps)) && (i2<2*buffer_size)) { storage_short[i1]=buff_short[i2]; i1++; i2++; }; num_rx_samps=num_rx_samps+num_rx_samps_latest_call; std::cout << "num_rx_samps=" << num_rx_samps << std::endl;
buff_short buff_short std::complex *y=(std::complex *) buff_short; [-2^15,2^15-1] |y|>10000, distortion?
Debugging • Developing in Matlab – testing on real hardware (tx_60GHz.m, rx_60GHz.m) • printf, cout (yes – seriously). • #define DISPLAY_VALUE(X) std::cout << #X << "=" << X << std::endl • ddd (GUI debugger) • Test important functions in harnesses: Example harness.m/harness.cpp/square_elements_of_array • harness.m : Creates test data input and writes to file • harness.cpp: Read data from file and transform it and call square_elements_of_array • square_elements_of_array.cpp: Contains function to be tested. • harness.cpp: Write result on file. • harness.m: Read data from file. • Call the matlab reference implementation square_elements_of_array.m. Look and run the example code!
Harness example Makefile square_elements_of_array_debug.cpp: square_elements_of_array.cpp cp square_elements_of_array.cpp square_elements_of_array_debug.cpp square_elements_of_array_debug.o: CXXFLAGS = -ggdb -Wall harness.o: CXXFLAGS = -ggdb -Wall harness: harness.o square_elements_of_array_debug.o $(CXX) -o $@ $^ $(LDLIBS)
Your case (proposal) signal_process_green_debug.cpp: signal_processing_green.cpp cp signal_processing_green.cpp signal_process_green_debug.cpp signal_process_green_debug.o: CXXFLAGS = -ggdb -Wall harness1.o CXXFLAGS = -ggdb -Wall harness1: harness1.o signal_process_green_debug.o $(CXX) -o $@ $^ $(LDLIBS) harness1.cpp: #include “signal_processing_green.hpp” … … fs.read((char * ) data_to_be_encoded,number_of_elements*sizeof(char)); … … encode(data_to_encoded,encoded_data); … … Test e.g convolutional- encoder
Example session with ddd and harness > ddd & > file → open program → harness • Mark line just before “std::cout << “dummy_string” • Program → Run() → set “run with arguments” to e.g. “--no_elements=5” • Klick step until the the pointer is just before.“// Save data to file” • Write “data_to_harness” in uppermost window press “Print” then observe lowest window. Then change to data_to_harness[0] and press print. Then change to data_to_harness[0]@5. • Press kill. Program → Run() to first breakpoint. • single-step (next) until you reach “square_elements_of_array”. • Klick “Step” to enter square_elements_of_array. • Write data_to_harness[0]@5 in the uppermost window. Press display. • Klick Step and watch the contents of the display as you transverse through the array. Kill the ddd if it hangs: killall -s 9 ddd
IT++: Example coded BPSK simulation BPSK bpsk; Convolutional_Code Code; ivec generator(3); generator(0)=0133; generator(1)=0165; generator(2)=0171; code.set_generator_polynomials(generator, 7); bvec bits=randb(100), encoded_bits decoded_bits; vec tx_signal, rx_signal; code.encode_tail(bits, encoded_bits); tx_signal = bpsk.modulate_bits(encoded_bits); rx_signal = tx_signal + sqrt(0.5)* (tx_signal.size()); code.decode_tail(rx_signal, decoded_bits);
IT++: Getting started #include <itpp/itbase.h> #include <itpp/itcomm.h> #include <complex> using namespace itpp; int main( int argc, char *argv[]) { cvec v1; cvec v2; cvec v3; v1="(5,3),(-13,-4),(3,36)"; // (real,imag) v2="(28,-13),(30,7),(-1,7)"; // (real,imag) v3=v1+v2; std::cout << "v3=" << v3 << std::endl; } g++ test.cpp -o test -litpp
code versioning systems CVS, SVN, git, …. purpose: Keep track of created textfiles (e.g. C++ code). Versions. Helps merge changes by multiple users. We will use git: distributed versioning system … but we will used in a centralized manner
Use same email and username as your github account Set up local repository (=local copy) >git clone https://github.com/pzett/green.git green_alice >cd green_alice >git config –local user.email alice@kth.se >git config -local user.name aliceg
file1.cpp file2.cpp file1.hpp file2.hpp file1.cpp file2.cpp file1.hpp file2.hpp How we will work Repository green Alice: * Implements or changes file1.cpp and file2.cpp Updates repository: git add file1.cpp file2.cpp git commit -m “comment” git push …. Bob: * Implements or changes file1.hpp and file2.hpp Updates repository: git add file1.hpp file2.hpp git commit -m “comment” git push git pull git pull
Conflict resolution • Alice and Bob have clean updated local repositories. • Alice modifies her file1.cpp • Bob modifies his file1.cpp • Bob does: • git add file1.cpp • git commit -m “change initialization of i1” • git push • Alice does: • git add file1.cpp • git commit -m “I changed initialization of i1 to 2” • git push CONFLICT (content): Merge conflict in file1.cpp Automatic merge failed; fix conflicts and then commit the result. Response
Conflict resolution continued • Alice opens file1.cpp: <<<<<<< HEAD i1=2; ======= I1=1; >>>>>>> 55f223d4ab24ae40f19ae551d51205b69a23177e • Alice and Bob discusses and finds that Alice was right. Alice removes Bobs code and pushes it. Alice> git add file1.cpp Alice> git commit -F .git/MERGE_MSG Alice> git push Bob> git pull • Alice and Bob now has identical local repositories with Alice's code. Alice's code Bob's code
Assignment • Make a CW transmitter program which uses 25Msps sample rate and transmits a CW between -12.5 and 12.5MHz. The parameter freq define the frequency in MHz . • Make a receiver program with 25Msps rate which calculates the power of the input signal every second. The DC component should be removed. • Make receiver program with 25Msps rate, which calculates the power of the input signal every second at the output of 25 filters centered at frequency -12,-11,-10,...,12. • Implement a non-trivial function relevant for your project, first in Matlab and then in C++, then verify it using the harness methodology.