diff --git a/lib/config/config.h b/lib/config/config.h index 87136e8..f0a282c 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -50,7 +50,7 @@ struct Config listen_on_serial0(String("none")), listen_on_serial1(String("readline")), listen_on_usb(String("readline")), rx_lora(NULL), tx_lora(NULL), // Enable Lora Send: - // rx_lora(configureLora("freq:920")),tx_lora(configureLora("freq:916")) + // rx_lora(configureLora("freq:920")), tx_lora(configureLora("freq:916")), is_host(false) {}; bool write_config(const char *path); diff --git a/rtl/install-macos.sh b/rtl/install-macos.sh new file mode 100644 index 0000000..b33d754 --- /dev/null +++ b/rtl/install-macos.sh @@ -0,0 +1,3 @@ +brew update +brew install rtl-sdr +rtl_test diff --git a/rtl/rtl-scanner.py b/rtl/rtl-scanner.py new file mode 100644 index 0000000..1b58a72 --- /dev/null +++ b/rtl/rtl-scanner.py @@ -0,0 +1,98 @@ +import numpy as np +from rtlsdr import RtlSdr +import time +import os + +def ascii_bar_chart(data, start_freq, step, max_height=20, symbol='#'): + """ + Converts an array of RSSI values into an ASCII vertical bar chart with RSSI labels on the left and MHz labels under every 5th step. + """ + min_rssi = min(data) + max_rssi = max(data) + normalized = [(x - min_rssi) / (max_rssi - min_rssi + 1e-6) for x in data] # Normalize to 0-1 + heights = [int(value * max_height) for value in normalized] + + # Generate left-aligned RSSI labels + rssi_range = np.linspace(max_rssi, min_rssi, max_height) + rssi_labels = [f"{rssi:.1f} dB".rjust(8) for rssi in rssi_range] + + # Print the vertical bars with RSSI labels + for level, label in zip(range(max_height, 0, -1), rssi_labels): + line = label + ' ' # Add RSSI label + for height in heights: + line += symbol if height >= level else ' ' + print(line) + + # Print x-axis + freq_labels = [f"{(start_freq + step * i) / 1e6:.0f}" if i % 5 == 0 else "" for i in range(len(data))] + print(" " * 8 + '-' * len(data)) # Bar separator + x_axis = " " * 8 + printed = 0 + previous_label = "" + for i, label in enumerate(freq_labels): + if label: + printed = 0 + previous_label = label + x_axis += label #.center(1) # Center the MHz label under the bar + else: + if printed < (5 - len(previous_label)): + x_axis += " " # No space between bars + printed += 1 + print(x_axis) + +def scan_frequency_range(start_freq, end_freq, step, sdr, fft_size=2*1024): + """ + Scans a frequency range using an RTL-SDR device and calculates RSSI for each frequency. + """ + center_frequencies = np.arange(start_freq, end_freq + step, step) + rssi_values = [] + + for freq in center_frequencies: + sdr.center_freq = freq + samples = sdr.read_samples(fft_size) + + # Perform FFT and calculate RSSI + power_spectrum = np.abs(np.fft.fft(samples))**2 + power_db = 10 * np.log10(power_spectrum + 1e-6) # Convert to dB + avg_rssi = np.percentile(power_db, 90) # 95 percentile RSSI for this frequency + rssi_values.append(avg_rssi) + + return rssi_values + +def main(): + # Frequency range in Hz + start_freq = 800e6 # 800 MHz + end_freq = 900e6 # 1200 MHz + step = 1e6 # 1 MHz steps + + # Initialize RTL-SDR + sdr = RtlSdr() + sdr.sample_rate = 2.048e6 # 2.048 MSPS + sdr.gain = 10 # Adjust gain as needed + + try: + while True: + start_time = time.time() + rssi_values = scan_frequency_range(start_freq, end_freq, step, sdr) + end_time = time.time() + + # Clear screen + os.system('clear' if os.name == 'posix' else 'cls') + + # Scan frequency range + print(f"Scanning range: {start_freq / 1e6:.0f} MHz to {end_freq / 1e6:.0f} MHz") + print(f"Scanning time: {end_time - start_time:.2f} seconds") + + # Display ASCII bar chart with RSSI and MHz labels + print("RSSI (dB):") + ascii_bar_chart(rssi_values, start_freq, step) + #time.sleep(0.5) # Refresh every 0.5 seconds + + except KeyboardInterrupt: + print("\nStopping scan...") + + finally: + sdr.close() + +if __name__ == '__main__': + main() diff --git a/src/main.cpp b/src/main.cpp index aa63139..a02d1ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -153,6 +153,8 @@ typedef enum // String SCAN_RANGES = String("850..890,920..950"); String SCAN_RANGES = ""; +std::unordered_map ignoredFreq = {/*{916, true}, {915, true}*/}; + size_t scan_pages_sz = 0; ScanPage *scan_pages; size_t scan_page = 0; @@ -1686,10 +1688,18 @@ void doScan() } else g = &getRSSI; - - uint16_t max_rssi = r.rssiMethod(g, &r, samples, result, - RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); - + uint16_t max_rssi = 120; + // Scan if not in the ignore list + if (ignoredFreq.find((int)r.current_frequency) == ignoredFreq.end()) + { + max_rssi = r.rssiMethod(g, &r, samples, result, + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); + } + else + { + // if ignored default RSSI value -120dB + max_rssi = 120; + } if (max_x_rssi[display_x] > max_rssi) { max_x_rssi[display_x] = max_rssi; @@ -1961,6 +1971,33 @@ void doScan() #endif } +std::unordered_map previousPac = {/*{916, true}, {915, true}*/}; + +std::unordered_map findMaxRssi(int16_t *rssis, uint32_t *freqs_khz, + int dump_sz, int level = 80) +{ + std::unordered_map maxRssiPerMHz; // Map to store max RSSI per MHz + + for (int i = 0; i < dump_sz; i++) + { + int16_t rssi = rssis[i]; + int freq_mhz = (int)freqs_khz[i] / 1000; // Convert kHz to MHz + + // Update the maximum RSSI for this MHz frequency + if (maxRssiPerMHz.find(freq_mhz) == maxRssiPerMHz.end() || + maxRssiPerMHz[freq_mhz] < rssi) + { + if (abs(rssi) < level) + { + maxRssiPerMHz[freq_mhz] = rssi; + } + } + } + + return maxRssiPerMHz; +} + +bool lock = false; int16_t checkRadio(RadioComms &comms) { radioIsScan = false; @@ -1971,7 +2008,7 @@ int16_t checkRadio(RadioComms &comms) Message *msg = comms.receive( config.is_host ? 2000 - : 200); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500 + : 500); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500 if (msg == NULL) { return status; @@ -1979,6 +2016,49 @@ int16_t checkRadio(RadioComms &comms) if (msg->type == SCAN_RESULT) { + // if (lock == false) + { + lock = true; + + // display.setDisplayRotation(1); + // display.println("Host Mode ->"); + + size_t dump_sz = msg->payload.dump.sz; + int16_t *rssi = msg->payload.dump.rssis; + uint32_t *fr = msg->payload.dump.freqs_khz; + + std::unordered_map maxMhzRssi = + findMaxRssi(rssi, fr, dump_sz, 85); + + int lx, ly, i = 0; + for (const auto &pair : maxMhzRssi) + { + if (i == 0 && maxMhzRssi.size() > 0) + { + display.clear(); + } + // screen overflow protection + if (lx < 130) + { + int16_t rssi = pair.second; + int16_t fr = (int)pair.first; + display.drawString(lx, ly, String(fr) + ":" + String(rssi)); + // go to next line + ly += 10; + if (ly > 60) + { + ly = 0; + + // go to next column + lx += 50; + } + display.display(); + } + i++; + } + lock = false; + } + HostComms->send(*msg); } else