mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
BLE Tx App / BLE Rx Filtering. (#1543)
BLE TX app creation BLE RX and TX app improvements
This commit is contained in:
parent
62307b93d7
commit
dceb7255b0
@ -245,6 +245,7 @@ set(CPPSRC
|
|||||||
apps/analog_audio_app.cpp
|
apps/analog_audio_app.cpp
|
||||||
apps/analog_tv_app.cpp
|
apps/analog_tv_app.cpp
|
||||||
apps/ble_app.cpp
|
apps/ble_app.cpp
|
||||||
|
apps/ble_tx_app.cpp
|
||||||
apps/capture_app.cpp
|
apps/capture_app.cpp
|
||||||
apps/ert_app.cpp
|
apps/ert_app.cpp
|
||||||
apps/gps_sim_app.cpp
|
apps/gps_sim_app.cpp
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
#include "portapack_persistent_memory.hpp"
|
#include "portapack_persistent_memory.hpp"
|
||||||
|
#include "ui_textentry.hpp"
|
||||||
|
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
using namespace modems;
|
using namespace modems;
|
||||||
@ -214,15 +215,32 @@ BLERxView::BLERxView(NavigationView& nav)
|
|||||||
&check_log,
|
&check_log,
|
||||||
&label_sort,
|
&label_sort,
|
||||||
&options_sort,
|
&options_sort,
|
||||||
|
&button_message,
|
||||||
&recent_entries_view,
|
&recent_entries_view,
|
||||||
|
&recent_entries_filter_view,
|
||||||
&recent_entry_detail_view});
|
&recent_entry_detail_view});
|
||||||
|
|
||||||
recent_entry_detail_view.hidden(true);
|
recent_entry_detail_view.hidden(true);
|
||||||
|
recent_entries_filter_view.hidden(true);
|
||||||
|
|
||||||
recent_entries_view.on_select = [this](const BleRecentEntry& entry) {
|
recent_entries_view.on_select = [this](const BleRecentEntry& entry) {
|
||||||
nav_.push<BleRecentEntryDetailView>(entry);
|
nav_.push<BleRecentEntryDetailView>(entry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
recent_entries_filter_view.on_select = [this](const BleRecentEntry& entry) {
|
||||||
|
nav_.push<BleRecentEntryDetailView>(entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
button_message.on_select = [this, &nav](Button&) {
|
||||||
|
text_prompt(
|
||||||
|
nav,
|
||||||
|
filterBuffer,
|
||||||
|
64,
|
||||||
|
[this](std::string& buffer) {
|
||||||
|
on_switch_table(buffer);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
field_frequency.set_step(0);
|
field_frequency.set_step(0);
|
||||||
|
|
||||||
check_log.set_value(logging);
|
check_log.set_value(logging);
|
||||||
@ -244,20 +262,27 @@ BLERxView::BLERxView(NavigationView& nav)
|
|||||||
case 0:
|
case 0:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
recent_entries_view.set_dirty();
|
recent_entries_view.set_dirty();
|
||||||
|
recent_entries_filter_view.set_dirty();
|
||||||
};
|
};
|
||||||
|
|
||||||
options_channel.set_selected_index(0, true);
|
options_channel.set_selected_index(0, true);
|
||||||
@ -319,8 +344,6 @@ void BLERxView::on_data(BlePacketData* packet) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// str_console += to_string_dec_uint(value);
|
|
||||||
|
|
||||||
str_console += " Len:";
|
str_console += " Len:";
|
||||||
str_console += to_string_dec_uint(packet->size);
|
str_console += to_string_dec_uint(packet->size);
|
||||||
|
|
||||||
@ -335,7 +358,7 @@ void BLERxView::on_data(BlePacketData* packet) {
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < packet->dataLen; i++) {
|
for (i = 0; i < packet->dataLen; i++) {
|
||||||
str_console += " " + to_string_hex(packet->data[i], 2);
|
str_console += to_string_hex(packet->data[i], 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
str_console += "\n";
|
str_console += "\n";
|
||||||
@ -345,9 +368,60 @@ void BLERxView::on_data(BlePacketData* packet) {
|
|||||||
// Start of Packet stuffing.
|
// Start of Packet stuffing.
|
||||||
// Masking off the top 2 bytes to avoid invalid keys.
|
// Masking off the top 2 bytes to avoid invalid keys.
|
||||||
auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF);
|
auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF);
|
||||||
|
updateEntry(packet, entry);
|
||||||
|
|
||||||
|
// Log at End of Packet.
|
||||||
|
if (logger && logging) {
|
||||||
|
logger->log_raw_data(str_console);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLERxView::on_switch_table(const std::string value) {
|
||||||
|
filter = value;
|
||||||
|
|
||||||
|
if (!value.empty()) {
|
||||||
|
removeEntriesWithoutKey(recent, filterEntries, [&value](const BleRecentEntry& entry) {
|
||||||
|
return entry.dataString.find(value) == std::string::npos;
|
||||||
|
});
|
||||||
|
|
||||||
|
recent_entries_view.set_dirty();
|
||||||
|
|
||||||
|
recent_entries_filter_view.hidden(false);
|
||||||
|
recent_entries_view.hidden(true);
|
||||||
|
} else {
|
||||||
|
recent_entries_view.hidden(false);
|
||||||
|
recent_entries_filter_view.hidden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
recent_entries_view.set_dirty();
|
||||||
|
recent_entries_filter_view.set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLERxView::set_parent_rect(const Rect new_parent_rect) {
|
||||||
|
View::set_parent_rect(new_parent_rect);
|
||||||
|
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
|
||||||
|
recent_entries_view.set_parent_rect(content_rect);
|
||||||
|
recent_entry_detail_view.set_parent_rect(content_rect);
|
||||||
|
recent_entries_filter_view.set_parent_rect(content_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLERxView::~BLERxView() {
|
||||||
|
receiver_model.disable();
|
||||||
|
baseband::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry) {
|
||||||
|
std::string data_string;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < packet->dataLen; i++) {
|
||||||
|
data_string += to_string_hex(packet->data[i], 2);
|
||||||
|
}
|
||||||
|
|
||||||
entry.dbValue = packet->max_dB;
|
entry.dbValue = packet->max_dB;
|
||||||
entry.timestamp = to_string_timestamp(rtc_time::now());
|
entry.timestamp = to_string_timestamp(rtc_time::now());
|
||||||
|
entry.dataString = data_string;
|
||||||
|
|
||||||
entry.packetData.type = packet->type;
|
entry.packetData.type = packet->type;
|
||||||
entry.packetData.size = packet->size;
|
entry.packetData.size = packet->size;
|
||||||
@ -370,48 +444,31 @@ void BLERxView::on_data(BlePacketData* packet) {
|
|||||||
case 0:
|
case 0:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sortEntriesBy(
|
sortEntriesBy(
|
||||||
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
||||||
|
sortEntriesBy(
|
||||||
|
filterEntries, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
recent_entries_view.set_dirty();
|
on_switch_table(filter);
|
||||||
|
|
||||||
// TODO: Crude hack, should be a more formal listener arrangement...
|
// TODO: Crude hack, should be a more formal listener arrangement...
|
||||||
if (entry.key() == recent_entry_detail_view.entry().key()) {
|
if (entry.key() == recent_entry_detail_view.entry().key()) {
|
||||||
recent_entry_detail_view.set_entry(entry);
|
recent_entry_detail_view.set_entry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log at End of Packet.
|
|
||||||
if (logger && logging) {
|
|
||||||
logger->log_raw_data(str_console);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void BLERxView::set_parent_rect(const Rect new_parent_rect) {
|
|
||||||
View::set_parent_rect(new_parent_rect);
|
|
||||||
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
|
|
||||||
recent_entries_view.set_parent_rect(content_rect);
|
|
||||||
recent_entry_detail_view.set_parent_rect(content_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
BLERxView::~BLERxView() {
|
|
||||||
receiver_model.disable();
|
|
||||||
baseband::shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// BleRecentEntry
|
|
||||||
// void BleRecentEntry::update(const BlePacketData * packet)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -77,6 +77,7 @@ struct BleRecentEntry {
|
|||||||
int dbValue;
|
int dbValue;
|
||||||
BlePacketData packetData;
|
BlePacketData packetData;
|
||||||
std::string timestamp;
|
std::string timestamp;
|
||||||
|
std::string dataString;
|
||||||
|
|
||||||
BleRecentEntry()
|
BleRecentEntry()
|
||||||
: BleRecentEntry{0} {
|
: BleRecentEntry{0} {
|
||||||
@ -87,14 +88,13 @@ struct BleRecentEntry {
|
|||||||
: macAddress{macAddress},
|
: macAddress{macAddress},
|
||||||
dbValue{},
|
dbValue{},
|
||||||
packetData{},
|
packetData{},
|
||||||
timestamp{} {
|
timestamp{},
|
||||||
|
dataString{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
Key key() const {
|
Key key() const {
|
||||||
return macAddress;
|
return macAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
// void update(const BlePacketData * packet);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using BleRecentEntries = RecentEntries<BleRecentEntry>;
|
using BleRecentEntries = RecentEntries<BleRecentEntry>;
|
||||||
@ -151,6 +151,8 @@ class BLERxView : public View {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void on_data(BlePacketData* packetData);
|
void on_data(BlePacketData* packetData);
|
||||||
|
void on_switch_table(const std::string value);
|
||||||
|
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry);
|
||||||
|
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
RxRadioState radio_state_{
|
RxRadioState radio_state_{
|
||||||
@ -165,6 +167,9 @@ class BLERxView : public View {
|
|||||||
uint32_t prev_value{0};
|
uint32_t prev_value{0};
|
||||||
uint8_t channel_number = 37;
|
uint8_t channel_number = 37;
|
||||||
|
|
||||||
|
std::string filterBuffer{};
|
||||||
|
std::string filter{};
|
||||||
|
|
||||||
static constexpr auto header_height = 12 + 2 * 16;
|
static constexpr auto header_height = 12 + 2 * 16;
|
||||||
|
|
||||||
OptionsField options_channel{
|
OptionsField options_channel{
|
||||||
@ -209,6 +214,10 @@ class BLERxView : public View {
|
|||||||
{"dB", 1},
|
{"dB", 1},
|
||||||
{"Recent", 2}}};
|
{"Recent", 2}}};
|
||||||
|
|
||||||
|
Button button_message{
|
||||||
|
{22 * 8, 3 * 8, 4 * 8, 16},
|
||||||
|
"Filter"};
|
||||||
|
|
||||||
Console console{
|
Console console{
|
||||||
{0, 4 * 16, 240, 240}};
|
{0, 4 * 16, 240, 240}};
|
||||||
|
|
||||||
@ -223,6 +232,7 @@ class BLERxView : public View {
|
|||||||
// {"Time", 8}}};
|
// {"Time", 8}}};
|
||||||
|
|
||||||
BleRecentEntries recent{};
|
BleRecentEntries recent{};
|
||||||
|
BleRecentEntries filterEntries{};
|
||||||
|
|
||||||
const RecentEntriesColumns columns{{
|
const RecentEntriesColumns columns{{
|
||||||
{"Mac Address", 20},
|
{"Mac Address", 20},
|
||||||
@ -231,6 +241,7 @@ class BLERxView : public View {
|
|||||||
|
|
||||||
BleRecentEntry entry_{};
|
BleRecentEntry entry_{};
|
||||||
BleRecentEntriesView recent_entries_view{columns, recent};
|
BleRecentEntriesView recent_entries_view{columns, recent};
|
||||||
|
BleRecentEntriesView recent_entries_filter_view{columns, filterEntries};
|
||||||
BleRecentEntryDetailView recent_entry_detail_view{nav_, entry_};
|
BleRecentEntryDetailView recent_entry_detail_view{nav_, entry_};
|
||||||
|
|
||||||
MessageHandlerRegistration message_handler_packet{
|
MessageHandlerRegistration message_handler_packet{
|
||||||
|
351
firmware/application/apps/ble_tx_app.cpp
Normal file
351
firmware/application/apps/ble_tx_app.cpp
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2017 Furrtek
|
||||||
|
* Copyright (C) 2023 TJ Baginski
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ble_tx_app.hpp"
|
||||||
|
|
||||||
|
#include "ui_fileman.hpp"
|
||||||
|
#include "ui_modemsetup.hpp"
|
||||||
|
|
||||||
|
#include "audio.hpp"
|
||||||
|
#include "baseband_api.hpp"
|
||||||
|
#include "io_file.hpp"
|
||||||
|
#include "modems.hpp"
|
||||||
|
#include "portapack_persistent_memory.hpp"
|
||||||
|
#include "rtc_time.hpp"
|
||||||
|
#include "string_format.hpp"
|
||||||
|
|
||||||
|
using namespace portapack;
|
||||||
|
using namespace modems;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
void BLELoggerTx::log_raw_data(const std::string& data) {
|
||||||
|
log_file.write_entry(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasValidHexPairs(const std::string& str, int totalPairs) {
|
||||||
|
int validPairs = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < totalPairs * 2; i += 2) {
|
||||||
|
char c1 = str[i];
|
||||||
|
char c2 = str[i + 1];
|
||||||
|
|
||||||
|
if (!(std::isxdigit(c1) && std::isxdigit(c2))) {
|
||||||
|
return false; // Return false if any pair is invalid.
|
||||||
|
}
|
||||||
|
|
||||||
|
validPairs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (validPairs == totalPairs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> splitIntoStrings(const char* input) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
int length = std::strlen(input);
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
while (start < length) {
|
||||||
|
int remaining = length - start;
|
||||||
|
int chunkSize = (remaining > 30) ? 30 : remaining;
|
||||||
|
result.push_back(std::string(input + start, chunkSize));
|
||||||
|
start += chunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t stringToUint32(const std::string& str) {
|
||||||
|
size_t pos = 0;
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
while (pos < str.size() && std::isdigit(str[pos])) {
|
||||||
|
int digit = str[pos] - '0';
|
||||||
|
|
||||||
|
// Check for overflow before adding the next digit
|
||||||
|
if (result > (UINT32_MAX - digit) / 10) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result * 10 + digit;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any non-digit characters left
|
||||||
|
if (pos < str.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readUntilSpace(File& file, char* result, std::size_t maxBufferSize) {
|
||||||
|
std::size_t bytesRead = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char ch;
|
||||||
|
File::Result<File::Size> readResult = file.read(&ch, 1);
|
||||||
|
|
||||||
|
if (readResult.is_ok() && readResult.value() > 0) {
|
||||||
|
if (ch == ' ') {
|
||||||
|
// Found a space character, stop reading
|
||||||
|
break;
|
||||||
|
} else if (bytesRead < maxBufferSize) {
|
||||||
|
// Append the character to the result if there's space
|
||||||
|
result[bytesRead++] = ch;
|
||||||
|
} else {
|
||||||
|
// Buffer is full, break to prevent overflow
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break; // End of file or error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null-terminate the result string
|
||||||
|
result[bytesRead] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
|
||||||
|
uint64_t freq_hz;
|
||||||
|
|
||||||
|
switch (channel_number) {
|
||||||
|
case 37:
|
||||||
|
freq_hz = 2'402'000'000ull;
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
freq_hz = 2'426'000'000ull;
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
freq_hz = 2'480'000'000ull;
|
||||||
|
break;
|
||||||
|
case 0 ... 10:
|
||||||
|
freq_hz = 2'404'000'000ull + channel_number * 2'000'000ull;
|
||||||
|
break;
|
||||||
|
case 11 ... 36:
|
||||||
|
freq_hz = 2'428'000'000ull + (channel_number - 11) * 2'000'000ull;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
freq_hz = UINT64_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq_hz;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
void BLETxView::focus() {
|
||||||
|
field_frequency.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BLETxView::is_active() const {
|
||||||
|
return (bool)is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::file_error() {
|
||||||
|
nav_.display_modal("Error", "File read error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::toggle() {
|
||||||
|
if (is_active()) {
|
||||||
|
stop();
|
||||||
|
} else {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::start() {
|
||||||
|
if (!is_active()) {
|
||||||
|
// Check if file is present before continuing.
|
||||||
|
File data_file;
|
||||||
|
|
||||||
|
auto error = data_file.open(file_path);
|
||||||
|
if (error) {
|
||||||
|
file_error();
|
||||||
|
check_loop.set_value(false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Send first or single packet.
|
||||||
|
progressbar.set_max(packet_count);
|
||||||
|
button_play.set_bitmap(&bitmap_stop);
|
||||||
|
baseband::set_btletx(channel_number, macAddress, advertisementData);
|
||||||
|
transmitter_model.enable();
|
||||||
|
|
||||||
|
is_running = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Send next packet.
|
||||||
|
baseband::set_btletx(channel_number, macAddress, advertisementData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((packet_counter % 10) == 0) {
|
||||||
|
text_packets_sent.set(to_string_dec_uint(packet_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_counter--;
|
||||||
|
|
||||||
|
progressbar.set_value(packet_count - packet_counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::stop() {
|
||||||
|
transmitter_model.disable();
|
||||||
|
progressbar.set_value(0);
|
||||||
|
button_play.set_bitmap(&bitmap_play);
|
||||||
|
check_loop.set_value(false);
|
||||||
|
text_packets_sent.set(to_string_dec_uint(packet_count));
|
||||||
|
packet_counter = packet_count;
|
||||||
|
is_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::on_tx_progress(const bool done) {
|
||||||
|
if (done) {
|
||||||
|
if (check_loop.value() && (packet_counter != 0) && is_active()) {
|
||||||
|
if ((timer_count % timer_period) == 0) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_active()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLETxView::BLETxView(NavigationView& nav)
|
||||||
|
: nav_{nav} {
|
||||||
|
baseband::run_image(portapack::spi_flash::image_tag_btle_tx);
|
||||||
|
|
||||||
|
add_children({&button_open,
|
||||||
|
&text_filename,
|
||||||
|
&progressbar,
|
||||||
|
&field_frequency,
|
||||||
|
&tx_view, // now it handles previous rfgain, rfamp.
|
||||||
|
&check_loop,
|
||||||
|
&button_play,
|
||||||
|
&label_speed,
|
||||||
|
&options_speed,
|
||||||
|
&options_channel,
|
||||||
|
&label_packets_sent,
|
||||||
|
&text_packets_sent,
|
||||||
|
&label_mac_address,
|
||||||
|
&text_mac_address,
|
||||||
|
&label_data_packet,
|
||||||
|
&console});
|
||||||
|
|
||||||
|
field_frequency.set_step(0);
|
||||||
|
|
||||||
|
button_play.on_select = [this](ImageButton&) {
|
||||||
|
this->toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
options_channel.on_change = [this](size_t, int32_t i) {
|
||||||
|
field_frequency.set_value(get_freq_by_channel_number(i));
|
||||||
|
channel_number = i;
|
||||||
|
};
|
||||||
|
|
||||||
|
options_speed.on_change = [this](size_t, int32_t i) {
|
||||||
|
timer_period = i;
|
||||||
|
};
|
||||||
|
|
||||||
|
options_speed.set_selected_index(0);
|
||||||
|
|
||||||
|
button_open.on_select = [this, &nav](Button&) {
|
||||||
|
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||||
|
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||||
|
on_file_changed(new_file_path);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::on_file_changed(const fs::path& new_file_path) {
|
||||||
|
file_path = fs::path(u"/") + new_file_path;
|
||||||
|
|
||||||
|
{ // Get the size of the data file.
|
||||||
|
File data_file;
|
||||||
|
auto error = data_file.open(file_path);
|
||||||
|
if (error) {
|
||||||
|
file_error();
|
||||||
|
file_path = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
readUntilSpace(data_file, macAddress, mac_address_size_str);
|
||||||
|
readUntilSpace(data_file, advertisementData, max_packet_size_str);
|
||||||
|
readUntilSpace(data_file, packetCount, max_packet_count_str);
|
||||||
|
|
||||||
|
uint64_t macAddressSize = strlen(macAddress);
|
||||||
|
uint64_t advertisementDataSize = strlen(advertisementData);
|
||||||
|
uint64_t packetCountSize = strlen(packetCount);
|
||||||
|
|
||||||
|
packet_count = stringToUint32(packetCount);
|
||||||
|
packet_counter = packet_count;
|
||||||
|
|
||||||
|
// Verify Data.
|
||||||
|
if ((macAddressSize == mac_address_size_str) && (advertisementDataSize < max_packet_size_str) && (packetCountSize < max_packet_count_str) &&
|
||||||
|
hasValidHexPairs(macAddress, macAddressSize / 2) && hasValidHexPairs(advertisementData, advertisementDataSize / 2) && (packet_count > 0) && (packet_count < UINT32_MAX)) {
|
||||||
|
text_packets_sent.set(to_string_dec_uint(packet_count));
|
||||||
|
|
||||||
|
std::string formattedMacAddress = to_string_formatted_mac_address(macAddress);
|
||||||
|
|
||||||
|
text_mac_address.set(formattedMacAddress);
|
||||||
|
|
||||||
|
std::vector<std::string> strings = splitIntoStrings(advertisementData);
|
||||||
|
|
||||||
|
console.clear(true);
|
||||||
|
|
||||||
|
for (const std::string& str : strings) {
|
||||||
|
console.writeln(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
text_filename.set(truncate(file_path.filename().string(), 12));
|
||||||
|
} else {
|
||||||
|
// file_error();
|
||||||
|
file_path = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::on_data(uint32_t value, bool is_data) {
|
||||||
|
std::string str_console = "";
|
||||||
|
|
||||||
|
if (is_data) {
|
||||||
|
str_console += (char)(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.write(str_console);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLETxView::set_parent_rect(const Rect new_parent_rect) {
|
||||||
|
View::set_parent_rect(new_parent_rect);
|
||||||
|
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
|
||||||
|
console.set_parent_rect(content_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLETxView::~BLETxView() {
|
||||||
|
transmitter_model.disable();
|
||||||
|
baseband::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace ui */
|
207
firmware/application/apps/ble_tx_app.hpp
Normal file
207
firmware/application/apps/ble_tx_app.hpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2017 Furrtek
|
||||||
|
* Copyright (C) 2023 TJ Baginski
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BLE_TX_APP_H__
|
||||||
|
#define __BLE_TX_APP_H__
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "ui_receiver.hpp"
|
||||||
|
#include "ui_transmitter.hpp"
|
||||||
|
#include "ui_freq_field.hpp"
|
||||||
|
#include "ui_record_view.hpp"
|
||||||
|
#include "app_settings.hpp"
|
||||||
|
#include "radio_state.hpp"
|
||||||
|
#include "replay_thread.hpp"
|
||||||
|
#include "log_file.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
#include "recent_entries.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class BLELoggerTx {
|
||||||
|
public:
|
||||||
|
Optional<File::Error> append(const std::string& filename) {
|
||||||
|
return log_file.append(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_raw_data(const std::string& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogFile log_file{};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
class BLETxView : public View {
|
||||||
|
public:
|
||||||
|
BLETxView(NavigationView& nav);
|
||||||
|
~BLETxView();
|
||||||
|
|
||||||
|
void set_parent_rect(const Rect new_parent_rect) override;
|
||||||
|
void paint(Painter&) override{};
|
||||||
|
|
||||||
|
void focus() override;
|
||||||
|
|
||||||
|
bool is_active() const;
|
||||||
|
void toggle();
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void handle_replay_thread_done(const uint32_t return_code);
|
||||||
|
void file_error();
|
||||||
|
|
||||||
|
std::string title() const override { return "BLE TX"; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void on_data(uint32_t value, bool is_data);
|
||||||
|
void on_tx_progress(const bool done);
|
||||||
|
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||||
|
|
||||||
|
NavigationView& nav_;
|
||||||
|
TxRadioState radio_state_{
|
||||||
|
2'402'000'000 /* frequency */,
|
||||||
|
4'000'000 /* bandwidth */,
|
||||||
|
4'000'000 /* sampling rate */
|
||||||
|
};
|
||||||
|
app_settings::SettingsManager settings_{
|
||||||
|
"ble_tx_app", app_settings::Mode::TX};
|
||||||
|
|
||||||
|
uint8_t console_color{0};
|
||||||
|
uint32_t prev_value{0};
|
||||||
|
|
||||||
|
std::filesystem::path file_path{};
|
||||||
|
uint8_t channel_number = 37;
|
||||||
|
char macAddress[13] = "010203040506";
|
||||||
|
char advertisementData[63] = "00112233445566778899AABBCCDDEEFF";
|
||||||
|
char packetCount[11] = "0";
|
||||||
|
|
||||||
|
bool is_running = false;
|
||||||
|
uint64_t timer_count{0};
|
||||||
|
uint64_t timer_period{256};
|
||||||
|
bool repeatLoop = false;
|
||||||
|
uint32_t packet_count{0};
|
||||||
|
uint32_t packet_counter{0};
|
||||||
|
|
||||||
|
static constexpr uint8_t mac_address_size_str{12};
|
||||||
|
static constexpr uint8_t max_packet_size_str{62};
|
||||||
|
static constexpr uint8_t max_packet_count_str{10};
|
||||||
|
static constexpr uint32_t max_packet_count{UINT32_MAX};
|
||||||
|
|
||||||
|
static constexpr auto header_height = 8 * 16;
|
||||||
|
|
||||||
|
Button button_open{
|
||||||
|
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
|
||||||
|
"Open file"};
|
||||||
|
|
||||||
|
Text text_filename{
|
||||||
|
{11 * 8, 0 * 16, 12 * 8, 16},
|
||||||
|
"-"};
|
||||||
|
|
||||||
|
ProgressBar progressbar{
|
||||||
|
{11 * 8, 1 * 16, 12 * 8, 16}};
|
||||||
|
|
||||||
|
TxFrequencyField field_frequency{
|
||||||
|
{0 * 8, 2 * 16},
|
||||||
|
nav_};
|
||||||
|
|
||||||
|
TransmitterView2 tx_view{
|
||||||
|
{11 * 8, 2 * 16},
|
||||||
|
/*short_ui*/ true};
|
||||||
|
|
||||||
|
Checkbox check_loop{
|
||||||
|
{21 * 8, 2 * 16},
|
||||||
|
4,
|
||||||
|
"Loop",
|
||||||
|
true};
|
||||||
|
|
||||||
|
ImageButton button_play{
|
||||||
|
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||||
|
&bitmap_play,
|
||||||
|
Color::green(),
|
||||||
|
Color::black()};
|
||||||
|
|
||||||
|
Labels label_speed{
|
||||||
|
{{0 * 8, 6 * 8}, "Speed:", Color::light_grey()}};
|
||||||
|
|
||||||
|
OptionsField options_speed{
|
||||||
|
{7 * 8, 6 * 8},
|
||||||
|
3,
|
||||||
|
{{"1 ", 256},
|
||||||
|
{"2 ", 128},
|
||||||
|
{"3 ", 64},
|
||||||
|
{"4 ", 32},
|
||||||
|
{"5 ", 16}}};
|
||||||
|
|
||||||
|
OptionsField options_channel{
|
||||||
|
{11 * 8, 6 * 8},
|
||||||
|
5,
|
||||||
|
{{"Ch.37 ", 37},
|
||||||
|
{"Ch.38", 38},
|
||||||
|
{"Ch.39", 39}}};
|
||||||
|
|
||||||
|
Labels label_packets_sent{
|
||||||
|
{{0 * 8, 10 * 8}, "Packets Left:", Color::light_grey()}};
|
||||||
|
|
||||||
|
Text text_packets_sent{
|
||||||
|
{13 * 8, 5 * 16, 10 * 8, 16},
|
||||||
|
"-"};
|
||||||
|
|
||||||
|
Labels label_mac_address{
|
||||||
|
{{0 * 8, 12 * 8}, "Mac Address:", Color::light_grey()}};
|
||||||
|
|
||||||
|
Text text_mac_address{
|
||||||
|
{12 * 8, 6 * 16, 20 * 8, 16},
|
||||||
|
"-"};
|
||||||
|
|
||||||
|
Labels label_data_packet{
|
||||||
|
{{0 * 8, 14 * 8}, "Packet Data:", Color::light_grey()}};
|
||||||
|
|
||||||
|
Console console{
|
||||||
|
{0, 7 * 16, 240, 240}};
|
||||||
|
|
||||||
|
std::string str_log{""};
|
||||||
|
bool logging{true};
|
||||||
|
bool logging_done{false};
|
||||||
|
|
||||||
|
std::unique_ptr<BLELoggerTx> logger{};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_packet{
|
||||||
|
Message::ID::AFSKData,
|
||||||
|
[this](Message* const p) {
|
||||||
|
const auto message = static_cast<const AFSKDataMessage*>(p);
|
||||||
|
this->on_data(message->value, message->is_data);
|
||||||
|
}};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_tx_progress{
|
||||||
|
Message::ID::TXProgress,
|
||||||
|
[this](const Message* const p) {
|
||||||
|
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||||
|
this->on_tx_progress(message.done);
|
||||||
|
}};
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace ui */
|
||||||
|
|
||||||
|
#endif /*__UI_AFSK_RX_H__*/
|
@ -155,6 +155,14 @@ void set_btle(uint8_t channel_number) {
|
|||||||
send_message(&message);
|
send_message(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_btletx(uint8_t channel_number, char* macAddress, char* advertisementData) {
|
||||||
|
const BTLETxConfigureMessage message{
|
||||||
|
channel_number,
|
||||||
|
macAddress,
|
||||||
|
advertisementData};
|
||||||
|
send_message(&message);
|
||||||
|
}
|
||||||
|
|
||||||
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
|
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
|
||||||
const NRFRxConfigureMessage message{
|
const NRFRxConfigureMessage message{
|
||||||
baudrate,
|
baudrate,
|
||||||
|
@ -73,6 +73,7 @@ void set_fsk(const size_t deviation);
|
|||||||
void set_aprs(const uint32_t baudrate);
|
void set_aprs(const uint32_t baudrate);
|
||||||
|
|
||||||
void set_btle(uint8_t channel_number);
|
void set_btle(uint8_t channel_number);
|
||||||
|
void set_btletx(uint8_t channel_number, char* macAddress, char* advertisementData);
|
||||||
|
|
||||||
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
|
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
|
||||||
|
|
||||||
|
@ -95,6 +95,20 @@ void sortEntriesBy(ContainerType& entries, KeySelector keySelector, SortOrder as
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename ContainerType, typename KeySelector>
|
||||||
|
void removeEntriesWithoutKey(ContainerType& entries, ContainerType& filteredEntries, KeySelector keySelector) {
|
||||||
|
// Clear the filteredEntries container
|
||||||
|
filteredEntries.clear();
|
||||||
|
|
||||||
|
auto it = entries.begin();
|
||||||
|
while (it != entries.end()) {
|
||||||
|
if (!keySelector(*it)) {
|
||||||
|
filteredEntries.emplace_back(*it); // Add a new entry to filteredEntries
|
||||||
|
}
|
||||||
|
++it; // Move to the next element, outside of the if block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
using RecentEntriesColumn = std::pair<std::string, size_t>;
|
using RecentEntriesColumn = std::pair<std::string, size_t>;
|
||||||
@ -279,6 +293,10 @@ class RecentEntriesView : public View {
|
|||||||
_table.focus();
|
_table.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_table(Entries& new_table) {
|
||||||
|
_table = new_table;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RecentEntriesHeader _header;
|
RecentEntriesHeader _header;
|
||||||
RecentEntriesTable<Entries> _table;
|
RecentEntriesTable<Entries> _table;
|
||||||
|
@ -323,6 +323,20 @@ std::string to_string_mac_address(const uint8_t* macAddress, uint8_t length) {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string to_string_formatted_mac_address(const char* macAddress) {
|
||||||
|
std::string formattedAddress;
|
||||||
|
|
||||||
|
for (int i = 0; i < 12; i += 2) {
|
||||||
|
if (i > 0) {
|
||||||
|
formattedAddress += ':';
|
||||||
|
}
|
||||||
|
formattedAddress += macAddress[i];
|
||||||
|
formattedAddress += macAddress[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedAddress;
|
||||||
|
}
|
||||||
|
|
||||||
std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision) {
|
std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision) {
|
||||||
const uint32_t powers_of_ten[5] = {1, 10, 100, 1000, 10000};
|
const uint32_t powers_of_ten[5] = {1, 10, 100, 1000, 10000};
|
||||||
std::string string{""};
|
std::string string{""};
|
||||||
|
@ -78,6 +78,7 @@ std::string to_string_file_size(uint32_t file_size);
|
|||||||
|
|
||||||
// Converts Mac Address to string.
|
// Converts Mac Address to string.
|
||||||
std::string to_string_mac_address(const uint8_t* macAddress, uint8_t length);
|
std::string to_string_mac_address(const uint8_t* macAddress, uint8_t length);
|
||||||
|
std::string to_string_formatted_mac_address(const char* macAddress);
|
||||||
|
|
||||||
/* Scales 'n' to be a value less than 1000. 'base_unit' is the index of the unit from
|
/* Scales 'n' to be a value less than 1000. 'base_unit' is the index of the unit from
|
||||||
* 'unit_prefix' that 'n' is in initially. 3 is the index of the '1s' unit. */
|
* 'unit_prefix' that 'n' is in initially. 3 is the index of the '1s' unit. */
|
||||||
|
@ -118,7 +118,12 @@ void AlphanumView::refresh_keys() {
|
|||||||
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (auto& button : buttons) {
|
for (auto& button : buttons) {
|
||||||
|
if (n > strlen(key_list)) {
|
||||||
|
button.set_text(std::string{'\0'});
|
||||||
|
} else {
|
||||||
button.set_text(std::string{key_list[n]});
|
button.set_text(std::string{key_list[n]});
|
||||||
|
}
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +142,9 @@ void AlphanumView::refresh_keys() {
|
|||||||
|
|
||||||
void AlphanumView::on_button(Button& button) {
|
void AlphanumView::on_button(Button& button) {
|
||||||
const auto c = button.text()[0];
|
const auto c = button.text()[0];
|
||||||
|
if (c != '\0') {
|
||||||
char_add(c);
|
char_add(c);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Consolidate shift handling.
|
// TODO: Consolidate shift handling.
|
||||||
if (shift_mode == ShiftMode::Shift) {
|
if (shift_mode == ShiftMode::Shift) {
|
||||||
|
@ -57,6 +57,7 @@ class AlphanumView : public TextEntryView {
|
|||||||
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, .";
|
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, .";
|
||||||
const char* const keys_digit = "1234567890()'`\"+-*/=<>_\\!?, .";
|
const char* const keys_digit = "1234567890()'`\"+-*/=<>_\\!?, .";
|
||||||
const char* const keys_symbl = "!@#$%^&*()[]'`\"{}|:;<>-_~?, .";
|
const char* const keys_symbl = "!@#$%^&*()[]'`\"{}|:;<>-_~?, .";
|
||||||
|
const char* const keys_hex = "1234567890ABCDEF ";
|
||||||
|
|
||||||
struct key_set_t {
|
struct key_set_t {
|
||||||
const char* name;
|
const char* name;
|
||||||
@ -64,9 +65,10 @@ class AlphanumView : public TextEntryView {
|
|||||||
const char* shifted;
|
const char* shifted;
|
||||||
};
|
};
|
||||||
|
|
||||||
const key_set_t key_sets[2] = {
|
const key_set_t key_sets[3] = {
|
||||||
{"abc", keys_lower, keys_upper},
|
{"abc", keys_lower, keys_upper},
|
||||||
{"123", keys_digit, keys_symbl}};
|
{"123", keys_digit, keys_symbl},
|
||||||
|
{"hex", keys_hex, keys_hex}};
|
||||||
|
|
||||||
int16_t focused_button = 0;
|
int16_t focused_button = 0;
|
||||||
uint32_t mode = 0; // Letters.
|
uint32_t mode = 0; // Letters.
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
#include "analog_audio_app.hpp"
|
#include "analog_audio_app.hpp"
|
||||||
#include "analog_tv_app.hpp"
|
#include "analog_tv_app.hpp"
|
||||||
#include "ble_app.hpp"
|
#include "ble_app.hpp"
|
||||||
|
#include "ble_tx_app.hpp"
|
||||||
#include "capture_app.hpp"
|
#include "capture_app.hpp"
|
||||||
#include "ert_app.hpp"
|
#include "ert_app.hpp"
|
||||||
#include "gps_sim_app.hpp"
|
#include "gps_sim_app.hpp"
|
||||||
@ -582,6 +583,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
|||||||
{"ADS-B TX", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
|
{"ADS-B TX", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
|
||||||
{"APRS TX", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
|
{"APRS TX", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
|
||||||
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
|
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
|
||||||
|
{"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLETxView>(); }},
|
||||||
{"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }},
|
{"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }},
|
||||||
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
|
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
|
||||||
{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }},
|
{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }},
|
||||||
|
@ -360,6 +360,14 @@ set(MODE_CPPSRC
|
|||||||
proc_btlerx.cpp
|
proc_btlerx.cpp
|
||||||
)
|
)
|
||||||
DeclareTargets(PBTR btlerx)
|
DeclareTargets(PBTR btlerx)
|
||||||
|
|
||||||
|
### BTLE TX
|
||||||
|
|
||||||
|
set(MODE_CPPSRC
|
||||||
|
proc_ble_tx.cpp
|
||||||
|
)
|
||||||
|
DeclareTargets(PBTT btletx)
|
||||||
|
|
||||||
### AIS
|
### AIS
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
|
422
firmware/baseband/proc_ble_tx.cpp
Normal file
422
firmware/baseband/proc_ble_tx.cpp
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2016 Furrtek
|
||||||
|
* Copyright (C) TJ Baginski
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "proc_ble_tx.hpp"
|
||||||
|
#include "portapack_shared_memory.hpp"
|
||||||
|
#include "sine_table_int8.hpp"
|
||||||
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define new_way
|
||||||
|
|
||||||
|
int BTLETxProcessor::gen_sample_from_phy_bit(char* bit, char* sample, int num_bit) {
|
||||||
|
int num_sample = (num_bit * SAMPLE_PER_SYMBOL) + (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL);
|
||||||
|
|
||||||
|
int8_t* tmp_phy_bit_over_sampling_int8 = (int8_t*)tmp_phy_bit_over_sampling;
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 1); i++) {
|
||||||
|
tmp_phy_bit_over_sampling_int8[i] = 0;
|
||||||
|
}
|
||||||
|
for (i = (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 1 + num_bit * SAMPLE_PER_SYMBOL); i < (2 * LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 2 + num_bit * SAMPLE_PER_SYMBOL); i++) {
|
||||||
|
tmp_phy_bit_over_sampling_int8[i] = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < (num_bit * SAMPLE_PER_SYMBOL); i++) {
|
||||||
|
if (i % SAMPLE_PER_SYMBOL == 0) {
|
||||||
|
tmp_phy_bit_over_sampling_int8[i + (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 1)] = (bit[i / SAMPLE_PER_SYMBOL]) * 2 - 1;
|
||||||
|
} else {
|
||||||
|
tmp_phy_bit_over_sampling_int8[i + (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 1)] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t tmp = 0;
|
||||||
|
sample[0] = cos_table_int8[tmp];
|
||||||
|
sample[1] = sin_table_int8[tmp];
|
||||||
|
|
||||||
|
int len_conv_result = num_sample - 1;
|
||||||
|
for (i = 0; i < len_conv_result; i++) {
|
||||||
|
int16_t acc = 0;
|
||||||
|
for (j = 3; j < (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL - 4); j++) {
|
||||||
|
acc = acc + gauss_coef_int8[(LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL) - j - 1] * tmp_phy_bit_over_sampling_int8[i + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = (tmp + acc) & 1023;
|
||||||
|
sample[(i + 1) * 2 + 0] = cos_table_int8[tmp];
|
||||||
|
sample[(i + 1) * 2 + 1] = sin_table_int8[tmp];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (num_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::octet_hex_to_bit(char* hex, char* bit) {
|
||||||
|
char tmp_hex[3];
|
||||||
|
|
||||||
|
tmp_hex[0] = hex[0];
|
||||||
|
tmp_hex[1] = hex[1];
|
||||||
|
tmp_hex[2] = 0;
|
||||||
|
|
||||||
|
int n = strtol(tmp_hex, NULL, 16);
|
||||||
|
|
||||||
|
bit[0] = 0x01 & (n >> 0);
|
||||||
|
bit[1] = 0x01 & (n >> 1);
|
||||||
|
bit[2] = 0x01 & (n >> 2);
|
||||||
|
bit[3] = 0x01 & (n >> 3);
|
||||||
|
bit[4] = 0x01 & (n >> 4);
|
||||||
|
bit[5] = 0x01 & (n >> 5);
|
||||||
|
bit[6] = 0x01 & (n >> 6);
|
||||||
|
bit[7] = 0x01 & (n >> 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BTLETxProcessor::convert_hex_to_bit(char* hex, char* bit, int stream_flip, int octet_limit) {
|
||||||
|
int num_hex_orig = strlen(hex);
|
||||||
|
|
||||||
|
int i, num_hex;
|
||||||
|
num_hex = num_hex_orig;
|
||||||
|
for (i = 0; i < num_hex_orig; i++) {
|
||||||
|
if (!((hex[i] >= 48 && hex[i] <= 57) || (hex[i] >= 65 && hex[i] <= 70) || (hex[i] >= 97 && hex[i] <= 102))) // not a hex
|
||||||
|
num_hex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_hex % 2 != 0) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_hex > (octet_limit * 2)) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (num_hex <= 1) { // NULL data
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp_str[max_char];
|
||||||
|
|
||||||
|
if (stream_flip == 1) {
|
||||||
|
strcpy(tmp_str, hex);
|
||||||
|
for (i = 0; i < num_hex; i = i + 2) {
|
||||||
|
hex[num_hex - i - 2] = tmp_str[i];
|
||||||
|
hex[num_hex - i - 1] = tmp_str[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_bit = num_hex * 4;
|
||||||
|
|
||||||
|
int j;
|
||||||
|
for (i = 0; i < num_hex; i = i + 2) {
|
||||||
|
j = i * 4;
|
||||||
|
octet_hex_to_bit(hex + i, bit + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (num_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::crc24(char* bit_in, int num_bit, char* init_hex, char* crc_result) {
|
||||||
|
char bit_store[24], bit_store_update[24];
|
||||||
|
int i;
|
||||||
|
convert_hex_to_bit(init_hex, bit_store, 0, 3);
|
||||||
|
|
||||||
|
for (i = 0; i < num_bit; i++) {
|
||||||
|
char new_bit = (bit_store[23] + bit_in[i]) % 2;
|
||||||
|
bit_store_update[0] = new_bit;
|
||||||
|
bit_store_update[1] = (bit_store[0] + new_bit) % 2;
|
||||||
|
bit_store_update[2] = bit_store[1];
|
||||||
|
bit_store_update[3] = (bit_store[2] + new_bit) % 2;
|
||||||
|
bit_store_update[4] = (bit_store[3] + new_bit) % 2;
|
||||||
|
bit_store_update[5] = bit_store[4];
|
||||||
|
bit_store_update[6] = (bit_store[5] + new_bit) % 2;
|
||||||
|
|
||||||
|
bit_store_update[7] = bit_store[6];
|
||||||
|
bit_store_update[8] = bit_store[7];
|
||||||
|
|
||||||
|
bit_store_update[9] = (bit_store[8] + new_bit) % 2;
|
||||||
|
bit_store_update[10] = (bit_store[9] + new_bit) % 2;
|
||||||
|
|
||||||
|
memcpy(bit_store_update + 11, bit_store + 10, 13);
|
||||||
|
|
||||||
|
memcpy(bit_store, bit_store_update, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 24; i++) {
|
||||||
|
crc_result[i] = bit_store[23 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::scramble(char* bit_in, int num_bit, int channel_number, char* bit_out) {
|
||||||
|
char bit_store[7], bit_store_update[7];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bit_store[0] = 1;
|
||||||
|
bit_store[1] = 0x01 & (channel_number >> 5);
|
||||||
|
bit_store[2] = 0x01 & (channel_number >> 4);
|
||||||
|
bit_store[3] = 0x01 & (channel_number >> 3);
|
||||||
|
bit_store[4] = 0x01 & (channel_number >> 2);
|
||||||
|
bit_store[5] = 0x01 & (channel_number >> 1);
|
||||||
|
bit_store[6] = 0x01 & (channel_number >> 0);
|
||||||
|
|
||||||
|
for (i = 0; i < num_bit; i++) {
|
||||||
|
bit_out[i] = (bit_store[6] + bit_in[i]) % 2;
|
||||||
|
|
||||||
|
bit_store_update[0] = bit_store[6];
|
||||||
|
|
||||||
|
bit_store_update[1] = bit_store[0];
|
||||||
|
bit_store_update[2] = bit_store[1];
|
||||||
|
bit_store_update[3] = bit_store[2];
|
||||||
|
|
||||||
|
bit_store_update[4] = (bit_store[3] + bit_store[6]) % 2;
|
||||||
|
|
||||||
|
bit_store_update[5] = bit_store[4];
|
||||||
|
bit_store_update[6] = bit_store[5];
|
||||||
|
|
||||||
|
memcpy(bit_store, bit_store_update, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::disp_bit_in_hex(char* bit, int num_bit) {
|
||||||
|
int i, a;
|
||||||
|
|
||||||
|
for (i = 0; i < num_bit; i = i + 8) {
|
||||||
|
a = bit[i] + bit[i + 1] * 2 + bit[i + 2] * 4 + bit[i + 3] * 8 + bit[i + 4] * 16 + bit[i + 5] * 32 + bit[i + 6] * 64 + bit[i + 7] * 128;
|
||||||
|
|
||||||
|
data_message.is_data = true;
|
||||||
|
data_message.value = (uint8_t)a;
|
||||||
|
shared_memory.application_queue.push(data_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::crc24_and_scramble_to_gen_phy_bit(char* crc_init_hex, PKT_INFO* pkt) {
|
||||||
|
crc24(pkt->info_bit + 5 * 8, pkt->num_info_bit - 5 * 8, crc_init_hex, pkt->info_bit + pkt->num_info_bit);
|
||||||
|
|
||||||
|
// disp_bit_in_hex(pkt->info_bit, pkt->num_info_bit + 3*8);
|
||||||
|
|
||||||
|
scramble(pkt->info_bit + 5 * 8, pkt->num_info_bit - 5 * 8 + 24, pkt->channel_number, pkt->phy_bit + 5 * 8);
|
||||||
|
memcpy(pkt->phy_bit, pkt->info_bit, 5 * 8);
|
||||||
|
pkt->num_phy_bit = pkt->num_info_bit + 24;
|
||||||
|
|
||||||
|
// disp_bit_in_hex(pkt->phy_bit, pkt->num_phy_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::fill_adv_pdu_header(PKT_INFO* pkt, int txadd, int rxadd, int payload_len, char* bit_out) {
|
||||||
|
if (pkt->pkt_type == ADV_IND || pkt->pkt_type == IBEACON) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 0;
|
||||||
|
bit_out[1] = 0;
|
||||||
|
bit_out[0] = 0;
|
||||||
|
} else if (pkt->pkt_type == ADV_DIRECT_IND) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 0;
|
||||||
|
bit_out[1] = 0;
|
||||||
|
bit_out[0] = 1;
|
||||||
|
} else if (pkt->pkt_type == ADV_NONCONN_IND || pkt->pkt_type == DISCOVERY) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 0;
|
||||||
|
bit_out[1] = 1;
|
||||||
|
bit_out[0] = 0;
|
||||||
|
} else if (pkt->pkt_type == SCAN_REQ) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 0;
|
||||||
|
bit_out[1] = 1;
|
||||||
|
bit_out[0] = 1;
|
||||||
|
} else if (pkt->pkt_type == SCAN_RSP) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 1;
|
||||||
|
bit_out[1] = 0;
|
||||||
|
bit_out[0] = 0;
|
||||||
|
} else if (pkt->pkt_type == CONNECT_REQ) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 1;
|
||||||
|
bit_out[1] = 0;
|
||||||
|
bit_out[0] = 1;
|
||||||
|
} else if (pkt->pkt_type == ADV_SCAN_IND) {
|
||||||
|
bit_out[3] = 0;
|
||||||
|
bit_out[2] = 1;
|
||||||
|
bit_out[1] = 1;
|
||||||
|
bit_out[0] = 0;
|
||||||
|
} else {
|
||||||
|
bit_out[3] = 1;
|
||||||
|
bit_out[2] = 1;
|
||||||
|
bit_out[1] = 1;
|
||||||
|
bit_out[0] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_out[4] = 0;
|
||||||
|
bit_out[5] = 0;
|
||||||
|
|
||||||
|
bit_out[6] = txadd;
|
||||||
|
bit_out[7] = rxadd;
|
||||||
|
|
||||||
|
bit_out[8] = 0x01 & (payload_len >> 0);
|
||||||
|
bit_out[9] = 0x01 & (payload_len >> 1);
|
||||||
|
bit_out[10] = 0x01 & (payload_len >> 2);
|
||||||
|
bit_out[11] = 0x01 & (payload_len >> 3);
|
||||||
|
bit_out[12] = 0x01 & (payload_len >> 4);
|
||||||
|
bit_out[13] = 0x01 & (payload_len >> 5);
|
||||||
|
|
||||||
|
bit_out[14] = 0;
|
||||||
|
bit_out[15] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BTLETxProcessor::calculate_sample_for_ADV_IND(PKT_INFO* pkt) {
|
||||||
|
pkt->num_info_bit = 0;
|
||||||
|
|
||||||
|
// gen preamble and access address
|
||||||
|
const char* AA = "AA";
|
||||||
|
const char* AAValue = "D6BE898E";
|
||||||
|
pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit((char*)AA, pkt->info_bit, 0, 1);
|
||||||
|
pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit((char*)AAValue, pkt->info_bit + pkt->num_info_bit, 0, 4);
|
||||||
|
|
||||||
|
// get txadd and rxadd
|
||||||
|
int txadd = 1, rxadd = 0;
|
||||||
|
|
||||||
|
pkt->num_info_bit = pkt->num_info_bit + 16; // 16 is header length
|
||||||
|
|
||||||
|
// get AdvA and AdvData
|
||||||
|
pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit(macAddress, pkt->info_bit + pkt->num_info_bit, 1, 6);
|
||||||
|
|
||||||
|
pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit(advertisementData, pkt->info_bit + pkt->num_info_bit, 0, 31);
|
||||||
|
|
||||||
|
int payload_len = (pkt->num_info_bit / 8) - 7;
|
||||||
|
|
||||||
|
fill_adv_pdu_header(pkt, txadd, rxadd, payload_len, pkt->info_bit + 5 * 8);
|
||||||
|
const char* checksumInit = "555555";
|
||||||
|
crc24_and_scramble_to_gen_phy_bit((char*)checksumInit, pkt);
|
||||||
|
|
||||||
|
#ifdef new_way
|
||||||
|
pkt->num_phy_sample = gen_sample_from_phy_bit(pkt->phy_bit, pkt->phy_sample, pkt->num_phy_bit);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disp_bit_in_hex(pkt->phy_sample, pkt->num_phy_sample);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BTLETxProcessor::calculate_sample_from_pkt_type(PKT_INFO* pkt) {
|
||||||
|
// Todo: Handle other Enum values.
|
||||||
|
// if (packetType == ADV_IND);
|
||||||
|
|
||||||
|
if (calculate_sample_for_ADV_IND(pkt) == -1) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BTLETxProcessor::calculate_pkt_info(PKT_INFO* pkt) {
|
||||||
|
if (calculate_sample_from_pkt_type(pkt) == -1) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::execute(const buffer_c8_t& buffer) {
|
||||||
|
int8_t re, im;
|
||||||
|
|
||||||
|
// This is called at 4M/2048 = 1953Hz
|
||||||
|
for (size_t i = 0; i < buffer.count; i++) {
|
||||||
|
if (configured) {
|
||||||
|
// This is going to loop through each sample bit and push it to the output buffer.
|
||||||
|
if (sample_count > length) {
|
||||||
|
configured = false;
|
||||||
|
sample_count = 0;
|
||||||
|
} else {
|
||||||
|
// Real and imaginary was already calculated in gen_sample_from_phy_bit.
|
||||||
|
// It was processed from each data bit, run through a Gaussian Filter, and then ran through sin and cos table to get each IQ bit.
|
||||||
|
re = (int8_t)packets.phy_sample[sample_count++];
|
||||||
|
im = (int8_t)packets.phy_sample[sample_count++];
|
||||||
|
|
||||||
|
buffer.p[i] = {re, im};
|
||||||
|
|
||||||
|
if (progress_count >= progress_notice) {
|
||||||
|
progress_count = 0;
|
||||||
|
txprogress_message.progress++;
|
||||||
|
txprogress_message.done = false;
|
||||||
|
shared_memory.application_queue.push(txprogress_message);
|
||||||
|
} else {
|
||||||
|
progress_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
re = 0;
|
||||||
|
im = 0;
|
||||||
|
|
||||||
|
buffer.p[i] = {re, im};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txprogress_message.done = true;
|
||||||
|
shared_memory.application_queue.push(txprogress_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::on_message(const Message* const message) {
|
||||||
|
if (message->id == Message::ID::BTLETxConfigure) {
|
||||||
|
configure(*reinterpret_cast<const BTLETxConfigureMessage*>(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTLETxProcessor::configure(const BTLETxConfigureMessage& message) {
|
||||||
|
channel_number = message.channel_number;
|
||||||
|
|
||||||
|
memcpy(macAddress, message.macAddress, sizeof(macAddress));
|
||||||
|
memcpy(advertisementData, message.advertisementData, sizeof(advertisementData));
|
||||||
|
|
||||||
|
packets.channel_number = channel_number;
|
||||||
|
packets.pkt_type = packetType;
|
||||||
|
|
||||||
|
// Calculates the samples based on the BLE packet data and generates IQ values into an array to be sent out.
|
||||||
|
calculate_pkt_info(&packets);
|
||||||
|
|
||||||
|
// Todo: determine if we need to copy these values to shared_memory.bb_data.data. I suspect that we might.
|
||||||
|
// I think once we add the UI level, only the generated BLE payload will be sent down. This layer will take care of PHY sample generation
|
||||||
|
// using the bits sent in by the UI level. In short, we will seperate this implementation to the UI level.
|
||||||
|
// memcpy(shared_memory.bb_data.data, (uint8_t *)packets.phy_sample, packets.num_phy_sample);
|
||||||
|
|
||||||
|
// 2 Sample buffers for each IQ data.
|
||||||
|
samples_per_bit = 8;
|
||||||
|
|
||||||
|
#ifdef new_way
|
||||||
|
// This is because each sample contains I and Q, but packet.num_phy_samples just returns the total samples.
|
||||||
|
length = (uint32_t)(packets.num_phy_sample * 2);
|
||||||
|
#else
|
||||||
|
length = (uint32_t)packets.num_phy_bit;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 200kHz shift frequency of BLE
|
||||||
|
shift_one = 200000 * (0xFFFFFFFFULL / 4000000);
|
||||||
|
shift_zero = -shift_one;
|
||||||
|
|
||||||
|
// Starting at sample_count 0 since packets.num_phy_sample contains every sample needed to be sent out.
|
||||||
|
sample_count = 0;
|
||||||
|
progress_count = 0;
|
||||||
|
progress_notice = 64;
|
||||||
|
|
||||||
|
txprogress_message.progress = 0;
|
||||||
|
txprogress_message.done = false;
|
||||||
|
configured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
EventDispatcher event_dispatcher{std::make_unique<BTLETxProcessor>()};
|
||||||
|
event_dispatcher.run();
|
||||||
|
return 0;
|
||||||
|
}
|
239
firmware/baseband/proc_ble_tx.hpp
Normal file
239
firmware/baseband/proc_ble_tx.hpp
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2016 Furrtek
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PROC_BLE_TX_H__
|
||||||
|
#define __PROC_BLE_TX_H__
|
||||||
|
|
||||||
|
#include "baseband_processor.hpp"
|
||||||
|
#include "baseband_thread.hpp"
|
||||||
|
|
||||||
|
class BTLETxProcessor : public BasebandProcessor {
|
||||||
|
public:
|
||||||
|
void execute(const buffer_c8_t& buffer) override;
|
||||||
|
void on_message(const Message* const message) override;
|
||||||
|
void configure(const BTLETxConfigureMessage& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int max_char{256};
|
||||||
|
static constexpr int SAMPLE_PER_SYMBOL{4};
|
||||||
|
static constexpr float AMPLITUDE{127.0};
|
||||||
|
static constexpr float MOD_IDX{0.5};
|
||||||
|
static constexpr int LEN_GAUSS_FILTER{4};
|
||||||
|
static constexpr int MAX_NUM_INFO_BYTE{43};
|
||||||
|
static constexpr int MAX_NUM_PHY_BYTE{47};
|
||||||
|
static constexpr int MAX_NUM_PHY_SAMPLE{(MAX_NUM_PHY_BYTE * 8 * SAMPLE_PER_SYMBOL) + (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL)};
|
||||||
|
|
||||||
|
enum PKT_TYPE {
|
||||||
|
INVALID_TYPE,
|
||||||
|
RAW,
|
||||||
|
DISCOVERY,
|
||||||
|
IBEACON,
|
||||||
|
ADV_IND,
|
||||||
|
ADV_DIRECT_IND,
|
||||||
|
ADV_NONCONN_IND,
|
||||||
|
ADV_SCAN_IND,
|
||||||
|
SCAN_REQ,
|
||||||
|
SCAN_RSP,
|
||||||
|
CONNECT_REQ,
|
||||||
|
LL_DATA,
|
||||||
|
LL_CONNECTION_UPDATE_REQ,
|
||||||
|
LL_CHANNEL_MAP_REQ,
|
||||||
|
LL_TERMINATE_IND,
|
||||||
|
LL_ENC_REQ,
|
||||||
|
LL_ENC_RSP,
|
||||||
|
LL_START_ENC_REQ,
|
||||||
|
LL_START_ENC_RSP,
|
||||||
|
LL_UNKNOWN_RSP,
|
||||||
|
LL_FEATURE_REQ,
|
||||||
|
LL_FEATURE_RSP,
|
||||||
|
LL_PAUSE_ENC_REQ,
|
||||||
|
LL_PAUSE_ENC_RSP,
|
||||||
|
LL_VERSION_IND,
|
||||||
|
LL_REJECT_IND,
|
||||||
|
NUM_PKT_TYPE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PKT_INFO {
|
||||||
|
int channel_number;
|
||||||
|
PKT_TYPE pkt_type;
|
||||||
|
|
||||||
|
int num_info_bit;
|
||||||
|
char info_bit[MAX_NUM_PHY_BYTE * 8]; // without CRC and whitening
|
||||||
|
|
||||||
|
int num_info_byte;
|
||||||
|
uint8_t info_byte[MAX_NUM_PHY_BYTE];
|
||||||
|
|
||||||
|
int num_phy_bit;
|
||||||
|
char phy_bit[MAX_NUM_PHY_BYTE * 8]; // all bits which will be fed to GFSK modulator
|
||||||
|
|
||||||
|
int num_phy_byte;
|
||||||
|
uint8_t phy_byte[MAX_NUM_PHY_BYTE];
|
||||||
|
|
||||||
|
int num_phy_sample;
|
||||||
|
char phy_sample[2 * MAX_NUM_PHY_SAMPLE]; // GFSK output to D/A (hackrf board)
|
||||||
|
int8_t phy_sample1[2 * MAX_NUM_PHY_SAMPLE]; // GFSK output to D/A (hackrf board)
|
||||||
|
|
||||||
|
int space; // how many millisecond null signal shouwl be padded after this packet
|
||||||
|
};
|
||||||
|
|
||||||
|
PKT_INFO packets{};
|
||||||
|
|
||||||
|
int calculate_pkt_info(PKT_INFO* pkt);
|
||||||
|
int calculate_sample_from_pkt_type(PKT_INFO* pkt);
|
||||||
|
int calculate_sample_for_ADV_IND(PKT_INFO* pkt);
|
||||||
|
void fill_adv_pdu_header(PKT_INFO* pkt, int txadd, int rxadd, int payload_len, char* bit_out);
|
||||||
|
void crc24_and_scramble_to_gen_phy_bit(char* crc_init_hex, PKT_INFO* pkt);
|
||||||
|
void disp_bit_in_hex(char* bit, int num_bit);
|
||||||
|
void scramble(char* bit_in, int num_bit, int channel_number, char* bit_out);
|
||||||
|
void crc24(char* bit_in, int num_bit, char* init_hex, char* crc_result);
|
||||||
|
int convert_hex_to_bit(char* hex, char* bit, int stream_flip, int octet_limit);
|
||||||
|
void octet_hex_to_bit(char* hex, char* bit);
|
||||||
|
int gen_sample_from_phy_bit(char* bit, char* sample, int num_bit);
|
||||||
|
bool configured = false;
|
||||||
|
|
||||||
|
float tmp_phy_bit_over_sampling[MAX_NUM_PHY_SAMPLE + 2 * LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL];
|
||||||
|
float gauss_coef[LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL] = {7.561773e-09, 1.197935e-06, 8.050684e-05, 2.326833e-03, 2.959908e-02, 1.727474e-01, 4.999195e-01, 8.249246e-01, 9.408018e-01, 8.249246e-01, 4.999195e-01, 1.727474e-01, 2.959908e-02, 2.326833e-03, 8.050684e-05, 1.197935e-06};
|
||||||
|
|
||||||
|
uint32_t samples_per_bit{4};
|
||||||
|
uint32_t channel_number{37};
|
||||||
|
char macAddress[13] = "FFFFFFFFFF";
|
||||||
|
char advertisementData[63] = {0};
|
||||||
|
|
||||||
|
uint32_t length{0};
|
||||||
|
uint32_t shift_zero{}, shift_one{};
|
||||||
|
uint32_t progress_notice{}, progress_count{0};
|
||||||
|
uint32_t sample_count{0};
|
||||||
|
uint32_t phase{0}, sphase{0};
|
||||||
|
|
||||||
|
uint8_t cur_bit{0};
|
||||||
|
uint16_t bit_pos{0};
|
||||||
|
|
||||||
|
uint16_t process{0};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
const int8_t gauss_coef_int8[16] = {
|
||||||
|
0, 0, 0, 0, 2, 11, 32, 53, 60, 53, 32, 11, 2, 0, 0, 0, };
|
||||||
|
|
||||||
|
const int8_t cos_table_int8[1024] = {
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126,
|
||||||
|
126, 126, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 122, 122, 122, 122,
|
||||||
|
122, 121, 121, 121, 121, 120, 120, 120, 120, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 116, 116, 116, 115, 115,
|
||||||
|
115, 114, 114, 114, 113, 113, 113, 112, 112, 112, 111, 111, 111, 110, 110, 109, 109, 109, 108, 108, 107, 107, 106, 106,
|
||||||
|
106, 105, 105, 104, 104, 103, 103, 102, 102, 102, 101, 101, 100, 100, 99, 99, 98, 98, 97, 97, 96, 96, 95, 95,
|
||||||
|
94, 94, 93, 93, 92, 91, 91, 90, 90, 89, 89, 88, 88, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81,
|
||||||
|
81, 80, 79, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 71, 70, 69, 69, 68, 67, 67, 66,
|
||||||
|
65, 65, 64, 63, 63, 62, 61, 61, 60, 59, 58, 58, 57, 56, 56, 55, 54, 54, 53, 52, 51, 51, 50, 49,
|
||||||
|
49, 48, 47, 46, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 32, 32,
|
||||||
|
31, 30, 29, 29, 28, 27, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13,
|
||||||
|
12, 12, 11, 10, 9, 9, 8, 7, 6, 5, 5, 4, 3, 2, 2, 1, 0, -1, -2, -2, -3, -4, -5, -5,
|
||||||
|
-6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24,
|
||||||
|
-25, -26, -26, -27, -28, -29, -29, -30, -31, -32, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42,
|
||||||
|
-43, -44, -44, -45, -46, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59,
|
||||||
|
-60, -61, -61, -62, -63, -63, -64, -65, -65, -66, -67, -67, -68, -69, -69, -70, -71, -71, -72, -72, -73, -74, -74, -75,
|
||||||
|
-76, -76, -77, -78, -78, -79, -79, -80, -81, -81, -82, -82, -83, -84, -84, -85, -85, -86, -86, -87, -88, -88, -89, -89,
|
||||||
|
-90, -90, -91, -91, -92, -93, -93, -94, -94, -95, -95, -96, -96, -97, -97, -98, -98, -99, -99, -100, -100, -101, -101, -102,
|
||||||
|
-102, -102, -103, -103, -104, -104, -105, -105, -106, -106, -106, -107, -107, -108, -108, -109, -109, -109, -110, -110, -111, -111, -111, -112,
|
||||||
|
-112, -112, -113, -113, -113, -114, -114, -114, -115, -115, -115, -116, -116, -116, -117, -117, -117, -118, -118, -118, -118, -119, -119, -119,
|
||||||
|
-120, -120, -120, -120, -121, -121, -121, -121, -122, -122, -122, -122, -122, -123, -123, -123, -123, -123, -124, -124, -124, -124, -124, -124,
|
||||||
|
-125, -125, -125, -125, -125, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127,
|
||||||
|
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -126,
|
||||||
|
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -124, -124, -123,
|
||||||
|
-123, -123, -123, -123, -122, -122, -122, -122, -122, -121, -121, -121, -121, -120, -120, -120, -120, -119, -119, -119, -118, -118, -118, -118,
|
||||||
|
-117, -117, -117, -116, -116, -116, -115, -115, -115, -114, -114, -114, -113, -113, -113, -112, -112, -112, -111, -111, -111, -110, -110, -109,
|
||||||
|
-109, -109, -108, -108, -107, -107, -106, -106, -106, -105, -105, -104, -104, -103, -103, -102, -102, -102, -101, -101, -100, -100, -99, -99,
|
||||||
|
-98, -98, -97, -97, -96, -96, -95, -95, -94, -94, -93, -93, -92, -91, -91, -90, -90, -89, -89, -88, -88, -87, -86, -86,
|
||||||
|
-85, -85, -84, -84, -83, -82, -82, -81, -81, -80, -79, -79, -78, -78, -77, -76, -76, -75, -74, -74, -73, -72, -72, -71,
|
||||||
|
-71, -70, -69, -69, -68, -67, -67, -66, -65, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -56, -56, -55,
|
||||||
|
-54, -54, -53, -52, -51, -51, -50, -49, -49, -48, -47, -46, -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -38, -38,
|
||||||
|
-37, -36, -35, -35, -34, -33, -32, -32, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19,
|
||||||
|
-19, -18, -17, -16, -16, -15, -14, -13, -12, -12, -11, -10, -9, -9, -8, -7, -6, -5, -5, -4, -3, -2, -2, -1,
|
||||||
|
0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18,
|
||||||
|
19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||||
|
37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, 52, 53, 54,
|
||||||
|
54, 55, 56, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 63, 64, 65, 65, 66, 67, 67, 68, 69, 69, 70,
|
||||||
|
71, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85,
|
||||||
|
85, 86, 86, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98,
|
||||||
|
98, 99, 99, 100, 100, 101, 101, 102, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 106, 107, 107, 108, 108, 109,
|
||||||
|
109, 109, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 116, 116, 116, 117, 117,
|
||||||
|
117, 118, 118, 118, 118, 119, 119, 119, 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123,
|
||||||
|
123, 123, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126,
|
||||||
|
126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, };
|
||||||
|
|
||||||
|
const int8_t sin_table_int8[1024] = {
|
||||||
|
0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18,
|
||||||
|
19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||||
|
37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, 52, 53, 54,
|
||||||
|
54, 55, 56, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 63, 64, 65, 65, 66, 67, 67, 68, 69, 69, 70,
|
||||||
|
71, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85,
|
||||||
|
85, 86, 86, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98,
|
||||||
|
98, 99, 99, 100, 100, 101, 101, 102, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 106, 107, 107, 108, 108, 109,
|
||||||
|
109, 109, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 116, 116, 116, 117, 117,
|
||||||
|
117, 118, 118, 118, 118, 119, 119, 119, 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123,
|
||||||
|
123, 123, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126,
|
||||||
|
126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125,
|
||||||
|
125, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 121, 121, 121, 121, 120, 120, 120,
|
||||||
|
120, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 116, 116, 116, 115, 115, 115, 114, 114, 114, 113, 113, 113, 112,
|
||||||
|
112, 112, 111, 111, 111, 110, 110, 109, 109, 109, 108, 108, 107, 107, 106, 106, 106, 105, 105, 104, 104, 103, 103, 102,
|
||||||
|
102, 102, 101, 101, 100, 100, 99, 99, 98, 98, 97, 97, 96, 96, 95, 95, 94, 94, 93, 93, 92, 91, 91, 90,
|
||||||
|
90, 89, 89, 88, 88, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 80, 79, 79, 78, 78, 77, 76,
|
||||||
|
76, 75, 74, 74, 73, 72, 72, 71, 71, 70, 69, 69, 68, 67, 67, 66, 65, 65, 64, 63, 63, 62, 61, 61,
|
||||||
|
60, 59, 58, 58, 57, 56, 56, 55, 54, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 46, 46, 45, 44, 44,
|
||||||
|
43, 42, 41, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 32, 32, 31, 30, 29, 29, 28, 27, 26, 26,
|
||||||
|
25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13, 12, 12, 11, 10, 9, 9, 8, 7,
|
||||||
|
6, 5, 5, 4, 3, 2, 2, 1, 0, -1, -2, -2, -3, -4, -5, -5, -6, -7, -8, -9, -9, -10, -11, -12,
|
||||||
|
-12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30,
|
||||||
|
-31, -32, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48,
|
||||||
|
-49, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59, -60, -61, -61, -62, -63, -63, -64, -65,
|
||||||
|
-65, -66, -67, -67, -68, -69, -69, -70, -71, -71, -72, -72, -73, -74, -74, -75, -76, -76, -77, -78, -78, -79, -79, -80,
|
||||||
|
-81, -81, -82, -82, -83, -84, -84, -85, -85, -86, -86, -87, -88, -88, -89, -89, -90, -90, -91, -91, -92, -93, -93, -94,
|
||||||
|
-94, -95, -95, -96, -96, -97, -97, -98, -98, -99, -99, -100, -100, -101, -101, -102, -102, -102, -103, -103, -104, -104, -105, -105,
|
||||||
|
-106, -106, -106, -107, -107, -108, -108, -109, -109, -109, -110, -110, -111, -111, -111, -112, -112, -112, -113, -113, -113, -114, -114, -114,
|
||||||
|
-115, -115, -115, -116, -116, -116, -117, -117, -117, -118, -118, -118, -118, -119, -119, -119, -120, -120, -120, -120, -121, -121, -121, -121,
|
||||||
|
-122, -122, -122, -122, -122, -123, -123, -123, -123, -123, -124, -124, -124, -124, -124, -124, -125, -125, -125, -125, -125, -125, -125, -126,
|
||||||
|
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
|
||||||
|
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -126, -126, -126, -126, -126, -126, -126, -126, -126,
|
||||||
|
-126, -126, -125, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -124, -124, -123, -123, -123, -123, -123, -122, -122, -122, -122,
|
||||||
|
-122, -121, -121, -121, -121, -120, -120, -120, -120, -119, -119, -119, -118, -118, -118, -118, -117, -117, -117, -116, -116, -116, -115, -115,
|
||||||
|
-115, -114, -114, -114, -113, -113, -113, -112, -112, -112, -111, -111, -111, -110, -110, -109, -109, -109, -108, -108, -107, -107, -106, -106,
|
||||||
|
-106, -105, -105, -104, -104, -103, -103, -102, -102, -102, -101, -101, -100, -100, -99, -99, -98, -98, -97, -97, -96, -96, -95, -95,
|
||||||
|
-94, -94, -93, -93, -92, -91, -91, -90, -90, -89, -89, -88, -88, -87, -86, -86, -85, -85, -84, -84, -83, -82, -82, -81,
|
||||||
|
-81, -80, -79, -79, -78, -78, -77, -76, -76, -75, -74, -74, -73, -72, -72, -71, -71, -70, -69, -69, -68, -67, -67, -66,
|
||||||
|
-65, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49,
|
||||||
|
-49, -48, -47, -46, -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -32,
|
||||||
|
-31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -13,
|
||||||
|
-12, -12, -11, -10, -9, -9, -8, -7, -6, -5, -5, -4, -3, -2, -2, -1, };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
PKT_TYPE packetType = ADV_IND;
|
||||||
|
|
||||||
|
TXProgressMessage txprogress_message{};
|
||||||
|
|
||||||
|
/* NB: Threads should be the last members in the class definition. */
|
||||||
|
|
||||||
|
// BasebandThread baseband_thread{4000000, this, baseband::Direction::Transmit};
|
||||||
|
// Rx for now because trying to test formulation of packet.
|
||||||
|
AFSKDataMessage data_message{false, 0};
|
||||||
|
BasebandThread baseband_thread{4000000, this, baseband::Direction::Transmit};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -114,6 +114,7 @@ class Message {
|
|||||||
POCSAGStats = 57,
|
POCSAGStats = 57,
|
||||||
FSKRxConfigure = 58,
|
FSKRxConfigure = 58,
|
||||||
BlePacket = 58,
|
BlePacket = 58,
|
||||||
|
BTLETxConfigure = 59,
|
||||||
MAX
|
MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -755,6 +756,22 @@ class BTLERxConfigureMessage : public Message {
|
|||||||
const uint8_t channel_number;
|
const uint8_t channel_number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BTLETxConfigureMessage : public Message {
|
||||||
|
public:
|
||||||
|
constexpr BTLETxConfigureMessage(
|
||||||
|
const uint8_t channel_number,
|
||||||
|
char* macAddress,
|
||||||
|
char* advertisementData)
|
||||||
|
: Message{ID::BTLETxConfigure},
|
||||||
|
channel_number(channel_number),
|
||||||
|
macAddress(macAddress),
|
||||||
|
advertisementData(advertisementData) {
|
||||||
|
}
|
||||||
|
const uint8_t channel_number;
|
||||||
|
char* macAddress;
|
||||||
|
char* advertisementData;
|
||||||
|
};
|
||||||
|
|
||||||
class NRFRxConfigureMessage : public Message {
|
class NRFRxConfigureMessage : public Message {
|
||||||
public:
|
public:
|
||||||
constexpr NRFRxConfigureMessage(
|
constexpr NRFRxConfigureMessage(
|
||||||
|
@ -78,6 +78,7 @@ constexpr image_tag_t image_tag_adsb_rx{'P', 'A', 'D', 'R'};
|
|||||||
constexpr image_tag_t image_tag_afsk_rx{'P', 'A', 'F', 'R'};
|
constexpr image_tag_t image_tag_afsk_rx{'P', 'A', 'F', 'R'};
|
||||||
constexpr image_tag_t image_tag_aprs_rx{'P', 'A', 'P', 'R'};
|
constexpr image_tag_t image_tag_aprs_rx{'P', 'A', 'P', 'R'};
|
||||||
constexpr image_tag_t image_tag_btle_rx{'P', 'B', 'T', 'R'};
|
constexpr image_tag_t image_tag_btle_rx{'P', 'B', 'T', 'R'};
|
||||||
|
constexpr image_tag_t image_tag_btle_tx{'P', 'B', 'T', 'T'};
|
||||||
constexpr image_tag_t image_tag_nrf_rx{'P', 'N', 'R', 'R'};
|
constexpr image_tag_t image_tag_nrf_rx{'P', 'N', 'R', 'R'};
|
||||||
constexpr image_tag_t image_tag_ais{'P', 'A', 'I', 'S'};
|
constexpr image_tag_t image_tag_ais{'P', 'A', 'I', 'S'};
|
||||||
constexpr image_tag_t image_tag_am_audio{'P', 'A', 'M', 'A'};
|
constexpr image_tag_t image_tag_am_audio{'P', 'A', 'M', 'A'};
|
||||||
|
1
sdcard/BLETX/BLE Tx Sample.txt
Normal file
1
sdcard/BLETX/BLE Tx Sample.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
010203040506 00112233445566778899AABBCCDDEEFF 1000
|
Loading…
Reference in New Issue
Block a user