diff --git a/lab_3/analyzeTrace.m b/lab_3/analyzeTrace.m new file mode 100644 index 0000000..d0e97b9 --- /dev/null +++ b/lab_3/analyzeTrace.m @@ -0,0 +1,43 @@ +function result = analyzeTrace(trace_file, channel, window, overlap, fft_precision, sample_rate) + trace = load(trace_file, "iq"); + + % 1 + duration = length(trace.iq) / sample_rate; + + % 2 + [S, F, T] = spectrogram(trace.iq, window, overlap, fft_precision, sample_rate, "centered"); + + % 3 + mag = abs(S).^2; + P_dB = linearTodB(mag); + + % Sum all frequency bins per time slot to obtain total channel power. + channel_power_linear = sum(mag, 1); + channel_power_dB = linearTodB(channel_power_linear); + + noise_floor_linear = prctile(channel_power_linear, 10); + noise_floor = linearTodB(noise_floor_linear); + + % 4 + occupancy = sum(channel_power_dB > noise_floor + 10) / numel(channel_power_dB) * 100; + + result.trace_file = trace_file; + result.iq = trace.iq; + result.channel = channel; + result.duration = duration; + + result.S = S; + result.F = F; + result.T = T; + + result.mag = mag; + result.P_dB = P_dB; + + result.channel_power_linear = channel_power_linear; + result.channel_power_dB = channel_power_dB; + + result.noise_floor_linear = noise_floor_linear; + result.noise_floor = noise_floor; + + result.occupancy = occupancy; +end diff --git a/lab_3/ex_spec.png b/lab_3/ex_spec.png new file mode 100644 index 0000000..081f10c Binary files /dev/null and b/lab_3/ex_spec.png differ diff --git a/lab_3/lab_3.m b/lab_3/lab_3.m index 4648c10..fe5eb09 100644 --- a/lab_3/lab_3.m +++ b/lab_3/lab_3.m @@ -1,4 +1,66 @@ -calculations_only = false; +%{ + +Disclaimer: + +To run the scripts, the provided traces have to be placed in ./traces, +e.g. ./traces/2412mhz.mat. + +The corresponding subtasks are labeled as % [int]. These labels indicate +which part of the code or which comment block belongs to the respective +task. + +task1.m +Contains the main calculations needed for Task 1. Some functionality was +moved into analyzeTrace.m, plotSpectrogram.m, and linearTodB.m, so it can +also be reused in task4.m. Task 1.5 is not included in the script, because +it involves interactive marking of transmissions and is therefore only +documented in the lab report. + +task2.m +The textual answers are repeated in the report. Some parts of the MATLAB +example code were copied to ./from_matlab_example and used to parse the +frames and decode the beacons. + +task3.m +Contains the solution for Task 3. The explanation is further summarized and +abstracted in the report. + +task4.m +Uses and adapts the MATLAB example code for OFDM beacon generation. A trace +file is stored in ./traces/wlan_fahren_wir_noch_beacons.mat, containing the +complex baseband vector of the generated beacon waveform. This is done so +the generated signal can be processed by the helper functions from Task 1 +and plotted in the same way as the provided SDR traces. + + +Struggles: + +I found this lab significantly more difficult and time-consuming than the +previous two labs. A major part of the effort was not only the +implementation itself, but also understanding the required background on +WiFi signals, spectrograms, noise floor estimation, occupancy calculation, +and beacon frame decoding. +Task 1 in particular was difficult because the instructions were very +compact and left several implementation details open. Concepts such as +percentile-based noise floor estimation, channel-wide power aggregation, +and occupancy thresholds were not immediately clear to me. This caused a +large amount of debugging, rechecking, and validation. +Another difficulty was that the WiFi background required for the lab was +quite extensive. Since parts of this topic were covered only during the lab +period, it was hard to apply the concepts confidently from the beginning. +Compared to the previous labs, this task felt much larger in scope and had +a significantly higher potential for mistakes, while still being part of a +pass/fail admission requirement. This made the lab feel disproportionate in +effort and risk. +In retrospect, the overall effort was very high. I estimate my total time +investment at over 30 hours, including background research, implementation, +debugging, interpretation, and report writing. +Despite these difficulties, the lab contributed substantially to my +understanding of WiFi signal analysis and beacon frame decoding. + +%} task1; -task2; \ No newline at end of file +task2; +task3; +task4; \ No newline at end of file diff --git a/lab_3/linearTodB.m b/lab_3/linearTodB.m new file mode 100644 index 0000000..78e9093 --- /dev/null +++ b/lab_3/linearTodB.m @@ -0,0 +1,3 @@ +function val = linearTodB(toConvert) + val = 10 * log10(toConvert); +end \ No newline at end of file diff --git a/lab_3/plotSpectrogram.m b/lab_3/plotSpectrogram.m new file mode 100644 index 0000000..9932d50 --- /dev/null +++ b/lab_3/plotSpectrogram.m @@ -0,0 +1,13 @@ +function plotSpectrogram(result, figure_id, p_dB_min, p_dB_max) + figure(figure_id); + imagesc(result.T, result.F, result.P_dB); + axis ij; + xlabel("Time [s]"); + ylabel("Freq. [Hz]"); + title("Spectrogram Trace " + figure_id + " / Channel " + result.channel); + cb = colorbar; + ylabel(cb, "Power [dB]"); + clim([p_dB_min p_dB_max]); + % Optional: + % xlim([0 0.05]); +end \ No newline at end of file diff --git a/lab_3/task1.m b/lab_3/task1.m index 2b96f7a..7822fbe 100644 --- a/lab_3/task1.m +++ b/lab_3/task1.m @@ -1,7 +1,4 @@ -trace1 = load("traces/2412mhz.mat", "iq"); -trace2 = load("traces/2432mhz.mat", "iq"); -trace3 = load("traces/2452mhz.mat", "iq"); -trace4 = load("traces/2472mhz.mat", "iq"); +calculations_only = false; window = 512; overlap = 64; @@ -9,81 +6,32 @@ fft_precision = 2048; sample_rate = 20e6; % 20 MS/s -> 20e6 S/s -% 1. -duration1 = length(trace1.iq) / sample_rate; -duration2 = length(trace2.iq) / sample_rate; -duration3 = length(trace3.iq) / sample_rate; -duration4 = length(trace4.iq) / sample_rate; +channels = [1 5 9 13]; -% 2. -% get all four plot values -[S1,F1,T1] = spectrogram(trace1.iq, window, overlap, fft_precision, sample_rate, "centered"); -[S2,F2,T2] = spectrogram(trace2.iq, window, overlap, fft_precision, sample_rate, "centered"); -[S3,F3,T3] = spectrogram(trace3.iq, window, overlap, fft_precision, sample_rate, "centered"); -[S4,F4,T4] = spectrogram(trace4.iq, window, overlap, fft_precision, sample_rate, "centered"); +res1 = analyzeTrace("traces/2412mhz.mat", channels(1), window, overlap, fft_precision, sample_rate); +res2 = analyzeTrace("traces/2432mhz.mat", channels(2), window, overlap, fft_precision, sample_rate); +res3 = analyzeTrace("traces/2452mhz.mat", channels(3), window, overlap, fft_precision, sample_rate); +res4 = analyzeTrace("traces/2472mhz.mat", channels(4), window, overlap, fft_precision, sample_rate); -mag1 = abs(S1).^2; -mag2 = abs(S2).^2; -mag3 = abs(S3).^2; -mag4 = abs(S4).^2; +results = [res1 res2 res3 res4]; -% begin calibrated color scale -P1_dB = 10 * log10(mag1); -P2_dB = 10 * log10(mag2); -P3_dB = 10 * log10(mag3); -P4_dB = 10 * log10(mag4); - -all_P_dB = [P1_dB(:); P2_dB(:); P3_dB(:); P4_dB(:)]; +% Calibrated color scale over all traces +all_P_dB = [ + res1.P_dB(:) + res2.P_dB(:) + res3.P_dB(:) + res4.P_dB(:) +]; p_dB_min = min(all_P_dB); p_dB_max = max(all_P_dB); -% end power calibrated color scale +% 2 if ~calculations_only - % plot all figures like example shows - figure(1); - imagesc(T1, F1, P1_dB); - axis ij; - xlabel("Time [s]"); - ylabel("Freq. [Hz]"); - title("Spectrogram Trace 1"); - cb = colorbar; - ylabel(cb, "Power [dB]"); - clim([p_dB_min p_dB_max]); - % xlim([0 0.05]); - - figure(2); - imagesc(T2, F2, P2_dB); - axis ij; - xlabel("Time [s]"); - ylabel("Freq. [Hz]"); - title("Spectrogram Trace 2"); - cb = colorbar; - ylabel(cb, "Power [dB]"); - clim([p_dB_min p_dB_max]); - % xlim([0 0.05]); - - figure(3); - imagesc(T3, F3, P3_dB); - axis ij; - xlabel("Time [s]"); - ylabel("Freq. [Hz]"); - title("Spectrogram Trace 3"); - cb = colorbar; - ylabel(cb, "Power [dB]"); - clim([p_dB_min p_dB_max]); - % xlim([0 0.05]); - - figure(4); - imagesc(T4, F4, P4_dB); - axis ij; % flip so its like the example fig - xlabel("Time [s]"); - ylabel("Freq. [Hz]"); - title("Spectrogram Trace 4"); - cb = colorbar; - ylabel(cb, "Power [dB]"); - clim([p_dB_min p_dB_max]); - % xlim([0 0.05]); % reduce view so its like the example fig + plotSpectrogram(res1, 1, p_dB_min, p_dB_max); + plotSpectrogram(res2, 2, p_dB_min, p_dB_max); + plotSpectrogram(res3, 3, p_dB_min, p_dB_max); + plotSpectrogram(res4, 4, p_dB_min, p_dB_max); end %{ @@ -115,41 +63,15 @@ occupancy4 = sum(P4_dB(:) > noise_floor4 + 10) / numel(P4_dB) * 100; % 3. % Sum all frequency bins per time slot to obtain total channel power. -channel_power1_linear = sum(mag1, 1); -channel_power2_linear = sum(mag2, 1); -channel_power3_linear = sum(mag3, 1); -channel_power4_linear = sum(mag4, 1); - -channel_power1_dB = linearTodB(channel_power1_linear); -channel_power2_dB = linearTodB(channel_power2_linear); -channel_power3_dB = linearTodB(channel_power3_linear); -channel_power4_dB = linearTodB(channel_power4_linear); - -noise_floor1_linear = prctile(channel_power1_linear, 10); -noise_floor2_linear = prctile(channel_power2_linear, 10); -noise_floor3_linear = prctile(channel_power3_linear, 10); -noise_floor4_linear = prctile(channel_power4_linear, 10); - -noise_floor1 = linearTodB(noise_floor1_linear); -noise_floor2 = linearTodB(noise_floor2_linear); -noise_floor3 = linearTodB(noise_floor3_linear); -noise_floor4 = linearTodB(noise_floor4_linear); - noise_floor_avg = linearTodB(mean([ - noise_floor1_linear - noise_floor2_linear - noise_floor3_linear - noise_floor4_linear + res1.noise_floor_linear + res2.noise_floor_linear + res3.noise_floor_linear + res4.noise_floor_linear ])); % 4. -occupancy1 = sum(channel_power1_dB > noise_floor1 + 10) / numel(channel_power1_dB) * 100; -occupancy2 = sum(channel_power2_dB > noise_floor2 + 10) / numel(channel_power2_dB) * 100; -occupancy3 = sum(channel_power3_dB > noise_floor3 + 10) / numel(channel_power3_dB) * 100; -occupancy4 = sum(channel_power4_dB > noise_floor4 + 10) / numel(channel_power4_dB) * 100; - -occupancies = [occupancy1 occupancy2 occupancy3 occupancy4]; -channels = [1 5 9 13]; +occupancies = [res1.occupancy res2.occupancy res3.occupancy res4.occupancy]; [occupancy_max, max_idx] = max(occupancies); busiest_channel = channels(max_idx); @@ -158,30 +80,26 @@ if ~calculations_only disp("Results Task 1:" + newline) disp("-- Trace duration ---") - disp("Channel 1 duration: " + duration1 + "s") - disp("Channel 5 duration: " + duration2 + "s") - disp("Channel 9 duration: " + duration3 + "s") - disp("Channel 13 duration: " + duration4 + "s" + newline) + disp("Channel 1 duration: " + res1.duration + "s") + disp("Channel 5 duration: " + res2.duration + "s") + disp("Channel 9 duration: " + res3.duration + "s") + disp("Channel 13 duration: " + res4.duration + "s" + newline) disp("--- Noise floor ---") disp("Distinct:") - disp("Channel 1 noise floor: " + noise_floor1 + "dB") - disp("Channel 5 noise floor: " + noise_floor2 + "dB") - disp("Channel 9 noise floor: " + noise_floor3 + "dB") - disp("Channel 13 noise floor: " + noise_floor4 + "dB" + newline) + disp("Channel 1 noise floor: " + res1.noise_floor + "dB") + disp("Channel 5 noise floor: " + res2.noise_floor + "dB") + disp("Channel 9 noise floor: " + res3.noise_floor + "dB") + disp("Channel 13 noise floor: " + res4.noise_floor + "dB" + newline) disp("Combined:") disp("Channel all noise floor: " + noise_floor_avg + "dB" + newline) disp("-- Occupancy ---") - disp("Channel 1 occupancy: " + occupancy1 + "%") - disp("Channel 5 occupancy: " + occupancy2 + "%") - disp("Channel 9 occupancy: " + occupancy3 + "%") - disp("Channel 13 occupancy: " + occupancy4 + "%" + newline) + disp("Channel 1 occupancy: " + res1.occupancy + "%") + disp("Channel 5 occupancy: " + res2.occupancy + "%") + disp("Channel 9 occupancy: " + res3.occupancy + "%") + disp("Channel 13 occupancy: " + res4.occupancy + "%" + newline) - disp("Busiest channel: " + busiest_channel) + disp("Busiest channel: " + busiest_channel + newline) end - -function val = linearTodB(toConvert) - val = 10 * log10(toConvert); -end \ No newline at end of file diff --git a/lab_3/task2.m b/lab_3/task2.m index cda12a0..414ed28 100644 --- a/lab_3/task2.m +++ b/lab_3/task2.m @@ -1,23 +1,27 @@ addpath("from_matlab_example"); -rxFrame1 = trace1.iq(:); -rxFrame2 = trace2.iq(:); -rxFrame3 = trace3.iq(:); -rxFrame4 = trace4.iq(:); +rxFrame1 = res1.iq(:); +rxFrame2 = res2.iq(:); +rxFrame3 = res3.iq(:); +rxFrame4 = res4.iq(:); -beaconFrames1 = extractBeaconFrames(rxFrame1); -beaconFrames2 = extractBeaconFrames(rxFrame2); -beaconFrames3 = extractBeaconFrames(rxFrame3); -beaconFrames4 = extractBeaconFrames(rxFrame4); +disp("Results Task 2:" + newline) % 1. disp("Beacon Frame 1") +beaconFrames1 = extractBeaconFrames(rxFrame1); printBeaconTable(beaconFrames1); + disp("Beacon Frame 2") +beaconFrames2 = extractBeaconFrames(rxFrame2); printBeaconTable(beaconFrames2); + disp("Beacon Frame 3") +beaconFrames3 = extractBeaconFrames(rxFrame3); printBeaconTable(beaconFrames3); + disp("Beacon Frame 4") +beaconFrames4 = extractBeaconFrames(rxFrame4); printBeaconTable(beaconFrames4); % 2. @@ -71,7 +75,7 @@ function beaconFrames = extractBeaconFrames(rxFrame) while searchOffset < length(rxFrame) oldOffset = searchOffset; - [bitsData, decParams, searchOffset, res] = recoverOFDMBits(rxFrame, searchOffset); + [bitsData, ~, searchOffset, res] = recoverOFDMBits(rxFrame, searchOffset); if searchOffset <= oldOffset searchOffset = oldOffset + 1; @@ -83,6 +87,10 @@ function beaconFrames = extractBeaconFrames(rxFrame) [cfgMAC, ~, decodeStatus] = wlanMPDUDecode(bitsData, SuppressWarnings=true); + if ~decodeStatus + disp(cfgMAC.FrameType + " at " + res.PacketOffset / 20e6 * 1000 + "ms") + end + if ~decodeStatus && matches(cfgMAC.FrameType, "Beacon") if isempty(cfgMAC.ManagementConfig.SSID) ssid = "Hidden"; diff --git a/lab_3/task3.m b/lab_3/task3.m new file mode 100644 index 0000000..994772c --- /dev/null +++ b/lab_3/task3.m @@ -0,0 +1,22 @@ +%{ +disp("Results Task 3:" + newline) +Looking at trace 1 it likely contains virtualized APs. +Analyzing the BSSIDs of trace 1 we can for example see: + +SSID BSSID +TUB-IoT 5C:E1:76:66:02:E2 +eduroam 5C:E1:76:66:02:E3 +TUB-Guest 5C:E1:76:66:02:E4 +_Free_Wifi_Berlin 5C:E1:76:66:02:E7 + +All four networks share a very similar BSSID, +the first five bytes are identical, +only the last byte of the MAC changes in minimal incremental steps, +so these are likely virtualized APs. + +In a ideal conditioned scenario the rss and snr of the networks would be +very similar, because all beacons would be transmitted from the same +physical location. This helps to indicate the use of virtual APs. +However, this is just an additional indicator, +it is not enough, because APs can also be really close to each other. +%} diff --git a/lab_3/task4.m b/lab_3/task4.m new file mode 100644 index 0000000..849cd82 --- /dev/null +++ b/lab_3/task4.m @@ -0,0 +1,65 @@ +% disp("Results Task 4:" + newline) + +saveToFile = true; + +ssid = "wlan fahren wir noch"; +beaconInterval = 100; +band = 2.4; +channel = 1; + +frameBodyConfig = wlanMACManagementConfig(BeaconInterval=beaconInterval, SSID=ssid); + +dsElementID = 3; +dsInformation = dec2hex(channel, 2); +frameBodyConfig = frameBodyConfig.addIE(dsElementID, dsInformation); + +beaconFrameConfig = wlanMACFrameConfig(FrameType="Beacon", ManagementConfig=frameBodyConfig, FromDS=false); + +[mpduBits, mpduLength] = wlanMACFrame(beaconFrameConfig, OutputFormat="bits"); + +fc = wlanChannelFrequency(channel, band); + +cfgNonHT = wlanNonHTConfig(PSDULength=mpduLength); + +osf = 1; +tbtt = 1e-3; +txWaveform = wlanWaveformGenerator(mpduBits, cfgNonHT, OversamplingFactor=osf, NumPackets=5, IdleTime=tbtt); + +Rs = wlanSampleRate(cfgNonHT,OversamplingFactor=osf); + +if saveToFile + iq = txWaveform; + % store to pass filepath to analyzeTrace + save("traces/wlan_fahren_wir_noch_beacons.mat", "iq"); +end + +% parameters from task1.m +generatedTrace = analyzeTrace("traces/wlan_fahren_wir_noch_beacons.mat", channel, window, overlap, fft_precision, Rs); + +generated_p_dB_min = prctile(generatedTrace.P_dB(:), 1); +generated_p_dB_max = prctile(generatedTrace.P_dB(:), 99.9); + +plotSpectrogram(generatedTrace, 5, generated_p_dB_min, generated_p_dB_max); + +%{ +The first thing I noticed is that most of the spectrogram is shown in the +lowest color range. This is expected, because the generated waveform is a +synthetic signal and does not include receiver noise, interference, other +WiFi transmissions, or non-WiFi signals. + +The only relevant signal components are the generated beacon frames. Between +the beacons, the waveform contains idle time, so the power is close to zero +and therefore appears at the lowest dB level in the spectrogram. + +Over time, the spectrum only changes when a beacon frame is transmitted. +During these short intervals, the OFDM signal occupies the 20 MHz channel. +Between the beacon frames, there is no meaningful signal energy. Since the +generation is done under ideal conditions, the repeated beacons look very +similar and there are no random channel effects, noise floor variations, or +additional transmissions as in the recorded SDR traces. + +For visualization, the idle time was reduced to 1 ms and NumPackets was set +to 5, so that multiple beacon frames are visible close to each other in the +spectrogram. This does not represent the normal beacon interval of 100 TU +(about 102.4 ms), but makes the generated frames easier to compare visually. +%} \ No newline at end of file diff --git a/lab_3/wifi_analysis.md b/lab_3/wifi_analysis.md new file mode 100644 index 0000000..a5b981f --- /dev/null +++ b/lab_3/wifi_analysis.md @@ -0,0 +1,84 @@ +# WiFi Beacon Analysis Report + +**Timo Niemann** + +> The code also contains the complete implementation and detailed explanations to the tasks. + +## Goal + +The goal of this lab was to analyze recorded WiFi signals in the 2.4 GHz band. For this, four SDR traces on channels 1, 5, 9, and 13 were evaluated. The first part focused on the visible activity in the spectrum. After that, beacon frames were decoded, the address fields were examined, and possible signs of virtualized access points were checked. Finally, a custom beacon frame was generated and viewed again as a spectrogram. + +## SDR Trace Analysis + +The traces contain complex IQ samples with a sample rate of 20 MS/s. For the spectrograms, the parameters from the task description were used: + +| Parameter | Value | +|---|---:| +| FFT length | `2048` | +| Window length | `512` | +| Overlap | `64` | +| Sample Rate | `20 MS/s` | +| Trace duration | `0.25 s` | + +The color scale was calibrated equally for all four spectrograms so that the channels can be compared fairly. The noise floor was estimated using the 10th percentile of the summed channel power. The occupancy was then calculated as the share of time windows whose power was more than 10 dB above this noise floor. Since the traces are not calibrated to an absolute receive power in dBm, the reported noise floor values are relative dB values derived from the summed spectrogram power. Therefore, they are useful for comparing the traces internally, but they should not be interpreted as absolute RF power levels. + +| Channel | Center Frequency | Noise Floor | Occupancy | +|:---:|:---:|:---:|:---:| +| 1 | 2412 MHz | -11.75 dB | 2.4 % | +| 5 | 2432 MHz | -13.25 dB | 11.23 % | +| 9 | 2452 MHz | -13.1 dB | 3.36 % | +| 13 | 2472 MHz | -11.1 dB | 2.23 % | + +The busiest channel was channel 5. + +## Decoded Beacon Frames + +With the adapted MATLAB example, beacon frames were detected in the traces and decoded with `wlanMPDUDecode`. Trace 1 was especially noticeable because several networks with very similar BSSIDs were found, for example: + +| SSID | BSSID | Vendor | Time in Record | +|---|---|---|---:| +| TUB-IoT | `5C:E1:76:66:02:E2` | Cisco | 40.1149ms | +| eduroam | `5C:E1:76:66:02:E3` | Cisco | 40.3019ms | +| TUB-Guest | `5C:E1:76:66:02:E4` | Cisco | 40.4586ms | +| _Free_Wifi_Berlin | `5C:E1:76:66:02:E7` | Cisco | 40.6286ms | + +The first five bytes of the BSSIDs are identical, and only the last byte is different. This strongly suggests the use of multiple virtual BSSIDs on the same physical access point. Similar SNR values would further support this assumption, but they are not enough as proof on their own, because several real APs can also be placed very close to each other. The packets are close in time to each other, this suites an internal in code iteration over the to-be-announced-beacons for one AP also highly suggesting these beacons came from one physical device. + +![Tasks example spectrogram](ex_spec.png) +Rectangle 1 and 2 are likely WiFi data frames, in the analysis of 2513mhz.mat I've came across an equal looking pattern at 18.4303ms in frame which was decoded as data frame, also they occupy a similar frequency range and show a similar time-frequency pattern, so I assume these as WiFi packets. Rectangle 3 is likely not a WiFi frame because it is firstly not in the frequency range of the other 2 likely data frames and also is much shorter. Short Packages in WiFi would be beacons or ACKs but a beacon is transmitted over the whole frequency band and an ACK is not sent out of the blue, meaning it follows forgoing data, which this marked capture does not seem to have. I classify rectangle 4 also as a non-WiFi frame, it has a relative similar frametime as the likely data frames beside, but the frequency band is much smaler than the repeating data frames. + +The address usage in beacon frames is as follows: +- Address 1: used to announce the destination station, in a beacon frame its FF:FF:FF:FF:FF:FF (meaning broadcast to all stations) +- Address 2: used to remark the source device address of the frame, the ap mac who sent the beacon is inserted +- Address 3: used for the bssid of the device sending the beacon, means usually address 2 equals address 3, when no virtual ap is used +- Address 4: this field does not exists, after the seq ctrl the frame body follows containing beacon data instead of an address, like timestamp, beacon interval, capability info, ssid, potentially the channel number, see the extracts from 2.1 + +For normal data frames, the meaning of the addresses depends on the direction: +- Downlink: AP -> Station + - Address 1: MAC of the destination station + - Address 2: MAC of the AP which transmits the frame + - Address 3: MAC of the initial sender which has started the frame + - Address 4: not used + +- Uplink: Station -> AP + - Address 1: MAC of the AP that receives and potentially forwards the frame + - Address 2: MAC of the sending station + - Address 3: MAC of the final destination station + - Address 4: not used + +The fourth address is only used in special cases, for example: +- 4 Address case: wireless bridge: + - Address 1: MAC of the AP that should receive / transmit through the frame, for example AP, bridge node, repeater or mesh node + - Address 2: MAC of the transmitter (into the other network) + - Address 3: MAC of the final destination + - Address 4: MAC of the initial sender + +## Generated Beacon + +In the last part, a custom beacon frame with the SSID `wlan fahren wir noch` was generated. The beacon was configured for channel 1 in the 2.4 GHz band and uses a beacon interval of `100 TU`. For the spectrogram view, five beacon frames were generated. For visualization, the idle time was reduced to `1 ms`, so that multiple beacon frames are visible within the short spectrogram window. + +In the spectrogram, only short and repeated signal regions are visible. Between them, there is almost no power because the signal was generated synthetically and does not contain real receiver noise, interference, or other WLAN frames. The beacon bursts look very similar because they were generated under ideal conditions. + +## Conclusion + +The analysis shows that the four recorded channels were used with different intensity. Channel 5 was the most active one in this recording, while the other channels only contained short activity phases. By decoding the beacon frames, several SSIDs and their BSSIDs could be identified. The very similar BSSIDs in Trace 1 strongly suggest the presence of a virtualized access point configuration. The generated beacon also confirms how beacon frames appear in a spectrogram as short, repeated transmissions. diff --git a/lab_3/wifi_analysis.pdf b/lab_3/wifi_analysis.pdf new file mode 100644 index 0000000..f6804ae Binary files /dev/null and b/lab_3/wifi_analysis.pdf differ