[SX126x] Added spectral scan in frequency
This commit is contained in:
parent
ee7c1e7604
commit
c10343e853
5 changed files with 186 additions and 12 deletions
|
@ -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!"));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue