mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-25 07:19:28 -05:00
Added tabs in OOK encoders app
Simplified credits scrolling
This commit is contained in:
parent
6fcb2efdcf
commit
cd6a1a7f3f
@ -29,6 +29,7 @@
|
||||
namespace encoders {
|
||||
|
||||
#define ENC_TYPES_COUNT 14
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
struct encoder_def_t {
|
||||
std::string name; // Encoder chip ref/name
|
||||
|
@ -38,111 +38,100 @@ using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
// This is pretty much WaterfallView but in the opposite direction
|
||||
CreditsWidget::CreditsWidget(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
}
|
||||
|
||||
void CreditsWidget::paint(Painter&) {
|
||||
}
|
||||
|
||||
void CreditsWidget::on_show() {
|
||||
clear();
|
||||
|
||||
const auto screen_r = screen_rect();
|
||||
display.scroll_set_area(screen_r.top(), screen_r.bottom());
|
||||
}
|
||||
|
||||
void CreditsWidget::on_hide() {
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void CreditsWidget::new_row(
|
||||
const std::array<Color, 240>& pixel_row
|
||||
) {
|
||||
const auto draw_y = display.scroll(-1);
|
||||
|
||||
display.draw_pixels(
|
||||
{ { 0, draw_y }, { 240, 1 } },
|
||||
pixel_row
|
||||
);
|
||||
}
|
||||
|
||||
void CreditsWidget::clear() {
|
||||
display.fill_rectangle(
|
||||
screen_rect(),
|
||||
Color::black()
|
||||
);
|
||||
}
|
||||
|
||||
void AboutView::update() {
|
||||
size_t c;
|
||||
int32_t n;
|
||||
std::string text;
|
||||
Coord y_val, x_pos = 0;
|
||||
uint32_t flag;
|
||||
size_t i = 0;
|
||||
std::array<Color, 240> pixel_row;
|
||||
|
||||
if (scroll & 1) {
|
||||
if (!((scroll >> 1) & 15)) {
|
||||
if (line_feed) {
|
||||
line_feed = false;
|
||||
slow_down++;
|
||||
if (slow_down & 1) return;
|
||||
|
||||
if (!timer) {
|
||||
if (loop) {
|
||||
credits_index = 0;
|
||||
loop = false;
|
||||
}
|
||||
|
||||
text = credits[credits_index].text;
|
||||
timer = credits[credits_index].delay;
|
||||
start_pos = credits[credits_index].start_pos;
|
||||
|
||||
if (timer < 0) {
|
||||
timer = 240;
|
||||
loop = true;
|
||||
} else {
|
||||
// Find a free text widget
|
||||
for (c = 0; c < 10; c++)
|
||||
if (text_line[c].screen_pos().y() >= 200) break;
|
||||
|
||||
if (c < 10) {
|
||||
flag = credits[credits_index].flag & 0x3F;
|
||||
line_feed = (credits[credits_index].flag & 0x40) ? true : false;
|
||||
|
||||
if (flag == SECTION) {
|
||||
if (!second) {
|
||||
text = credits[credits_index].role;
|
||||
if (credits[credits_index].name != "")
|
||||
second = true;
|
||||
else {
|
||||
credits_index++;
|
||||
line_feed = true;
|
||||
}
|
||||
} else {
|
||||
text = credits[credits_index].name;
|
||||
second = false;
|
||||
line_feed = true;
|
||||
credits_index++;
|
||||
}
|
||||
x_pos = (240 - (text.size() * 8)) / 2;
|
||||
} else if (flag == TITLE) {
|
||||
text = credits[credits_index].role;
|
||||
x_pos = 120 - (text.size() * 8);
|
||||
if (credits[credits_index].name != "")
|
||||
text += " " + credits[credits_index].name;
|
||||
credits_index++;
|
||||
} else if (flag == MEMBER) {
|
||||
if (!second) {
|
||||
text = credits[credits_index].role;
|
||||
if (credits[credits_index].name != "")
|
||||
second = true;
|
||||
else
|
||||
credits_index++;
|
||||
} else {
|
||||
text = credits[credits_index].name;
|
||||
second = false;
|
||||
credits_index++;
|
||||
}
|
||||
x_pos = 136;
|
||||
timer += 16;
|
||||
}
|
||||
|
||||
if (!(flag & 0x80)) {
|
||||
text_line[c].set_parent_rect({{ x_pos, 200 - 16 }, { (Dim)text.size() * 8, 17 }});
|
||||
text_line[c].set(text);
|
||||
text_line[c].hidden(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
render_line = 0;
|
||||
credits_index++;
|
||||
} else
|
||||
timer--;
|
||||
|
||||
if (render_line < 16) {
|
||||
for (const auto c : text) {
|
||||
const auto glyph = style().font.glyph(c);
|
||||
|
||||
const size_t start = (glyph.size().width() / 8) * render_line;
|
||||
for(Dim c=0; c<glyph.size().width(); c++) {
|
||||
const auto pixel = glyph.pixels()[start + (c >> 3)] & (1U << (c & 0x7));
|
||||
pixel_row[start_pos + i + c] = pixel ? Color::white() : Color::black();
|
||||
}
|
||||
|
||||
// Scroll text lines
|
||||
for (c = 0; c < 10; c++) {
|
||||
y_val = text_line[c].screen_pos().y() - 16;
|
||||
if (y_val < 32) {
|
||||
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x(), 200 }, { text_line[c].size() }});
|
||||
text_line[c].hidden(true);
|
||||
} else {
|
||||
if (y_val < 200) {
|
||||
text_line[c].set_parent_rect({{ text_line[c].screen_pos().x(), y_val - 1 }, { text_line[c].size() }});
|
||||
n = (y_val - 32) >> 2;
|
||||
if (n > 19)
|
||||
n = (38 - n);
|
||||
else
|
||||
n -= 3;
|
||||
if (n > 3) n = 3;
|
||||
if (n < 0) n = 0;
|
||||
text_line[c].set_style(&styles[n]);
|
||||
const auto advance = glyph.advance();
|
||||
i += advance.x();
|
||||
}
|
||||
render_line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
scroll++;
|
||||
|
||||
credits_display.new_row(pixel_row);
|
||||
}
|
||||
|
||||
AboutView::AboutView(
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
add_child(&button_ok);
|
||||
|
||||
for (auto& text : text_line) {
|
||||
text.set("");
|
||||
text.set_parent_rect({
|
||||
static_cast<Coord>(0),
|
||||
static_cast<Coord>(200),
|
||||
0, 0
|
||||
) {
|
||||
add_children({
|
||||
&credits_display,
|
||||
&button_ok
|
||||
});
|
||||
add_child(&text);
|
||||
}
|
||||
|
||||
button_ok.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
|
@ -24,7 +24,6 @@
|
||||
#define __UI_ABOUT_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
@ -32,6 +31,21 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
class CreditsWidget : public Widget {
|
||||
public:
|
||||
CreditsWidget(Rect parent_rect);
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter&) override;
|
||||
|
||||
void new_row(const std::array<Color, 240>& pixel_row);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
};
|
||||
|
||||
class AboutView : public View {
|
||||
public:
|
||||
AboutView(NavigationView& nav);
|
||||
@ -43,33 +57,14 @@ public:
|
||||
private:
|
||||
void update();
|
||||
|
||||
uint8_t credits_index = 0;
|
||||
uint32_t scroll = 0;
|
||||
bool second = false;
|
||||
bool line_feed = false;
|
||||
uint8_t credits_index { 0 };
|
||||
uint8_t render_line { 0 };
|
||||
Coord start_pos { 0 };
|
||||
uint8_t slow_down { 0 };
|
||||
int32_t timer { 0 };
|
||||
bool loop { false };
|
||||
|
||||
Style styles[4] = { style_black, style_dark_grey, style_light_grey, style_white };
|
||||
|
||||
static constexpr Style style_white {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(255, 255, 255)
|
||||
};
|
||||
static constexpr Style style_light_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(170, 170, 170)
|
||||
};
|
||||
static constexpr Style style_dark_grey {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(85, 85, 85)
|
||||
};
|
||||
static constexpr Style style_black {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color(0, 0, 0)
|
||||
};
|
||||
std::string text { };
|
||||
|
||||
enum flags {
|
||||
SECTION = 0,
|
||||
@ -81,30 +76,35 @@ private:
|
||||
};
|
||||
|
||||
typedef struct credits_t {
|
||||
std::string role;
|
||||
std::string name;
|
||||
flags flag;
|
||||
size_t start_pos;
|
||||
std::string text;
|
||||
int32_t delay;
|
||||
} credits_t;
|
||||
|
||||
const credits_t credits[16] = { {"Portapack|HAVOC", "Git hash " GIT_REVISION, SECTION},
|
||||
{"Gurus", "J. Boone", TITLE},
|
||||
{"M. Ossmann", "", MEMBER_LF},
|
||||
{"HAVOC", "Furrtek", TITLE_LF},
|
||||
{"POCSAG rx", "T. Sailer", TITLE},
|
||||
{"E. Oenal", "", MEMBER_LF},
|
||||
{"RDS waveform", "C. Jacquet", TITLE_LF},
|
||||
{"Xy. infos", "cLx", TITLE_LF},
|
||||
{"World map", "NASA", TITLE_LF},
|
||||
{"Thanks", "", SECTION},
|
||||
{"Rainer Matla", "Keld Norman", TITLE},
|
||||
{"Giorgio C.", "DC1RDB", TITLE},
|
||||
{"Sigmounte", "Waax", TITLE},
|
||||
{"Windyoona", "Channels", TITLE},
|
||||
{"F4GEV", "", TITLE_LF},
|
||||
{"", "MMXVII", END}
|
||||
const credits_t credits[19] = {
|
||||
{ 60, "PortaPack|HAVOC", 0 },
|
||||
{ 7 * 8, "Git hash " GIT_REVISION, 16 },
|
||||
{ 9 * 8, "Gurus J. Boone", 0 },
|
||||
{ 16 * 8, "M. Ossmann", 16 },
|
||||
{ 9 * 8, "HAVOC Furrtek", 16 },
|
||||
{ 5 * 8, "POCSAG rx T. Sailer", 0 },
|
||||
{ 16 * 8, "E. Oenal", 16 },
|
||||
{ 2 * 8, "RDS waveform C. Jacquet", 16 },
|
||||
{ 5 * 8, "Xy. infos cLx", 16 },
|
||||
{ 0, "OOK scan trick Samy Kamkar", 16 },
|
||||
{ 5 * 8, "World map NASA", 24 },
|
||||
{ 12 * 8, "Thanks", 16 },
|
||||
{ 1 * 8, "Rainer Matla Keld Norman", 0 },
|
||||
{ 1 * 8, " Giorgio C. DC1RDB", 0 },
|
||||
{ 1 * 8, " Sigmounte Waax", 0 },
|
||||
{ 1 * 8, " Windyoona Channels", 0 },
|
||||
{ 1 * 8, " F4GEV", 24 },
|
||||
{ 12 * 8, "MMXVII", -1 }
|
||||
};
|
||||
|
||||
std::array<Text, 10> text_line { };
|
||||
CreditsWidget credits_display {
|
||||
{ 0, 16, 240, 240 }
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 72, 272, 96, 24 },
|
||||
|
@ -25,47 +25,131 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void EncodersView::focus() {
|
||||
EncodersConfigView::EncodersConfigView(
|
||||
NavigationView& nav, Rect parent_rect
|
||||
) {
|
||||
using name_t = std::string;
|
||||
using value_t = int32_t;
|
||||
using option_t = std::pair<name_t, value_t>;
|
||||
std::vector<option_t> enc_options;
|
||||
size_t i;
|
||||
|
||||
set_parent_rect(parent_rect);
|
||||
hidden(true);
|
||||
|
||||
// Default encoder def
|
||||
encoder_def = &encoder_defs[0];
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&options_enctype,
|
||||
&numberfield_clk,
|
||||
&numberfield_bitduration,
|
||||
&numberfield_wordduration,
|
||||
&symfield_word,
|
||||
&text_format,
|
||||
&waveform
|
||||
});
|
||||
|
||||
// Load encoder types in option field
|
||||
for (i = 0; i < ENC_TYPES_COUNT; i++)
|
||||
enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i));
|
||||
|
||||
options_enctype.on_change = [this](size_t index, int32_t) {
|
||||
on_type_change(index);
|
||||
};
|
||||
|
||||
options_enctype.set_options(enc_options);
|
||||
options_enctype.set_selected_index(0);
|
||||
|
||||
symfield_word.on_change = [this]() {
|
||||
generate_frame();
|
||||
};
|
||||
|
||||
// Selecting input clock changes symbol and word duration
|
||||
numberfield_clk.on_change = [this](int32_t value) {
|
||||
// value is in kHz, new_value is in us
|
||||
int32_t new_value = 1000000 / ((value * 1000) / encoder_def->clk_per_symbol);
|
||||
if (new_value != numberfield_bitduration.value()) {
|
||||
numberfield_bitduration.set_value(new_value, false);
|
||||
numberfield_wordduration.set_value(new_value * encoder_def->word_length, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Selecting symbol duration changes input clock and word duration
|
||||
numberfield_bitduration.on_change = [this](int32_t value) {
|
||||
int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_symbol);
|
||||
if (new_value != numberfield_clk.value()) {
|
||||
numberfield_clk.set_value(new_value, false);
|
||||
numberfield_wordduration.set_value(value * encoder_def->word_length, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Selecting word duration changes input clock and symbol duration
|
||||
numberfield_wordduration.on_change = [this](int32_t value) {
|
||||
int32_t new_value = value / encoder_def->word_length;
|
||||
if (new_value != numberfield_bitduration.value()) {
|
||||
numberfield_bitduration.set_value(new_value, false);
|
||||
numberfield_clk.set_value(1000000 / (((float)new_value * 1000) / encoder_def->clk_per_symbol), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void EncodersConfigView::focus() {
|
||||
options_enctype.focus();
|
||||
}
|
||||
|
||||
EncodersView::~EncodersView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
void EncodersConfigView::on_type_change(size_t index) {
|
||||
std::string word_format, format_string = "";
|
||||
size_t word_length;
|
||||
char symbol_type;
|
||||
//size_t address_length;
|
||||
|
||||
//enc_type = index;
|
||||
|
||||
encoder_def = &encoder_defs[index];
|
||||
|
||||
numberfield_clk.set_value(encoder_def->default_speed / 1000);
|
||||
|
||||
// SymField setup
|
||||
word_length = encoder_def->word_length;
|
||||
symfield_word.set_length(word_length);
|
||||
size_t n = 0, i = 0;
|
||||
while (n < word_length) {
|
||||
symbol_type = encoder_def->word_format.at(i++);
|
||||
if (symbol_type == 'A') {
|
||||
symfield_word.set_symbol_list(n++, encoder_def->address_symbols);
|
||||
format_string += 'A';
|
||||
} else if (symbol_type == 'D') {
|
||||
symfield_word.set_symbol_list(n++, encoder_def->data_symbols);
|
||||
format_string += 'D';
|
||||
}
|
||||
}
|
||||
|
||||
void EncodersView::generate_frame() {
|
||||
size_t i;
|
||||
// Ugly :( Pad to erase
|
||||
format_string.append(24 - format_string.size(), ' ');
|
||||
|
||||
debug_text.clear();
|
||||
text_format.set(format_string);
|
||||
|
||||
i = 0;
|
||||
for (auto c : encoder_def->word_format) {
|
||||
if (c == 'S')
|
||||
debug_text += encoder_def->sync;
|
||||
else
|
||||
debug_text += encoder_def->bit_format[symfield_word.get_sym(i++)];
|
||||
generate_frame();
|
||||
}
|
||||
|
||||
draw_waveform();
|
||||
void EncodersConfigView::on_show() {
|
||||
// TODO: Remove ?
|
||||
//options_enctype.set_selected_index(enc_type);
|
||||
//on_type_change(enc_type);
|
||||
}
|
||||
|
||||
void EncodersView::draw_waveform() {
|
||||
uint32_t n, length;
|
||||
|
||||
length = debug_text.length();
|
||||
void EncodersConfigView::draw_waveform() {
|
||||
size_t length = frame_symbols.length();
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < length; n++) {
|
||||
if (debug_text[n] == '0')
|
||||
if (frame_symbols[n] == '0')
|
||||
waveform_buffer[n] = 0;
|
||||
else
|
||||
waveform_buffer[n] = 1;
|
||||
@ -75,17 +159,82 @@ void EncodersView::draw_waveform() {
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
void EncodersConfigView::generate_frame() {
|
||||
size_t i = 0;
|
||||
|
||||
frame_symbols.clear();
|
||||
|
||||
for (auto c : encoder_def->word_format) {
|
||||
if (c == 'S')
|
||||
frame_symbols += encoder_def->sync;
|
||||
else
|
||||
frame_symbols += encoder_def->bit_format[symfield_word.get_sym(i++)];
|
||||
}
|
||||
|
||||
draw_waveform();
|
||||
}
|
||||
|
||||
uint8_t EncodersConfigView::repeat_min() {
|
||||
return encoder_def->repeat_min;
|
||||
}
|
||||
|
||||
uint32_t EncodersConfigView::samples_per_bit() {
|
||||
return OOK_SAMPLERATE / ((numberfield_clk.value() * 1000) / encoder_def->clk_per_fragment);
|
||||
}
|
||||
|
||||
uint32_t EncodersConfigView::pause_symbols() {
|
||||
return encoder_def->pause_symbols;
|
||||
}
|
||||
|
||||
void EncodersScanView::focus() {
|
||||
field_debug.focus();
|
||||
}
|
||||
|
||||
EncodersScanView::EncodersScanView(
|
||||
NavigationView& nav, Rect parent_rect
|
||||
) {
|
||||
set_parent_rect(parent_rect);
|
||||
hidden(true);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_debug,
|
||||
&text_debug
|
||||
});
|
||||
|
||||
// DEBUG
|
||||
field_debug.on_change = [this](int32_t value) {
|
||||
uint32_t l;
|
||||
de_bruijn debruijn_seq;
|
||||
debruijn_seq.init(value);
|
||||
l = 1;
|
||||
l <<= value;
|
||||
l--;
|
||||
if (l > 25)
|
||||
l = 25;
|
||||
text_debug.set(to_string_bin(debruijn_seq.compute(l), 25));
|
||||
};
|
||||
}
|
||||
|
||||
void EncodersView::focus() {
|
||||
tab_view.focus();
|
||||
}
|
||||
|
||||
EncodersView::~EncodersView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void EncodersView::update_progress() {
|
||||
char str[16];
|
||||
std::string str_buffer;
|
||||
|
||||
// text_status.set(" ");
|
||||
|
||||
if (tx_mode == SINGLE) {
|
||||
strcpy(str, to_string_dec_uint(repeat_index).c_str());
|
||||
strcat(str, "/");
|
||||
strcat(str, to_string_dec_uint(encoder_def->repeat_min).c_str());
|
||||
text_status.set(str);
|
||||
str_buffer = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(repeat_min);
|
||||
text_status.set(str_buffer);
|
||||
progress.set_value(repeat_index);
|
||||
|
||||
/*} else if (tx_mode == SCAN) {
|
||||
strcpy(str, to_string_dec_uint(repeat_index).c_str());
|
||||
strcat(str, "/");
|
||||
@ -107,7 +256,8 @@ void EncodersView::on_txdone(int n, const bool txdone) {
|
||||
|
||||
if (!txdone) {
|
||||
// Repeating...
|
||||
repeat_index = n + 1;
|
||||
//repeat_index = n + 1;
|
||||
|
||||
/*if (tx_mode == SCAN) {
|
||||
scan_progress++;
|
||||
update_progress();
|
||||
@ -147,14 +297,12 @@ void EncodersView::on_txdone(int n, const bool txdone) {
|
||||
}
|
||||
}
|
||||
|
||||
void EncodersView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void EncodersView::start_tx(const bool scan) {
|
||||
char ook_bitstream[256];
|
||||
uint32_t ook_bitstream_length;
|
||||
(void)scan;
|
||||
uint8_t* ook_bitstream = shared_memory.bb_data.data;
|
||||
uint32_t ook_bitstream_length;
|
||||
|
||||
repeat_min = view_config.repeat_min();
|
||||
|
||||
/*if (scan) {
|
||||
if (tx_mode != SCAN) {
|
||||
@ -170,17 +318,17 @@ void EncodersView::start_tx(const bool scan) {
|
||||
} else {*/
|
||||
tx_mode = SINGLE;
|
||||
repeat_index = 1;
|
||||
progress.set_max(encoder_def->repeat_min);
|
||||
progress.set_max(repeat_min);
|
||||
update_progress();
|
||||
//}
|
||||
|
||||
generate_frame();
|
||||
view_config.generate_frame();
|
||||
|
||||
// Clear bitstream
|
||||
memset(ook_bitstream, 0, 256);
|
||||
|
||||
size_t n = 0;
|
||||
for (auto c : debug_text) {
|
||||
for (auto c : view_config.frame_symbols) {
|
||||
if (c != '0')
|
||||
ook_bitstream[n >> 3] |= (1 << (7 - (n & 7)));
|
||||
n++;
|
||||
@ -188,158 +336,41 @@ void EncodersView::start_tx(const bool scan) {
|
||||
|
||||
ook_bitstream_length = n;
|
||||
|
||||
transmitter_model.set_sampling_rate(2280000U);
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
memcpy(shared_memory.bb_data.data, ook_bitstream, 256);
|
||||
|
||||
baseband::set_ook_data(
|
||||
ook_bitstream_length,
|
||||
// 2280000/2 = 1140000Hz = 0,877192982us
|
||||
// numberfield_clk.value() / encoder_def->clk_per_fragment
|
||||
// 455000 / 12 = 37917Hz = 26,37339452us
|
||||
228000 / ((numberfield_clk.value() * 1000) / encoder_def->clk_per_fragment),
|
||||
encoder_def->repeat_min,
|
||||
encoder_def->pause_symbols
|
||||
view_config.samples_per_bit(),
|
||||
repeat_min,
|
||||
view_config.pause_symbols()
|
||||
);
|
||||
}
|
||||
|
||||
void EncodersView::on_type_change(size_t index) {
|
||||
std::string word_format, format_string = "";
|
||||
size_t word_length;
|
||||
char symbol_type;
|
||||
//size_t address_length;
|
||||
|
||||
enc_type = index;
|
||||
|
||||
encoder_def = &encoder_defs[enc_type];
|
||||
|
||||
numberfield_clk.set_value(encoder_def->default_speed / 1000);
|
||||
|
||||
// SymField setup
|
||||
word_length = encoder_def->word_length;
|
||||
symfield_word.set_length(word_length);
|
||||
size_t n = 0, i = 0;
|
||||
while (n < word_length) {
|
||||
symbol_type = encoder_def->word_format.at(i++);
|
||||
if (symbol_type == 'A') {
|
||||
symfield_word.set_symbol_list(n++, encoder_def->address_symbols);
|
||||
format_string += 'A';
|
||||
} else if (symbol_type == 'D') {
|
||||
symfield_word.set_symbol_list(n++, encoder_def->data_symbols);
|
||||
format_string += 'D';
|
||||
}
|
||||
}
|
||||
|
||||
// Ugly :( Pad to erase
|
||||
while (format_string.length() < 24)
|
||||
format_string += ' ';
|
||||
|
||||
text_format.set(format_string);
|
||||
|
||||
generate_frame();
|
||||
}
|
||||
|
||||
void EncodersView::on_show() {
|
||||
// TODO: Remove ?
|
||||
options_enctype.set_selected_index(enc_type);
|
||||
on_type_change(enc_type);
|
||||
}
|
||||
|
||||
EncodersView::EncodersView(NavigationView& nav) {
|
||||
using name_t = std::string;
|
||||
using value_t = int32_t;
|
||||
using option_t = std::pair<name_t, value_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t enc_options;
|
||||
size_t i;
|
||||
|
||||
EncodersView::EncodersView(
|
||||
NavigationView& nav
|
||||
) : nav_ { nav }
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook);
|
||||
|
||||
// Default encoder def
|
||||
encoder_def = &encoder_defs[0];
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&options_enctype,
|
||||
&numberfield_clk,
|
||||
&numberfield_bitduration,
|
||||
&numberfield_wordduration,
|
||||
//&field_debug,
|
||||
&symfield_word,
|
||||
&text_format,
|
||||
//&text_format_a, // DEBUG
|
||||
//&text_format_d, // DEBUG
|
||||
&waveform,
|
||||
&tab_view,
|
||||
&view_config,
|
||||
&view_scan,
|
||||
&text_status,
|
||||
&progress,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// Load encoder types
|
||||
for (i = 0; i < ENC_TYPES_COUNT; i++)
|
||||
enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i));
|
||||
|
||||
options_enctype.on_change = [this](size_t index, int32_t value) {
|
||||
(void)value;
|
||||
this->on_type_change(index);
|
||||
};
|
||||
|
||||
options_enctype.set_options(enc_options);
|
||||
options_enctype.set_selected_index(0);
|
||||
|
||||
symfield_word.on_change = [this]() {
|
||||
this->generate_frame();
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
/*field_debug.on_change = [this](int32_t value) {
|
||||
uint32_t l;
|
||||
de_bruijn debruijn_seq;
|
||||
debruijn_seq.init(value);
|
||||
l = 1;
|
||||
l <<= value;
|
||||
l--;
|
||||
if (l > 25)
|
||||
l = 25;
|
||||
text_format.set(to_string_bin(debruijn_seq.compute(l), 25));
|
||||
};*/
|
||||
|
||||
// Selecting input clock changes symbol and word duration
|
||||
numberfield_clk.on_change = [this](int32_t value) {
|
||||
//int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_symbol);
|
||||
// value is in kHz, new_value is in us
|
||||
int32_t new_value = 1000000 / ((value * 1000) / encoder_def->clk_per_symbol);
|
||||
if (new_value != numberfield_bitduration.value()) {
|
||||
numberfield_bitduration.set_value(new_value, false);
|
||||
numberfield_wordduration.set_value(new_value * encoder_def->word_length, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Selecting symbol duration changes input clock and word duration
|
||||
numberfield_bitduration.on_change = [this](int32_t value) {
|
||||
int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_symbol);
|
||||
if (new_value != numberfield_clk.value()) {
|
||||
numberfield_clk.set_value(new_value, false);
|
||||
numberfield_wordduration.set_value(value * encoder_def->word_length, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Selecting word duration changes input clock and symbol duration
|
||||
numberfield_wordduration.on_change = [this](int32_t value) {
|
||||
int32_t new_value = value / encoder_def->word_length;
|
||||
if (new_value != numberfield_bitduration.value()) {
|
||||
numberfield_bitduration.set_value(new_value, false);
|
||||
numberfield_clk.set_value(1000000 / (((float)new_value * 1000) / encoder_def->clk_per_symbol), false);
|
||||
}
|
||||
};
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -21,37 +21,151 @@
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "de_bruijn.hpp"
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
|
||||
namespace ui {
|
||||
|
||||
class EncodersConfigView : public View {
|
||||
public:
|
||||
EncodersConfigView(NavigationView& nav, Rect parent_rect);
|
||||
|
||||
EncodersConfigView(const EncodersConfigView&) = delete;
|
||||
EncodersConfigView(EncodersConfigView&&) = delete;
|
||||
EncodersConfigView& operator=(const EncodersConfigView&) = delete;
|
||||
EncodersConfigView& operator=(EncodersConfigView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
uint8_t repeat_min();
|
||||
uint32_t samples_per_bit();
|
||||
uint32_t pause_symbols();
|
||||
void generate_frame();
|
||||
|
||||
std::string frame_symbols = "0";
|
||||
|
||||
private:
|
||||
//bool abort_scan = false;
|
||||
//uint8_t scan_count;
|
||||
//double scan_progress;
|
||||
//unsigned int scan_index;
|
||||
int8_t waveform_buffer[512];
|
||||
const encoder_def_t * encoder_def { };
|
||||
//uint8_t enc_type = 0;
|
||||
|
||||
void draw_waveform();
|
||||
void on_bitfield();
|
||||
void on_type_change(size_t index);
|
||||
|
||||
Labels labels {
|
||||
{ { 1 * 8, 0 }, "Type:", Color::light_grey() },
|
||||
{ { 16 * 8, 0 }, "Clk:", Color::light_grey() },
|
||||
{ { 24 * 8, 0 }, "kHz", Color::light_grey() },
|
||||
{ { 16 * 8, 2 * 8 }, "Bit:", Color::light_grey() },
|
||||
{ { 25 * 8, 2 * 8 }, "us", Color::light_grey() },
|
||||
{ { 15 * 8, 4 * 8 }, "Word:", Color::light_grey() },
|
||||
{ { 26 * 8, 4 * 8 }, "us", Color::light_grey() },
|
||||
{ { 2 * 8, 6 * 8 }, "Word:", Color::light_grey() },
|
||||
{ { 1 * 8, 13 * 8 }, "Waveform:", Color::light_grey() }
|
||||
};
|
||||
|
||||
OptionsField options_enctype { // Options are loaded at runtime
|
||||
{ 6 * 8, 0 },
|
||||
7,
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
NumberField numberfield_clk {
|
||||
{ 21 * 8, 0 },
|
||||
3,
|
||||
{ 1, 500 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField numberfield_bitduration {
|
||||
{ 21 * 8, 2 * 8 },
|
||||
4,
|
||||
{ 50, 9999 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField numberfield_wordduration {
|
||||
{ 21 * 8, 4 * 8 },
|
||||
5,
|
||||
{ 300, 99999 },
|
||||
100,
|
||||
' '
|
||||
};
|
||||
|
||||
SymField symfield_word {
|
||||
{ 2 * 8, 8 * 8 },
|
||||
20,
|
||||
SymField::SYMFIELD_DEF
|
||||
};
|
||||
|
||||
Text text_format {
|
||||
{ 2 * 8, 10 * 8, 24 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 16 * 8, 240, 32 },
|
||||
waveform_buffer,
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Color::yellow()
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class EncodersScanView : public View {
|
||||
public:
|
||||
EncodersScanView(NavigationView& nav, Rect parent_rect);
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
Labels labels {
|
||||
{ { 1 * 8, 1 * 8 }, "Test", Color::light_grey() }
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
NumberField field_debug {
|
||||
{ 1 * 8, 6 * 8 },
|
||||
2,
|
||||
{ 3, 16 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
Text text_debug {
|
||||
{ 1 * 8, 8 * 8, 24 * 8, 16 },
|
||||
""
|
||||
};
|
||||
};
|
||||
|
||||
class EncodersView : public View {
|
||||
public:
|
||||
EncodersView(NavigationView& nav);
|
||||
~EncodersView();
|
||||
|
||||
EncodersView(const EncodersView&) = delete;
|
||||
EncodersView(EncodersView&&) = delete;
|
||||
EncodersView& operator=(const EncodersView&) = delete;
|
||||
EncodersView& operator=(EncodersView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
std::string title() const override { return "Encoders TX"; };
|
||||
std::string title() const override { return "OOK transmit"; };
|
||||
|
||||
private:
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
NavigationView& nav_;
|
||||
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
@ -59,36 +173,14 @@ private:
|
||||
SCAN
|
||||
};
|
||||
|
||||
uint8_t enc_type = 0;
|
||||
const encoder_def_t * encoder_def { };
|
||||
tx_modes tx_mode = IDLE;
|
||||
//bool abort_scan = false;
|
||||
//uint8_t scan_count;
|
||||
//double scan_progress;
|
||||
//unsigned int scan_index;
|
||||
std::string debug_text = "0";
|
||||
uint8_t repeat_index { 0 };
|
||||
int8_t waveform_buffer[512];
|
||||
uint8_t repeat_min { 0 };
|
||||
|
||||
void draw_waveform();
|
||||
void on_bitfield();
|
||||
void on_type_change(size_t index);
|
||||
void generate_frame();
|
||||
void update_progress();
|
||||
void start_tx(const bool scan);
|
||||
void on_txdone(int n, const bool txdone);
|
||||
|
||||
const Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
const Style style_cancel {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
const Style style_address {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
@ -100,78 +192,14 @@ private:
|
||||
.foreground = Color::blue(),
|
||||
};
|
||||
|
||||
Labels labels {
|
||||
{ { 1 * 8, 4 * 8 }, "Type:", Color::light_grey() },
|
||||
{ { 16 * 8, 4 * 8 }, "Clk:", Color::light_grey() },
|
||||
{ { 24 * 8, 4 * 8 }, "kHz", Color::light_grey() },
|
||||
{ { 16 * 8, 6 * 8 }, "Bit:", Color::light_grey() },
|
||||
{ { 25 * 8, 6 * 8 }, "us", Color::light_grey() },
|
||||
{ { 15 * 8, 8 * 8 }, "Word:", Color::light_grey() },
|
||||
{ { 26 * 8, 8 * 8 }, "us", Color::light_grey() },
|
||||
{ { 2 * 8, 10 * 8 }, "Word:", Color::light_grey() },
|
||||
{ { 1 * 8, 17 * 8 }, "Waveform:", Color::light_grey() }
|
||||
};
|
||||
Rect view_rect = { 0, 5 * 8, 240, 168 };
|
||||
|
||||
OptionsField options_enctype { // Options are loaded at runtime
|
||||
{ 6 * 8, 32 },
|
||||
7,
|
||||
{
|
||||
}
|
||||
};
|
||||
EncodersConfigView view_config { nav_, view_rect };
|
||||
EncodersScanView view_scan { nav_, view_rect };
|
||||
|
||||
NumberField numberfield_clk {
|
||||
{ 21 * 8, 4 * 8 },
|
||||
3,
|
||||
{ 1, 500 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField numberfield_bitduration {
|
||||
{ 21 * 8, 6 * 8 },
|
||||
4,
|
||||
{ 50, 9999 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField numberfield_wordduration {
|
||||
{ 21 * 8, 8 * 8 },
|
||||
5,
|
||||
{ 300, 99999 },
|
||||
100,
|
||||
' '
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
/*NumberField field_debug {
|
||||
{ 21 * 8, 10 * 8 },
|
||||
2,
|
||||
{ 3, 16 },
|
||||
1,
|
||||
' '
|
||||
};*/
|
||||
|
||||
SymField symfield_word {
|
||||
{ 2 * 8, 12 * 8 },
|
||||
20,
|
||||
SymField::SYMFIELD_DEF
|
||||
};
|
||||
Text text_format {
|
||||
{ 2 * 8, 14 * 8, 24 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
//Text text_format_a; // DEBUG
|
||||
//Text text_format_d; // DEBUG
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 160, 240, 32 },
|
||||
waveform_buffer,
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Color::yellow()
|
||||
TabView tab_view {
|
||||
{ "Config", Color::cyan(), &view_config },
|
||||
{ "Scan", Color::green(), &view_scan },
|
||||
};
|
||||
|
||||
Text text_status {
|
||||
|
@ -219,6 +219,8 @@ void GeoMapView::focus() {
|
||||
void GeoMapView::update_position(float lat, float lon) {
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ void OOKProcessor::on_message(const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const OOKConfigureMessage*>(p);
|
||||
|
||||
if (message.id == Message::ID::OOKConfigure) {
|
||||
samples_per_bit = message.samples_per_bit;
|
||||
samples_per_bit = message.samples_per_bit / 10;
|
||||
repeat = message.repeat - 1;
|
||||
length = message.stream_length - 1;
|
||||
pause = message.pause_symbols + 1;
|
||||
|
Loading…
Reference in New Issue
Block a user