diff --git a/SpectrumScan_changes.py b/SpectrumScan_changes.py new file mode 100644 index 0000000..f1a6b1f --- /dev/null +++ b/SpectrumScan_changes.py @@ -0,0 +1,213 @@ +#!/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()