commit 1ed10b5a950d3ba779d66dec1b9782720eac4abc Author: Egor Shitikov Date: Mon Jul 22 16:17:04 2024 -0700 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..130eec1 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Lora SA(Spectrum Analyzer) +RF Spectrum Analyzer using Lora Radio + +RadioLib SX126x Spectrum Scan + +Perform a spectrum power scan using SX126x. +The output is in the form of scan lines, each line has 33 power bins. +First power bin corresponds to -11 dBm, the second to -15 dBm and so on. +Higher number of samples in a bin corresponds to more power received +at that level. + +The spectrum analyzer perform power measurements in the configured bandwidth. + +To show the results in a plot, run the Python script +RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py + +# VSCode Platform.IO installation + +# Hardware + +# 3D printed case diff --git a/SpectrumScan.py b/SpectrumScan.py new file mode 100644 index 0000000..acc37a0 --- /dev/null +++ b/SpectrumScan.py @@ -0,0 +1,176 @@ +#!/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 = 33 + +# 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 = [] + + # open the COM port + with serial.Serial(args.port, args.speed, timeout=None) as com: + while(True): + # update the progress bar + if not freq_mode: + printProgressBar(row, scan_len) + + # read a single line + try: + line = com.readline().decode('utf-8') + except: + continue + + 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] + arr *= (num_samples/arr.max()) + + if freq_mode: + scan_len = len(freq_list) + + # create the figure + fig, ax = plt.subplots() + + # 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() \ No newline at end of file diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/media/UCOG-SA.JPEG b/media/UCOG-SA.JPEG new file mode 100644 index 0000000..2af6b66 Binary files /dev/null and b/media/UCOG-SA.JPEG differ diff --git a/media/ucog-mono.png b/media/ucog-mono.png new file mode 100644 index 0000000..0f73927 Binary files /dev/null and b/media/ucog-mono.png differ diff --git a/media/ucog.png b/media/ucog.png new file mode 100644 index 0000000..5767fc2 Binary files /dev/null and b/media/ucog.png differ diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..0be4ad5 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:heltec_wifi_lora_32_V3] +platform = espressif32 +board = heltec_wifi_lora_32_V3 +framework = arduino +upload_speed = 115200 +monitor_speed = 115200 +board_build.f_cpu = 240000000 +lib_deps = + ropg/Heltec_ESP32_LoRa_v3@^0.9.1 diff --git a/src/images.h b/src/images.h new file mode 100644 index 0000000..7461691 --- /dev/null +++ b/src/images.h @@ -0,0 +1,101 @@ + +//'Logo_UCOG', 128x64px +//https://www.online-utility.org/image/convert/to/XBM +//https://javl.github.io/image2cpp/ +//#define 1721604660673_width 128 +//#define 1721604660673_height 64 + +const unsigned char epd_bitmap_ucog [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x10, 0x84, 0xC3, 0x81, 0x03, 0x00, 0x60, 0x00, + 0xCC, 0x07, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x30, 0xE6, 0xF3, 0xE3, + 0x07, 0x00, 0x60, 0x00, 0x0C, 0x3E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x30, 0x66, 0x10, 0x36, 0x00, 0x00, 0x60, 0x00, 0x06, 0xF8, 0x00, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x30, 0x26, 0x18, 0x36, 0x00, 0x00, 0x60, 0x00, + 0x07, 0xE0, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x30, 0x26, 0x18, 0x36, + 0x07, 0x00, 0x60, 0x00, 0x03, 0x00, 0x1F, 0x00, 0xC0, 0x01, 0x00, 0x00, + 0x30, 0x26, 0x18, 0x36, 0x06, 0x00, 0x60, 0x80, 0x01, 0x00, 0x7C, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0x30, 0x66, 0x38, 0x76, 0x06, 0x0C, 0x60, 0xC0, + 0x01, 0x00, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0xE0, 0xE3, 0xF3, 0xE3, + 0x07, 0x0E, 0x60, 0xC0, 0x03, 0x00, 0x80, 0x0F, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x60, 0xE0, 0x07, 0x00, 0x00, 0x3E, + 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x60, 0x60, + 0x0E, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0x01, 0x60, 0x70, 0x1C, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x60, 0x30, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x60, 0x18, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xF0, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xFC, 0x0D, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x67, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x61, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x60, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0xF0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x67, 0xE0, 0x83, + 0x03, 0x00, 0x00, 0xE0, 0xFF, 0xE1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, + 0xD0, 0x63, 0x60, 0x07, 0x07, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x03, 0x00, + 0x00, 0x08, 0x00, 0x02, 0xF0, 0x61, 0x60, 0x1C, 0x1F, 0x80, 0xFF, 0x07, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xF8, + 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, 0x02, + 0xF0, 0x7F, 0x60, 0xDC, 0x1F, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x00, 0x00, 0x02, 0xD0, 0x61, 0x60, 0x07, 0x0F, 0x00, 0xC0, 0xFF, + 0x03, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x67, 0xE0, 0x83, + 0x03, 0x00, 0x00, 0xF0, 0xFF, 0xC1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7E, 0xE0, 0xC1, 0x01, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0x61, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x67, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0xFC, 0x0D, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0xF0, 0x18, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x18, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x60, 0x30, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x60, 0x70, 0x3C, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x60, 0x60, + 0x1E, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x07, 0x60, 0xE0, 0x0F, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x03, 0x00, 0x0F, 0x60, 0xC0, 0x03, 0x00, 0x80, 0x0F, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x07, 0x00, 0x0C, 0x60, 0xC0, + 0x01, 0x00, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x05, + 0x00, 0x08, 0x60, 0xC0, 0x01, 0x00, 0x7C, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x83, 0x0C, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x1F, 0x00, + 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x0C, 0x00, 0x00, 0x60, 0x00, + 0x07, 0xE0, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x0F, + 0x00, 0x00, 0x60, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x0E, 0x3E, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x00, 0x80, 0x67, 0x18, 0x00, 0x00, 0x60, 0x00, + 0xCC, 0x07, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xFC, 0x01, 0x00, 0x00, + 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 1040) +const int epd_bitmap_allArray_LEN = 1; +const unsigned char* epd_bitmap_allArray[1] = { + epd_bitmap_ucog +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..51f5108 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,244 @@ + /** + RadioLib SX126x Spectrum Scan + + This example shows how to perform a spectrum power scan using SX126x. + The output is in the form of scan lines, each line has 33 power bins. + First power bin corresponds to -11 dBm, the second to -15 dBm and so on. + Higher number of samples in a bin corresponds to more power received + at that level. + + To show the results in a plot, run the Python script + RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py + + WARNING: This functionality is experimental and requires a binary patch + to be uploaded to the SX126x device. There may be some undocumented + side effects! + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// frequency range in MHz to scan +#define FREQ_BEGIN 400.00 +#define FREQ_END 500.00 + +// Measurement bandwidth. Allowed bandwidth values (in kHz) are: +// 4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, +// 78.2, 93.8, 117.3, 156.2, 187.2, 234.3, 312.0, 373.6 and 467.0 +#define BANDWIDTH 93.8//467.0 + +// (optional) major and minor tickmarks at x MHz +#define MAJOR_TICKS 10 +// #define MINOR_TICKS 5 + +// Turns the 'PRG' button into the power button, long press is off +#define HELTEC_POWER_BUTTON // must be before "#include " +#include +#include +#include + +// This file contains binary patch for the SX1262 +#include "modules/SX126x/patches/SX126x_patch_scan.h" + +// Prints the scan measurement bins from the SX1262 in hex +#define PRINT_SCAN_VALUES +#define PRINT_PROFILE_TIME + +// numbers of the spectrum screan lines = width of screan +#define STEPS 128 +// Number of samples for each scan. Fewer samples = better temporal resolution. +#define SAMPLES 256 //(scan time = 1294) +#define MAJOR_TICK_LENGTH 3 +#define MINOR_TICK_LENGTH 1 +#define X_AXIS_WEIGHT 2 +#define HEIGHT RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE +// +#define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH) +#define STATUS_TEXT_TOP (64 - 14) +#define RANGE (float)(FREQ_END - FREQ_BEGIN) +#define SINGLE_STEP (float)(RANGE / STEPS) + +// Array to store the scan results +uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; +// global variable +unsigned short int scan_var = 0; +// initialized flag +bool initialized = false; +bool led_flag = true; +// drone tetection flag +unsigned short int drone_detected = 0; + +unsigned int start_scan_text = (128 / 2) - 3; + +unsigned int scan_time = 0; + +uint64_t start = 0; + +unsigned int x,y = 0; + + +/** + * @brief Draws ticks on the display at regular whole intervals. + * + * @param every The interval between ticks in MHz. + * @param length The length of each tick in pixels. + */ +void drawTicks(float every, int length) { + float first_tick = FREQ_BEGIN + (every - (FREQ_BEGIN - (int)(FREQ_BEGIN / every) * every)); + if (first_tick < FREQ_BEGIN){ + first_tick += every; + } + for (float tick_freq = first_tick; tick_freq <= FREQ_END; tick_freq += every) { + int tick = round((tick_freq - FREQ_BEGIN) / SINGLE_STEP); + display.drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, HEIGHT + X_AXIS_WEIGHT + length); + } +} + + +/** + * @brief Decorates the display: everything but the plot itself. + */ +void displayDecorate() { + if (!initialized) { + // begining and end ticks + display.fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH); + display.fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH); + // frequencies + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, SCALE_TEXT_TOP, String(FREQ_BEGIN)); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(127, SCALE_TEXT_TOP, String(FREQ_END)); + } + + if(led_flag == true){ + digitalWrite(35, HIGH); + led_flag = false; + } else { + digitalWrite(35, LOW); + led_flag = true; + } + // Status text block + if (drone_detected == 0) { + // "Scanning" + display.setTextAlignment(TEXT_ALIGN_CENTER); + //clear status line + display.setColor(BLACK); + display.fillRect(0, STATUS_TEXT_TOP, 128, 16); + display.setColor(WHITE); + if (scan_var == 0) { + display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scanning. "); + } + else if (scan_var == 1) { + display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scanning.. "); + } + else if (scan_var == 2) { + display.drawString(start_scan_text, STATUS_TEXT_TOP, "Scanning..."); + } + scan_var++; + if (scan_var == 3) scan_var = 0; + } + + if (!initialized) { + // X-axis + display.fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); + // ticks + #ifdef MAJOR_TICKS + drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); + #endif + #ifdef MINOR_TICKS + drawTicks(MINOR_TICKS, MINOR_TICK_LENGTH); + #endif + } + initialized = true; +} + + +void setup() { + pinMode(35, OUTPUT); + heltec_setup(); + display.clear(); + // draw the logo + display.drawXbm(0, 0, 128, 64, epd_bitmap_ucog); + display.display(); + heltec_delay(4000); + // initialize SX1262 FSK modem at the initial frequency + both.println("Init radio"); + RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); + // upload a patch to the SX1262 to enable spectral scan + // NOTE: this patch is uploaded into volatile memory, + // and must be re-uploaded on every power up + both.println("Upload SX1262 patch"); + // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., enable spectral scan and must be uploaded again on every power cycle. + RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); + // configure scan bandwidth and disable the data shaping + both.println("Setting up radio"); + RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); + // and disable the data shaping + RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); + both.println("Starting scaning..."); + float vbat = heltec_vbat(); + both.printf("Vbat: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); + heltec_delay(800); + + display.clear(); + displayDecorate(); +} + +void loop() { + displayDecorate(); + #ifdef PRINT_PROFILE_TIME + start = millis(); + #endif + // clear the scan plot rectangle + display.setColor(BLACK); + display.fillRect(0, 0, STEPS, HEIGHT); + display.setColor(WHITE); + + // do the scan + for (x = 0; x < STEPS; x++) { + float freq = FREQ_BEGIN + (RANGE * ((float) x / STEPS)); + radio.setFrequency(freq); + #ifdef PRINT_SCAN_VALUES + Serial.println(); + Serial.print("step-"); + Serial.print(x); + Serial.print(" Frequancy:"); + Serial.print(freq); + Serial.println(); + #endif + // start spectral scan + radio.spectralScanStart(SAMPLES, 1); + // wait for spectral scan to finish + while(radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) { + heltec_delay(1); + } + digitalWrite(25, HIGH); + // read the results Array to which the results will be saved + radio.spectralScanGetResult(result); + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) { + #ifdef PRINT_SCAN_VALUES + Serial.printf("%04X,", result[y]); + #endif + if (result[y]) { + display.setPixel(x, y); + } + } + #ifdef PRINT_SCAN_VALUES + Serial.println(); + #endif + // wait a little bit before the next scan, otherwise the SX1262 hangs + heltec_delay(4); + } + #ifdef PRINT_SCAN_VALUES + Serial.println(); + #endif + display.display(); + #ifdef PRINT_PROFILE_TIME + scan_time = millis() - start; + Serial.printf("Scan took %lld ms\n", scan_time); + #endif +} + diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html