diff --git a/.gitignore b/.gitignore index 374da672..8cf77745 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,8 @@ extras/decoder/log.txt extras/decoder/out.txt +# Spectrum scan +extras/SX126x_Spectrum_Scan/out/* + # PlatformIO .pio* diff --git a/examples/SX126x/SX126x_Spectrum_Scan/SX126x_Spectrum_Scan.ino b/examples/SX126x/SX126x_Spectrum_Scan/SX126x_Spectrum_Scan.ino new file mode 100644 index 00000000..9e0bdcd2 --- /dev/null +++ b/examples/SX126x/SX126x_Spectrum_Scan/SX126x_Spectrum_Scan.ino @@ -0,0 +1,112 @@ +/* + RadioLib SX126x Spectrum Scan Example + + 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/ +*/ + +// include the library +#include + +// this file contains binary patch for the SX1262 +#include + +// SX1262 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1262 radio = new Module(10, 2, 3, 9); + +void setup() { + Serial.begin(115200); + + // initialize SX1262 FSK modem with default settings + Serial.print(F("[SX1262] Initializing ... ")); + int state = radio.beginFSK(); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // 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 + Serial.print(F("[SX1262] Uploading patch ... ")); + state = radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan)); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // configure scan bandwidth to 234.4 kHz + // and disable the data shaping + Serial.print(F("[SX1262] Setting scan parameters ... ")); + state = radio.setRxBandwidth(234.3); + state |= radio.setDataShaping(RADIOLIB_SHAPING_NONE); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + Serial.print(F("[SX1262] Starting spectral scan ... ")); + + // start spectral scan + // number of bands: 2048 (fewer bands = better temporal resolution) + int state = radio.spectralScanStart(2048); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // wait for spectral scan to finish + while(radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) { + delay(10); + } + + // read the results + uint16_t results[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; + state = radio.spectralScanGetResult(results); + if(state == RADIOLIB_ERR_NONE) { + // we have some results, print it + Serial.print("SCAN "); + for(uint8_t i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) { + Serial.print(results[i]); + Serial.print(','); + } + Serial.println(" END"); + } + + // wait a little bit before the next scan + delay(5); +} \ No newline at end of file diff --git a/extras/SX126x_Spectrum_Scan/SpectrumScan.py b/extras/SX126x_Spectrum_Scan/SpectrumScan.py new file mode 100644 index 00000000..59eb9d63 --- /dev/null +++ b/extras/SX126x_Spectrum_Scan/SpectrumScan.py @@ -0,0 +1,136 @@ +#!/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_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})') + args = parser.parse_args() + + # create the color map and the result array + arr = np.zeros((SCAN_WIDTH, args.len)) + + # scanline counter + row = 0 + + # open the COM port + with serial.Serial(args.port, args.speed, timeout=None) as com: + while(True): + # update the progress bar + printProgressBar(row, args.len) + + # read a single line + line = com.readline().decode('utf-8') + + # 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 row >= args.len: + break + + # create the figure + fig, ax = plt.subplots() + + # display the result as heatmap + extent = [0, args.len, -4*(SCAN_WIDTH + 1), args.offset] + im = ax.imshow(arr, 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}' + 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() diff --git a/keywords.txt b/keywords.txt index c13d0ff6..664dee11 100644 --- a/keywords.txt +++ b/keywords.txt @@ -195,6 +195,11 @@ getCurrentLimit KEYWORD2 getIrqStatus KEYWORD2 getLastError KEYWORD2 setRxBoostedGainMode KEYWORD2 +uploadPatch KEYWORD2 +spectralScanStart KEYWORD2 +spectralScanAbort KEYWORD2 +spectralScanGetStatus KEYWORD2 +spectralScanGetResult KEYWORD2 # nRF24 setIrqAction KEYWORD2 diff --git a/src/BuildOpt.h b/src/BuildOpt.h index b868b99e..d376d2e8 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -120,6 +120,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -155,6 +156,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -190,6 +192,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // ESP32 doesn't support tone(), but it can be emulated via LED control peripheral @@ -229,6 +232,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -264,6 +268,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -299,6 +304,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -334,6 +340,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; #define RADIOLIB_TONE_UNSUPPORTED @@ -370,6 +377,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -405,6 +413,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -440,6 +449,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -475,6 +485,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -510,6 +521,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds @@ -549,6 +561,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds @@ -588,6 +601,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -623,6 +637,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -658,6 +673,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds @@ -697,6 +713,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -732,6 +749,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) // Arduino API callbacks #define RADIOLIB_CB_ARGS_PIN_MODE (void, pinMode, uint8_t pin, PINMODE mode) @@ -788,6 +806,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -843,6 +862,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks @@ -879,6 +899,7 @@ #define RADIOLIB_DEFAULT_SPI_SETTINGS SPISettings(2000000, MSBFIRST, SPI_MODE0) #define RADIOLIB_NONVOLATILE PROGMEM #define RADIOLIB_NONVOLATILE_READ_BYTE(addr) pgm_read_byte(addr) + #define RADIOLIB_NONVOLATILE_READ_DWORD(addr) pgm_read_dword(addr) #define RADIOLIB_TYPE_ALIAS(type, alias) using alias = type; // Arduino API callbacks diff --git a/src/RadioLib.h b/src/RadioLib.h index 419e0bea..76bef67b 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -98,6 +98,7 @@ #include "protocols/Pager/Pager.h" #include "protocols/RTTY/RTTY.h" #include "protocols/SSTV/SSTV.h" +#include "protocols/SSDV/SSDV.h" #include "protocols/FSK4/FSK4.h" #include "protocols/APRS/APRS.h" #include "protocols/ExternalRadio/ExternalRadio.h" diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index f78b9c1e..f06009b2 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -206,13 +206,13 @@ int16_t SX126x::reset(bool verify) { } // standby command failed, check timeout and try again - if(_mod->millis() - start >= 3000) { + if(_mod->millis() - start >= 1000) { // timed out, possibly incorrect wiring return(state); } // wait a bit to not spam the module - _mod->delay(10); + _mod->delay(100); } } @@ -1378,6 +1378,95 @@ void SX126x::readBit(RADIOLIB_PIN_TYPE pin) { } #endif +int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) { + // set to standby RC mode + int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC); + RADIOLIB_ASSERT(state); + + // check the version + #if defined(RADIOLIB_DEBUG) + char ver_pre[16]; + _mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_pre); + RADIOLIB_DEBUG_PRINT(F("Pre-update version string: ")); + RADIOLIB_DEBUG_PRINTLN(ver_pre); + #endif + + // enable patch update + _mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_ENABLED); + + // upload the patch + uint8_t data[4]; + for(uint32_t i = 0; i < len / sizeof(uint32_t); i++) { + uint32_t bin = 0; + if(nonvolatile) { + bin = RADIOLIB_NONVOLATILE_READ_DWORD(patch + i); + } else { + bin = patch[i]; + } + data[0] = (bin >> 24) & 0xFF; + data[1] = (bin >> 16) & 0xFF; + data[2] = (bin >> 8) & 0xFF; + data[3] = bin & 0xFF; + _mod->SPIwriteRegisterBurst(RADIOLIB_SX126X_REG_PATCH_MEMORY_BASE + i*sizeof(uint32_t), data, sizeof(uint32_t)); + } + + // disable patch update + _mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_DISABLED); + + // update + _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_PRAM_UPDATE, NULL, 0); + + // check the version again + #if defined(RADIOLIB_DEBUG) + char ver_post[16]; + _mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_post); + RADIOLIB_DEBUG_PRINT(F("Post-update version string: ")); + RADIOLIB_DEBUG_PRINTLN(ver_post); + #endif + + return(state); +} + +int16_t SX126x::spectralScanStart(uint16_t numBands, uint8_t window, uint8_t interval) { + // abort first - not sure if this is strictly needed, but the example code does this + spectralScanAbort(); + + // set the RSSI window size + _mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, window); + + // start Rx with infinite timeout + int16_t state = setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF); + RADIOLIB_ASSERT(state); + + // now set the actual spectral scan parameters + uint8_t data[3] = { (uint8_t)((numBands >> 8) & 0xFF), (uint8_t)(numBands & 0xFF), interval }; + return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS, data, 3)); +} + +void SX126x::spectralScanAbort() { + _mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00); +} + +int16_t SX126x::spectralScanGetStatus() { + uint8_t status = _mod->SPIreadRegister(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS); + if(status == RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED) { + return(RADIOLIB_ERR_NONE); + } + return(RADIOLIB_ERR_RANGING_TIMEOUT); +} + +int16_t SX126x::spectralScanGetResult(uint16_t* results) { + // read the raw results + uint8_t data[2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; + _mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_RESULT, 2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, data); + + // convert it + for(uint8_t i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) { + results[i] = ((uint16_t)data[i*2] << 8) | ((uint16_t)data[i*2 + 1]); + } + return(RADIOLIB_ERR_NONE); +} + int16_t SX126x::setTCXO(float voltage, uint32_t delay) { // check if TCXO is enabled at all if(this->XTAL) { diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index c90afedd..b680b702 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -412,14 +412,19 @@ #define RADIOLIB_SX126X_RX_GAIN_SPECTRAL_SCAN 0xCB // 7 0 spectral scan // RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE -#define RADIOLIB_SX126X_PATCH_UPDATE_DISABLED 0b00010000 // 4 4 patch update: disabled -#define RADIOLIB_SX126X_PATCH_UPDATE_ENABLED 0b00000000 // 4 4 enabled +#define RADIOLIB_SX126X_PATCH_UPDATE_DISABLED 0b00000000 // 4 4 patch update: disabled +#define RADIOLIB_SX126X_PATCH_UPDATE_ENABLED 0b00010000 // 4 4 enabled // RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS -#define RADIOLIB_SX126X_SPECTRAL_SCAN_NONE 0x00 // 7 0 spectral scan status: none -#define RADIOLIB_SX126X_SPECTRAL_SCAN_ONGOING 0x0F // 7 0 ongoing -#define RADIOLIB_SX126X_SPECTRAL_SCAN_ABORTED 0xF0 // 7 0 aborted -#define RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED 0xFF // 7 0 completed +#define RADIOLIB_SX126X_SPECTRAL_SCAN_NONE 0x00 // 7 0 spectral scan status: none +#define RADIOLIB_SX126X_SPECTRAL_SCAN_ONGOING 0x0F // 7 0 ongoing +#define RADIOLIB_SX126X_SPECTRAL_SCAN_ABORTED 0xF0 // 7 0 aborted +#define RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED 0xFF // 7 0 completed + +// RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW +#define RADIOLIB_SX126x_SPECTRAL_SCAN_WINDOW_DEFAULT (0x05 << 2) // 7 0 default RSSI average window + +#define RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE (33) /*! \class SX126x @@ -1041,20 +1046,68 @@ class SX126x: public PhysicalLayer { #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*! - \brief Dummy method, to ensure PhysicalLayer compatibility. + \brief Set interrupt service routine function to call when data bit is receveid in direct mode. - \param func Ignored. + \param func Pointer to interrupt service routine. */ void setDirectAction(void (*func)(void)); /*! - \brief Dummy method, to ensure PhysicalLayer compatibility. + \brief Function to read and process data bit in direct reception mode. - \param pin Ignored. + \param pin Pin on which to read. */ void readBit(RADIOLIB_PIN_TYPE pin); #endif + /*! + \brief 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. + + \param patch Binary patch to upload. + + \param len Length of the binary patch in 4-byte words. + + \param nonvolatile Set to true when the patch is saved in non-volatile memory of the host processor, + or to false when the patch is in its RAM. + + \returns \ref status_codes + */ + int16_t uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile = true); + + /*! + \brief Start spectral scan. Requires binary path to be uploaded. + + \param numBands Number of bands for the scan. Fewer bands = better temporal resolution, but fewer power samples. + + \param window RSSI averaging window size. + + \param interval Scan interval length, one of RADIOLIB_SX126X_SCAN_INTERVAL_* macros. + + \returns \ref status_codes + */ + int16_t spectralScanStart(uint16_t numBands, uint8_t window = RADIOLIB_SX126x_SPECTRAL_SCAN_WINDOW_DEFAULT, uint8_t interval = RADIOLIB_SX126X_SCAN_INTERVAL_8_20_US); + + /*! + \brief Abort an ongoing spectral scan. + */ + void spectralScanAbort(); + + /*! + \brief Read the status of spectral scan. + + \returns \ref status_codes + */ + int16_t spectralScanGetStatus(); + + /*! + \brief Read the result of spectral scan. + + \param results Array to which the results will be saved, must be RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE long. + + \returns \ref status_codes + */ + int16_t spectralScanGetResult(uint16_t* results); #if !defined(RADIOLIB_GODMODE) protected: diff --git a/src/modules/SX126x/patches/SX126x_patch_scan.h b/src/modules/SX126x/patches/SX126x_patch_scan.h new file mode 100644 index 00000000..765e9003 --- /dev/null +++ b/src/modules/SX126x/patches/SX126x_patch_scan.h @@ -0,0 +1,80 @@ +#if !defined(_RADIOLIB_SX126X_PATCH_SCAN_H) +#define _RADIOLIB_SX126X_PATCH_SCAN_H + +#include "../../../TypeDef.h" + +#if !defined(RADIOLIB_EXCLUDE_SX126X) + +// the following is a binary patch to the SX1262 +// this patch is needed to enable spectral scan functionality +const uint32_t sx126x_patch_scan[] RADIOLIB_NONVOLATILE = { + 0x337fe1, 0x337fdb, 0x337fd5, 0x337fcf, 0x3a7fc8, 0x3f3fff, + 0x0378ff, 0x0379ff, 0x3a7fb7, 0x16a901, 0x16a801, 0x23ffff, + 0x0378ff, 0x0379ff, 0x3a7faf, 0x16a901, 0x16a801, 0x23ffff, + 0x0378ff, 0x0379ff, 0x3a7f34, 0x16a901, 0x16a801, 0x23ffff, + 0x0378ff, 0x0379ff, 0x3a7e80, 0x16a901, 0x16a801, 0x23ffff, + 0x0378ff, 0x0379ff, 0x3a7fc3, 0x16a901, 0x16a801, 0x337fc9, + 0x0378ff, 0x0379ff, 0x3a7fc0, 0x16a901, 0x16a801, 0x337fc9, + 0x0378ff, 0x0379ff, 0x3a7fbd, 0x16a901, 0x16a801, 0x337fc9, + 0x0378ff, 0x0379ff, 0x3a7fba, 0x16a901, 0x16a801, 0x337fc9, + 0x23ffff, 0x0ea1fc, 0x0ea0df, 0x0eafc9, 0x02cf0e, 0x23ffff, + 0x0eacff, 0x0eabff, 0x23ffff, 0x0eacff, 0x0eabff, 0x23ffff, + 0x0eacff, 0x0eabff, 0x23ffff, 0x0eacff, 0x0eabff, 0x23ffff, + 0x0378ff, 0x0379ff, 0x3a7fc8, 0x0eacfd, 0x0eabff, 0x16a901, + 0x16a801, 0x23ffff, 0x0374ff, 0x0375ff, 0x0378ff, 0x0379ff, + 0x16affe, 0x0ea5ff, 0x0ea465, 0x1dbb04, 0x0e1bfd, 0x307fa3, + 0x1caf00, 0x327fa0, 0x0eacf7, 0x0eabff, 0x337f3a, 0x1cad04, + 0x0eacfe, 0x0eabff, 0x0dbfdd, 0x307f97, 0x0dafcc, 0x0defbb, + 0x0dbfdd, 0x347f9b, 0x0eecef, 0x0caffc, 0x04ade8, 0x0cbdcd, + 0x01bde8, 0x04abe8, 0x0ebbbf, 0x01bbe8, 0x04abe8, 0x0ebbdf, + 0x01bbe8, 0x1ca800, 0x0ea9ff, 0x04abe3, 0x0e2b01, 0x01bbe3, + 0x04abee, 0x0e2b01, 0x01bbee, 0x1ca202, 0x1ca301, 0x02f300, + 0x02f201, 0x0ea064, 0x0ea1f7, 0x18ab00, 0x367f58, 0x0ea041, + 0x0ea1f7, 0x18ab00, 0x1c1b03, 0x317f3f, 0x1ea201, 0x1ea300, + 0x0cb23f, 0x327f4a, 0x1cab04, 0x0eaefe, 0x0dbfbb, 0x307f6c, + 0x0dafee, 0x0dbfbb, 0x347f6f, 0x04abee, 0x0cbbeb, 0x01bbee, + 0x0eacff, 0x0eabff, 0x0c1b9f, 0x327f64, 0x0c1c8f, 0x317f5c, + 0x3fffff, 0x0d1fcc, 0x0d5fbb, 0x0c1b9f, 0x327f5d, 0x0c1c8f, + 0x357f63, 0x0ea064, 0x0ea1f7, 0x18ab00, 0x327f7c, 0x1cad04, + 0x0eacfe, 0x0dbfdd, 0x307f51, 0x0dafcc, 0x0dbfdd, 0x347f54, + 0x0d8fcb, 0x04acee, 0x0c2bcb, 0x01bbee, 0x0eacfd, 0x0eabff, + 0x337f3a, 0x1cad04, 0x0eacfe, 0x0dbfdd, 0x307f43, 0x0dafcc, + 0x0dbfdd, 0x347f46, 0x0d8fcb, 0x04acee, 0x0c2bcb, 0x337f6a, + 0x0cb23f, 0x367f75, 0x0dbf22, 0x0dff33, 0x337f75, 0x16af02, + 0x16a901, 0x16a801, 0x16a501, 0x16a401, 0x23ffff, 0x0374ff, + 0x0375ff, 0x0378ff, 0x0379ff, 0x16afe0, 0x0ea5ff, 0x0ea465, + 0x1ca802, 0x0ea9ff, 0x0eafff, 0x02ff00, 0x0eaff7, 0x02ff01, + 0x0eafef, 0x02ff02, 0x0eafe7, 0x02ff03, 0x0eafdf, 0x02ff04, + 0x0eafd7, 0x02ff05, 0x0eafcf, 0x02ff06, 0x0eafc7, 0x02ff07, + 0x0eafbf, 0x02ff08, 0x0eafb7, 0x02ff09, 0x0eafaf, 0x02ff0a, + 0x0eafa7, 0x02ff0b, 0x0eaf9f, 0x02ff0c, 0x0eaf97, 0x02ff0d, + 0x0eaf8f, 0x02ff0e, 0x0eaf87, 0x02ff0f, 0x0eaf7f, 0x02ff10, + 0x0eaf77, 0x02ff11, 0x0eaf6f, 0x02ff12, 0x0eaf67, 0x02ff13, + 0x0eaf5f, 0x02ff14, 0x0eaf57, 0x02ff15, 0x0eaf4f, 0x02ff16, + 0x0eaf47, 0x02ff17, 0x0eaf3f, 0x02ff18, 0x0eaf37, 0x02ff19, + 0x0eaf2f, 0x02ff1a, 0x0eaf27, 0x02ff1b, 0x0eaf1f, 0x02ff1c, + 0x0eaf17, 0x02ff1d, 0x0eaf0f, 0x02ff1e, 0x0eaf07, 0x02ff1f, + 0x04abee, 0x0e2b01, 0x01bbee, 0x0eacff, 0x0eabff, 0x0cafc0, + 0x0ec0ff, 0x0cafb1, 0x0ed1fb, 0x0eafff, 0x02cf00, 0x0d1fcc, + 0x0d5fbb, 0x0e1bff, 0x327edb, 0x0e1c00, 0x347ee6, 0x0ea2ff, + 0x0ea3ff, 0x0ea032, 0x0ea1f8, 0x0eaff0, 0x02cf00, 0x0ea064, + 0x0ea1f7, 0x18ad00, 0x1c1300, 0x327ece, 0x1c1201, 0x317e9b, + 0x0e1dff, 0x367e9b, 0x0ea041, 0x0ea1f7, 0x18ab00, 0x0eebdf, + 0x0cafbc, 0x0eabff, 0x0ccccc, 0x0cdbbb, 0x0cafc0, 0x0ec0ff, + 0x0cafb1, 0x0ed1fb, 0x18ab01, 0x0eacff, 0x18ae02, 0x0eadff, + 0x0ccecc, 0x0cddbb, 0x0d1fcc, 0x0d5fbb, 0x0cafbe, 0x0eadff, + 0x02ce01, 0x02cc02, 0x0d1f22, 0x0d5f33, 0x0ea064, 0x0ea1f7, + 0x18ad00, 0x0eacff, 0x0eabff, 0x0c1b9f, 0x327ea9, 0x0c1c8f, + 0x317ea1, 0x3fffff, 0x0d1fcc, 0x0d5fbb, 0x0c1b9f, 0x327ea2, + 0x0c1c8f, 0x357ea8, 0x1c1300, 0x327e9e, 0x1c1201, 0x317e9b, + 0x0e1dff, 0x327ecb, 0x0e1dff, 0x367e90, 0x0ea032, 0x0ea1f8, + 0x0eaf00, 0x02cf00, 0x0ea1fb, 0x0ea0ff, 0x0eaf00, 0x02cf00, + 0x337e88, 0x0ea032, 0x0ea1f8, 0x0eaf0f, 0x02cf00, 0x0ea1fb, + 0x0ea0ff, 0x0eaf0f, 0x02cf00, 0x0eacfd, 0x0eabff, 0x16af20, + 0x16a901, 0x16a801, 0x16a501, 0x16a401, 0x23ffff, 0x0eacf7, + 0x0eabff, 0x23ffff +}; + +#endif + +#endif