[SX126x] Added spectral scan in frequency

pull/714/head
jgromes 2023-03-27 18:48:32 +02:00
rodzic ee7c1e7604
commit c10343e853
5 zmienionych plików z 186 dodań i 12 usunięć

Wyświetl plik

@ -80,7 +80,7 @@ void loop() {
// start spectral scan
// number of scans in each line is 2048
// fewer scans leads to better temporal resolution,
// number of samples: 2048 (fewer samples = better temporal resolution)
int state = radio.spectralScanStart(2048);
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));

Wyświetl plik

@ -0,0 +1,129 @@
/*
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. The example performs frequency sweep over a given range.
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 <RadioLib.h>
// this file contains binary patch for the SX1262
#include <modules/SX126x/patches/SX126x_patch_scan.h>
// 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);
// frequency range in MHz to scan
const float freqStart = 431;
const float freqEnd = 435;
void setup() {
Serial.begin(115200);
// initialize SX1262 FSK modem at the initial frequency
Serial.print(F("[SX1262] Initializing ... "));
int state = radio.beginFSK(freqStart);
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() {
// perform scan over the entire frequency range
float freq = freqStart;
while(freq <= freqEnd) {
Serial.print("FREQ ");
Serial.println(freq, 2);
// start spectral scan
// number of samples: 2048 (fewer samples = better temporal resolution)
Serial.print(F("[SX1262] Starting spectral scan ... "));
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);
// set the next frequency
// the frequency step should be slightly smaller
// or the same as the Rx bandwidth set in setup
freq += 0.2;
radio.setFrequency(freq);
}
}

Wyświetl plik

@ -16,6 +16,7 @@ SCAN_WIDTH = 33
# scanline Serial start/end markers
SCAN_MARK_START = 'SCAN '
SCAN_MARK_FREQ = 'FREQ '
SCAN_MARK_END = ' END'
# output path
@ -82,22 +83,48 @@ def main():
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, args.len))
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
printProgressBar(row, args.len)
if not freq_mode:
printProgressBar(row, scan_len)
# read a single line
line = com.readline().decode('utf-8')
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):
@ -110,21 +137,34 @@ def main():
row = row + 1
# check if we're done
if row >= args.len:
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, args.len, -4*(SCAN_WIDTH + 1), args.offset]
im = ax.imshow(arr, cmap=args.map, extent=extent)
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}'
plt.xlabel("Time [sample]")
if freq_mode:
plt.xlabel("Frequency [Hz]")
else:
plt.xlabel("Time [sample]")
plt.ylabel("RSSI [dBm]")
ax.set_aspect('auto')
fig.suptitle(title)

Wyświetl plik

@ -1464,7 +1464,7 @@ int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile)
return(state);
}
int16_t SX126x::spectralScanStart(uint16_t numScans, uint8_t window, uint8_t interval) {
int16_t SX126x::spectralScanStart(uint16_t numSamples, uint8_t window, uint8_t interval) {
// abort first - not sure if this is strictly needed, but the example code does this
spectralScanAbort();
@ -1476,7 +1476,7 @@ int16_t SX126x::spectralScanStart(uint16_t numScans, uint8_t window, uint8_t int
RADIOLIB_ASSERT(state);
// now set the actual spectral scan parameters
uint8_t data[3] = { (uint8_t)((numScans >> 8) & 0xFF), (uint8_t)(numScans & 0xFF), interval };
uint8_t data[3] = { (uint8_t)((numSamples >> 8) & 0xFF), (uint8_t)(numSamples & 0xFF), interval };
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS, data, 3));
}
@ -1567,6 +1567,10 @@ int16_t SX126x::setDio2AsRfSwitch(bool enable) {
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1));
}
int16_t SX126x::setFs() {
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_FS, NULL, 0));
}
int16_t SX126x::setTx(uint32_t timeout) {
uint8_t data[] = { (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)} ;
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX, data, 3));

Wyświetl plik

@ -1086,7 +1086,7 @@ class SX126x: public PhysicalLayer {
/*!
\brief Start spectral scan. Requires binary path to be uploaded.
\param numScans Number of scans for each iteration. Fewer scans = better temporal resolution, but fewer power samples.
\param numSamples Number of samples for each scan. Fewer samples = better temporal resolution.
\param window RSSI averaging window size.
@ -1094,7 +1094,7 @@ class SX126x: public PhysicalLayer {
\returns \ref status_codes
*/
int16_t spectralScanStart(uint16_t numScans, uint8_t window = RADIOLIB_SX126x_SPECTRAL_SCAN_WINDOW_DEFAULT, uint8_t interval = RADIOLIB_SX126X_SCAN_INTERVAL_8_20_US);
int16_t spectralScanStart(uint16_t numSamples, 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.
@ -1121,6 +1121,7 @@ class SX126x: public PhysicalLayer {
protected:
#endif
// SX126x SPI command implementations
int16_t setFs();
int16_t setTx(uint32_t timeout = 0);
int16_t setRx(uint32_t timeout);
int16_t setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin);