= Using FPGAs for Spectrum Sensing and Modulation Recognition = == Project Objective == This project seeks to aid the use of machine learning to recognize different wireless devices. The project will use software defined radios (SDR) to record various devices, such as iphones, bluetooth earbuds, and WiFi laptops. These recordings will become the training data to a set of neural networks. Additionally, we will be constructing a matched filter to compare its performance against the machine learning based modulation recognition. == Who We Are == || [[Image(Picture1.jpg)]]\\Ryan Davis\\Class of 2021\\Rutgers University\\Computer Engineering and\\ Computer Science\\ || || [[Image(Picture2.png)]]\\Zhuohuan Li\\Class of 2020\\Rutgers University\\Computer Engineering\\ || || [[Image(Picture3.jpg)]]\\Sid Mandayam\\Class of 2022\\Rutgers University\\Computer Science and\\ Mathematics\\ || || [[Image(Picture2.jpg)]]\\Jacob Morin\\Class of 2021\\Pingry High School\\ || == Reading Material == * [https://www.xilinx.com/support/documentation/sw_manuals/ug998-vivado-intro-fpga-design-hls.pdf Overview of FPGA architecture (especially for Xilinx devices), and comparison between FPGA and CPU] * [https://arachnoid.com/software_defined_radios/#Theory__The_Frequency_Domain Overview of I/Q Communication Theory] * [https://www.mathworks.com/help/wlan/ref/wlanwaveformgenerator.html Artificial WiFi Packet Generation] * [https://www.sciencedirect.com/topics/engineering/matched-filter Matched Filter Demodulation] == Week 1 Activities == * Get ORBIT/COSMOS account and familiarize oneself with the testbed procedures * Learn about FPGAs * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%201.pdf Presentation 1] == Week 2 Activities == * Write a UDP client / server in Go * Generate IQ samples for IEEE 802.11 WiFi packets * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%202.pdf Presentation 2] == Week 3 Activities == * Rework UDP client / server to work with Go to Verilog compiler * Transmit and receive generated WiFi packets using the USRPs on the Grid * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%203%206_17.pdf Presentation 3] == Week 4 Activities == * Automate data collection on the Grid * Learn more about Go lang * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%204%206_25.pdf Presentation 4] == Week 5 Activities == * Begin looking in to matched filters * Finish data collection on the Grid (a lot of debugging) * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%205%207_2.pdf Presentation 5] == Week 6 Activities == * Matched Filters in MATLAB * Collect data * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%206%207_9.pdf Presentation 6] == Week 7 Activities == * Dataset documentation / Big metadata file * Matched Filters in Go * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Presentation%207%207_16.pdf Presentation 7] == Final Presentation == * [https://www.orbit-lab.org/attachment/wiki/Other/Summer/2020/FPGAspectrum/Final%20Presentation%209%207_30.pdf Final Presentation] == Poster == [[Image(UFSSMRPoster.pptx.png)]] == WiFi Packet Generation == \\ {{{#!matlab % Ryan Davis PAYLOAD = 1500; % bytes PATH = ""; for i = 0:7 filename = "WiFi_802.11n_" + i + "MCS_int16.dat"; ht = wlanHTConfig("MCS", i, "ChannelBandwidth", "CBW20"); payload = randi([0 1], [1 PAYLOAD*8]); txSig = wlanWaveformGenerator(payload,ht); output_txSig = formatOutput(txSig); fid = fopen(PATH + filename, "w"); fwrite(fid, output_txSig, "int16"); fclose(fid); end clear; function arr = formatOutput(txSig) arr = zeros(size(txSig)*2); j = 1; for i = 1:size(txSig) arr(j) = cast(real(txSig(i)), "int16"); arr(j+1) = cast(imag(txSig(i)), "int16"); j=j+2; end end }}} == Experiment Automation == \\ {{{#!ruby # Ryan Davis # Adapted from code by Ivan Seskar Experiment.name = "Artificial_WiFi" Experiment.project = "Artificial_WiFi" consoleExec("omf tell -t all -a offh") consoleExec("omf load -i usrpcal_2020-02-24.ndz -t node3-2,node3-19,node8-7,node8-14,node13-7,node13-14,node18-2,node18-19,node3-1,node3-20,node18-1,node18-20 -r 0") consoleExec("omf tell -t node3-2,node3-19,node8-7,node8-14,node13-7,node13-14,node18-2,node18-19,node3-1,node3-20,node18-1,node18-20 -a on") freqs = ["2412e6", "2437e6", "2462e6", "5180e6", "5240e6", "5745e6", "5825e6"] mcss = ["0", "1", "2", "3", "4", "5", "6", "7"] topos = {"3" => [["node18-1", "node18-2"], ["node3-1", "node3-2"], ["node18-19", "node18-20"], ["node3-19", "node3-20"]], # format: dist => [["txnode_A", "rxnode_A"], ["txnode_B", "rxnode_B"] ... ] "15" => [["node13-7", "node8-7"], ["node13-14","node8-14"]], "45" => [["node18-1", "node3-1"], ["node18-2", "node3-2"], ["node18-19", "node3-19"], ["node18-20", "node3-20"]], "72" => [["node18-20", "node3-1"], ["node18-1", "node3-20"]] } defApplication('tx', 'tx_samples_from_file') { |a| a.version(1, 0, 0) a.shortDescription = "" a.description = "" a.path = "export LC_ALL=C;/usr/local/lib/uhd/examples/tx_samples_from_file" a.defProperty('freq', "center frequency in Hz", '--freq', {:dynamic => false, :type => :string}) a.defProperty('ant', "antenna to be used", '--subdev', {:dynamic => false, :type => :string}) a.defProperty('rate', "baseband rate in Hz", '--rate', {:dynamic => false, :type => :string}) a.defProperty('gain', "receiver gain in dB", '--gain', {:dynamic => false, :type => :string}) a.defProperty('file', "reveived baseband waveform file name", '--file', {:dynamic => false, :type => :string}) a.defProperty('type', "sample types", '--type', {:dynamic => false, :type => :string}) a.defProperty('repeat', "continuously repeat", '--repeat', {:dynamic => false, :type => :string}) } defApplication('rx', 'rx_samples_to_file') { |a| a.version(1, 0, 0) a.shortDescription = "" a.description = "" a.path = "export LC_ALL=C;/usr/local/lib/uhd/examples/rx_samples_to_file" a.defProperty('freq', "center frequency in Hz", '--freq', {:dynamic => false, :type => :string}) a.defProperty('rate', "baseband rate in Hz", '--rate', {:dynamic => false, :type => :string}) a.defProperty('gain', "receiver gain in dB", '--gain', {:dynamic => false, :type => :string}) a.defProperty('nsamps', "number of samples", '--nsamps', {:dynamic => false, :type => :string}) a.defProperty('file', "reveived baseband waveform file name", '--file', {:dynamic => false, :type => :string}) a.defProperty('type', "sample types", '--type', {:dynamic => false, :type => :string}) a.defProperty('discardtime', "time at which storing samples starts", '--discardtime', {:dynamic => false, :type => :string}) } topos.each { |dist, topo| topo.each { |node_pair| freqs.each { |freq| mcss.each { |mcs| infilename = "WiFi_802.11n_"+mcs+"_int16.dat" exptag = dist+"ft_"+freq+"Hz_"+mcs+"MCS_"+node_pair[0]+","+node_pair[1] outfilename = "/root/rx_"+exptag+"_int16.dat" info("Setting up group for experiment: " + exptag) rxGroup = node_pair[1]+"@"+exptag txGroup = node_pair[0]+"@"+exptag defGroup(txGroup, node_pair[0]) { |n| n.addApplication('tx') { |app| app.setProperty('freq', freq) app.setProperty('ant', "A:0") app.setProperty('gain', "30") app.setProperty('type', "short") app.setProperty('file', infilename) app.setProperty('rate', "20e6") app.setProperty('repeat'," ") } } defGroup(rxGroup, node_pair[1]) { |n| n.addApplication('rx') { |app| app.setProperty('freq',freq) app.setProperty('gain', "30") app.setProperty('type',"short") app.setProperty('nsamps',"40000000") app.setProperty('file',outfilename) app.setProperty('rate',"40e6") } } } } } } trap("INT") { allGroups.stopApplications allGroups.exec("/usr/bin/pkill -9 -f rx_samples") allGroups.exec("/usr/bin/pkill -9 -f tx_samples") exit! } onEvent(:ALL_UP_AND_INSTALLED) { |event| # Wait for nodes to associate and give it an extra 5 sec just in case... sleep 5 # scp IQ binary file to transmitters txfiles = "txfiles/WiFi*" topos.each { |dist, topo| topo.each { |node_pair| consoleExec("ssh root@" + node_pair[0] + " rm -f /root/*.dat") consoleExec("ssh root@" + node_pair[1] + " rm -f /root/*.dat") consoleExec("scp -o StrictHostKeyChecking=no " + txfiles + " root@"+node_pair[0]+":"); consoleExec("scp -o StrictHostKeyChecking=no test.sh root@"+node_pair[0]+":"); # TODO temp for debugging } } topos.each { |dist, topo| topo.each { |node_pair| freqs.each { |freq| mcss.each { |mcs| exptag = dist+"ft_"+freq+"Hz_"+mcs+"MCS_"+node_pair[0]+","+node_pair[1] info ("\nStarting experiment: " + exptag) rxGroup = node_pair[1]+"@"+exptag txGroup = node_pair[0]+"@"+exptag group(rxGroup).startApplications # start receiver (captures for 1 sec) sleep 1 # make sure we do not start transmitting before receiving group(txGroup).startApplications # start transmitter sleep 5 # wait for applications to hopefully finish group(txGroup).stopApplications group(rxGroup).stopApplications outfilename = "/root/rx_"+exptag+"_int16.dat" consoleExec("scp -o StrictHostKeyChecking=no root@" + node_pair[1] + ":" + outfilename + " /spare/spectrum/Artificial_WiFi/") #copy recved data to archive consoleExec("ssh root@" + node_pair[1] + " rm -f " +outfilename) } } } } allGroups.stopApplications Experiment.done } }}} == Random Number Generation with LFSR == {{{#!go // Jacob Morin // Call with either 1 or 3 command arguments: // First command argument: amount of loops // Second command argument: start // Third command argument: end package mainimport ( "fmt" "os" "strconv" )func lfsr3(sequence chan uint8, start int, end int, repititions int) { for i := 0; i <= repititions; i++ { //starting value var seed uint16 = uint16(i) //keeps track of the value at any given time var lfsr uint16 = seed //number of times it takes for the lfsr to revert to its start state var period int = 0 //last bit of lfsr var value uint16 = 0 //number of 0's in lfsr var numZeros int = 0 //number of 1's in lfsr var numOnes int = 0 stop := false //until LFSR goes back to its start state (stop will become true) for stop != true { //increments count period++ //shifts lfsr ^= lfsr >> 7 //7 right lfsr ^= lfsr << 9 //9 left lfsr ^= lfsr >> 13 //13 right if (period > start) && (period < end) { //adds last bit to sequence value = lfsr & 1 if value == 0 { numZeros++ sequence <- 0 } if value == 1 { numOnes++ sequence <- 1 } } //if LFSR reaches startState, make stop true to exit the loop if lfsr == seed { stop = true } } }}func main() { //makes channel for the random number sequence := make(chan uint8) //command line args := os.Args len := len(args) //first argument in command line determines how many times to loop if len > 1 { repititions, err := strconv.Atoi(args[1]) if err != nil { fmt.Println("error") } // assumed values start := 6000 end := 7000 // second argument in command line overrides start // third argument in command line overrides end if len > 3 { y, err := strconv.Atoi(args[2]) if err != nil { fmt.Println("error") } start = y z, err := strconv.Atoi(args[3]) if err != nil { fmt.Println("error") } end = z } //calls function go lfsr3(sequence, start, end, repititions) printSequence := "" //Call lfsr3 'repitions' number of times for i := 1; i <= repititions; i++ { //number of values per sequences for j := 0; j < 999; j++ { //receives and prints value value := <-sequence printSequence += strconv.Itoa(int(value)) } fmt.Println("At", i, "got random number:", printSequence, "\n") printSequence = "" } }} }}} == Matched Filter == {{{#!matlab % Zhuohuan Li % Sid Mandayam antenna = phased.IsotropicAntennaElement('FrequencyRange',[5e9 15e9]); transmitter = phased.Transmitter('Gain',20,'InUseOutputPort',true); fc = 10e9; target = phased.RadarTarget('Model','Nonfluctuating',... 'MeanRCS',1,'OperatingFrequency',fc); txloc = [0;0;0]; tgtloc = [5000;5000;10]; transmitterplatform = phased.Platform('InitialPosition',txloc); targetplatform = phased.Platform('InitialPosition',tgtloc); [tgtrng,tgtang] = rangeangle(targetplatform.InitialPosition,... transmitterplatform.InitialPosition);waveform = phased.RectangularWaveform('PulseWidth',25e-6,... 'OutputFormat','Pulses','PRF',10e3,'NumPulses',1); c = physconst('LightSpeed'); maxrange = c/(2*waveform.PRF); SNR = npwgnthresh(1e-6,1,'noncoherent'); Pt = radareqpow(c/fc,maxrange,SNR,... waveform.PulseWidth,'RCS',target.MeanRCS,'Gain',transmitter.Gain);transmitter.PeakPower = Pt;radiator = phased.Radiator('PropagationSpeed',c,... 'OperatingFrequency',fc,'Sensor',antenna); channel = phased.FreeSpace('PropagationSpeed',c,... 'OperatingFrequency',fc,'TwoWayPropagation',false); collector = phased.Collector('PropagationSpeed',c,... 'OperatingFrequency',fc,'Sensor',antenna); receiver = phased.ReceiverPreamp('NoiseFigure',0,... 'EnableInputPort',true,'SeedSource','Property','Seed',2e3); filter = phased.MatchedFilter(... 'Coefficients',getMatchedFilter(waveform),... 'GainOutputPort',true);wf = waveform(); [wf,txstatus] = transmitter(wf); wf = radiator(wf,tgtang); wf = channel(wf,txloc,tgtloc,[0;0;0],[0;0;0]); wf = target(wf); wf = channel(wf,tgtloc,txloc,[0;0;0],[0;0;0]); wf = collector(wf,tgtang); rx_puls = receiver(wf,~txstatus); [mf_puls,mfgain] = filter(rx_puls); Gd = length(filter.Coefficients)-1;mf_puls=[mf_puls(Gd+1:end); mf_puls(1:Gd)]; % main line of matched filter subplot(2,1,1) t = unigrid(0,1e-6,1e-4,'[)'); rangegates = c.*t; rangegates = rangegates/2; % end with this lineplot(rangegates,abs(rx_puls)) title('Received Pulse') xlabel('Meters') ylabel('Amplitude') hold on plot([tgtrng, tgtrng], [0 max(abs(rx_puls))],'r') subplot(2,1,2) plot(rangegates,abs(mf_puls)) title('After Matched Filter') xlabel('Meters') ylabel('Amplitude') hold on plot([tgtrng, tgtrng], [0 max(abs(mf_puls))],'r') hold off }}}