Adding simple FSK Rx Processor. Can be used with New Apps. (#2716)

* Work to allow for unique beacon parsing functions.

* Fixing pull.

* Changes.

* Formatting.

* Fix Copyright

* Update firmware/application/apps/ble_rx_app.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update firmware/baseband/proc_btlerx.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* PR suggestions.

* Fix String.

* FSK Rx Improvements. Works for my custom protocol.

* Fix buffer size.

* Refactor

* Formatting.

* Formatting.

* Fixing compiling, and BLE Rx UI/Performance.

* More improvements.

* Fixing stuck state.

* More stuck parsing fix.

* Combining PR changes.

* Improvements from previous PR.

* Fix dbM calculation relative to device RSSI.

* Formatting.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: TJ <tj.baginski@cognosos.com>
This commit is contained in:
Netro
2025-06-28 19:02:12 -04:00
committed by GitHub
parent 4e276cdc71
commit f90d3fabce
13 changed files with 521 additions and 448 deletions

View File

@@ -57,95 +57,6 @@ std::string pad_string_with_spaces(int snakes) {
return paddedStr;
}
struct GainEntry {
uint8_t lna;
uint8_t vga;
uint8_t gain;
};
// Only LNA with VGA 0-4 is tested to be accurate. Max zeroized gain tested to be 16dBm.
// Beyond that it is hard to tell distance to transmitting device.
// Test was conducted within a few inches of the device.
// Device was transmitting at 0dBm.
constexpr GainEntry gain_table[] =
{
{40, 0, 19},
{32, 0, 18},
{24, 0, 15},
{16, 0, 8},
{8, 0, 2},
{0, 0, 0},
{40, 2, 20},
{32, 2, 22},
{24, 2, 14},
{16, 2, 8},
{8, 2, 2},
{0, 2, 0},
{40, 4, 21},
{32, 4, 22},
{24, 4, 15},
{16, 4, 10},
{8, 4, 3},
{0, 4, 0},
{40, 6, 26},
{32, 6, 22},
{24, 6, 15},
{16, 6, 10},
{8, 6, 4},
{0, 6, 0},
{40, 8, 26},
{32, 8, 26},
{24, 8, 18},
{16, 8, 12},
{8, 8, 6},
{0, 8, 1},
{40, 10, 26},
{32, 10, 26},
{24, 10, 20},
{16, 10, 15},
{8, 10, 8},
{0, 10, 3},
{40, 12, 26},
{32, 12, 26},
{24, 12, 23},
{16, 12, 17},
{8, 12, 10},
{0, 12, 4},
{40, 14, 26},
{32, 14, 26},
{24, 14, 25},
{16, 14, 19},
{8, 14, 12},
{0, 14, 6},
{40, 16, 26},
{32, 16, 26},
{24, 16, 26},
{16, 16, 20},
{8, 16, 13},
{0, 16, 7},
{40, 18, 26},
{32, 18, 26},
{24, 18, 26},
{16, 18, 21},
{8, 18, 14},
{0, 18, 8},
{40, 20, 26},
{32, 20, 26},
{24, 20, 26},
{16, 20, 23},
{8, 20, 16},
{0, 20, 10},
};
uint8_t get_total_gain(uint8_t lna, uint8_t vga) {
for (const auto& entry : gain_table) {
if (entry.lna == lna && entry.vga == vga)
return entry.gain;
}
return 0;
}
uint64_t copy_mac_address_to_uint64(const uint8_t* macAddress) {
uint64_t result = 0;
@@ -275,10 +186,10 @@ void RecentEntriesTable<BleRecentEntries>::draw(
if (!entry.nameString.empty() && entry.include_name) {
line = entry.nameString;
if (line.length() < 10) {
line += pad_string_with_spaces(10 - line.length());
if (line.length() < 17) {
line += pad_string_with_spaces(17 - line.length());
} else {
line = truncate(line, 10);
line = truncate(line, 17);
}
} else {
line = to_string_mac_address(entry.packetData.macAddress, 6, false);
@@ -289,12 +200,12 @@ void RecentEntriesTable<BleRecentEntries>::draw(
if (!entry.informationString.empty()) {
hitsStr = entry.informationString;
} else {
hitsStr = "Hits: " + to_string_dec_int(entry.numHits);
hitsStr = to_string_dec_int(entry.numHits);
}
// Pushing single digit values down right justified.
int hitsDigits = hitsStr.length();
uint8_t hits_spacing = 14 - hitsDigits;
uint8_t hits_spacing = 8 - hitsDigits;
// Pushing single digit values down right justified.
std::string dbStr = to_string_dec_int(entry.dbValue);
@@ -879,17 +790,29 @@ void BLERxView::on_data(BlePacketData* packet) {
// Start of Packet stuffing.
// Masking off the top 2 bytes to avoid invalid keys.
BleRecentEntry tempEntry;
uint64_t key = macAddressEncoded & 0xFFFFFFFFFFFF;
bool packetExists = false;
if (updateEntry(packet, tempEntry, (ADV_PDU_TYPE)packet->type)) {
auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF);
// If found store into tempEntry to modify.
auto it = find(recent, key);
if (it != recent.end()) {
recent.push_front(*it);
recent.erase(it);
updateEntry(packet, recent.front(), (ADV_PDU_TYPE)packet->type);
packetExists = true;
} else {
recent.emplace_front(key);
truncate_entries(recent);
// Preserve exisisting data from entry.
tempEntry.macAddress = macAddressEncoded;
tempEntry.numHits = ++entry.numHits;
packetExists = updateEntry(packet, recent.front(), (ADV_PDU_TYPE)packet->type);
entry = tempEntry;
// If parsing failed, remove entry.
if (!packetExists) {
recent.erase(recent.begin());
}
}
if (packetExists) {
handle_filter_options(options_filter.selected_index());
handle_entries_sort(options_sort.selected_index());
@@ -899,7 +822,7 @@ void BLERxView::on_data(BlePacketData* packet) {
while (it != searchList.end()) {
std::string searchStr = (std::string)*it;
if (entry.dataString.find(searchStr) != std::string::npos) {
if (recent.front().dataString.find(searchStr) != std::string::npos) {
searchList.erase(it);
found_count++;
break;
@@ -1000,13 +923,10 @@ void BLERxView::on_timer() {
timer_count = 0;
if (auto_channel) {
int min = 37;
int max = 39;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
baseband::set_btlerx(channel_number);
int randomChannel = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(randomChannel));
baseband::set_btlerx(randomChannel);
channel_number = (channel_number < 39) ? channel_number + 1 : 37;
}
}
if (ble_rx_error != BLE_RX_NO_ERROR) {
@@ -1093,7 +1013,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
data_string += to_string_hex(packet->data[i], 2);
}
entry.dbValue = 2 * (packet->max_dB - get_total_gain(receiver_model.lna(), receiver_model.vga()));
entry.dbValue = packet->max_dB - (receiver_model.lna() + receiver_model.vga() + (receiver_model.rf_amp() ? 14 : 0));
entry.timestamp = to_string_timestamp(rtc_time::now());
entry.dataString = data_string;
@@ -1111,6 +1031,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
entry.pduType = pdu_type;
entry.channelNumber = channel_number;
entry.numHits++;
if (entry.vendor_status == MAC_VENDOR_UNKNOWN) {
std::string vendor_name;
@@ -1125,8 +1046,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
entry.include_name = check_name.value();
// Only parse name for advertisment packets and empty name entries
if (pdu_type == ADV_IND || pdu_type == ADV_NONCONN_IND) // || pdu_type == SCAN_RSP || pdu_type == ADV_SCAN_IND)
{
if (pdu_type == ADV_IND || pdu_type == ADV_NONCONN_IND || pdu_type == SCAN_RSP || pdu_type == ADV_SCAN_IND) {
if (uniqueParsing) {
// Add your unique beacon parsing function here.
}
@@ -1145,6 +1065,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
bool BLERxView::parse_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString) {
uint8_t currentByte, currentLength, currentType = 0;
std::string tempName = "";
for (currentByte = 0; currentByte < length;) {
currentLength = data[currentByte++];
@@ -1154,18 +1075,25 @@ bool BLERxView::parse_beacon_data(const uint8_t* data, uint8_t length, std::stri
for (int i = 0; ((i < currentLength - 1) && (currentByte < length)); i++) {
// parse the name of bluetooth device: 0x08->Shortened Local Name; 0x09->Complete Local Name
if (currentType == 0x08 || currentType == 0x09) {
nameString += (char)data[currentByte];
tempName += (char)data[currentByte];
}
currentByte++;
}
}
if (nameString.empty()) {
nameString = "None";
if (!tempName.empty()) {
nameString = tempName;
break;
}
}
informationString = "";
if (!informationString.empty()) {
// Option to change title of Hits Column.
// Setting to default for now.
columns.set(1, "Hits", 7);
}
return true;
}

View File

@@ -267,7 +267,7 @@ class BLERxView : public View {
bool auto_channel = false;
int16_t timer_count{0};
int16_t timer_period{6}; // 100ms
int16_t timer_period{2}; // 25ms
std::string filterBuffer{};
std::string listFileBuffer{};
@@ -386,9 +386,9 @@ class BLERxView : public View {
BleRecentEntries recent{};
BleRecentEntries tempList{};
const RecentEntriesColumns columns{{
{"Name", 10},
{"Information", 13},
RecentEntriesColumns columns{{
{"Name", 17},
{"Hits", 7},
{"dBm", 4},
}};

View File

@@ -186,13 +186,14 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t
send_message(&message);
}
void set_fsk(const size_t deviation) {
void set_fsk(const uint8_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes) {
const FSKRxConfigureMessage message{
taps_200k_decim_0,
taps_16k0_decim_1,
taps_11k0_channel,
2,
deviation};
samplesPerSymbol,
syncWord,
syncWordLength,
preamble,
preambleLength,
numDataBytes};
send_message(&message);
}

View File

@@ -80,7 +80,7 @@ void set_pitch_rssi(int32_t avg, bool enabled);
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
void kill_afsk();
void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_fsk(const size_t deviation);
void set_fsk(const uint32_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes);
void set_aprs(const uint32_t baudrate);
void set_btlerx(uint8_t channel_number);

View File

@@ -24,7 +24,7 @@
namespace ui {
RecentEntriesColumns::RecentEntriesColumns(
const std::initializer_list<RecentEntriesColumn> columns)
std::initializer_list<RecentEntriesColumn> columns)
: _columns{columns} {
}

View File

@@ -133,14 +133,27 @@ class RecentEntriesColumns {
public:
using ContainerType = std::vector<RecentEntriesColumn>;
RecentEntriesColumns(
const std::initializer_list<RecentEntriesColumn> columns);
RecentEntriesColumns(std::initializer_list<RecentEntriesColumn> columns);
ContainerType::const_iterator begin() const { return std::begin(_columns); }
ContainerType::const_iterator end() const { return std::end(_columns); }
ContainerType::iterator begin() { return _columns.begin(); }
ContainerType::iterator end() { return _columns.end(); }
ContainerType::const_iterator begin() const { return _columns.begin(); }
ContainerType::const_iterator end() const { return _columns.end(); }
void set(size_t index, const std::string& name, size_t width) {
if (index < _columns.size()) {
_columns[index] = {name, width};
}
}
const RecentEntriesColumn& at(size_t index) const {
return _columns.at(index);
}
size_t size() const { return _columns.size(); }
private:
const ContainerType _columns;
ContainerType _columns;
};
class RecentEntriesHeader : public Widget {