mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-03-28 17:42:59 +01:00
214 lines
6.2 KiB
Python
214 lines
6.2 KiB
Python
#!/usr/bin/python3
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
import argparse
|
|
import serial
|
|
import sys
|
|
import numpy as np
|
|
import matplotlib as mpl
|
|
import matplotlib.pyplot as plt
|
|
|
|
from datetime import datetime
|
|
from argparse import RawTextHelpFormatter
|
|
|
|
# number of samples in each scanline
|
|
SCAN_WIDTH = 20
|
|
SCAN_MIN_FREQ = "FREQ 850"
|
|
|
|
# scanline Serial start/end markers
|
|
SCAN_MARK_START = "SCAN "
|
|
SCAN_MARK_FREQ = "FREQ "
|
|
SCAN_MARK_END = " END"
|
|
|
|
# output path
|
|
OUT_PATH = "out"
|
|
|
|
# default settings
|
|
DEFAULT_BAUDRATE = 115200
|
|
DEFAULT_COLOR_MAP = "viridis"
|
|
DEFAULT_SCAN_LEN = 200
|
|
DEFAULT_RSSI_OFFSET = -11
|
|
|
|
|
|
# Print iterations progress
|
|
# from https://stackoverflow.com/questions/3173320/text-progress-bar-in-terminal-with-block-characters
|
|
def printProgressBar(
|
|
iteration,
|
|
total,
|
|
prefix="",
|
|
suffix="",
|
|
decimals=1,
|
|
length=50,
|
|
fill="█",
|
|
printEnd="\r",
|
|
):
|
|
"""
|
|
Call in a loop to create terminal progress bar
|
|
@params:
|
|
iteration - Required : current iteration (Int)
|
|
total - Required : total iterations (Int)
|
|
prefix - Optional : prefix string (Str)
|
|
suffix - Optional : suffix string (Str)
|
|
decimals - Optional : positive number of decimals in percent complete (Int)
|
|
length - Optional : character length of bar (Int)
|
|
fill - Optional : bar fill character (Str)
|
|
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
|
|
"""
|
|
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
|
filledLength = int(length * iteration // total)
|
|
bar = fill * filledLength + "-" * (length - filledLength)
|
|
print(f"\r{prefix} |{bar}| {percent}% {suffix}", end=printEnd)
|
|
if iteration == total:
|
|
print()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=RawTextHelpFormatter,
|
|
description="""
|
|
RadioLib SX126x_Spectrum_Scan plotter script. Displays output from SX126x_Spectrum_Scan example
|
|
as grayscale and
|
|
|
|
Depends on pyserial and matplotlib, install by:
|
|
'python3 -m pip install pyserial matplotlib'
|
|
|
|
Step-by-step guide on how to use the script:
|
|
1. Upload the SX126x_Spectrum_Scan example to your Arduino board with SX1262 connected.
|
|
2. Run the script with appropriate arguments.
|
|
3. Once the scan is complete, output files will be saved to out/
|
|
""",
|
|
)
|
|
parser.add_argument("port", type=str, help="COM port to connect to the device")
|
|
parser.add_argument(
|
|
"--speed",
|
|
default=DEFAULT_BAUDRATE,
|
|
type=int,
|
|
help=f"COM port baudrate (defaults to {DEFAULT_BAUDRATE})",
|
|
)
|
|
parser.add_argument(
|
|
"--map",
|
|
default=DEFAULT_COLOR_MAP,
|
|
type=str,
|
|
help=f'Matplotlib color map to use for the output (defaults to "{DEFAULT_COLOR_MAP}")',
|
|
)
|
|
parser.add_argument(
|
|
"--len",
|
|
default=DEFAULT_SCAN_LEN,
|
|
type=int,
|
|
help=f"Number of scanlines to record (defaults to {DEFAULT_SCAN_LEN})",
|
|
)
|
|
parser.add_argument(
|
|
"--offset",
|
|
default=DEFAULT_RSSI_OFFSET,
|
|
type=int,
|
|
help=f"Default RSSI offset in dBm (defaults to {DEFAULT_RSSI_OFFSET})",
|
|
)
|
|
parser.add_argument(
|
|
"--freq", default=-1, type=float, help=f"Default starting frequency in MHz"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
freq_mode = False
|
|
scan_len = args.len
|
|
if args.freq != -1:
|
|
freq_mode = True
|
|
scan_len = 1000
|
|
|
|
# create the color map and the result array
|
|
arr = np.zeros((SCAN_WIDTH, scan_len))
|
|
|
|
# scanline counter
|
|
row = 0
|
|
|
|
# list of frequencies in frequency mode
|
|
freq_list = []
|
|
|
|
start=False
|
|
|
|
# open the COM port
|
|
with serial.Serial(args.port, args.speed, timeout=None) as com:
|
|
while True:
|
|
# read a single line
|
|
try:
|
|
line = com.readline().decode("utf-8")
|
|
#print(line)
|
|
except:
|
|
continue
|
|
|
|
if start:
|
|
if SCAN_MIN_FREQ not in line:
|
|
continue
|
|
else:
|
|
start=False
|
|
# update the progress bar
|
|
if not freq_mode:
|
|
printProgressBar(row, scan_len)
|
|
|
|
|
|
|
|
if SCAN_MARK_FREQ in line:
|
|
new_freq = float(line.split(" ")[1])
|
|
if (len(freq_list) > 1) and (new_freq < freq_list[-1]):
|
|
break
|
|
|
|
freq_list.append(new_freq)
|
|
print("{:.3f}".format(new_freq), end="\r")
|
|
continue
|
|
|
|
# check the markers
|
|
if (SCAN_MARK_START in line) and (SCAN_MARK_END in line):
|
|
# get the values
|
|
scanline = line[len(SCAN_MARK_START) : -len(SCAN_MARK_END)].split(",")
|
|
for col in range(SCAN_WIDTH):
|
|
arr[col][row] = int(scanline[col])
|
|
|
|
# increment the row counter
|
|
row = row + 1
|
|
|
|
# check if we're done
|
|
if (not freq_mode) and (row >= scan_len):
|
|
break
|
|
|
|
# scale to the number of scans (sum of any given scanline)
|
|
num_samples = arr.sum(axis=0)[0]
|
|
print("NUM SAMPLES:",num_samples)
|
|
print("ARR.MAX:",arr.max())
|
|
print("ARR.SHAPE:",arr.shape)
|
|
print("LEN_FREQS:",len(freq_list))
|
|
arr *= num_samples / arr.max()
|
|
|
|
if freq_mode:
|
|
scan_len = len(freq_list)
|
|
|
|
# create the figure
|
|
fig, ax = plt.subplots()
|
|
|
|
#print(arr)
|
|
print(freq_list)
|
|
|
|
# display the result as heatmap
|
|
extent = [0, scan_len, -4 * (SCAN_WIDTH + 1), args.offset]
|
|
if freq_mode:
|
|
extent[0] = freq_list[0]
|
|
extent[1] = freq_list[-1]
|
|
im = ax.imshow(arr[:, :scan_len], cmap=args.map, extent=extent)
|
|
fig.colorbar(im)
|
|
|
|
# set some properites and show
|
|
timestamp = datetime.now().strftime("%y-%m-%d %H-%M-%S")
|
|
title = f"RadioLib SX126x Spectral Scan {timestamp}"
|
|
if freq_mode:
|
|
plt.xlabel("Frequency [Hz]")
|
|
else:
|
|
plt.xlabel("Time [sample]")
|
|
plt.ylabel("RSSI [dBm]")
|
|
ax.set_aspect("auto")
|
|
fig.suptitle(title)
|
|
fig.canvas.manager.set_window_title(title)
|
|
plt.savefig(f'{OUT_PATH}/{title.replace(" ", "_")}.png', dpi=300)
|
|
plt.show()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|