diff --git a/aerpaw/radio/CSwSNRRX.py b/aerpaw/radio/CSwSNRRX.py index 4131ee6..dd683cf 100644 --- a/aerpaw/radio/CSwSNRRX.py +++ b/aerpaw/radio/CSwSNRRX.py @@ -267,6 +267,10 @@ class CSwSNRRX(gr.top_block): '/root/Quality', num_uavs, slot_duration, guard_interval) self.blocks_file_sink_0 = TdmTaggedFileSink( '/root/Power', num_uavs, slot_duration, guard_interval) + self.blocks_file_sink_noisefloor = TdmTaggedFileSink( + '/root/NoiseFloor', num_uavs, slot_duration, guard_interval) + self._freqoffset_file = open('/root/FreqOffset', 'w') + self._freqoffset_file.write('tx_uav_id,value\n') self.blocks_divide_xx_0 = blocks.divide_ff(1) self.blocks_complex_to_real_0_0 = blocks.complex_to_real(1) self.blocks_complex_to_real_0 = blocks.complex_to_real(1) @@ -310,6 +314,7 @@ class CSwSNRRX(gr.top_block): self.connect((self.blocks_nlog10_ff_0_0, 0), (self.blocks_add_const_vxx_0, 0)) self.connect((self.blocks_nlog10_ff_0_0, 0), (self.blocks_sub_xx_0, 0)) self.connect((self.blocks_nlog10_ff_0_0_0, 0), (self.blocks_sub_xx_0, 1)) + self.connect((self.blocks_nlog10_ff_0_0_0, 0), (self.blocks_file_sink_noisefloor, 0)) self.connect((self.blocks_stream_to_vector_0_0, 0), (self.epy_block_0, 0)) self.connect((self.blocks_sub_xx_0, 0), (self.blocks_file_sink_0_0_0, 0)) self.connect((self.blocks_vector_to_stream_0_0, 0), (self.blocks_keep_m_in_n_0, 0)) @@ -321,6 +326,26 @@ class CSwSNRRX(gr.top_block): self.connect((self.freq_xlating_fft_filter_ccc_0_0, 0), (self.blocks_stream_to_vector_0_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.blocks_multiply_xx_0, 0)) + ################################################## + # Frequency offset polling thread + ################################################## + def _freq_offset_probe(): + frame_dur = slot_duration * num_uavs + while True: + val = self.digital_fll_band_edge_cc_0_0.get_frequency() + freq_hz = val * samp_rate / (2 * math.pi) + now = time.time() + slot_time = now % frame_dur + current_slot = int(slot_time / slot_duration) + time_into_slot = slot_time - current_slot * slot_duration + tx_id = -1 if time_into_slot < guard_interval else current_slot + self._freqoffset_file.write(f'{tx_id},{freq_hz}\n') + self._freqoffset_file.flush() + time.sleep(0.01) + _freq_offset_thread = threading.Thread(target=_freq_offset_probe) + _freq_offset_thread.daemon = True + _freq_offset_thread.start() + def get_args(self): return self.args diff --git a/aerpaw/results/plotRadioLogs.m b/aerpaw/results/plotRadioLogs.m index dd5efe0..5a59d94 100644 --- a/aerpaw/results/plotRadioLogs.m +++ b/aerpaw/results/plotRadioLogs.m @@ -24,16 +24,25 @@ function [f, R] = plotRadioLogs(resultsPath) R{ii}(bad, :) = []; end + % Compute path loss from Power (post-processing) + % Power = 20*log10(peak_mag) - rxGain; path loss = txGain - rxGain - Power + txGain_dB = 76; % from startchannelsounderTXGRC.sh GAIN_TX + rxGain_dB = 30; % from startchannelsounderRXGRC.sh GAIN_RX + for ii = 1:numel(R) + R{ii}.PathLoss = txGain_dB - rxGain_dB - R{ii}.Power; + R{ii}.FreqOffset = R{ii}.FreqOffset / 1e6; % Hz to MHz + end + % Build legend labels and color map for up to 4 UAVs nUAV = numel(R); colors = lines(nUAV * nUAV); styles = ["-o", "-s", "-^", "-d", "-v", "-p", "-h", "-<", "->", "-+", "-x", "-*"]; - metricNames = ["SNR", "Power", "Quality"]; - yLabels = ["SNR (dB)", "Power (dB)", "Quality"]; + metricNames = ["SNR", "Power", "Quality", "PathLoss", "NoiseFloor", "FreqOffset"]; + yLabels = ["SNR (dB)", "Power (dB)", "Quality", "Path Loss (dB)", "Noise Floor (dB)", "Freq Offset (MHz)"]; f = figure; - tl = tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'compact'); + tl = tiledlayout(numel(metricNames), 1, 'TileSpacing', 'compact', 'Padding', 'compact'); for mi = 1:numel(metricNames) ax = nexttile(tl); diff --git a/aerpaw/results/readRadioLogs.m b/aerpaw/results/readRadioLogs.m index 658b97e..fefcb75 100644 --- a/aerpaw/results/readRadioLogs.m +++ b/aerpaw/results/readRadioLogs.m @@ -4,19 +4,19 @@ function R = readRadioLogs(logPath) end arguments (Output) - R (:, 6) table; + R (:, 8) table; end % Extract receiving UAV ID from directory name (e.g. "uav0_..." → 0) [~, dirName] = fileparts(logPath); rxID = int32(sscanf(dirName, 'uav%d')); - metrics = ["quality", "snr", "power"]; + metrics = ["quality", "snr", "power", "noisefloor", "freqoffset"]; logs = dir(logPath); logs = logs(endsWith({logs(:).name}, metrics + "_log.txt")); - R = table(datetime.empty(0,1), zeros(0,1,'int32'), zeros(0,1,'int32'), zeros(0,1), zeros(0,1), zeros(0,1), ... - 'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality"]); + R = table(datetime.empty(0,1), zeros(0,1,'int32'), zeros(0,1,'int32'), zeros(0,1), zeros(0,1), zeros(0,1), zeros(0,1), zeros(0,1), ... + 'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality", "NoiseFloor", "FreqOffset"]); for ii = 1:numel(logs) filepath = fullfile(logs(ii).folder, logs(ii).name); @@ -43,13 +43,15 @@ function R = readRadioLogs(logPath) val = data{3}; n = numel(ts); - t = table(ts, txId, repmat(rxID, n, 1), NaN(n,1), NaN(n,1), NaN(n,1), ... - 'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality"]); + t = table(ts, txId, repmat(rxID, n, 1), NaN(n,1), NaN(n,1), NaN(n,1), NaN(n,1), NaN(n,1), ... + 'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality", "NoiseFloor", "FreqOffset"]); switch metric - case "snr", t.SNR = val; - case "power", t.Power = val; - case "quality", t.Quality = val; + case "snr", t.SNR = val; + case "power", t.Power = val; + case "quality", t.Quality = val; + case "noisefloor", t.NoiseFloor = val; + case "freqoffset", t.FreqOffset = val; end R = [R; t]; %#ok diff --git a/aerpaw/results/resultsAnalysis.m b/aerpaw/results/resultsAnalysis.m index 1255096..b6f004e 100644 --- a/aerpaw/results/resultsAnalysis.m +++ b/aerpaw/results/resultsAnalysis.m @@ -1,5 +1,5 @@ %% Plot AERPAW logs (trajectory, radio) -resultsPath = fullfile(matlab.project.rootProject().RootFolder, "sandbox", "t1"); % Define path to results copied from AERPAW platform +resultsPath = fullfile(matlab.project.rootProject().RootFolder, "sandbox", "t2"); % Define path to results copied from AERPAW platform % Plot GPS logged data and scenario information (domain, objective, obstacles) seaToGroundLevel = 110; % measured approximately from USGS national map viewer diff --git a/aerpaw/scripts/startRadio.sh b/aerpaw/scripts/startRadio.sh index 555f874..980aeab 100755 --- a/aerpaw/scripts/startRadio.sh +++ b/aerpaw/scripts/startRadio.sh @@ -8,18 +8,32 @@ screen -S rxGRC -dm \ 2>&1 | ts $TS_FORMAT \ | tee $RESULTS_DIR/$LOG_PREFIX\_radio_channelsounderrxgrc_log.txt" -screen -S power -dm bash -c "stdbuf -oL -eL tail -F /root/Power\ +screen -S power -dm \ + bash -c "stdbuf -oL -eL tail -F /root/Power\ 2>&1 | ts $TS_FORMAT \ | tee $RESULTS_DIR/$LOG_PREFIX\_power_log.txt" -screen -S quality -dm bash -c "stdbuf -oL -eL tail -F /root/Quality\ +screen -S quality -dm \ + bash -c "stdbuf -oL -eL tail -F /root/Quality\ 2>&1 | ts $TS_FORMAT \ | tee $RESULTS_DIR/$LOG_PREFIX\_quality_log.txt" -screen -S snr -dm bash -c "stdbuf -oL -eL tail -F /root/SNR\ +screen -S snr -dm \ + bash -c "stdbuf -oL -eL tail -F /root/SNR\ 2>&1 | ts $TS_FORMAT \ | tee $RESULTS_DIR/$LOG_PREFIX\_snr_log.txt" +screen -S noisefloor -dm \ + bash -c "stdbuf -oL -eL tail -F /root/NoiseFloor\ + 2>&1 | ts $TS_FORMAT \ + | tee $RESULTS_DIR/$LOG_PREFIX\_noisefloor_log.txt" + +screen -S freqoffset -dm \ + bash -c "stdbuf -oL -eL tail -F /root/FreqOffset\ + 2>&1 | ts $TS_FORMAT \ + | tee $RESULTS_DIR/$LOG_PREFIX\_freqoffset_log.txt" + + #TX screen -S txGRC -dm \