mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-03-28 17:42:59 +01:00
Merge branch 'testable' of https://github.com/Genaker/LoraSA into rtl-sdr
This commit is contained in:
@@ -18,6 +18,16 @@ void BarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
|
||||
Chart::reset(x, y, w, h);
|
||||
}
|
||||
|
||||
void BarChart::clear()
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
ys[i] = min_y;
|
||||
}
|
||||
|
||||
redraw_all = true;
|
||||
}
|
||||
|
||||
int BarChart::updatePoint(float x, float y)
|
||||
{
|
||||
if (x < min_x || x >= max_x)
|
||||
|
||||
@@ -81,6 +81,7 @@ struct BarChart : ProgressChart, Listener
|
||||
};
|
||||
|
||||
void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override;
|
||||
void clear();
|
||||
int updatePoint(float x, float y) override;
|
||||
void drawOne(int x) override;
|
||||
void draw() override;
|
||||
|
||||
@@ -262,15 +262,22 @@ bool ReadlineComms::send(Message &m)
|
||||
Serial.println(name + ": the message is: " + p);
|
||||
break;
|
||||
case MessageType::SCAN_RESULT:
|
||||
case MessageType::SCAN_MAX_RESULT:
|
||||
p = _scan_result_str(m.payload.dump);
|
||||
break;
|
||||
case MessageType::CONFIG_TASK:
|
||||
p = m.payload.config.is_set ? "SET " : "GET ";
|
||||
ConfigTaskType ctt = m.payload.config.task_type;
|
||||
p = ctt == GET ? "GET "
|
||||
: ctt == SET ? "SET "
|
||||
: ctt == GETSET_SUCCESS ? "Success: "
|
||||
: "Failed to set: ";
|
||||
p += *m.payload.config.key;
|
||||
if (m.payload.config.is_set)
|
||||
if (ctt == SET || ctt == GETSET_SUCCESS)
|
||||
{
|
||||
p += " " + *m.payload.config.value;
|
||||
}
|
||||
|
||||
p += "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -368,7 +375,7 @@ Message *_parsePacket(String p)
|
||||
{
|
||||
Message *m = new Message();
|
||||
m->type = MessageType::CONFIG_TASK;
|
||||
m->payload.config.is_set = false;
|
||||
m->payload.config.task_type = GET;
|
||||
m->payload.config.key = new String(_stringParam(p, ""));
|
||||
m->payload.config.value = NULL;
|
||||
return m;
|
||||
@@ -378,7 +385,7 @@ Message *_parsePacket(String p)
|
||||
{
|
||||
Message *m = new Message();
|
||||
m->type = MessageType::CONFIG_TASK;
|
||||
m->payload.config.is_set = true;
|
||||
m->payload.config.task_type = SET;
|
||||
m->payload.config.key = new String(_stringParam(p, ""));
|
||||
m->payload.config.value = new String(_stringParam(p, ""));
|
||||
|
||||
@@ -414,7 +421,7 @@ String _wrap_str(String v)
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
if (type == SCAN_RESULT)
|
||||
if (type == SCAN_RESULT || type == SCAN_MAX_RESULT)
|
||||
{
|
||||
if (payload.dump.sz > 0)
|
||||
{
|
||||
@@ -430,7 +437,8 @@ Message::~Message()
|
||||
{
|
||||
delete payload.config.key;
|
||||
|
||||
if (payload.config.is_set)
|
||||
ConfigTaskType ctt = payload.config.task_type;
|
||||
if (ctt == GETSET_SUCCESS || ctt == SET)
|
||||
{
|
||||
delete payload.config.value;
|
||||
}
|
||||
|
||||
@@ -13,15 +13,31 @@
|
||||
#define SERIAL0 Serial0
|
||||
#endif
|
||||
|
||||
#ifndef SCAN_MAX_RESULT_KHZ_SCALE
|
||||
// kHz scale: round frequency, so it fits into 2 bytes
|
||||
// 2500000 / 40 = 62500, scale 40 fits 2.5GHz into two bytes
|
||||
#define SCAN_MAX_RESULT_KHZ_SCALE 40
|
||||
#endif
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
WRAP = 0,
|
||||
SCAN,
|
||||
SCAN_RESULT,
|
||||
SCAN_MAX_RESULT,
|
||||
CONFIG_TASK,
|
||||
_MAX_MESSAGE_TYPE = CONFIG_TASK
|
||||
};
|
||||
|
||||
enum ConfigTaskType
|
||||
{
|
||||
GET = 0,
|
||||
SET,
|
||||
GETSET_SUCCESS,
|
||||
SET_FAIL,
|
||||
_MAX_CONFIG_TASK_TYPE = SET_FAIL
|
||||
};
|
||||
|
||||
struct Wrapper
|
||||
{
|
||||
int32_t length;
|
||||
@@ -45,7 +61,7 @@ struct ConfigTask
|
||||
{
|
||||
String *key;
|
||||
String *value;
|
||||
bool is_set;
|
||||
ConfigTaskType task_type;
|
||||
};
|
||||
|
||||
struct Message
|
||||
@@ -62,6 +78,29 @@ struct Message
|
||||
~Message();
|
||||
};
|
||||
|
||||
struct Endpoint
|
||||
{
|
||||
union
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t loop : 1, // self
|
||||
uart0 : 1, uart1 : 1,
|
||||
lora : 1, // rx or tx_lora, depending on is_host
|
||||
host : 1; // USB
|
||||
};
|
||||
uint8_t addr;
|
||||
};
|
||||
};
|
||||
|
||||
struct RoutedMessage
|
||||
{
|
||||
Endpoint from;
|
||||
Endpoint to;
|
||||
Message *message;
|
||||
};
|
||||
|
||||
struct Comms
|
||||
{
|
||||
String name;
|
||||
|
||||
@@ -48,24 +48,25 @@ size_t _write(uint8_t *m, size_t sz, size_t p, uint8_t *v, size_t v_sz)
|
||||
#define RSSI_HI -80
|
||||
#define DETAIL_RSSIS 8
|
||||
#define RSSI_LO (RSSI_HI - 1 - DETAIL_RSSIS)
|
||||
int16_t RadioComms::send(Message &m)
|
||||
|
||||
uint8_t *_serialize_scan_result(Message &m, size_t &p, uint8_t *msg)
|
||||
{
|
||||
if (m.type != SCAN_RESULT)
|
||||
{
|
||||
return RADIOLIB_ERR_INVALID_FUNCTION;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t msg[MAX_MSG];
|
||||
size_t dump_sz = m.payload.dump.sz;
|
||||
size_t p = _write(msg, MAX_MSG, 0, (uint8_t)m.type);
|
||||
size_t max_msg = p;
|
||||
p = _write(msg, max_msg, 0, (uint8_t)m.type);
|
||||
|
||||
// first cut: dump the RSSI as-is
|
||||
// optimize the message size later
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4);
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4);
|
||||
p = _write(msg, MAX_MSG, p, (uint8_t *)&dump_sz, 2);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[0], 4);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)&m.payload.dump.freqs_khz[dump_sz - 1], 4);
|
||||
|
||||
size_t rem = MAX_MSG - p;
|
||||
size_t rem = max_msg - p;
|
||||
if (rem > dump_sz)
|
||||
rem = dump_sz;
|
||||
|
||||
@@ -75,7 +76,7 @@ int16_t RadioComms::send(Message &m)
|
||||
{
|
||||
if (i * rem / dump_sz > p - pp)
|
||||
{
|
||||
p = _write(msg, MAX_MSG, p, bits);
|
||||
p = _write(msg, max_msg, p, bits);
|
||||
bits = 0;
|
||||
}
|
||||
int16_t v = m.payload.dump.rssis[i];
|
||||
@@ -97,10 +98,121 @@ int16_t RadioComms::send(Message &m)
|
||||
|
||||
if (dump_sz > 0)
|
||||
{
|
||||
p = _write(msg, MAX_MSG, p, bits);
|
||||
p = _write(msg, max_msg, p, bits);
|
||||
}
|
||||
|
||||
return radio.transmit(msg, p);
|
||||
return msg;
|
||||
}
|
||||
|
||||
uint8_t *_serialize_scan_max_result(Message &m, size_t &p, uint8_t *msg)
|
||||
{
|
||||
if (m.type != SCAN_MAX_RESULT)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t dump_sz = m.payload.dump.sz;
|
||||
size_t max_msg = p;
|
||||
p = _write(msg, max_msg, 0, (uint8_t)m.type);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)&dump_sz, 2);
|
||||
|
||||
int16_t b = SCAN_MAX_RESULT_KHZ_SCALE; // scale to fit khz into 2 bytes
|
||||
p = _write(msg, max_msg, p, (uint8_t)b);
|
||||
|
||||
for (int i = 0; i < dump_sz; i++)
|
||||
{
|
||||
b = m.payload.dump.freqs_khz[i] / SCAN_MAX_RESULT_KHZ_SCALE;
|
||||
p = _write(msg, max_msg, p, (uint8_t *)&b, 2);
|
||||
b = m.payload.dump.rssis[i];
|
||||
if (b >= 0)
|
||||
{
|
||||
b = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
b += 255;
|
||||
if (b < 0)
|
||||
{
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
p = _write(msg, max_msg, p, (uint8_t)b);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
uint8_t *_serialize_config_task(Message &m, size_t &p, uint8_t *msg)
|
||||
{
|
||||
if (m.type != CONFIG_TASK)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
size_t max_msg = p;
|
||||
ConfigTaskType ctt = m.payload.config.task_type;
|
||||
p = _write(msg, max_msg, 0, (uint8_t)m.type);
|
||||
p = _write(msg, max_msg, p, (uint8_t)ctt);
|
||||
|
||||
int key_len = m.payload.config.key->length();
|
||||
if (max_msg - p < key_len + 1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = _write(msg, max_msg, p, (uint8_t)key_len);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)m.payload.config.key->c_str(), key_len);
|
||||
|
||||
if (ctt == GET || ctt == SET_FAIL)
|
||||
{
|
||||
return msg;
|
||||
}
|
||||
|
||||
int v_len = m.payload.config.value->length();
|
||||
|
||||
if (max_msg - p < v_len + 1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = _write(msg, max_msg, p, (uint8_t)v_len);
|
||||
p = _write(msg, max_msg, p, (uint8_t *)m.payload.config.value->c_str(), v_len);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int16_t RadioComms::send(Message &m)
|
||||
{
|
||||
uint8_t msg_buf[MAX_MSG];
|
||||
size_t p = MAX_MSG;
|
||||
uint8_t *msg = NULL;
|
||||
|
||||
if (m.type == SCAN_RESULT)
|
||||
{
|
||||
msg = _serialize_scan_result(m, p, msg_buf);
|
||||
}
|
||||
else if (m.type == MessageType::SCAN_MAX_RESULT)
|
||||
{
|
||||
msg = _serialize_scan_max_result(m, p, msg_buf);
|
||||
}
|
||||
else if (m.type == MessageType::CONFIG_TASK)
|
||||
{
|
||||
msg = _serialize_config_task(m, p, msg_buf);
|
||||
}
|
||||
|
||||
if (msg == NULL)
|
||||
{
|
||||
Serial.printf("Failed to encode message\n");
|
||||
return RADIOLIB_ERR_INVALID_FUNCTION;
|
||||
}
|
||||
|
||||
int16_t status = radio.transmit(msg, p);
|
||||
|
||||
if (msg != msg_buf)
|
||||
{
|
||||
delete[] msg;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v, size_t len)
|
||||
@@ -118,6 +230,126 @@ size_t _read(uint8_t *buf, size_t sz, size_t p, uint8_t *v)
|
||||
return _read(buf, sz, p, v, 1);
|
||||
}
|
||||
|
||||
Message *_deserialize_scan_result(size_t len, size_t &p, uint8_t *packet)
|
||||
{
|
||||
Message *message = new Message();
|
||||
message->type = SCAN_RESULT;
|
||||
|
||||
uint32_t s, e;
|
||||
size_t dump_sz = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&dump_sz, 2);
|
||||
p = _read(packet, len, p, (uint8_t *)&s, 4);
|
||||
p = _read(packet, len, p, (uint8_t *)&e, 4);
|
||||
size_t rem = len - p;
|
||||
|
||||
message->payload.dump.sz = dump_sz;
|
||||
if (dump_sz > 0)
|
||||
{
|
||||
message->payload.dump.rssis = new int16_t[dump_sz];
|
||||
message->payload.dump.freqs_khz = new uint32_t[dump_sz];
|
||||
message->payload.dump.freqs_khz[0] = s;
|
||||
message->payload.dump.freqs_khz[dump_sz - 1] = e;
|
||||
|
||||
for (int i = 1; i < dump_sz - 1; i++)
|
||||
{
|
||||
uint32_t incr = (e - s) / (dump_sz - i);
|
||||
s += incr;
|
||||
message->payload.dump.freqs_khz[i] = s;
|
||||
}
|
||||
|
||||
for (int i = 0, k = 0; i < rem; i++)
|
||||
{
|
||||
int j = (i + 1) * dump_sz / rem;
|
||||
int16_t rssi = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&rssi);
|
||||
rssi -= 255;
|
||||
for (; k < j; k++)
|
||||
{
|
||||
message->payload.dump.rssis[k] = rssi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
Message *_deserialize_scan_max_result(size_t len, size_t &p, uint8_t *packet)
|
||||
{
|
||||
Message *message = new Message();
|
||||
message->type = SCAN_MAX_RESULT;
|
||||
|
||||
uint32_t b = 0;
|
||||
size_t dump_sz = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&dump_sz, 2);
|
||||
uint32_t *freqs = new uint32_t[dump_sz];
|
||||
int16_t *rssis = new int16_t[dump_sz];
|
||||
message->payload.dump.sz = dump_sz;
|
||||
message->payload.dump.freqs_khz = freqs;
|
||||
message->payload.dump.rssis = rssis;
|
||||
|
||||
uint32_t scale = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&scale);
|
||||
|
||||
for (int i = 0; i < dump_sz; i++)
|
||||
{
|
||||
p = _read(packet, len, p, (uint8_t *)&b, 2);
|
||||
freqs[i] = scale * b;
|
||||
|
||||
b = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&b);
|
||||
rssis[i] = ((int16_t)b) - 255;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
Message *_deserialize_config_task(size_t len, size_t &p, uint8_t *packet)
|
||||
{
|
||||
Message *message = new Message();
|
||||
message->type = CONFIG_TASK;
|
||||
|
||||
ConfigTaskType ctt = GET;
|
||||
p = _read(packet, len, p, (uint8_t *)&ctt);
|
||||
message->payload.config.task_type = ctt;
|
||||
|
||||
size_t key_len = 0;
|
||||
size_t p1 = _read(packet, len, p, (uint8_t *)&key_len);
|
||||
memmove(packet + p, packet + p + 1, key_len);
|
||||
packet[p + key_len] = 0;
|
||||
String *key = new String((char *)packet + p);
|
||||
message->payload.config.key = key;
|
||||
|
||||
p = p1 + key_len;
|
||||
|
||||
if (key->length() != key_len)
|
||||
{
|
||||
delete message;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ctt == GET || ctt == SET_FAIL)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
size_t v_len = 0;
|
||||
p1 = _read(packet, len, p, (uint8_t *)&v_len);
|
||||
memmove(packet + p, packet + p + 1, v_len);
|
||||
packet[p + v_len] = 0;
|
||||
String *value = new String((char *)packet + p);
|
||||
message->payload.config.value = value;
|
||||
|
||||
p = p1 + v_len;
|
||||
|
||||
if (value->length() != v_len)
|
||||
{
|
||||
delete message;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
volatile bool _received = false;
|
||||
void _rcv() { _received = true; }
|
||||
|
||||
@@ -184,43 +416,15 @@ Message *RadioComms::receive(uint16_t timeout_ms)
|
||||
Message *message = NULL;
|
||||
if (b == SCAN_RESULT)
|
||||
{
|
||||
message = new Message();
|
||||
message->type = SCAN_RESULT;
|
||||
|
||||
uint32_t s, e;
|
||||
size_t dump_sz = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&s, 4);
|
||||
p = _read(packet, len, p, (uint8_t *)&e, 4);
|
||||
p = _read(packet, len, p, (uint8_t *)&dump_sz, 2);
|
||||
size_t rem = len - p;
|
||||
|
||||
message->payload.dump.sz = dump_sz;
|
||||
if (dump_sz > 0)
|
||||
{
|
||||
message->payload.dump.rssis = new int16_t[dump_sz];
|
||||
message->payload.dump.freqs_khz = new uint32_t[dump_sz];
|
||||
message->payload.dump.freqs_khz[0] = s;
|
||||
message->payload.dump.freqs_khz[dump_sz - 1] = e;
|
||||
|
||||
for (int i = 1; i < dump_sz - 1; i++)
|
||||
{
|
||||
uint32_t incr = (e - s) / (dump_sz - i);
|
||||
s += incr;
|
||||
message->payload.dump.freqs_khz[i] = s;
|
||||
}
|
||||
|
||||
for (int i = 0, k = 0; i < rem; i++)
|
||||
{
|
||||
int j = (i + 1) * dump_sz / rem;
|
||||
int16_t rssi = 0;
|
||||
p = _read(packet, len, p, (uint8_t *)&rssi);
|
||||
rssi -= 255;
|
||||
for (; k < j; k++)
|
||||
{
|
||||
message->payload.dump.rssis[k] = rssi;
|
||||
}
|
||||
}
|
||||
}
|
||||
message = _deserialize_scan_result(len, p, packet);
|
||||
}
|
||||
else if (b == SCAN_MAX_RESULT)
|
||||
{
|
||||
message = _deserialize_scan_max_result(len, p, packet);
|
||||
}
|
||||
else if (b == CONFIG_TASK)
|
||||
{
|
||||
message = _deserialize_config_task(len, p, packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -107,23 +107,26 @@ Config Config::init()
|
||||
return c;
|
||||
}
|
||||
|
||||
#define UPDATE_BOOL(val, key, value) \
|
||||
if (key.equalsIgnoreCase(#val)) \
|
||||
{ \
|
||||
String v = value; \
|
||||
bool p = v.equalsIgnoreCase("true"); \
|
||||
if (!p && !v.equalsIgnoreCase("false")) \
|
||||
{ \
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", \
|
||||
key.c_str(), value.c_str()); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
val = p; \
|
||||
} \
|
||||
return true; \
|
||||
}
|
||||
|
||||
bool Config::updateConfig(String key, String value)
|
||||
{
|
||||
if (key.equalsIgnoreCase("print_profile_time"))
|
||||
{
|
||||
String v = value;
|
||||
bool p = v.equalsIgnoreCase("true");
|
||||
if (!p && !v.equalsIgnoreCase("false"))
|
||||
{
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(),
|
||||
value.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
print_profile_time = p;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UPDATE_BOOL(print_profile_time, key, value);
|
||||
|
||||
if (key.equalsIgnoreCase("log_data_json_interval"))
|
||||
{
|
||||
@@ -167,21 +170,9 @@ bool Config::updateConfig(String key, String value)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.equalsIgnoreCase("is_host"))
|
||||
{
|
||||
String v = value;
|
||||
bool p = v.equalsIgnoreCase("true");
|
||||
if (!p && !v.equalsIgnoreCase("false"))
|
||||
{
|
||||
Serial.printf("Expected bool for '%s', found '%s' - ignoring\n", key.c_str(),
|
||||
value.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
is_host = p;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UPDATE_BOOL(is_host, key, value);
|
||||
|
||||
UPDATE_BOOL(lora_enabled, key, value);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -221,7 +212,7 @@ String detectionStrategyToStr(Config &c)
|
||||
res += ".." + String(c.scan_ranges[i].end_khz);
|
||||
if (c.scan_ranges[i].step_khz < s)
|
||||
{
|
||||
res += ":" + String(c.scan_ranges[i].step_khz);
|
||||
res += "+" + String(c.scan_ranges[i].step_khz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -512,6 +503,8 @@ bool Config::write_config(const char *path)
|
||||
f.println("tx_lora = " + getConfig("tx_lora"));
|
||||
f.println("is_host = " + getConfig("is_host"));
|
||||
|
||||
f.println("lora_enabled = " + getConfig("lora_enabled"));
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
|
||||
#include <SD.h>
|
||||
|
||||
template <typename L, typename R> struct Result
|
||||
{
|
||||
bool is_ok;
|
||||
union
|
||||
{
|
||||
L not_ok;
|
||||
R ok;
|
||||
};
|
||||
};
|
||||
|
||||
struct ScanRange
|
||||
{
|
||||
uint64_t start_khz;
|
||||
@@ -10,6 +20,22 @@ struct ScanRange
|
||||
uint64_t step_khz;
|
||||
};
|
||||
|
||||
struct ScanPage
|
||||
{
|
||||
uint64_t start_mhz;
|
||||
uint64_t end_mhz;
|
||||
size_t page_sz;
|
||||
ScanRange *scan_ranges;
|
||||
|
||||
~ScanPage()
|
||||
{
|
||||
if (page_sz > 0)
|
||||
{
|
||||
delete[] scan_ranges;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct LoRaConfig
|
||||
{
|
||||
float freq;
|
||||
@@ -25,6 +51,28 @@ struct LoRaConfig
|
||||
|
||||
LoRaConfig *configureLora(String cfg);
|
||||
|
||||
#ifndef FREQ_RX
|
||||
#define FREQ_RX 866
|
||||
#endif
|
||||
|
||||
#ifndef FREQ_TX
|
||||
#define FREQ_TX (FREQ_RX + 2)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_RX String("freq:") + String(FREQ_RX)
|
||||
#define DEFAULT_TX String("freq:") + String(FREQ_TX)
|
||||
|
||||
#ifndef DEFAULT_TX_SCAN_ON_BOOT
|
||||
#define DEFAULT_TX_SCAN_ON_BOOT false
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_IS_LORA_HOST
|
||||
#define DEFAULT_IS_LORA_HOST false
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_LORA_ENABLED
|
||||
#define DEFAULT_LORA_ENABLED false
|
||||
#endif
|
||||
#define CREATE_MISSING_CONFIG true
|
||||
struct Config
|
||||
{
|
||||
@@ -42,16 +90,16 @@ struct Config
|
||||
LoRaConfig *tx_lora;
|
||||
|
||||
bool is_host;
|
||||
bool lora_enabled;
|
||||
|
||||
Config()
|
||||
: create_missing_config(CREATE_MISSING_CONFIG), print_profile_time(false),
|
||||
detection_strategy(String("RSSI")), samples(0), scan_ranges_sz(0),
|
||||
scan_ranges(NULL), log_data_json_interval(1000),
|
||||
listen_on_serial0(String("none")), listen_on_serial1(String("readline")),
|
||||
listen_on_usb(String("readline")), rx_lora(NULL), tx_lora(NULL),
|
||||
// Enable Lora Send:
|
||||
// rx_lora(configureLora("freq:920")), tx_lora(configureLora("freq:916")),
|
||||
is_host(false) {};
|
||||
listen_on_usb(String("readline")), rx_lora(configureLora(DEFAULT_RX)),
|
||||
tx_lora(configureLora(DEFAULT_TX)), is_host(DEFAULT_IS_LORA_HOST),
|
||||
lora_enabled(DEFAULT_LORA_ENABLED) {};
|
||||
|
||||
bool write_config(const char *path);
|
||||
|
||||
|
||||
@@ -34,22 +34,6 @@ constexpr float LO_RSSI_THRESHOLD = HI_RSSI_THRESHOLD - 66;
|
||||
#define SAMPLES_RSSI 20
|
||||
#endif
|
||||
|
||||
struct ScanPage
|
||||
{
|
||||
uint64_t start_mhz;
|
||||
uint64_t end_mhz;
|
||||
size_t page_sz;
|
||||
ScanRange *scan_ranges;
|
||||
|
||||
~ScanPage()
|
||||
{
|
||||
if (page_sz > 0)
|
||||
{
|
||||
delete[] scan_ranges;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Scan
|
||||
{
|
||||
uint64_t epoch;
|
||||
|
||||
@@ -70,6 +70,7 @@ build_flags =
|
||||
-DHELTEC
|
||||
-DFREQ_BEGIN=130
|
||||
-DFREQ_END=180
|
||||
-DFREQ_RX=133
|
||||
-DRADIOLIB_CHECK_PARAMS=0
|
||||
|
||||
[env:lilygo-T3S3-v1-2-sx1262]
|
||||
@@ -122,6 +123,7 @@ build_flags =
|
||||
-DUSING_LR1121
|
||||
-DFREQ_BEGIN=2400
|
||||
-DFREQ_END=2500
|
||||
-DFREQ_RX=2440
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_LILYGO_T3_S3_V1_X
|
||||
@@ -150,6 +152,7 @@ build_flags =
|
||||
-DUSING_SX1280PA
|
||||
-DFREQ_BEGIN=2400
|
||||
-DFREQ_END=2500
|
||||
-DFREQ_RX=2440
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_LILYGO_T3_S3_V1_X
|
||||
|
||||
566
src/main.cpp
566
src/main.cpp
@@ -911,7 +911,7 @@ void configureDetection()
|
||||
|
||||
CONF_FREQ_BEGIN = config.scan_ranges[0].start_khz / 1000;
|
||||
CONF_FREQ_END = config.scan_ranges[0].end_khz / 1000;
|
||||
for (int i = 0; i < config.scan_ranges_sz; i++)
|
||||
for (int i = 1; i < config.scan_ranges_sz; i++)
|
||||
{
|
||||
CONF_FREQ_BEGIN = min(CONF_FREQ_BEGIN, config.scan_ranges[i].start_khz / 1000);
|
||||
CONF_FREQ_END = max(CONF_FREQ_END, config.scan_ranges[i].end_khz / 1000);
|
||||
@@ -926,6 +926,12 @@ void configureDetection()
|
||||
range = RANGE;
|
||||
|
||||
configurePages();
|
||||
|
||||
if (bar != NULL)
|
||||
{
|
||||
bar->bar.min_x = CONF_FREQ_BEGIN;
|
||||
bar->bar.max_x = CONF_FREQ_END;
|
||||
}
|
||||
}
|
||||
|
||||
void readConfigFile()
|
||||
@@ -1409,41 +1415,174 @@ bool is_new_x_pixel(int x)
|
||||
return false;
|
||||
}
|
||||
|
||||
void checkComms()
|
||||
/*
|
||||
* Given message type and config, modify from/to to route the message to the
|
||||
* right destination.
|
||||
*/
|
||||
void routeMessage(RoutedMessage &m)
|
||||
{
|
||||
if (m.message == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.message->type == MessageType::SCAN_RESULT ||
|
||||
m.message->type == MessageType::SCAN_MAX_RESULT)
|
||||
{
|
||||
m.to.host = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.message->type == MessageType::CONFIG_TASK &&
|
||||
(m.message->payload.config.task_type == ConfigTaskType::GETSET_SUCCESS ||
|
||||
m.message->payload.config.task_type == ConfigTaskType::SET_FAIL))
|
||||
{
|
||||
m.to.addr = 0;
|
||||
m.to.host = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.is_host &&
|
||||
(m.message->type == MessageType::SCAN ||
|
||||
m.message->type == MessageType::CONFIG_TASK &&
|
||||
m.message->payload.config.key->equalsIgnoreCase("detection_strategy")))
|
||||
{
|
||||
m.to.lora = 1; // apply to self, and send over to lora
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t sendMessage(RadioComms &c, Message &m);
|
||||
|
||||
void loraSendMessage(Message &m);
|
||||
|
||||
Result<int16_t, Message *> checkRadio(RadioComms &c);
|
||||
|
||||
void display_scan_result(ScanTaskResult &dump);
|
||||
|
||||
/*
|
||||
* If m.to is LOOP, the message is directed at this module; enact the message.
|
||||
* If m.to is not LOOP, send the message via the respective interface.
|
||||
*/
|
||||
void sendMessage(RoutedMessage &m)
|
||||
{
|
||||
if (m.message == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Message *msg = m.message;
|
||||
|
||||
if (m.to.loop)
|
||||
{
|
||||
switch (msg->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = msg->payload.scan;
|
||||
requested_host = !!m.from.host;
|
||||
break;
|
||||
case MessageType::CONFIG_TASK:
|
||||
{
|
||||
ConfigTaskType ctt = msg->payload.config.task_type;
|
||||
|
||||
// sanity check; GETSET_SUCCESS and SET_FAIL should get routed to HOST
|
||||
if (ctt == ConfigTaskType::GET || ctt == ConfigTaskType::SET)
|
||||
{
|
||||
// must be GET or SET - both require sending back a response
|
||||
RoutedMessage resp;
|
||||
resp.to.addr = m.from.addr;
|
||||
resp.to.loop = 0;
|
||||
|
||||
resp.from.addr = 0;
|
||||
resp.from.loop = 1;
|
||||
resp.message = new Message();
|
||||
resp.message->type = msg->type;
|
||||
|
||||
String old_v = config.getConfig(*msg->payload.config.key);
|
||||
bool success = true;
|
||||
if (ctt == ConfigTaskType::SET)
|
||||
{
|
||||
success = config.updateConfig(*msg->payload.config.key,
|
||||
*msg->payload.config.value);
|
||||
|
||||
if (success &&
|
||||
msg->payload.config.key->equalsIgnoreCase("detection_strategy"))
|
||||
{
|
||||
configureDetection(); // redo the pages and scan ranges
|
||||
}
|
||||
}
|
||||
|
||||
resp.message->payload.config.key = new String(*msg->payload.config.key);
|
||||
if (success)
|
||||
{
|
||||
resp.message->payload.config.task_type = GETSET_SUCCESS;
|
||||
resp.message->payload.config.value = new String(old_v);
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.message->payload.config.task_type = SET_FAIL;
|
||||
}
|
||||
|
||||
sendMessage(resp);
|
||||
|
||||
delete resp.message;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SCAN_RESULT:
|
||||
case SCAN_MAX_RESULT:
|
||||
if (config.is_host)
|
||||
{
|
||||
display_scan_result(m.message->payload.dump);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m.to.host)
|
||||
{
|
||||
HostComms->send(*m.message);
|
||||
}
|
||||
|
||||
if (m.to.uart0)
|
||||
{
|
||||
Comms0->send(*m.message);
|
||||
}
|
||||
|
||||
if (m.to.uart1)
|
||||
{
|
||||
Comms1->send(*m.message);
|
||||
}
|
||||
|
||||
if (m.to.lora)
|
||||
{
|
||||
if (config.is_host && TxComms != NULL)
|
||||
{
|
||||
checkRadio(*TxComms); // waiting for peer to squak first, so message sending
|
||||
// will land on the receiving cycle
|
||||
}
|
||||
|
||||
loraSendMessage(*m.message);
|
||||
}
|
||||
}
|
||||
|
||||
RoutedMessage checkComms()
|
||||
{
|
||||
RoutedMessage mess;
|
||||
mess.from.addr = 0;
|
||||
mess.to.addr = 0;
|
||||
mess.to.loop = 1;
|
||||
mess.message = NULL;
|
||||
|
||||
while (HostComms->available() > 0)
|
||||
{
|
||||
Message *m = HostComms->receive();
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan;
|
||||
requested_host = true;
|
||||
Serial.println("Host: forwarding message SCAN to peer");
|
||||
Comms0->send(*m); // forward to peer
|
||||
Comms1->send(*m); // forward to peer
|
||||
break;
|
||||
case MessageType::CONFIG_TASK:
|
||||
if (m->payload.config.is_set)
|
||||
{
|
||||
String v = config.getConfig(*m->payload.config.key);
|
||||
bool r =
|
||||
config.updateConfig(*m->payload.config.key, *m->payload.config.value);
|
||||
Serial.printf("SET config (%s): %s = %s (was: %s)\n", r ? "OK" : "failed",
|
||||
m->payload.config.key->c_str(),
|
||||
m->payload.config.value->c_str(), v.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("GET config: %s = %s\n", m->payload.config.key->c_str(),
|
||||
config.getConfig(*m->payload.config.key).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
mess.from.host = 1;
|
||||
mess.message = m;
|
||||
return mess;
|
||||
}
|
||||
|
||||
while (Comms0->available() > 0)
|
||||
@@ -1453,18 +1592,9 @@ void checkComms()
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan; // receive from peer
|
||||
requested_host = false;
|
||||
break;
|
||||
|
||||
case MessageType::SCAN_RESULT:
|
||||
HostComms->send(*m); // forward from peer
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
mess.from.uart0 = 1;
|
||||
mess.message = m;
|
||||
return mess;
|
||||
}
|
||||
|
||||
while (Comms1->available() > 0)
|
||||
@@ -1474,19 +1604,38 @@ void checkComms()
|
||||
if (m == NULL)
|
||||
continue;
|
||||
|
||||
switch (m->type)
|
||||
{
|
||||
case MessageType::SCAN:
|
||||
report_scans = m->payload.scan; // receive from peer
|
||||
requested_host = false;
|
||||
break;
|
||||
|
||||
case MessageType::SCAN_RESULT:
|
||||
HostComms->send(*m); // forward from peer
|
||||
break;
|
||||
}
|
||||
delete m;
|
||||
mess.from.uart1 = 1;
|
||||
mess.message = m;
|
||||
return mess;
|
||||
}
|
||||
|
||||
// NB: swapping the use of Tx and Rx comms, so a pair of modules
|
||||
// with identical rx/tx_lora config can talk
|
||||
RadioComms *rx = config.is_host ? TxComms : RxComms;
|
||||
|
||||
if (rx != NULL && (config.is_host || config.lora_enabled))
|
||||
{
|
||||
Result<int16_t, Message *> res = checkRadio(*rx);
|
||||
|
||||
if (!res.is_ok)
|
||||
{
|
||||
int16_t status = res.not_ok;
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Error getting a message: %d\n", status);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mess.from.lora = 1;
|
||||
mess.message = res.ok;
|
||||
}
|
||||
|
||||
return mess;
|
||||
}
|
||||
|
||||
mess.from.loop = 1;
|
||||
return mess;
|
||||
}
|
||||
|
||||
// MAX Frequency RSSI BIN value of the samples
|
||||
@@ -1494,9 +1643,7 @@ int max_rssi_x = 999;
|
||||
|
||||
void doScan();
|
||||
|
||||
void reportScan(RadioComms &c);
|
||||
|
||||
int16_t checkRadio(RadioComms &c);
|
||||
void reportScan();
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
@@ -1505,28 +1652,17 @@ void loop(void)
|
||||
r.detection_count = 0;
|
||||
drone_detected_frequency_start = 0;
|
||||
|
||||
checkComms();
|
||||
|
||||
if (config.is_host)
|
||||
for (RoutedMessage mess = checkComms(); mess.message != NULL; mess = checkComms())
|
||||
{
|
||||
if (TxComms != NULL)
|
||||
{
|
||||
// NB: swapping the use of Tx and Rx comms, so a pair of modules
|
||||
// with identical rx/tx_lora config can talk
|
||||
int16_t status = checkRadio(*TxComms);
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Error getting a message: %d\n", status);
|
||||
}
|
||||
}
|
||||
routeMessage(mess);
|
||||
sendMessage(mess);
|
||||
delete mess.message;
|
||||
}
|
||||
else
|
||||
|
||||
if (!config.is_host)
|
||||
{
|
||||
doScan();
|
||||
if (TxComms != NULL)
|
||||
reportScan(*TxComms);
|
||||
if (RxComms != NULL)
|
||||
checkRadio(*RxComms);
|
||||
reportScan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1677,14 +1813,15 @@ void doScan()
|
||||
float (*g)(void *);
|
||||
samples = CONF_SAMPLES;
|
||||
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI"))
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI") ||
|
||||
config.detection_strategy.equalsIgnoreCase("RSSI_MAX"))
|
||||
g = &getRSSI;
|
||||
else if (config.detection_strategy.equalsIgnoreCase("CAD"))
|
||||
{
|
||||
g = &getCAD;
|
||||
samples = min(
|
||||
1,
|
||||
CONF_SAMPLES); // TODO: do we need to support values other than 1
|
||||
samples = min(1,
|
||||
CONF_SAMPLES); // TODO: do we need to support values
|
||||
// other than 1
|
||||
}
|
||||
else
|
||||
g = &getRSSI;
|
||||
@@ -1998,99 +2135,228 @@ std::unordered_map<int, int16_t> findMaxRssi(int16_t *rssis, uint32_t *freqs_khz
|
||||
}
|
||||
|
||||
bool lock = false;
|
||||
int16_t checkRadio(RadioComms &comms)
|
||||
Result<int16_t, Message *> checkRadio(RadioComms &comms)
|
||||
{
|
||||
radioIsScan = false;
|
||||
int16_t status = comms.configureRadio();
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
return status;
|
||||
Result<int16_t, Message *> ret;
|
||||
ret.is_ok = false;
|
||||
|
||||
ret.not_ok = comms.configureRadio();
|
||||
if (ret.not_ok != RADIOLIB_ERR_NONE)
|
||||
return ret;
|
||||
|
||||
ret.is_ok = true;
|
||||
|
||||
Message *msg = comms.receive(
|
||||
config.is_host
|
||||
? 2000
|
||||
: 500); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500
|
||||
if (msg == NULL)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
: 200); // 200ms should be enough to receive 500 bytes at SF 7 and BW 500
|
||||
ret.ok = msg;
|
||||
|
||||
if (msg->type == SCAN_RESULT)
|
||||
{
|
||||
// if (lock == false)
|
||||
{
|
||||
lock = true;
|
||||
|
||||
// display.setDisplayRotation(1);
|
||||
// display.println("Host Mode ->");
|
||||
|
||||
size_t dump_sz = msg->payload.dump.sz;
|
||||
int16_t *rssi = msg->payload.dump.rssis;
|
||||
uint32_t *fr = msg->payload.dump.freqs_khz;
|
||||
|
||||
std::unordered_map<int, int16_t> maxMhzRssi =
|
||||
findMaxRssi(rssi, fr, dump_sz, 85);
|
||||
|
||||
int lx, ly, i = 0;
|
||||
for (const auto &pair : maxMhzRssi)
|
||||
{
|
||||
if (i == 0 && maxMhzRssi.size() > 0)
|
||||
{
|
||||
display.clear();
|
||||
}
|
||||
// screen overflow protection
|
||||
if (lx < 130)
|
||||
{
|
||||
int16_t rssi = pair.second;
|
||||
int16_t fr = (int)pair.first;
|
||||
display.drawString(lx, ly, String(fr) + ":" + String(rssi));
|
||||
// go to next line
|
||||
ly += 10;
|
||||
if (ly > 60)
|
||||
{
|
||||
ly = 0;
|
||||
|
||||
// go to next column
|
||||
lx += 50;
|
||||
}
|
||||
display.display();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
lock = false;
|
||||
}
|
||||
|
||||
HostComms->send(*msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("Received a message of unsupported type: %d\n", msg->type);
|
||||
}
|
||||
|
||||
delete msg;
|
||||
|
||||
return status;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void reportScan(RadioComms &comms)
|
||||
std::unordered_map<int, int> previousPac = {/*{916, true}, {915, true}*/};
|
||||
|
||||
std::unordered_map<int, int16_t> findMaxRssi(int16_t *rssis, uint32_t *freqs_khz,
|
||||
int dump_sz, int level = 80)
|
||||
{
|
||||
std::unordered_map<int, int16_t> maxRssiPerMHz; // Map to store max RSSI per MHz
|
||||
|
||||
for (int i = 0; i < dump_sz; i++)
|
||||
{
|
||||
int16_t rssi = rssis[i];
|
||||
int freq_mhz = (int)freqs_khz[i] / 1000; // Convert kHz to MHz
|
||||
|
||||
// Update the maximum RSSI for this MHz frequency
|
||||
if (maxRssiPerMHz.find(freq_mhz) == maxRssiPerMHz.end() ||
|
||||
maxRssiPerMHz[freq_mhz] < rssi)
|
||||
{
|
||||
if (abs(rssi) < level)
|
||||
{
|
||||
maxRssiPerMHz[freq_mhz] = rssi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxRssiPerMHz;
|
||||
}
|
||||
|
||||
bool lock = false;
|
||||
|
||||
int16_t sendMessage(RadioComms &comms, Message &msg)
|
||||
{
|
||||
radioIsScan = false;
|
||||
int16_t status = comms.configureRadio();
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Failed to configure Radio: %d\n", status);
|
||||
return;
|
||||
return status;
|
||||
}
|
||||
|
||||
Message m;
|
||||
m.type = SCAN_RESULT;
|
||||
m.payload.dump = frequency_scan_result.dump;
|
||||
status = comms.send(m);
|
||||
if (false)
|
||||
{
|
||||
lock = true;
|
||||
|
||||
m.payload.dump.sz = 0; // dump is shared, so should not delete underlying arrays
|
||||
// display.setDisplayRotation(1);
|
||||
// display.println("Host Mode ->");
|
||||
|
||||
size_t dump_sz = msg->payload.dump.sz;
|
||||
int16_t *rssi = msg->payload.dump.rssis;
|
||||
uint32_t *fr = msg->payload.dump.freqs_khz;
|
||||
|
||||
std::unordered_map<int, int16_t> maxMhzRssi = findMaxRssi(rssi, fr, dump_sz, 85);
|
||||
|
||||
int lx, ly, i = 0;
|
||||
for (const auto &pair : maxMhzRssi)
|
||||
{
|
||||
if (i == 0 && maxMhzRssi.size() > 0)
|
||||
{
|
||||
display.clear();
|
||||
}
|
||||
// screen overflow protection
|
||||
if (lx < 130)
|
||||
{
|
||||
int16_t rssi = pair.second;
|
||||
int16_t fr = (int)pair.first;
|
||||
display.drawString(lx, ly, String(fr) + ":" + String(rssi));
|
||||
// go to next line
|
||||
ly += 10;
|
||||
if (ly > 60)
|
||||
{
|
||||
ly = 0;
|
||||
|
||||
// go to next column
|
||||
lx += 50;
|
||||
}
|
||||
display.display();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
lock = false;
|
||||
}
|
||||
|
||||
status = comms.send(msg);
|
||||
|
||||
if (status != RADIOLIB_ERR_NONE)
|
||||
{
|
||||
Serial.printf("Failed to send scan result: %d\n", status);
|
||||
Serial.printf("Failed to send message of type %d: %d\n", msg.type, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void loraSendMessage(Message &msg)
|
||||
{
|
||||
RadioComms *tx = config.is_host ? RxComms : TxComms;
|
||||
if (tx == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(*tx, msg);
|
||||
}
|
||||
|
||||
void reportScan()
|
||||
{
|
||||
if (!config.lora_enabled)
|
||||
return;
|
||||
|
||||
Message m;
|
||||
m.type = SCAN_RESULT;
|
||||
m.payload.dump.sz = 0;
|
||||
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI"))
|
||||
{
|
||||
size_t sz = frequency_scan_result.dump.sz;
|
||||
m.payload.dump.sz = sz;
|
||||
m.payload.dump.freqs_khz = new uint32_t[sz];
|
||||
m.payload.dump.rssis = new int16_t[sz];
|
||||
|
||||
memcpy(m.payload.dump.freqs_khz, frequency_scan_result.dump.freqs_khz,
|
||||
sizeof(uint32_t) * sz);
|
||||
memcpy(m.payload.dump.rssis, frequency_scan_result.dump.rssis,
|
||||
sizeof(int16_t) * sz);
|
||||
}
|
||||
else if (config.detection_strategy.equalsIgnoreCase("RSSI_MAX"))
|
||||
{
|
||||
m.type = SCAN_MAX_RESULT;
|
||||
|
||||
size_t sz = config.scan_ranges_sz;
|
||||
m.payload.dump.sz = sz;
|
||||
m.payload.dump.freqs_khz = new uint32_t[sz];
|
||||
m.payload.dump.rssis = new int16_t[sz];
|
||||
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
int16_t rssi = -999;
|
||||
for (int j = 0; j < frequency_scan_result.dump.sz; j++)
|
||||
{
|
||||
uint32_t f = frequency_scan_result.dump.freqs_khz[j];
|
||||
|
||||
if (config.scan_ranges[i].start_khz > f ||
|
||||
config.scan_ranges[i].end_khz < f)
|
||||
continue;
|
||||
|
||||
rssi = max(rssi, frequency_scan_result.dump.rssis[j]);
|
||||
}
|
||||
|
||||
m.payload.dump.freqs_khz[i] =
|
||||
(config.scan_ranges[i].start_khz + config.scan_ranges[i].end_khz) / 2;
|
||||
m.payload.dump.rssis[i] = rssi;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
loraSendMessage(m);
|
||||
}
|
||||
|
||||
void display_scan_result(ScanTaskResult &dump)
|
||||
{
|
||||
if (bar == NULL)
|
||||
return;
|
||||
// assuming this module and the peer are in sync w.r.t. scan ranges
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI"))
|
||||
{
|
||||
for (int i = 0; i < dump.sz; i++)
|
||||
bar->bar.updatePoint(dump.freqs_khz[i] / 1000, dump.rssis[i]);
|
||||
|
||||
bar->draw();
|
||||
display.display();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.detection_strategy.equalsIgnoreCase("RSSI_MAX"))
|
||||
{
|
||||
float step = (bar->bar.max_x - bar->bar.min_x) / bar->bar.width;
|
||||
|
||||
bar->bar.clear();
|
||||
|
||||
for (int i = 0; i < config.scan_ranges_sz; i++)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < dump.sz; j++)
|
||||
{
|
||||
if (config.scan_ranges[i].start_khz <= dump.freqs_khz[j] &&
|
||||
config.scan_ranges[i].end_khz >= dump.freqs_khz[j])
|
||||
break;
|
||||
}
|
||||
|
||||
int16_t rssi = j < dump.sz ? dump.rssis[j] : bar->bar.min_y;
|
||||
|
||||
for (float f = config.scan_ranges[i].start_khz / 1000;
|
||||
f <= config.scan_ranges[i].end_khz / 1000; f += step)
|
||||
bar->bar.updatePoint(f, rssi);
|
||||
}
|
||||
|
||||
bar->draw();
|
||||
display.display();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user