mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-03-28 17:42:59 +01:00
Merge pull request #111 from Genaker/rtl-sdr
rtl and display radio packet
This commit is contained in:
@@ -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);
|
||||
|
||||
3
rtl/install-macos.sh
Normal file
3
rtl/install-macos.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
brew update
|
||||
brew install rtl-sdr
|
||||
rtl_test
|
||||
98
rtl/rtl-scanner.py
Normal file
98
rtl/rtl-scanner.py
Normal file
@@ -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()
|
||||
90
src/main.cpp
90
src/main.cpp
@@ -153,6 +153,8 @@ typedef enum
|
||||
// String SCAN_RANGES = String("850..890,920..950");
|
||||
String SCAN_RANGES = "";
|
||||
|
||||
std::unordered_map<int, bool> 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<int, int> previousPac = {/*{916, true}, {915, true}*/};
|
||||
|
||||
std::unordered_map<int, int16_t> findMaxRssi(int16_t *rssis, uint32_t *freqs_khz,
|
||||
int dump_sz, int level = 80)
|
||||
{
|
||||
std::unordered_map<int, int16_t> 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<int, int16_t> 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
|
||||
|
||||
Reference in New Issue
Block a user