Add software

This commit is contained in:
RocketGod
2022-09-22 09:26:57 -07:00
parent fee0ab05fd
commit 957ea3d712
4511 changed files with 1943182 additions and 0 deletions

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2015 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.
*/
#include "ui_alphanum.hpp"
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
#include <algorithm>
namespace ui {
void AlphanumView::paint(Painter&) {
draw_cursor();
}
AlphanumView::AlphanumView(
NavigationView& nav,
std::string& str,
size_t max_length
) : TextEntryView(nav, str, max_length)
{
size_t n;
add_children({
&button_mode,
&text_raw,
&field_raw
});
const auto button_fn = [this](Button& button) {
this->on_button(button);
};
n = 0;
for (auto& button : buttons) {
button.id = n;
button.on_highlight = [this](Button& button) {
focused_button = button.id;
};
button.on_select = button_fn;
button.set_parent_rect({
static_cast<Coord>((n % 5) * (240 / 5)),
static_cast<Coord>((n / 5) * 38 + 24),
240 / 5, 38
});
add_child(&button);
n++;
}
set_mode(mode);
button_mode.on_select = [this](Button&) {
set_mode(mode + 1);
};
field_raw.set_value('0');
field_raw.on_select = [this](NumberField&) {
char_add(field_raw.value());
update_text();
};
button_ok.on_select = [this, &nav](Button&) {
if (on_changed)
on_changed(_str);
nav.pop();
};
update_text();
}
void AlphanumView::set_mode(const uint32_t new_mode) {
size_t n = 0;
if (new_mode < 3)
mode = new_mode;
else
mode = 0;
const char * key_list = key_sets[mode].second;
for (auto& button : buttons) {
const std::string label {
key_list[n]
};
button.set_text(label);
n++;
}
if (mode < 2)
button_mode.set_text(key_sets[mode + 1].first);
else
button_mode.set_text(key_sets[0].first);
}
void AlphanumView::on_button(Button& button) {
const auto c = button.text()[0];
if (c == '<')
char_delete();
else
char_add(c);
update_text();
}
bool AlphanumView::on_encoder(const EncoderEvent delta) {
focused_button += delta;
if (focused_button < 0) {
focused_button = buttons.size() - 1;
}
else if (focused_button >= (int16_t)buttons.size()) {
focused_button = 0;
}
buttons[focused_button].focus();
return true;
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 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 __ALPHANUM_H__
#define __ALPHANUM_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_textentry.hpp"
#include "ui_menu.hpp"
namespace ui {
class AlphanumView : public TextEntryView {
public:
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
AlphanumView(const AlphanumView&) = delete;
AlphanumView(AlphanumView&&) = delete;
AlphanumView& operator=(const AlphanumView&) = delete;
AlphanumView& operator=(AlphanumView&&) = delete;
void paint(Painter& painter) override;
bool on_encoder(const EncoderEvent delta) override;
private:
const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
const char * const keys_lower = "abcdefghijklmnopqrstuvwxyz' .<";
const char * const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
const std::pair<std::string, const char *> key_sets[3] = {
{ "Upper", keys_upper },
{ "Lower", keys_lower },
{ "Digit", keys_digit }
};
int16_t focused_button = 0;
uint32_t mode = 0; // Uppercase
void set_mode(const uint32_t new_mode);
void on_button(Button& button);
std::array<Button, 30> buttons { };
Button button_mode {
{ 21 * 8, 33 * 8, 8 * 8, 32 },
""
};
Text text_raw {
{ 1 * 8, 33 * 8, 4 * 8, 16 },
"Raw:"
};
NumberField field_raw {
{ 5 * 8, 33 * 8 },
3,
{ 1, 255 },
1,
'0'
};
Button button_ok {
{ 10 * 8, 33 * 8, 9 * 8, 32 },
"OK"
};
};
} /* namespace ui */
#endif/*__ALPHANUM_H__*/

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_audio.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void Audio::paint(Painter& painter) {
const auto r = screen_rect();
constexpr int db_min = -96;
constexpr int db_max = 0;
constexpr int db_delta = db_max - db_min;
const range_t<int> x_rms_range { 0, r.width() - 1 };
const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta);
const range_t<int> x_max_range { x_rms + 1, r.width() };
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
const Rect r0 { r.left(), r.top(), x_rms, r.height() };
painter.fill_rectangle(
r0,
Color::green()
);
const Rect r1 { r.left() + x_rms, r.top(), 1, r.height() };
painter.fill_rectangle(
r1,
Color::black()
);
const Rect r2 { r.left() + x_rms + 1, r.top(), x_max - (x_rms + 1), r.height() };
painter.fill_rectangle(
r2,
Color::red()
);
const Rect r3 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
painter.fill_rectangle(
r3,
Color::black()
);
}
void Audio::on_statistics_update(const AudioStatistics& statistics) {
rms_db_ = statistics.rms_db;
max_db_ = statistics.max_db;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_AUDIO_H__
#define __UI_AUDIO_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include <cstdint>
namespace ui {
class Audio : public Widget {
public:
Audio(
const Rect parent_rect
) : Widget { parent_rect },
rms_db_ { -120 },
max_db_ { -120 }
{
}
void paint(Painter& painter) override;
private:
int32_t rms_db_;
int32_t max_db_;
MessageHandlerRegistration message_handler_statistics {
Message::ID::AudioStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const AudioStatistics& statistics);
};
}
#endif/*__UI_AUDIO_H__*/

View File

@ -0,0 +1,229 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
*
* 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 "ui_btngrid.hpp"
#include "rtc_time.hpp"
namespace ui {
/* BtnGridView **************************************************************/
BtnGridView::BtnGridView(
Rect new_parent_rect,
bool keep_highlight
) : keep_highlight { keep_highlight }
{
set_parent_rect(new_parent_rect);
set_focusable(true);
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};
add_child(&arrow_more);
arrow_more.set_focusable(false);
arrow_more.set_foreground(Color::black());
}
BtnGridView::~BtnGridView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
for (auto item : menu_item_views) {
delete item;
}
}
void BtnGridView::set_max_rows(int rows) {
rows_ = rows;
}
int BtnGridView::rows() {
return rows_;
}
void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
displayed_max = (parent_rect().size().height() / button_h);
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * button_h), 8, 8 } );
displayed_max *= rows_;
// TODO: Clean this up :(
if (menu_item_views.size()) {
for (auto item : menu_item_views) {
remove_child(item);
delete item;
}
menu_item_views.clear();
}
button_w = 240 / rows_;
for (size_t c = 0; c < displayed_max; c++) {
auto item = new NewButton { };
menu_item_views.push_back(item);
add_child(item);
item->set_parent_rect({
(int)(c % rows_) * button_w,
(int)(c / rows_) * button_h,
button_w, button_h
});
}
update_items();
}
void BtnGridView::set_arrow_enabled(bool new_value) {
if(new_value){
add_child(&arrow_more);
}
else{
remove_child(&arrow_more);
}
};
void BtnGridView::on_tick_second() {
if (more && blink)
arrow_more.set_foreground(Color::white());
else
arrow_more.set_foreground(Color::black());
blink = !blink;
arrow_more.set_dirty();
}
void BtnGridView::clear() {
menu_items.clear();
}
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
for (auto item : new_items) {
menu_items.push_back(item);
}
update_items();
}
void BtnGridView::update_items() {
size_t i = 0;
if ((menu_items.size()) > (displayed_max + offset)) {
more = true;
blink = true;
} else
more = false;
for (NewButton* item : menu_item_views) {
if (i >= menu_items.size()) break;
// Assign item data to NewButtons according to offset
item->set_text(menu_items[i + offset].text);
item->set_bitmap(menu_items[i + offset].bitmap);
item->set_color(menu_items[i + offset].color);
item->on_select = menu_items[i + offset].on_select;
item->set_dirty();
i++;
}
}
NewButton* BtnGridView::item_view(size_t index) const {
return menu_item_views[index];
}
bool BtnGridView::set_highlighted(int32_t new_value) {
int32_t item_count = (int32_t)menu_items.size();
if (new_value < 0)
return false;
if (new_value >= item_count)
new_value = item_count - 1;
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
// Shift BtnGridView up
highlighted_item = new_value;
offset = new_value - displayed_max + rows_;
update_items();
} else if ((uint32_t)new_value < offset) {
// Shift BtnGridView down
highlighted_item = new_value;
offset = (new_value / rows_) * rows_;
update_items();
} else {
// Just update highlight
highlighted_item = new_value;
if (visible())
item_view(highlighted_item - offset)->focus();
}
return true;
}
uint32_t BtnGridView::highlighted_index() {
return highlighted_item;
}
void BtnGridView::on_focus() {
item_view(highlighted_item - offset)->focus();
}
void BtnGridView::on_blur() {
#if 0
if (!keep_highlight)
item_view(highlighted_item - offset)->unhighlight();
#endif
}
bool BtnGridView::on_key(const KeyEvent key) {
switch(key) {
case KeyEvent::Up:
return set_highlighted(highlighted_item - rows_);
case KeyEvent::Down:
return set_highlighted(highlighted_item + rows_);
case KeyEvent::Right:
return set_highlighted(highlighted_item + 1);
case KeyEvent::Left:
return set_highlighted(highlighted_item - 1);
case KeyEvent::Select:
if( menu_items[highlighted_item].on_select ) {
menu_items[highlighted_item].on_select();
}
return true;
default:
return false;
}
}
bool BtnGridView::on_encoder(const EncoderEvent event) {
return set_highlighted(highlighted_item + event);
}
} /* namespace ui */

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
*
* 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 __UI_BTNGRID_H__
#define __UI_BTNGRID_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "bitmap.hpp"
#include "signal.hpp"
#include <cstddef>
#include <string>
#include <functional>
namespace ui {
struct GridItem {
std::string text;
ui::Color color;
const Bitmap* bitmap;
std::function<void(void)> on_select;
// TODO: Prevent default-constructed GridItems.
};
class BtnGridView : public View {
public:
BtnGridView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
~BtnGridView();
void add_items(std::initializer_list<GridItem> new_items);
void set_max_rows(int rows);
int rows();
void clear();
NewButton* item_view(size_t index) const;
bool set_highlighted(int32_t new_value);
uint32_t highlighted_index();
void set_parent_rect(const Rect new_parent_rect) override;
void set_arrow_enabled(bool new_value);
void on_focus() override;
void on_blur() override;
bool on_key(const KeyEvent event) override;
bool on_encoder(const EncoderEvent event) override;
private:
int rows_ { 3 };
void update_items();
void on_tick_second();
bool keep_highlight { false };
SignalToken signal_token_tick_second { };
std::vector<GridItem> menu_items { };
std::vector<NewButton*> menu_item_views { };
Image arrow_more {
{ 228, 320 - 8, 8, 8 },
&bitmap_more,
Color::white(),
Color::black()
};
int button_w = 240 / rows_;
static constexpr int button_h = 48;
bool blink = false;
bool more = false;
size_t displayed_max { 0 };
size_t highlighted_item { 0 };
size_t offset { 0 };
};
} /* namespace ui */
#endif/*__UI_BTNGRID_H__*/

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_channel.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void Channel::paint(Painter& painter) {
const auto r = screen_rect();
constexpr int db_min = -96;
constexpr int db_max = 0;
constexpr int db_delta = db_max - db_min;
const range_t<int> x_max_range { 0, r.width() - 1 };
const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta);
const Rect r0 { r.left(), r.top(), x_max, r.height() };
painter.fill_rectangle(
r0,
Color::blue()
);
const Rect r1 { r.left() + x_max, r.top(), 1, r.height() };
painter.fill_rectangle(
r1,
Color::white()
);
const Rect r2 { r.left() + x_max + 1, r.top(), r.width() - (x_max + 1), r.height() };
painter.fill_rectangle(
r2,
Color::black()
);
}
void Channel::on_statistics_update(const ChannelStatistics& statistics) {
max_db_ = statistics.max_db;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_CHANNEL_H__
#define __UI_CHANNEL_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
namespace ui {
class Channel : public Widget {
public:
Channel(
const Rect parent_rect
) : Widget { parent_rect },
max_db_ { -120 }
{
}
void paint(Painter& painter) override;
private:
int32_t max_db_;
MessageHandlerRegistration message_handler_stats {
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const ChannelStatistics& statistics);
};
}
#endif/*__UI_CHANNEL_H__*/

View File

@ -0,0 +1,962 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_font_fixed_8x16.hpp"
#include <cstdint>
namespace ui {
namespace font {
namespace {
const uint8_t fixed_8x16_glyph_data[] = {
// Font based on a modded https://www.1001freefonts.com/8-bit-operator.font
// Index: 0 (0x00) Char: 0x0020 (' ')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 1 (0x01) Char: 0x0021 ('!')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 2 (0x02) Char: 0x0022 ('"')
0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 3 (0x03) Char: 0x0023 ('#')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66,
0x66, 0x66, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 4 (0x04) Char: 0x0024 ('$')
0x00, 0x00, 0x18, 0x18, 0x3C, 0x66, 0x66, 0x0C,
0x18, 0x30, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00,
// Index: 5 (0x05) Char: 0x0025 ('%')
0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30,
0x18, 0x0C, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00,
// Index: 6 (0x06) Char: 0x0026 ('&')
0x00, 0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x1C,
0x0E, 0x7B, 0x33, 0x33, 0x6E, 0x00, 0x00, 0x00,
// Index: 7 (0x07) Char: 0x0027 (''')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 8 (0x08) Char: 0x0028 ('(')
0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00,
// Index: 9 (0x09) Char: 0x0029 (')')
0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30,
0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00,
// Index: 10 (0x0A) Char: 0x002A ('*')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1C,
0x7F, 0x1C, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 11 (0x0B) Char: 0x002B ('+')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 12 (0x0C) Char: 0x002C (',')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
// Index: 13 (0x0D) Char: 0x002D ('-')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 14 (0x0E) Char: 0x002E ('.')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 15 (0x0F) Char: 0x002F ('/')
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x30, 0x30,
0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x00, 0x00,
// Index: 16 (0x10) Char: 0x0030 ('0')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x76,
0x7E, 0x6E, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 17 (0x11) Char: 0x0031 ('1')
0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 18 (0x12) Char: 0x0032 ('2')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 19 (0x13) Char: 0x0033 ('3')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
0x38, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 20 (0x14) Char: 0x0034 ('4')
0x00, 0x00, 0x00, 0x00, 0x60, 0x70, 0x78, 0x6C,
0x66, 0x66, 0x7E, 0x60, 0x60, 0x00, 0x00, 0x00,
// Index: 21 (0x15) Char: 0x0035 ('5')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x3E,
0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 22 (0x16) Char: 0x0036 ('6')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x3E, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 23 (0x17) Char: 0x0037 ('7')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30,
0x30, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 24 (0x18) Char: 0x0038 ('8')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 25 (0x19) Char: 0x0039 ('9')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x7C, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 26 (0x1A) Char: 0x003A (':')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 27 (0x1B) Char: 0x003B (';')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
// Index: 28 (0x1C) Char: 0x003C ('<')
0x00, 0x00, 0x00, 0x00, 0x20, 0x30, 0x18, 0x0C,
0x06, 0x0C, 0x18, 0x30, 0x20, 0x00, 0x00, 0x00,
// Index: 29 (0x1D) Char: 0x003D ('=')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E,
0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 30 (0x1E) Char: 0x003E ('>')
0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18,
0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0x00, 0x00,
// Index: 31 (0x1F) Char: 0x003F ('?')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x60,
0x30, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 32 (0x20) Char: 0x0040 ('@')
0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xF3, 0xDB,
0xDB, 0xDB, 0xF3, 0x03, 0x7E, 0x00, 0x00, 0x00,
// Index: 33 (0x21) Char: 0x0041 ('A')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 34 (0x22) Char: 0x0042 ('B')
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
// Index: 35 (0x23) Char: 0x0043 ('C')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 36 (0x24) Char: 0x0044 ('D')
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
// Index: 37 (0x25) Char: 0x0045 ('E')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 38 (0x26) Char: 0x0046 ('F')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
// Index: 39 (0x27) Char: 0x0047 ('G')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x76, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 40 (0x28) Char: 0x0048 ('H')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 41 (0x29) Char: 0x0049 ('I')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 42 (0x2A) Char: 0x004A ('J')
0x00, 0x00, 0x00, 0x00, 0x70, 0x60, 0x60, 0x60,
0x60, 0x60, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 43 (0x2B) Char: 0x004B ('K')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x36,
0x1E, 0x36, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 44 (0x2C) Char: 0x004C ('L')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 45 (0x2D) Char: 0x004D ('M')
0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x77, 0x7F,
0x6B, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00,
// Index: 46 (0x2E) Char: 0x004E ('N')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x6E, 0x7E,
0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 47 (0x2F) Char: 0x004F ('O')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 48 (0x30) Char: 0x0050 ('P')
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x3E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
// Index: 49 (0x31) Char: 0x0051 ('Q')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x30, 0x60, 0x00,
// Index: 50 (0x32) Char: 0x0052 ('R')
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 51 (0x33) Char: 0x0053 ('S')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x0C,
0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 52 (0x34) Char: 0x0054 ('T')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 53 (0x35) Char: 0x0055 ('U')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 54 (0x36) Char: 0x0056 ('V')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00,
// Index: 55 (0x37) Char: 0x0057 ('W')
0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6B, 0x6B,
0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00,
// Index: 56 (0x38) Char: 0x0058 ('X')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C,
0x18, 0x3C, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 57 (0x39) Char: 0x0059 ('Y')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 58 (0x3A) Char: 0x005A ('Z')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x60, 0x60, 0x30,
0x18, 0x0C, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 59 (0x3B) Char: 0x005B ('[')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00,
// Index: 60 (0x3C) Char: 0x005C ('\')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x0C, 0x0C,
0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00,
// Index: 61 (0x3D) Char: 0x005D (']')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00,
// Index: 62 (0x3E) Char: 0x005E ('^')
0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x63, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 63 (0x3F) Char: 0x005F ('_')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00,
// Index: 64 (0x40) Char: 0x0060 ('`')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 65 (0x41) Char: 0x0061 ('a')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 66 (0x42) Char: 0x0062 ('b')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
// Index: 67 (0x43) Char: 0x0063 ('c')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 68 (0x44) Char: 0x0064 ('d')
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 69 (0x45) Char: 0x0065 ('e')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 70 (0x46) Char: 0x0066 ('f')
0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00,
// Index: 71 (0x47) Char: 0x0067 ('g')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x33,
0x33, 0x1E, 0x03, 0x1E, 0x33, 0x33, 0x1E, 0x00,
// Index: 72 (0x48) Char: 0x0068 ('h')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 73 (0x49) Char: 0x0069 ('i')
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x1C, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 74 (0x4A) Char: 0x006A ('j')
0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
// Index: 75 (0x4B) Char: 0x006B ('k')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x66, 0x66,
0x36, 0x1E, 0x36, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 76 (0x4C) Char: 0x006C ('l')
0x00, 0x00, 0x00, 0x00, 0x1C, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 77 (0x4D) Char: 0x006D ('m')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xDB,
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00,
// Index: 78 (0x4E) Char: 0x006E ('n')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 79 (0x4F) Char: 0x006F ('o')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 80 (0x50) Char: 0x0070 ('p')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
// Index: 81 (0x51) Char: 0x0071 ('q')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00,
// Index: 82 (0x52) Char: 0x0072 ('r')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x66,
0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
// Index: 83 (0x53) Char: 0x0073 ('s')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 84 (0x54) Char: 0x0074 ('t')
0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x3E, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x38, 0x00, 0x00, 0x00,
// Index: 85 (0x55) Char: 0x0075 ('u')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 86 (0x56) Char: 0x0076 ('v')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00,
// Index: 87 (0x57) Char: 0x0077 ('w')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6B,
0x6B, 0x6B, 0x6B, 0x36, 0x36, 0x00, 0x00, 0x00,
// Index: 88 (0x58) Char: 0x0078 ('x')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 89 (0x59) Char: 0x0079 ('y')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
// Index: 90 (0x5A) Char: 0x007A ('z')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x60,
0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 91 (0x5B) Char: 0x007B ('{')
0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18,
0x0E, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00,
// Index: 92 (0x5C) Char: 0x007C ('|')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 93 (0x5D) Char: 0x007D ('}')
0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18,
0x70, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00,
// Index: 94 (0x5E) Char: 0x007E ('~')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E,
0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 95 (0x5F) Char: 0x007F ('')
0x00, 0x00, 0x7C, 0xC6, 0x92, 0x92, 0x82, 0xBA,
0xFE, 0xEE, 0xD6, 0xD6, 0xEE, 0xFE, 0x7C, 0x00,
// Index: 96 (0x60) Char: 0x00A0 ('<27>')
0x00, 0x00, 0x7C, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE,
0xFE, 0xBA, 0x82, 0xAA, 0x92, 0xC6, 0x7C, 0x00,
// Index: 97 (0x61) Char: 0x00A1 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
// Index: 98 (0x62) Char: 0x00A2 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3C, 0x66,
0x06, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 99 (0x63) Char: 0x00A3 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x1F, 0x06, 0x06, 0x66, 0x3F, 0x00, 0x00, 0x00,
// Index: 100 (0x64) Char: 0x00A4 ('<27>')
0x00, 0x00, 0x00, 0x22, 0x1C, 0x36, 0x22, 0x36,
0x1C, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 101 (0x65) Char: 0x00A5 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
0x3C, 0x7E, 0x18, 0x7E, 0x18, 0x00, 0x00, 0x00,
// Index: 102 (0x66) Char: 0x00A6 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 103 (0x67) Char: 0x00A7 ('<27>')
0x00, 0x00, 0x00, 0x3C, 0x46, 0x0E, 0x1A, 0x36,
0x6C, 0x58, 0x70, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 104 (0x68) Char: 0x00A8 ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 105 (0x69) Char: 0x00A9 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x99,
0x85, 0x85, 0x99, 0x42, 0x3C, 0x00, 0x00, 0x00,
// Index: 106 (0x6A) Char: 0x00AA ('<27>')
0x00, 0x00, 0x1C, 0x20, 0x3C, 0x36, 0x3C, 0x00,
0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 107 (0x6B) Char: 0x00AB ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x66,
0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 108 (0x6C) Char: 0x00AC ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 109 (0x6D) Char: 0x00AD ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 110 (0x6E) Char: 0x00AE ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x9D,
0xA5, 0x9D, 0xA5, 0x42, 0x3C, 0x00, 0x00, 0x00,
// Index: 111 (0x6F) Char: 0x00AF ('<27>')
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 112 (0x70) Char: 0x00B0 ('<27>')
0x00, 0x00, 0x00, 0x1C, 0x36, 0x36, 0x36, 0x1C,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 113 (0x71) Char: 0x00B1 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x00, 0x00,
// Index: 114 (0x72) Char: 0x00B2 ('<27>')
0x00, 0x04, 0x0A, 0x08, 0x04, 0x02, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 115 (0x73) Char: 0x00B3 ('<27>')
0x00, 0x06, 0x08, 0x06, 0x08, 0x08, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 116 (0x74) Char: 0x00B4 ('<27>')
0x00, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 117 (0x75) Char: 0x00B5 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
// Index: 118 (0x76) Char: 0x00B6 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x7E, 0x6F, 0x6F, 0x6F,
0x6E, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00,
// Index: 119 (0x77) Char: 0x00B7 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 120 (0x78) Char: 0x00B8 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x00,
// Index: 121 (0x79) Char: 0x00B9 ('<27>')
0x00, 0x04, 0x06, 0x04, 0x04, 0x04, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 122 (0x7A) Char: 0x00BA ('<27>')
0x00, 0x0E, 0x0A, 0x0E, 0x00, 0x0E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 123 (0x7B) Char: 0x00BB ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66,
0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 124 (0x7C) Char: 0x00BC ('<27>')
0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0xA0,
0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00,
// Index: 125 (0x7D) Char: 0x00BD ('<27>')
0x00, 0x04, 0x06, 0x84, 0x44, 0x24, 0x0E, 0x40,
0xA8, 0x84, 0x42, 0x20, 0xE0, 0x00, 0x00, 0x00,
// Index: 126 (0x7E) Char: 0x00BE ('<27>')
0x00, 0x06, 0x08, 0x86, 0x48, 0x28, 0x06, 0xA0,
0xA8, 0xA4, 0xE2, 0x80, 0x80, 0x00, 0x00, 0x00,
// Index: 127 (0x7F) Char: 0x00BF ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x18, 0x0C, 0x06, 0x66, 0x66, 0x3C, 0x00,
// Index: 128 (0x80) Char: 0x00C0 ('<27>')
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 129 (0x81) Char: 0x00C1 ('<27>')
0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 130 (0x82) Char: 0x00C2 ('<27>')
0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 131 (0x83) Char: 0x00C3 ('<27>')
0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33,
0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
// Index: 132 (0x84) Char: 0x00C4 ('<27>')
0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 133 (0x85) Char: 0x00C5 ('<27>')
0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 134 (0x86) Char: 0x00C6 ('<27>')
0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33,
0xF3, 0x3F, 0x33, 0x33, 0xF3, 0x00, 0x00, 0x00,
// Index: 135 (0x87) Char: 0x00C7 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C,
// Index: 136 (0x88) Char: 0x00C8 ('<27>')
0x00, 0x0C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 137 (0x89) Char: 0x00C9 ('<27>')
0x00, 0x30, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 138 (0x8A) Char: 0x00CA ('<27>')
0x18, 0x3C, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 139 (0x8B) Char: 0x00CB ('<27>')
0x00, 0x66, 0x66, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 140 (0x8C) Char: 0x00CC ('<27>')
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 141 (0x8D) Char: 0x00CD ('<27>')
0x00, 0x30, 0x18, 0x00, 0x3C, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 142 (0x8E) Char: 0x00CE ('<27>')
0x0C, 0x1E, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
// Index: 143 (0x8F) Char: 0x00CF ('<27>')
0x00, 0x32, 0x32, 0x00, 0x1E, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
// Index: 144 (0x90) Char: 0x00D0 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x6E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
// Index: 145 (0x91) Char: 0x00D1 ('<27>')
0x00, 0x6E, 0x3B, 0x00, 0x33, 0x33, 0x37, 0x3F,
0x3B, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
// Index: 146 (0x92) Char: 0x00D2 ('<27>')
0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 147 (0x93) Char: 0x00D3 ('<27>')
0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 148 (0x94) Char: 0x00D4 ('<27>')
0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 149 (0x95) Char: 0x00D5 ('<27>')
0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
// Index: 150 (0x96) Char: 0x00D6 ('<27>')
0x00, 0x66, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 151 (0x97) Char: 0x00D7 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66,
0x3C, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00,
// Index: 152 (0x98) Char: 0x00D8 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x5E, 0x73, 0x33, 0x3B,
0x3F, 0x37, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
// Index: 153 (0x99) Char: 0x00D9 ('<27>')
0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 154 (0x9A) Char: 0x00DA ('<27>')
0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 155 (0x9B) Char: 0x00DB ('<27>')
0x18, 0x3C, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 156 (0x9C) Char: 0x00DC ('<27>')
0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 157 (0x9D) Char: 0x00DD ('<27>')
0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66,
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 158 (0x9E) Char: 0x00DE ('<27>')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
0x66, 0x66, 0x3E, 0x06, 0x06, 0x00, 0x00, 0x00,
// Index: 159 (0x9F) Char: 0x00DF ('<27>')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66,
0x36, 0x66, 0x66, 0x66, 0x36, 0x00, 0x00, 0x00,
// Index: 160 (0xA0) Char: 0x00E0 ('<27>')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 161 (0xA1) Char: 0x00E1 ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 162 (0xA2) Char: 0x00E2 ('<27>')
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 163 (0xA3) Char: 0x00E3 ('<27>')
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33,
0x30, 0x3E, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00,
// Index: 164 (0xA4) Char: 0x00E4 ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 165 (0xA5) Char: 0x00E5 ('<27>')
0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x3C, 0x66,
0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 166 (0xA6) Char: 0x00E6 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33,
0x30, 0xFE, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
// Index: 167 (0xA7) Char: 0x00E7 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66,
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x18, 0x0C,
// Index: 168 (0xA8) Char: 0x00E8 ('<27>')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 169 (0xA9) Char: 0x00E9 ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 170 (0xAA) Char: 0x00EA ('<27>')
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 171 (0xAB) Char: 0x00EB ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 172 (0xAC) Char: 0x00EC ('<27>')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x1C, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 173 (0xAD) Char: 0x00ED ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x1C, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 174 (0xAE) Char: 0x00EE ('<27>')
0x00, 0x00, 0x0C, 0x1E, 0x32, 0x00, 0x0E, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
// Index: 175 (0xAF) Char: 0x00EF ('<27>')
0x00, 0x00, 0x00, 0x32, 0x32, 0x00, 0x0E, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00,
// Index: 176 (0xB0) Char: 0x00F0 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x30, 0x7C, 0x30, 0x3E,
0x33, 0x33, 0x33, 0x33, 0x3E, 0x00, 0x00, 0x00,
// Index: 177 (0xB1) Char: 0x00F1 ('<27>')
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1F, 0x33,
0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00,
// Index: 178 (0xB2) Char: 0x00F2 ('<27>')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 179 (0xB3) Char: 0x00F3 ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 180 (0xB4) Char: 0x00F4 ('<27>')
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x3C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 181 (0xB5) Char: 0x00F5 ('<27>')
0x00, 0x00, 0x00, 0x6E, 0x3B, 0x00, 0x1E, 0x33,
0x33, 0x33, 0x33, 0x33, 0x1E, 0x00, 0x00, 0x00,
// Index: 182 (0xB6) Char: 0x00F6 ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x3C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 183 (0xB7) Char: 0x00F7 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
// Index: 184 (0xB8) Char: 0x00F8 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7E, 0x33,
0x3B, 0x3F, 0x37, 0x33, 0x1F, 0x00, 0x00, 0x00,
// Index: 185 (0xB9) Char: 0x00F9 ('<27>')
0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 186 (0xBA) Char: 0x00FA ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 187 (0xBB) Char: 0x00FB ('<27>')
0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 188 (0xBC) Char: 0x00FC ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 189 (0xBD) Char: 0x00FD ('<27>')
0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
// Index: 190 (0xBE) Char: 0x00FE ('<27>')
0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
// Index: 191 (0xBF) Char: 0x00FF ('<27>')
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00,
// Index: 192 (0xC0) Char: 0x010C ('C')
0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x06,
0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 193 (0xC1) Char: 0x010D ('c')
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x06, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 194 (0xC2) Char: 0x010E ('D')
0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00,
// Index: 195 (0xC3) Char: 0x010F ('d')
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 196 (0xC4) Char: 0x011A ('E')
0x66, 0x3C, 0x18, 0x00, 0x7E, 0x06, 0x06, 0x06,
0x3E, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x00,
// Index: 197 (0xC5) Char: 0x011B ('e')
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
0x66, 0x7E, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 198 (0xC6) Char: 0x0131 ('i')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x18,
0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00,
// Index: 199 (0xC7) Char: 0x0147 ('N')
0x66, 0x3C, 0x18, 0x00, 0x66, 0x66, 0x6E, 0x7E,
0x76, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 200 (0xC8) Char: 0x0148 ('n')
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 201 (0xC9) Char: 0x0152 ('<27>')
0x00, 0x00, 0x00, 0x00, 0xFE, 0x33, 0x33, 0x33,
0xF3, 0x33, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
// Index: 202 (0xCA) Char: 0x0153 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x33,
0x33, 0xF3, 0x33, 0x33, 0xFE, 0x00, 0x00, 0x00,
// Index: 203 (0xCB) Char: 0x0158 ('R')
0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66, 0x66, 0x66,
0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 204 (0xCC) Char: 0x0159 ('r')
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3E, 0x66,
0x66, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00,
// Index: 205 (0xCD) Char: 0x0160 ('<27>')
0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x0C,
0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 206 (0xCE) Char: 0x0161 ('<27>')
0x00, 0x00, 0x66, 0x3C, 0x18, 0x00, 0x3C, 0x66,
0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 207 (0xCF) Char: 0x0164 ('T')
0x66, 0x3C, 0x18, 0x00, 0x7E, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 208 (0xD0) Char: 0x0165 ('t')
0x00, 0x00, 0x00, 0x00, 0x86, 0x86, 0x1F, 0x86,
0x06, 0x06, 0x06, 0x06, 0x1C, 0x00, 0x00, 0x00,
// Index: 209 (0xD1) Char: 0x016E ('U')
0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 210 (0xD2) Char: 0x016F ('u')
0x00, 0x00, 0x3C, 0x66, 0x3C, 0x00, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00,
// Index: 211 (0xD3) Char: 0x0178 ('<27>')
0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66,
0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 212 (0xD4) Char: 0x017D ('<27>')
0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30, 0x30, 0x18,
0x0C, 0x06, 0x02, 0x02, 0x3E, 0x00, 0x00, 0x00,
// Index: 213 (0xD5) Char: 0x017E ('<27>')
0x00, 0x00, 0x32, 0x1E, 0x0C, 0x00, 0x3E, 0x30,
0x18, 0x0C, 0x06, 0x02, 0x3E, 0x00, 0x00, 0x00,
// Index: 214 (0xD6) Char: 0x0192 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x0C, 0x3E,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x06, 0x00,
// Index: 215 (0xD7) Char: 0x2014 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 216 (0xD8) Char: 0x2018 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 217 (0xD9) Char: 0x2019 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 218 (0xDA) Char: 0x201A ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0x10, 0x08, 0x00,
// Index: 219 (0xDB) Char: 0x201C ('<27>')
0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 220 (0xDC) Char: 0x201D ('<27>')
0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 221 (0xDD) Char: 0x201E ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x36, 0x36, 0x24, 0x12, 0x00,
// Index: 222 (0xDE) Char: 0x2020 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
// Index: 223 (0xDF) Char: 0x2022 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 224 (0xE0) Char: 0x2026 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
// Index: 225 (0xE1) Char: 0x2039 ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 226 (0xE2) Char: 0x203A ('<27>')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18,
0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
// Index: 227 (0xE3) Char: 0x20AC ('<27>')
0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x1F,
0x06, 0x1F, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00,
// Index: 228 (0xE4) Char: 0x2122 ('<27>')
0x00, 0x00, 0x00, 0x00, 0xDF, 0xAA, 0x8A, 0x8A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
}
const ui::Font fixed_8x16 {
8, 16,
fixed_8x16_glyph_data,
0x20, 223,
};
} /* namespace font */
} /* namespace ui */

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_FONT_FIXED_8X16_H__
#define __UI_FONT_FIXED_8X16_H__
#include "ui_text.hpp"
namespace ui {
namespace font {
extern const ui::Font fixed_8x16;
} /* namespace font */
} /* namspace ui */
#endif/*__UI_FONT_FIXED_8X16_H__*/

View File

@ -0,0 +1,378 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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.
*/
#include "ui_geomap.hpp"
#include "portapack.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
#include "string_format.hpp"
#include "complex.hpp"
namespace ui {
GeoPos::GeoPos(
const Point pos,
const alt_unit altitude_unit
) : altitude_unit_(altitude_unit) {
set_parent_rect({pos, { 30 * 8, 3 * 16 }});
add_children({
&labels_position,
&field_altitude,
&text_alt_unit,
&field_lat_degrees,
&field_lat_minutes,
&field_lat_seconds,
&text_lat_decimal,
&field_lon_degrees,
&field_lon_minutes,
&field_lon_seconds,
&text_lon_decimal
});
// Defaults
set_altitude(0);
set_lat(0);
set_lon(0);
const auto changed_fn = [this](int32_t) {
float lat_value = lat();
float lon_value = lon();
text_lat_decimal.set(to_string_decimal(lat_value, 5));
text_lon_decimal.set(to_string_decimal(lon_value, 5));
if (on_change && report_change)
on_change(altitude(), lat_value, lon_value);
};
field_altitude.on_change = changed_fn;
field_lat_degrees.on_change = changed_fn;
field_lat_minutes.on_change = changed_fn;
field_lat_seconds.on_change = changed_fn;
field_lon_degrees.on_change = changed_fn;
field_lon_minutes.on_change = changed_fn;
field_lon_seconds.on_change = changed_fn;
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
}
void GeoPos::set_read_only(bool v) {
for(auto child : children_)
child->set_focusable(!v);
}
// Stupid hack to avoid an event loop
void GeoPos::set_report_change(bool v) {
report_change = v;
}
void GeoPos::focus() {
field_altitude.focus();
}
void GeoPos::set_altitude(int32_t altitude) {
field_altitude.set_value(altitude);
}
void GeoPos::set_lat(float lat) {
field_lat_degrees.set_value(lat);
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
}
void GeoPos::set_lon(float lon) {
field_lon_degrees.set_value(lon);
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
}
float GeoPos::lat() {
if (field_lat_degrees.value() < 0) {
return -1 * ( -1 * field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0));
} else {
return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0);
}
};
float GeoPos::lon() {
if (field_lon_degrees.value() < 0) {
return -1 * (-1 * field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0));
} else {
return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0);
}
};
int32_t GeoPos::altitude() {
return field_altitude.value();
};
GeoMap::GeoMap(
Rect parent_rect
) : Widget { parent_rect }
{
//set_focusable(true);
}
void GeoMap::paint(Painter& painter) {
u_int16_t line;
std::array<ui::Color, 240> map_line_buffer;
const auto r = screen_rect();
// Ony redraw map if it moved by at least 1 pixel
if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) {
for (line = 0; line < r.height(); line++) {
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
map_file.read(map_line_buffer.data(), r.width() << 1);
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
}
prev_x_pos = x_pos;
prev_y_pos = y_pos;
}
//center tag above point
if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces
painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
}
if (mode_ == PROMPT) {
// Cross
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
} else if (angle_ < 360){
//if we have a valid angle draw bearing
draw_bearing(r.center(), angle_, 10, Color::red());
}
else {
//draw a small cross
display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red());
display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red());
}
}
bool GeoMap::on_touch(const TouchEvent event) {
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
set_highlighted(true);
if (on_move) {
Point p = event.point - screen_rect().center();
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
return true;
}
}
return false;
}
void GeoMap::move(const float lon, const float lat) {
lon_ = lon;
lat_ = lat;
Rect map_rect = screen_rect();
// Using WGS 84/Pseudo-Mercator projection
x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2);
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
double map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
double lat_rad = sin(lat * pi / 180);
double map_world_lon = map_width / (2 * pi);
double map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
// Cap position
if (x_pos > (map_width - map_rect.width()))
x_pos = map_width - map_rect.width();
if (y_pos > (map_height + map_rect.height()))
y_pos = map_height - map_rect.height();
}
bool GeoMap::init() {
auto result = map_file.open("ADSB/world_map.bin");
if (result.is_valid())
return false;
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
map_center_x = map_width >> 1;
map_center_y = map_height >> 1;
lon_ratio = 180.0 / map_center_x;
lat_ratio = -90.0 / map_center_y;
return true;
}
void GeoMap::set_mode(GeoMapMode mode) {
mode_ = mode;
}
void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
Point arrow_a, arrow_b, arrow_c;
for (size_t thickness = 0; thickness < 3; thickness++) {
arrow_a = polar_to_point(angle, size) + origin;
arrow_b = polar_to_point(angle + 180 - 35, size) + origin;
arrow_c = polar_to_point(angle + 180 + 35, size) + origin;
display.draw_line(arrow_a, arrow_b, color);
display.draw_line(arrow_b, arrow_c, color);
display.draw_line(arrow_c, arrow_a, color);
size--;
}
}
void GeoMapView::focus() {
geopos.focus();
if (!map_opened)
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
}
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
lat_ = lat;
lon_ = lon;
altitude_ = altitude;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.set_altitude(altitude_);
geopos.set_report_change(true);
geomap.set_angle(angle);
geomap.move(lon_, lat_);
geomap.set_dirty();
}
void GeoMapView::setup() {
add_child(&geomap);
geopos.set_altitude(altitude_);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
altitude_ = altitude;
lat_ = lat;
lon_ = lon;
geomap.move(lon_, lat_);
geomap.set_dirty();
};
geomap.on_move = [this](float move_x, float move_y) {
lon_ += move_x;
lat_ += move_y;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lon(lon_);
geopos.set_lat(lat_);
geopos.set_report_change(true);
geomap.move(lon_, lat_);
geomap.set_dirty();
};
}
GeoMapView::~GeoMapView() {
if (on_close_)
on_close_();
}
// Display mode
GeoMapView::GeoMapView(
NavigationView& nav,
const std::string& tag,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
uint16_t angle,
const std::function<void(void)> on_close
) : nav_ (nav),
altitude_ (altitude),
altitude_unit_ (altitude_unit),
lat_ (lat),
lon_ (lon),
angle_ (angle),
on_close_(on_close)
{
mode_ = DISPLAY;
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
setup();
geomap.set_mode(mode_);
geomap.set_tag(tag);
geomap.set_angle(angle);
geomap.move(lon_, lat_);
geopos.set_read_only(true);
}
// Prompt mode
GeoMapView::GeoMapView(
NavigationView& nav,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
const std::function<void(int32_t, float, float)> on_done
) : nav_ (nav),
altitude_ (altitude),
altitude_unit_ (altitude_unit),
lat_ (lat),
lon_ (lon)
{
mode_ = PROMPT;
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
setup();
add_child(&button_ok);
geomap.set_mode(mode_);
geomap.move(lon_, lat_);
button_ok.on_select = [this, on_done, &nav](Button&) {
if (on_done)
on_done(altitude_, lat_, lon_);
nav.pop();
};
}
} /* namespace ui */

View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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 __GEOMAP_H__
#define __GEOMAP_H__
#include "ui.hpp"
#include "file.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "portapack.hpp"
namespace ui {
enum GeoMapMode {
DISPLAY,
PROMPT
};
class GeoPos : public View {
public:
enum alt_unit {
FEET = 0,
METERS
};
std::function<void(int32_t, float, float)> on_change { };
GeoPos(const Point pos, const alt_unit altitude_unit);
void focus() override;
void set_read_only(bool v);
void set_altitude(int32_t altitude);
void set_lat(float lat);
void set_lon(float lon);
int32_t altitude();
float lat();
float lon();
void set_report_change(bool v);
private:
bool read_only { false };
bool report_change { true };
alt_unit altitude_unit_ { };
Labels labels_position {
{ { 1 * 8, 0 * 16 }, "Alt:", Color::light_grey() },
{ { 1 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font
{ { 1 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() },
};
NumberField field_altitude {
{ 6 * 8, 0 * 16 },
5,
{ -1000, 50000 },
250,
' '
};
Text text_alt_unit {
{ 12 * 8, 0 * 16, 2 * 8, 16 },
""
};
NumberField field_lat_degrees {
{ 5 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' '
};
NumberField field_lat_minutes {
{ 10 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
};
NumberField field_lat_seconds {
{ 13 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' '
};
Text text_lat_decimal {
{ 17 * 8, 1 * 16, 13 * 8, 1 * 16 },
""
};
NumberField field_lon_degrees {
{ 5 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' '
};
NumberField field_lon_minutes {
{ 10 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
};
NumberField field_lon_seconds {
{ 13 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' '
};
Text text_lon_decimal {
{ 17 * 8, 2 * 16, 13 * 8, 1 * 16 },
""
};
};
class GeoMap : public Widget {
public:
std::function<void(float, float)> on_move { };
GeoMap(Rect parent_rect);
void paint(Painter& painter) override;
bool on_touch(const TouchEvent event) override;
bool init();
void set_mode(GeoMapMode mode);
void move(const float lon, const float lat);
void set_tag(std::string new_tag) {
tag_ = new_tag;
}
void set_angle(uint16_t new_angle){
angle_ = new_angle;
}
private:
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
GeoMapMode mode_ { };
File map_file { };
uint16_t map_width { }, map_height { };
int32_t map_center_x { }, map_center_y { };
float lon_ratio { }, lat_ratio { };
int32_t x_pos { }, y_pos { };
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
float lat_ { };
float lon_ { };
uint16_t angle_ { };
std::string tag_ { };
};
class GeoMapView : public View {
public:
GeoMapView(
NavigationView& nav,
const std::string& tag,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
uint16_t angle,
const std::function<void(void)> on_close = nullptr
);
GeoMapView(NavigationView& nav,
int32_t altitude,
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
const std::function<void(int32_t, float, float)> on_done
);
~GeoMapView();
GeoMapView(const GeoMapView&) = delete;
GeoMapView(GeoMapView&&) = delete;
GeoMapView& operator=(const GeoMapView&) = delete;
GeoMapView& operator=(GeoMapView&&) = delete;
void focus() override;
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
std::string title() const override { return "Map view"; };
private:
NavigationView& nav_;
void setup();
const std::function<void(int32_t, float, float)> on_done { };
const Dim banner_height = 3 * 16;
GeoMapMode mode_ { };
int32_t altitude_ { };
GeoPos::alt_unit altitude_unit_ { };
float lat_ { };
float lon_ { };
uint16_t angle_ { };
std::function<void(void)> on_close_ { nullptr };
bool map_opened { };
GeoPos geopos {
{ 0, 0 },
altitude_unit_
};
GeoMap geomap {
{ 0, banner_height, 240, 320 - 16 - banner_height }
};
Button button_ok {
{ 20 * 8, 8, 8 * 8, 2 * 16 },
"OK"
};
};
} /* namespace ui */
#endif

View File

@ -0,0 +1,302 @@
/*
* 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.
*/
#include "ui_menu.hpp"
#include "rtc_time.hpp"
namespace ui {
/* MenuItemView **********************************************************/
void MenuItemView::set_item(MenuItem* item_) {
item = item_;
}
void MenuItemView::highlight() {
set_highlighted(true);
set_dirty();
}
void MenuItemView::unhighlight() {
set_highlighted(false);
set_dirty();
}
void MenuItemView::paint(Painter& painter) {
Coord offset_x { };
if (!item) return;
const auto r = screen_rect();
const auto paint_style = (highlighted() && (parent()->has_focus() || keep_highlight)) ? style().invert() : style();
const auto font_height = paint_style.font.line_height();
ui::Color final_item_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? paint_style.foreground : item->color;
ui::Color final_bg_color = (highlighted() && (parent()->has_focus() || keep_highlight)) ? item->color : paint_style.background;
if (final_item_color.v == final_bg_color.v) final_item_color = paint_style.foreground;
painter.fill_rectangle(
r,
final_bg_color
);
if (item->bitmap) {
painter.draw_bitmap(
{ r.location().x() + 4, r.location().y() + 4 },
*item->bitmap,
final_item_color,
final_bg_color
);
offset_x = 26;
} else
offset_x = 0;
Style text_style {
.font = paint_style.font,
.background = final_bg_color,
.foreground = final_item_color
};
painter.draw_string(
{ r.location().x() + offset_x, r.location().y() + (r.size().height() - font_height) / 2 },
text_style,
item->text
);
}
/* MenuView **************************************************************/
MenuView::MenuView(
Rect new_parent_rect,
bool keep_highlight
) : keep_highlight { keep_highlight }
{
set_parent_rect(new_parent_rect);
set_focusable(true);
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};
add_child(&arrow_more);
arrow_more.set_focusable(false);
arrow_more.set_foreground(Color::black());
}
MenuView::~MenuView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
for (auto item : menu_item_views) {
delete item;
}
}
void MenuView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
displayed_max = (parent_rect().size().height() / item_height);
arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * item_height), 8, 8 } );
// TODO: Clean this up :(
if (menu_item_views.size()) {
for (auto item : menu_item_views) {
remove_child(item);
delete item;
}
menu_item_views.clear();
}
for (size_t c = 0; c < displayed_max; c++) {
auto item = new MenuItemView { keep_highlight };
menu_item_views.push_back(item);
add_child(item);
Coord y_pos = c * item_height;
item->set_parent_rect({
{ 0, y_pos },
{ size().width(), (Coord)item_height }
});
}
update_items();
}
void MenuView::on_tick_second() {
if (more && blink)
arrow_more.set_foreground(Color::white());
else
arrow_more.set_foreground(Color::black());
blink = !blink;
arrow_more.set_dirty();
}
void MenuView::clear() {
for (auto item : menu_item_views) {
item->set_item(nullptr);
}
menu_items.clear();
}
void MenuView::add_item(MenuItem new_item) {
menu_items.push_back(new_item);
update_items();
}
void MenuView::add_items(std::initializer_list<MenuItem> new_items) {
for (auto item : new_items) {
add_item(item);
}
}
void MenuView::update_items() {
size_t i = 0;
if (menu_items.size() > displayed_max + offset) {
more = true;
blink = true;
} else
more = false;
for (auto item : menu_item_views) {
if (i >= menu_items.size()) break;
// Assign item data to MenuItemViews according to offset
item->set_item(&menu_items[i + offset]);
item->set_dirty();
if (highlighted_item == (i + offset)) {
item->highlight();
} else
item->unhighlight();
i++;
}
}
MenuItemView* MenuView::item_view(size_t index) const {
return menu_item_views[index];
}
bool MenuView::set_highlighted(int32_t new_value) {
int32_t item_count = (int32_t)menu_items.size();
if (new_value < 0)
return false;
if (new_value >= item_count)
new_value = item_count - 1;
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
// Shift MenuView up
highlighted_item = new_value;
offset = new_value - displayed_max + 1;
update_items();
} else if ((uint32_t)new_value < offset) {
// Shift MenuView down
highlighted_item = new_value;
offset = new_value;
update_items();
} else {
// Just update highlight
item_view(highlighted_item - offset)->unhighlight();
highlighted_item = new_value;
item_view(highlighted_item - offset)->highlight();
}
if (on_highlight)
on_highlight();
return true;
}
uint32_t MenuView::highlighted_index() {
return highlighted_item;
}
void MenuView::on_focus() {
item_view(highlighted_item - offset)->highlight();
}
void MenuView::on_blur() {
if (!keep_highlight)
item_view(highlighted_item - offset)->unhighlight();
}
bool MenuView::on_key(const KeyEvent key) {
switch(key) {
case KeyEvent::Up:
return set_highlighted(highlighted_item - 1);
case KeyEvent::Down:
return set_highlighted(highlighted_item + 1);
case KeyEvent::Select:
case KeyEvent::Right:
if( menu_items[highlighted_item].on_select ) {
menu_items[highlighted_item].on_select();
}
return true;
case KeyEvent::Left:
if( on_left ) {
on_left();
}
return true;
default:
return false;
}
}
bool MenuView::on_encoder(const EncoderEvent event) {
set_highlighted(highlighted_item + event);
return true;
}
/* TODO: This could be handled by default behavior, if the UI stack were to
* transmit consumable events from the top of the hit-stack down, and each
* MenuItem could respond to a touch and update its parent MenuView.
*/
/*
bool MenuView::on_touch(const TouchEvent event) {
size_t i = 0;
for(const auto child : children_) {
if( child->screen_rect().contains(event.point) ) {
return set_highlighted(i);
}
i++;
}
return false;
}
*/
} /* namespace ui */

View File

@ -0,0 +1,125 @@
/*
* 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 __UI_MENU_H__
#define __UI_MENU_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "bitmap.hpp"
#include "signal.hpp"
#include <cstddef>
#include <string>
#include <functional>
namespace ui {
struct MenuItem {
std::string text;
ui::Color color;
const Bitmap* bitmap;
std::function<void(void)> on_select;
// TODO: Prevent default-constructed MenuItems.
// I managed to construct a menu with three extra, unspecified menu items
// in the array that were default constructed...
};
class MenuItemView : public Widget {
public:
MenuItemView(
bool keep_highlight
) : keep_highlight { keep_highlight }
{
}
MenuItemView(const MenuItemView&) = delete;
MenuItemView(MenuItemView&&) = delete;
MenuItemView& operator=(const MenuItemView&) = delete;
MenuItemView& operator=(MenuItemView&&) = delete;
void paint(Painter& painter) override;
void set_item(MenuItem* item_);
void highlight();
void unhighlight();
private:
MenuItem* item { nullptr };
bool keep_highlight = false;
};
class MenuView : public View {
public:
std::function<void(void)> on_left { };
std::function<void(void)> on_highlight { nullptr };
MenuView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
~MenuView();
void add_item(MenuItem new_item);
void add_items(std::initializer_list<MenuItem> new_items);
void clear();
MenuItemView* item_view(size_t index) const;
bool set_highlighted(int32_t new_value);
uint32_t highlighted_index();
void set_parent_rect(const Rect new_parent_rect) override;
void on_focus() override;
void on_blur() override;
bool on_key(const KeyEvent event) override;
bool on_encoder(const EncoderEvent event) override;
private:
void update_items();
void on_tick_second();
bool keep_highlight { false };
SignalToken signal_token_tick_second { };
std::vector<MenuItem> menu_items { };
std::vector<MenuItemView*> menu_item_views { };
Image arrow_more {
{ 228, 320 - 8, 8, 8 },
&bitmap_more,
Color::white(),
Color::black()
};
const size_t item_height = 24;
bool blink = false;
bool more = false;
size_t displayed_max { 0 };
size_t highlighted_item { 0 };
size_t offset { 0 };
};
} /* namespace ui */
#endif/*__UI_MENU_H__*/

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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.
*/
#include "ui_qrcode.hpp"
#include "qrcodegen.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
#include "string_format.hpp"
#include "complex.hpp"
namespace ui {
QRCodeImage::QRCodeImage(
Rect parent_rect
) : Widget { parent_rect }
{
}
QRCodeImage::~QRCodeImage( )
{
}
QRCodeImage::QRCodeImage(const QRCodeImage&Image) : Widget { }
{
(void)Image;
}
QRCodeImage & QRCodeImage::operator=(const QRCodeImage&Image)
{
(void)Image;
return *this;
}
void QRCodeImage::paint(Painter& painter) {
(void)painter ;
// The structure to manage the QR code
QRCode qrcode;
//Either small or large QR code can be shown..
if(portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code
int qr_version = 2;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
display.fill_rectangle(Rect(10, 30, 220, 220), Color::white());
for (uint8_t y = 0; y < qrcode.size; y++) {
for (uint8_t x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y)) {
display.fill_rectangle(Rect(20+(x*8), 40+(y*8), 8, 8), Color::black());
}
}
}
}
else { // show small QR code
int qr_version = 10;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
for (uint8_t y = 0; y < qrcode.size; y++) {
for (uint8_t x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y)) {
display.draw_pixel(Point(95+x,100+y), Color::black());
}
}
}
}
}
void QRCodeView::focus() {
button_close.focus();
}
QRCodeView::~QRCodeView() {
if (on_close_)
on_close_();
}
QRCodeView::QRCodeView(
NavigationView& nav,
const char * qr_text,
const std::function<void(void)> on_close
) : nav_ (nav),
on_close_(on_close)
{
add_children({
&qr_code,
&button_close});
//text_qr.set(qr_text);
qr_code.set_text(qr_text);
button_close.on_select = [&nav](Button&){
nav.pop();
};
}
} /* namespace ui */

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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 __QRCODE_H__
#define __QRCODE_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "qrcodegen.hpp"
#include "portapack.hpp"
namespace ui {
class QRCodeImage : public Widget {
public:
QRCodeImage(Rect parent_rect);
void set_text(const char * qr_text) {
qr_text_ = qr_text;
}
void paint(Painter& painter) override;
// for -weffc++ to be killed
~QRCodeImage(); // destructor
QRCodeImage(const QRCodeImage&Image);
QRCodeImage & operator=(const QRCodeImage &Image); // assignment
private:
const char * qr_text_ = NULL ;
};
class QRCodeView : public View {
public:
QRCodeView(
NavigationView& nav,
const char * qr_text,
const std::function<void(void)> on_close = nullptr
);
~QRCodeView();
QRCodeView(const QRCodeView&) = delete;
QRCodeView(QRCodeView&&) = delete;
QRCodeView& operator=(const QRCodeView&) = delete;
QRCodeView& operator=(QRCodeView&&) = delete;
std::string title() const override { return "QR code"; };
void focus() override;
private:
NavigationView& nav_;
std::function<void(void)> on_close_ { nullptr };
QRCodeImage qr_code {
{ 50, 100, 100, 100 }
};
//Text text_qr {
// { 0 * 8, 10 * 16, 32 * 8, 1 * 8 },
// "-"
//};
Button button_close {
{ 9 * 8, 31 * 8, 12 * 8, 3 * 16 },
"Back"
};
};
} /* namespace ui */
#endif

View File

@ -0,0 +1,381 @@
/*
* 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.
*/
#include "ui_receiver.hpp"
#include "ui_freqman.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "string_format.hpp"
#include "max2837.hpp"
namespace ui {
/* FrequencyField ********************************************************/
FrequencyField::FrequencyField(
const Point parent_pos
) : Widget { { parent_pos, { 8 * 10, 16 } } },
length_ { 11 },
range(rf::tuning_range)
{
set_focusable(true);
}
rf::Frequency FrequencyField::value() const {
return value_;
}
void FrequencyField::set_value(rf::Frequency new_value) {
new_value = clamp_value(new_value);
if( new_value != value_ ) {
value_ = new_value;
if( on_change ) {
on_change(value_);
}
set_dirty();
}
}
void FrequencyField::set_step(rf::Frequency new_value) {
step = new_value;
// TODO: Quantize current frequency to a step of the new size?
}
void FrequencyField::paint(Painter& painter) {
const std::string str_value = to_string_short_freq(value_);
const auto paint_style = has_focus() ? style().invert() : style();
painter.draw_string(
screen_pos(),
paint_style,
str_value
);
}
bool FrequencyField::on_key(const ui::KeyEvent event) {
if( event == ui::KeyEvent::Select ) {
if( on_edit ) {
on_edit();
return true;
}
}
return false;
}
bool FrequencyField::on_encoder(const EncoderEvent delta) {
set_value(value() + (delta * step));
return true;
}
bool FrequencyField::on_touch(const TouchEvent event) {
if( event.type == TouchEvent::Type::Start ) {
focus();
}
return true;
}
void FrequencyField::on_focus() {
if( on_show_options ) {
on_show_options();
}
}
rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
return range.clip(value);
}
/* FrequencyKeypadView ***************************************************/
bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) {
focused_button += delta;
if (focused_button < 0) {
focused_button = buttons.size() - 1;
}
else if (focused_button >= (int16_t)buttons.size()) {
focused_button = 0;
}
buttons[focused_button].focus();
return true;
}
FrequencyKeypadView::FrequencyKeypadView(
NavigationView& nav,
const rf::Frequency value
) {
add_child(&text_value);
const auto button_fn = [this](Button& button) {
this->on_button(button);
};
const char* const key_caps = "123456789<0.";
int n = 0;
for(auto& button : buttons) {
add_child(&button);
const std::string label {
key_caps[n]
};
button.id = n;
button.on_highlight = [this](Button& button) {
focused_button = button.id;
};
button.on_select = button_fn;
button.set_parent_rect({
(n % 3) * button_w,
(n / 3) * button_h + 24,
button_w, button_h
});
button.set_text(label);
n++;
}
add_children({
&button_save,
&button_load,
&button_close
});
button_save.on_select = [this, &nav](Button&) {
nav.push<FrequencySaveView>(this->value());
};
button_load.on_select = [this, &nav](Button&) {
auto load_view = nav.push<FrequencyLoadView>();
load_view->on_frequency_loaded = [this](rf::Frequency value) {
set_value(value);
};
};
button_close.on_select = [this, &nav](Button&) {
if( on_changed ) {
on_changed(this->value());
}
nav.pop();
};
set_value(value);
}
void FrequencyKeypadView::focus() {
button_close.focus();
}
rf::Frequency FrequencyKeypadView::value() const {
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
}
void FrequencyKeypadView::set_value(const rf::Frequency new_value) {
mhz.set(new_value / 1000000);
mhz.remove_leading_zeros();
submhz.set((new_value % 1000000) / submhz_base);
submhz.remove_trailing_zeros();
update_text();
}
void FrequencyKeypadView::on_button(Button& button) {
const auto s = button.text();
if( s == "." ) {
field_toggle();
} else if( s == "<" ) {
digit_delete();
} else {
digit_add(s[0]);
}
update_text();
}
void FrequencyKeypadView::digit_add(const char c) {
if( state == State::DigitMHz ) {
if( clear_field_if_digits_entered ) {
mhz.clear();
}
mhz.add_digit(c);
} else {
submhz.add_digit(c);
}
clear_field_if_digits_entered = false;
}
void FrequencyKeypadView::digit_delete() {
if( state == State::DigitMHz ) {
mhz.delete_digit();
} else {
submhz.delete_digit();
}
}
void FrequencyKeypadView::field_toggle() {
if( state == State::DigitMHz ) {
state = State::DigitSubMHz;
submhz.clear();
} else {
state = State::DigitMHz;
clear_field_if_digits_entered = true;
}
}
void FrequencyKeypadView::update_text() {
const auto s = mhz.as_string() + "." + submhz.as_string();
text_value.set(s);
}
/* FrequencyOptionsView **************************************************/
FrequencyOptionsView::FrequencyOptionsView(
const Rect parent_rect,
const Style* const style
) : View { parent_rect }
{
set_style(style);
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_step_changed(v);
};
field_ppm.on_change = [this](int32_t v) {
this->on_reference_ppm_correction_changed(v);
};
add_children({
&text_step,
&field_step,
});
if( portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::Xtal ) {
add_child(&field_ppm);
add_child(&text_ppm);
}
}
void FrequencyOptionsView::set_step(rf::Frequency f) {
field_step.set_by_value(f);
}
void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) {
field_ppm.set_value(v);
}
void FrequencyOptionsView::on_step_changed(rf::Frequency v) {
if( on_change_step ) {
on_change_step(v);
}
}
void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) {
if( on_change_reference_ppm_correction ) {
on_change_reference_ppm_correction(v);
}
}
/* RFAmpField ************************************************************/
RFAmpField::RFAmpField(
Point parent_pos
) : NumberField {
parent_pos,
1,
{ 0, 1 },
1,
' ',
}
{
set_value(receiver_model.rf_amp());
on_change = [](int32_t v) {
receiver_model.set_rf_amp(v);
};
}
/* RadioGainOptionsView **************************************************/
RadioGainOptionsView::RadioGainOptionsView(
const Rect parent_rect,
const Style* const style
) : View { parent_rect }
{
set_style(style);
add_children({
&label_rf_amp,
&field_rf_amp,
});
}
/* LNAGainField **********************************************************/
LNAGainField::LNAGainField(
Point parent_pos
) : NumberField {
parent_pos, 2,
{ max2837::lna::gain_db_range.minimum, max2837::lna::gain_db_range.maximum },
max2837::lna::gain_db_step,
' ',
}
{
set_value(receiver_model.lna());
on_change = [](int32_t v) {
receiver_model.set_lna(v);
};
}
void LNAGainField::on_focus() {
//Widget::on_focus();
if( on_show_options ) {
on_show_options();
}
}
/* VGAGainField **********************************************************/
VGAGainField::VGAGainField(
Point parent_pos
) : NumberField {
parent_pos, 2,
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
max2837::vga::gain_db_step,
' ',
}
{
set_value(receiver_model.vga());
on_change = [](int32_t v) {
receiver_model.set_vga(v);
};
}
void VGAGainField::on_focus() {
//Widget::on_focus();
if( on_show_options ) {
on_show_options();
}
}
} /* namespace ui */

View File

@ -0,0 +1,350 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_RECEIVER_H__
#define __UI_RECEIVER_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "rf_path.hpp"
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <functional>
namespace ui {
class FrequencyField : public Widget {
public:
std::function<void(rf::Frequency)> on_change { };
std::function<void(void)> on_edit { };
std::function<void(void)> on_show_options { };
using range_t = rf::FrequencyRange;
FrequencyField(const Point parent_pos);
rf::Frequency value() const;
void set_value(rf::Frequency new_value);
void set_step(rf::Frequency new_value);
void paint(Painter& painter) override;
bool on_key(const ui::KeyEvent event) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
void on_focus() override;
private:
const size_t length_;
const range_t range;
rf::Frequency value_ { 0 };
rf::Frequency step { 25000 };
rf::Frequency clamp_value(rf::Frequency value);
};
template<size_t N>
class FieldString {
public:
enum Justify {
Right,
Left,
};
constexpr FieldString(
Justify justify
) : justify { justify }
{
}
uint32_t as_int() const {
uint32_t value = 0;
for(const auto c : s) {
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
value = (value * 10) + digit;
}
return value;
}
void set(uint32_t value) {
std::generate(s.rbegin(), s.rend(), [&value]() {
const char digit = (value % 10) + '0';
value /= 10;
return digit;
});
}
void clear() {
s.fill(' ');
}
void add_digit(const char c) {
insert_right(c);
}
void delete_digit() {
if( justify == Justify::Right ) {
shift_right();
s.front() = ' ';
} else {
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
return a != ' ';
});
if( first_digit != s.rend() ) {
*first_digit = ' ';
}
}
}
std::string as_string() const {
return { s.data(), s.size() };
}
void remove_leading_zeros() {
remove_zeros(s.begin(), s.end());
}
void remove_trailing_zeros() {
remove_zeros(s.rbegin(), s.rend());
}
private:
using array_type = std::array<char, N>;
array_type s { };
Justify justify { Justify::Left };
template<typename Iterator>
void remove_zeros(Iterator begin, Iterator end) {
const auto first_significant_digit =
std::find_if(begin, end, [](const char& a) {
return a != '0';
});
std::fill(begin, first_significant_digit, ' ');
}
void insert_right(const char c) {
auto insert_point = s.end() - 1;
if( justify == Justify::Left ) {
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
return a == ' ';
});
}
if( *insert_point != ' ' ) {
insert_point = shift_left();
}
*insert_point = c;
}
typename array_type::iterator shift_left() {
return std::move(s.begin() + 1, s.end(), s.begin());
}
typename array_type::iterator shift_right() {
return std::move_backward(s.begin(), s.end() - 1, s.end());
}
};
class FrequencyKeypadView : public View {
public:
std::function<void(rf::Frequency)> on_changed { };
FrequencyKeypadView(
NavigationView& nav,
const rf::Frequency value
);
void focus() override;
rf::Frequency value() const;
void set_value(const rf::Frequency new_value);
bool on_encoder(const EncoderEvent delta) override;
private:
int16_t focused_button = 0;
static constexpr int button_w = 240 / 3;
static constexpr int button_h = 48;
static constexpr int mhz_digits = 4;
static constexpr int submhz_digits = 4;
static constexpr int mhz_mod = pow(10, mhz_digits);
static constexpr int submhz_base = pow(10, 6 - submhz_digits);
static constexpr int text_digits = mhz_digits + 1 + submhz_digits;
Text text_value {
{ 0, 4, 240, 16 }
};
std::array<Button, 12> buttons { };
Button button_save {
{ 0, button_h * 5, 60, button_h },
"Save"
};
Button button_load {
{ 60, button_h * 5, 60, button_h },
"Load"
};
Button button_close {
{ 128, button_h * 5, 112, button_h },
"Done"
};
/* TODO: Template arg required in enum?! */
FieldString<mhz_digits> mhz { FieldString<4>::Justify::Right };
FieldString<submhz_digits> submhz { FieldString<4>::Justify::Left };
enum State {
DigitMHz,
DigitSubMHz
};
State state { DigitMHz };
bool clear_field_if_digits_entered { true };
void on_button(Button& button);
void digit_add(const char c);
void digit_delete();
void field_toggle();
void update_text();
};
class FrequencyStepView : public OptionsField {
public:
FrequencyStepView(
Point parent_pos
) : OptionsField {
parent_pos,
5,
{
{ " 10", 10 }, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
{ " 50", 50 }, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
{ " 100", 100 },
{ " 1k ", 1000 },
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
{ " 5k ", 5000 },
{ " 6k3", 6250 },
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
{ " 10k ", 10000 },
{ " 12k5", 12500 },
{ " 25k ", 25000 },
{ "100k ", 100000 },
{ " 1M ", 1000000 },
{ " 10M ", 10000000 },
}
}
{
}
};
class FrequencyOptionsView : public View {
public:
std::function<void(rf::Frequency)> on_change_step { };
std::function<void(int32_t)> on_change_reference_ppm_correction { };
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
void set_step(rf::Frequency f);
void set_reference_ppm_correction(int32_t v);
private:
Text text_step {
{ 0 * 8, 0 * 16, 4 * 8, 1 * 16 },
"Step"
};
FrequencyStepView field_step {
{ 5 * 8, 0 * 16 },
};
void on_step_changed(rf::Frequency v);
void on_reference_ppm_correction_changed(int32_t v);
NumberField field_ppm {
{ 23 * 8, 0 * 16 },
3,
{ -99, 99 },
1,
'0',
};
Text text_ext {
{ 23 * 8, 0 * 16, 3 * 8, 1 * 16 },
"EXT",
};
Text text_ppm {
{ 27 * 8, 0 * 16, 3 * 8, 16 },
"PPM",
};
};
class RFAmpField : public NumberField {
public:
RFAmpField(Point parent_pos);
};
class RadioGainOptionsView : public View {
public:
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
private:
Text label_rf_amp {
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
"Amp"
};
RFAmpField field_rf_amp {
{ 4 * 8, 0 * 16},
};
};
class LNAGainField : public NumberField {
public:
std::function<void(void)> on_show_options { };
LNAGainField(Point parent_pos);
void on_focus() override;
};
class VGAGainField : public NumberField {
public:
std::function<void(void)> on_show_options { };
VGAGainField(Point parent_pos);
void on_focus() override;
};
} /* namespace ui */
#endif/*__UI_RECEIVER_H__*/

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_rssi.hpp"
#include "baseband_api.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void RSSI::paint(Painter& painter) {
const auto r = screen_rect();
constexpr int rssi_sample_range = 256;
constexpr float rssi_voltage_min = 0.4;
constexpr float rssi_voltage_max = 2.2;
constexpr float adc_voltage_max = 3.3;
constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
constexpr int raw_delta = raw_max - raw_min;
const range_t<int> x_avg_range { 0, r.width() - 1 };
const auto x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta);
const range_t<int> x_min_range { 0, x_avg };
const auto x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta);
const range_t<int> x_max_range { x_avg + 1, r.width() };
const auto x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta);
const Rect r0 { r.left(), r.top(), x_min, r.height() };
painter.fill_rectangle(
r0,
Color::blue()
);
const Rect r1 { r.left() + x_min, r.top(), x_avg - x_min, r.height() };
painter.fill_rectangle(
r1,
Color::red()
);
const Rect r2 { r.left() + x_avg, r.top(), 1, r.height() };
painter.fill_rectangle(
r2,
Color::white()
);
const Rect r3 { r.left() + x_avg + 1, r.top(), x_max - (x_avg + 1), r.height() };
painter.fill_rectangle(
r3,
Color::red()
);
const Rect r4 { r.left() + x_max, r.top(), r.width() - x_max, r.height() };
painter.fill_rectangle(
r4,
Color::black()
);
if (pitch_rssi_enabled) {
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
}
}
void RSSI::set_pitch_rssi(bool enabled) {
pitch_rssi_enabled = enabled;
if (!enabled) baseband::set_pitch_rssi(0, false);
}
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
min_ = statistics.min;
avg_ = statistics.accumulator / statistics.count;
max_ = statistics.max;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_RSSI_H__
#define __UI_RSSI_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
namespace ui {
class RSSI : public Widget {
public:
RSSI(
const Rect parent_rect
) : Widget { parent_rect },
min_ { 0 },
avg_ { 0 },
max_ { 0 }
{
}
void paint(Painter& painter) override;
private:
int32_t min_;
int32_t avg_;
int32_t max_;
bool pitch_rssi_enabled = false;
MessageHandlerRegistration message_handler_stats {
Message::ID::RSSIStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
}
};
MessageHandlerRegistration message_handler_pitch_rssi {
Message::ID::PitchRSSIConfigure,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const PitchRSSIConfigureMessage*>(p);
this->set_pitch_rssi(message.enabled);
}
};
void on_statistics_update(const RSSIStatistics& statistics);
void set_pitch_rssi(bool enabled);
};
}
#endif/*__UI_RSSI_H__*/

View File

@ -0,0 +1,394 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "ui_spectrum.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include "string_format.hpp"
#include <cmath>
#include <array>
namespace ui {
namespace spectrum {
/* AudioSpectrumView******************************************************/
AudioSpectrumView::AudioSpectrumView(
const Rect parent_rect
) : View { parent_rect }
{
set_focusable(true);
add_children({
&labels,
&field_frequency,
&waveform
});
field_frequency.on_change = [this](int32_t) {
set_dirty();
};
field_frequency.set_value(0);
}
void AudioSpectrumView::paint(Painter& painter) {
const auto r = screen_rect();
painter.fill_rectangle(r, Color::black());
//if( !spectrum_sampling_rate ) return;
// Cursor
const Rect r_cursor {
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
1, cursor_band_height
};
painter.fill_rectangle(
r_cursor,
Color::red()
);
}
void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum* spectrum) {
for (size_t i = 0; i < spectrum->db.size(); i++)
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
waveform.set_dirty();
}
/* FrequencyScale ********************************************************/
void FrequencyScale::on_show() {
clear();
}
void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
if( (spectrum_sampling_rate != new_sampling_rate) ) {
spectrum_sampling_rate = new_sampling_rate;
set_dirty();
}
}
void FrequencyScale::set_channel_filter(
const int low_frequency,
const int high_frequency,
const int transition
) {
if( (channel_filter_low_frequency != low_frequency) ||
(channel_filter_high_frequency != high_frequency) ||
(channel_filter_transition != transition) ) {
channel_filter_low_frequency = low_frequency;
channel_filter_high_frequency = high_frequency;
channel_filter_transition = transition;
set_dirty();
}
}
void FrequencyScale::paint(Painter& painter) {
const auto r = screen_rect();
clear_background(painter, r);
if( !spectrum_sampling_rate ) {
// Can't draw without non-zero scale.
return;
}
draw_filter_ranges(painter, r);
draw_frequency_ticks(painter, r);
if (_blink) {
const Rect r_cursor {
118 + cursor_position, r.bottom() - filter_band_height,
5, filter_band_height
};
painter.fill_rectangle(
r_cursor,
Color::red()
);
}
}
void FrequencyScale::clear() {
spectrum_sampling_rate = 0;
set_dirty();
}
void FrequencyScale::clear_background(Painter& painter, const Rect r) {
painter.fill_rectangle(r, Color::black());
}
void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
const auto x_center = r.width() / 2;
const Rect tick { r.left() + x_center, r.top(), 1, r.height() };
painter.fill_rectangle(tick, Color::white());
constexpr int tick_count_max = 4;
float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max;
int magnitude = 1;
int magnitude_n = 0;
while(rough_tick_interval >= 10.0f) {
rough_tick_interval /= 10;
magnitude *= 10;
magnitude_n += 1;
}
const int tick_interval = std::ceil(rough_tick_interval);
auto tick_offset = tick_interval;
while((tick_offset * magnitude) < spectrum_sampling_rate / 2) {
const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate;
const std::string zero_pad =
((magnitude_n % 3) == 0) ? "" :
((magnitude_n % 3) == 1) ? "0" : "00";
const std::string unit =
(magnitude_n >= 6) ? "M" :
(magnitude_n >= 3) ? "k" : "";
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
const auto label_width = style().font.size_of(label).width();
const Coord offset_low = r.left() + x_center - pixel_offset;
const Rect tick_low { offset_low, r.top(), 1, r.height() };
painter.fill_rectangle(tick_low, Color::white());
painter.draw_string({ offset_low + 2, r.top() }, style(), label );
const Coord offset_high = r.left() + x_center + pixel_offset;
const Rect tick_high { offset_high, r.top(), 1, r.height() };
painter.fill_rectangle(tick_high, Color::white());
painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label );
tick_offset += tick_interval;
}
}
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
if( channel_filter_low_frequency != channel_filter_high_frequency ) {
const auto x_center = r.width() / 2;
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
if( channel_filter_transition ) {
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
const Rect r_all {
r.left() + x_low - trans, r.bottom() - filter_band_height,
x_high - x_low + trans*2, filter_band_height
};
painter.fill_rectangle(
r_all,
Color::yellow()
);
}
const Rect r_pass {
r.left() + x_low, r.bottom() - filter_band_height,
x_high - x_low, filter_band_height
};
painter.fill_rectangle(
r_pass,
Color::green()
);
}
}
void FrequencyScale::on_focus() {
_blink = true;
on_tick_second();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};
}
void FrequencyScale::on_blur() {
rtc_time::signal_tick_second -= signal_token_tick_second;
_blink = false;
set_dirty();
}
bool FrequencyScale::on_encoder(const EncoderEvent delta) {
cursor_position += delta;
cursor_position = std::min<int32_t>(cursor_position, 119);
cursor_position = std::max<int32_t>(cursor_position, -120);
set_dirty();
return true;
}
bool FrequencyScale::on_key(const KeyEvent key) {
if( key == KeyEvent::Select ) {
if( on_select ) {
on_select((cursor_position * spectrum_sampling_rate) / 240);
cursor_position = 0;
return true;
}
}
return false;
}
void FrequencyScale::on_tick_second() {
set_dirty();
_blink = !_blink;
}
/* WaterfallView *********************************************************/
void WaterfallView::on_show() {
clear();
const auto screen_r = screen_rect();
display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void WaterfallView::on_hide() {
/* TODO: Clear region to eliminate brief flash of content at un-shifted
* position?
*/
display.scroll_disable();
}
void WaterfallView::paint(Painter& painter) {
// Do nothing.
(void)painter;
}
void WaterfallView::on_channel_spectrum(
const ChannelSpectrum& spectrum
) {
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
std::array<Color, 240> pixel_row;
for(size_t i=0; i<120; i++) {
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
pixel_row[i] = pixel_color;
}
for(size_t i=120; i<240; i++) {
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
pixel_row[i] = pixel_color;
}
const auto draw_y = display.scroll(1);
display.draw_pixels(
{ { 0, draw_y }, { pixel_row.size(), 1 } },
pixel_row
);
}
void WaterfallView::clear() {
display.fill_rectangle(
screen_rect(),
Color::black()
);
}
/* WaterfallWidget *******************************************************/
WaterfallWidget::WaterfallWidget(const bool cursor) {
add_children({
&waterfall_view,
&frequency_scale
});
frequency_scale.set_focusable(cursor);
// Making the event climb up all the way up to here kinda sucks
frequency_scale.on_select = [this](int32_t offset) {
if (on_select) on_select(offset);
};
}
void WaterfallWidget::on_show() {
baseband::spectrum_streaming_start();
}
void WaterfallWidget::on_hide() {
baseband::spectrum_streaming_stop();
}
void WaterfallWidget::show_audio_spectrum_view(const bool show) {
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
if (show) {
audio_spectrum_view = std::make_unique<AudioSpectrumView>(audio_spectrum_view_rect);
add_child(audio_spectrum_view.get());
update_widgets_rect();
} else {
audio_spectrum_update = false;
remove_child(audio_spectrum_view.get());
audio_spectrum_view.reset();
update_widgets_rect();
}
}
void WaterfallWidget::update_widgets_rect() {
if (audio_spectrum_view) {
frequency_scale.set_parent_rect({ 0, audio_spectrum_height, screen_rect().width(), scale_height });
waterfall_view.set_parent_rect(waterfall_reduced_rect);
} else {
frequency_scale.set_parent_rect({ 0, 0, screen_rect().width(), scale_height });
waterfall_view.set_parent_rect(waterfall_normal_rect);
}
waterfall_view.on_show();
}
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
waterfall_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
waterfall_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
update_widgets_rect();
}
void WaterfallWidget::paint(Painter& painter) {
// TODO:
(void)painter;
}
void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
waterfall_view.on_channel_spectrum(spectrum);
sampling_rate = spectrum.sampling_rate;
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
frequency_scale.set_channel_filter(
spectrum.channel_filter_low_frequency,
spectrum.channel_filter_high_frequency,
spectrum.channel_filter_transition
);
}
void WaterfallWidget::on_audio_spectrum() {
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
}
} /* namespace spectrum */
} /* namespace ui */

View File

@ -0,0 +1,202 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __UI_SPECTRUM_H__
#define __UI_SPECTRUM_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
namespace ui {
namespace spectrum {
class AudioSpectrumView : public View {
public:
AudioSpectrumView(const Rect parent_rect);
void paint(Painter& painter) override;
void on_audio_spectrum(const AudioSpectrum* spectrum);
private:
static constexpr int cursor_band_height = 4;
int16_t audio_spectrum[128] { 0 };
Labels labels {
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
};
NumberField field_frequency {
{ 0 * 8, 0 * 16 },
5,
{ 0, 48000 },
48000 / 240,
' '
};
Waveform waveform {
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
audio_spectrum,
128,
0,
false,
Color::white()
};
};
class FrequencyScale : public Widget {
public:
std::function<void(int32_t offset)> on_select { };
void on_show() override;
void on_focus() override;
void on_blur() override;
bool on_encoder(const EncoderEvent delta) override;
bool on_key(const KeyEvent key) override;
void set_spectrum_sampling_rate(const int new_sampling_rate);
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
void paint(Painter& painter) override;
private:
static constexpr int filter_band_height = 4;
void on_tick_second();
bool _blink { false };
int32_t cursor_position { 0 };
SignalToken signal_token_tick_second { };
int spectrum_sampling_rate { 0 };
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
int channel_filter_low_frequency { 0 };
int channel_filter_high_frequency { 0 };
int channel_filter_transition { 0 };
void clear();
void clear_background(Painter& painter, const Rect r);
void draw_frequency_ticks(Painter& painter, const Rect r);
void draw_filter_ranges(Painter& painter, const Rect r);
};
class WaterfallView : public Widget {
public:
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void on_channel_spectrum(const ChannelSpectrum& spectrum);
private:
void clear();
};
class WaterfallWidget : public View {
public:
std::function<void(int32_t offset)> on_select { };
WaterfallWidget(const bool cursor = false);
WaterfallWidget(const WaterfallWidget&) = delete;
WaterfallWidget(WaterfallWidget&&) = delete;
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
void on_show() override;
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void show_audio_spectrum_view(const bool show);
void paint(Painter& painter) override;
private:
void update_widgets_rect();
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
WaterfallView waterfall_view { };
FrequencyScale frequency_scale { };
ChannelSpectrumFIFO* channel_fifo { nullptr };
AudioSpectrum* audio_spectrum_data { nullptr };
bool audio_spectrum_update { false };
std::unique_ptr<AudioSpectrumView> audio_spectrum_view { };
int sampling_rate { 0 };
int32_t cursor_position { 0 };
ui::Rect waterfall_normal_rect { };
ui::Rect waterfall_reduced_rect { };
MessageHandlerRegistration message_handler_channel_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->channel_fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_audio_spectrum {
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
this->audio_spectrum_data = message.data;
this->audio_spectrum_update = true;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->channel_fifo ) {
ChannelSpectrum channel_spectrum;
while( channel_fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
if (this->audio_spectrum_update) {
this->audio_spectrum_update = false;
this->on_audio_spectrum();
}
}
};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void on_audio_spectrum();
};
} /* namespace spectrum */
} /* namespace ui */
#endif/*__UI_SPECTRUM_H__*/

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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.
*/
#include "ui_tabview.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui {
Tab::Tab() {
set_focusable(true);
};
void Tab::set(
uint32_t index,
Dim width,
std::string text,
Color text_color
) {
set_parent_rect({ (Coord)(index * width), 0, width, 24 });
text_ = text.substr(0, (width - 8) / 8);
text_color_ = text_color;
index_ = index;
}
void Tab::paint(Painter& painter) {
const auto rect = screen_rect();
const Color color = highlighted() ? Color::black() : Color::grey();
painter.fill_rectangle({ rect.left(), rect.top(), rect.width() - 8, rect.height() }, color);
if (!highlighted())
painter.draw_hline({ rect.left(), rect.top() }, rect.width() - 9, Color::light_grey());
painter.draw_bitmap(
{ rect.right() - 8, rect.top() },
bitmap_tab_edge,
color,
Color::dark_grey()
);
auto text_point = rect.center() - Point(4, 0) - Point(text_.size() * 8 / 2, 16 / 2);
painter.draw_string(
text_point,
{ ui::font::fixed_8x16, color, text_color_ },
text_
);
if (has_focus())
painter.draw_hline(text_point + Point(0, 16), text_.size() * 8, Color::white());
}
bool Tab::on_key(const KeyEvent key) {
if( key == KeyEvent::Select ) {
static_cast<TabView*>(parent())->set_selected(index_);
return true;
}
return false;
}
bool Tab::on_touch(const TouchEvent event) {
switch(event.type) {
case TouchEvent::Type::Start:
focus();
set_dirty();
return true;
case TouchEvent::Type::End:
static_cast<TabView*>(parent())->set_selected(index_);
return true;
default:
return false;
}
}
void TabView::set_selected(uint32_t index) {
Tab * tab;
if (index >= n_tabs)
return;
// Hide previous view
views[current_tab]->hidden(true);
tab = &tabs[current_tab];
tab->set_highlighted(false);
tab->set_focusable(true);
tab->set_dirty();
// Show new view
views[index]->hidden(false);
tab = &tabs[index];
current_tab = index;
tab->set_highlighted(true);
tab->set_focusable(false);
tab->set_dirty();
}
void TabView::on_show() {
set_selected(current_tab);
}
void TabView::focus() {
views[current_tab]->focus();
}
TabView::TabView(std::initializer_list<TabDef> tab_definitions) {
size_t i = 0;
n_tabs = tab_definitions.size();
if (n_tabs > MAX_TABS)
n_tabs = MAX_TABS;
size_t tab_width = 240 / n_tabs;
set_parent_rect({ 0, 0, 30 * 8, 3 * 8 });
for (auto &tab_definition : tab_definitions) {
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
views[i] = tab_definition.view;
add_child(&tabs[i]);
i++;
if (i == MAX_TABS) break;
}
}
TabView::~TabView() {
}
} /* namespace ui */

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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 __UI_TABVIEW_H__
#define __UI_TABVIEW_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
namespace ui {
#define MAX_TABS 5
class Tab : public Widget {
public:
Tab();
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
void set(uint32_t index, Dim width, std::string text, Color text_color);
private:
std::string text_ { };
Color text_color_ { };
uint32_t index_ { };
};
class TabView : public View {
public:
struct TabDef {
std::string text;
ui::Color color;
View* view;
};
TabView(std::initializer_list<TabDef> tab_definitions);
~TabView();
void focus() override;
void on_show() override;
void set_selected(uint32_t index);
uint32_t selected() {
return current_tab;
};
private:
size_t n_tabs { };
std::array<Tab, MAX_TABS> tabs { };
std::array<View*, MAX_TABS> views { };
uint32_t current_tab { 0 };
};
} /* namespace ui */
#endif/*__UI_TABVIEW_H__*/

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2015 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.
*/
#include "ui_textentry.hpp"
//#include "portapack_persistent_memory.hpp"
#include "ui_alphanum.hpp"
//#include "ui_handwrite.hpp"
using namespace portapack;
namespace ui {
void text_prompt(NavigationView& nav, std::string& str, const size_t max_length, const std::function<void(std::string&)> on_done) {
//if (persistent_memory::ui_config_textentry() == 0) {
auto te_view = nav.push<AlphanumView>(str, max_length);
te_view->on_changed = [on_done](std::string& value) {
if (on_done)
on_done(value);
};
/*} else {
auto te_view = nav.push<HandWriteView>(str, max_length);
te_view->on_changed = [on_done](std::string * value) {
if (on_done)
on_done(value);
};
}*/
}
void TextEntryView::update_text() {
if (cursor_pos < 30)
text_input.set(_str + std::string(_max_length - _str.length(), ' '));
else
text_input.set('<' + _str.substr(cursor_pos - 29, 29));
draw_cursor();
}
void TextEntryView::char_delete() {
if (!cursor_pos) return;
cursor_pos--;
_str.resize(cursor_pos);
}
void TextEntryView::char_add(const char c) {
if (cursor_pos >= _max_length) return;
_str += c;
cursor_pos++;
}
void TextEntryView::draw_cursor() {
Point draw_pos;
draw_pos = { text_input.screen_rect().location().x() + std::min((Coord)cursor_pos, (Coord)28) * 8,
text_input.screen_rect().location().y() + 16 };
// Erase previous
display.fill_rectangle(
{ { text_input.screen_rect().location().x(), draw_pos.y() }, { text_input.screen_rect().size().width(), 4 } },
Color::black()
);
// Draw new
display.fill_rectangle(
{ draw_pos, { 8, 4 } },
Color::white()
);
}
void TextEntryView::focus() {
button_ok.focus();
}
TextEntryView::TextEntryView(
NavigationView& nav,
std::string& str,
size_t max_length
) : _str(str),
_max_length(max_length)
{
// Trim from right
//_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), _str->end());
if (_str.length() > _max_length)
_str.resize(_max_length);
_str.reserve(_max_length);
cursor_pos = _str.length();
add_children({
&text_input,
&button_ok
});
button_ok.on_select = [this, &nav](Button&) {
_str.resize(cursor_pos);
if (on_changed)
on_changed(_str);
nav.pop();
};
}
} /* namespace ui */

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2015 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 __UI_TEXTENTRY_H__
#define __UI_TEXTENTRY_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
namespace ui {
class TextEntryView : public View {
public:
std::function<void(std::string&)> on_changed { };
void focus() override;
std::string title() const override { return "Text entry"; };
protected:
TextEntryView(NavigationView& nav, std::string& str, size_t max_length);
TextEntryView(const TextEntryView&) = delete;
TextEntryView(TextEntryView&&) = delete;
TextEntryView& operator=(const TextEntryView&) = delete;
TextEntryView& operator=(TextEntryView&&) = delete;
void char_add(const char c);
void char_delete();
void draw_cursor();
void update_text();
std::string& _str;
size_t _max_length;
uint32_t cursor_pos { 0 };
Text text_input {
{ 0, 0, 240, 16 }
};
Button button_ok {
{ 10 * 8, 33 * 8, 9 * 8, 32 },
"OK"
};
};
void text_prompt(NavigationView& nav, std::string& str, size_t max_length, const std::function<void(std::string&)> on_done = nullptr);
} /* namespace ui */
#endif/*__UI_TEXTENTRY_H__*/

View File

@ -0,0 +1,192 @@
/*
* 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.
*/
#include "ui_transmitter.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "string_format.hpp"
#include "max2837.hpp"
namespace ui {
/* TransmitterView *******************************************************/
void TransmitterView::paint(Painter& painter) {
size_t c;
Point pos = { 0, screen_pos().y() };
for (c = 0; c < 20; c++) {
painter.draw_bitmap(
pos,
bitmap_stripes,
ui::Color(191, 191, 0),
ui::Color::black()
);
if (c != 9)
pos += { 24, 0 };
else
pos = { 0, screen_pos().y() + 32 + 8 };
}
}
void TransmitterView::on_tuning_frequency_changed(rf::Frequency f) {
transmitter_model.set_tuning_frequency(f);
}
void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) {
transmitter_model.set_channel_bandwidth(channel_bandwidth);
}
void TransmitterView::on_tx_gain_changed(int32_t tx_gain) {
transmitter_model.set_tx_gain(tx_gain);
update_gainlevel_styles();
}
void TransmitterView::on_tx_amp_changed(bool rf_amp) {
transmitter_model.set_rf_amp(rf_amp);
update_gainlevel_styles();
}
void TransmitterView::update_gainlevel_styles() {
const Style *new_style_ptr = NULL;
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
if(tot_gain > POWER_THRESHOLD_HIGH) {
new_style_ptr = &style_power_high;
} else if(tot_gain > POWER_THRESHOLD_MED) {
new_style_ptr = &style_power_med;
} else if(tot_gain > POWER_THRESHOLD_LOW) {
new_style_ptr = &style_power_low;
}
field_gain.set_style(new_style_ptr);
text_gain.set_style(new_style_ptr);
field_amp.set_style(new_style_ptr);
text_amp.set_style(new_style_ptr);
}
void TransmitterView::set_transmitting(const bool transmitting) {
if (transmitting) {
button_start.set_text("STOP");
button_start.set_style(&style_stop);
} else {
button_start.set_text("START");
button_start.set_style(&style_start);
}
transmitting_ = transmitting;
}
void TransmitterView::on_show() {
field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency_step.set_by_value(receiver_model.frequency_step());
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
update_gainlevel_styles();
}
void TransmitterView::focus() {
button_start.focus();
}
TransmitterView::TransmitterView(
const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock
) : lock_ { lock }
{
set_parent_rect({ 0, y, 30 * 8, 6 * 8 });
add_children({
&field_frequency,
&field_frequency_step,
&text_gain,
&field_gain,
&button_start,
&text_amp,
&field_amp,
});
set_transmitting(false);
if (lock_) {
field_frequency.set_focusable(false);
field_frequency.set_style(&style_locked);
} else {
if (channel_bandwidth) {
add_children({
&text_bw,
&field_bw
});
field_bw.on_change = [this](int32_t v) {
on_channel_bandwidth_changed(v * 1000);
};
field_bw.set_value(channel_bandwidth);
}
}
//field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency.set_step(frequency_step);
field_frequency.on_change = [this](rf::Frequency f) {
on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this]() {
if (on_edit_frequency)
on_edit_frequency();
};
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
this->field_frequency.set_step(v);
};
field_gain.on_change = [this](uint32_t tx_gain) {
on_tx_gain_changed(tx_gain);
};
field_amp.on_change = [this](uint32_t rf_amp) {
on_tx_amp_changed((bool) rf_amp);
};
button_start.on_select = [this](Button&){
if (transmitting_) {
if (on_stop)
on_stop();
} else {
if (on_start)
on_start();
}
};
}
TransmitterView::~TransmitterView() {
audio::output::stop();
transmitter_model.disable();
baseband::shutdown();
}
} /* namespace ui */

View File

@ -0,0 +1,170 @@
/*
* 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 __UI_TRANSMITTER_H__
#define __UI_TRANSMITTER_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "ui_receiver.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "rf_path.hpp"
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <functional>
#define POWER_THRESHOLD_HIGH 47
#define POWER_THRESHOLD_MED 38
#define POWER_THRESHOLD_LOW 17
namespace ui {
class TXGainField : public NumberField {
public:
std::function<void(void)> on_show_options { };
TXGainField(Point parent_pos);
};
class TransmitterView : public View {
public:
std::function<void(void)> on_edit_frequency { };
std::function<void(void)> on_start { };
std::function<void(void)> on_stop { };
TransmitterView(const Coord y, const uint64_t frequency_step, const uint32_t channel_bandwidth, const bool lock);
TransmitterView(
const Coord y, const uint32_t frequency_step, const uint32_t channel_bandwidth
) : TransmitterView { y, frequency_step, channel_bandwidth, false }
{
}
~TransmitterView();
void on_show() override;
void paint(Painter& painter) override;
void focus() override;
void set_transmitting(const bool transmitting);
private:
const Style style_start {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_stop {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
const Style style_locked {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::dark_grey(),
};
const Style style_power_low {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::yellow(),
};
const Style style_power_med {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::orange(),
};
const Style style_power_high {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
bool lock_ { false };
bool transmitting_ { false };
FrequencyField field_frequency {
{ 0, 1 * 8 }
};
Text text_gain {
{ 0, 3 * 8, 5 * 8, 1 * 16 },
"Gain:"
};
NumberField field_gain {
{ 5 * 8, 3 * 8 },
2,
{ max2837::tx::gain_db_range.minimum, max2837::tx::gain_db_range.maximum },
max2837::tx::gain_db_step,
' '
};
Text text_bw {
{ 18 * 8, 1 * 8, 3 * 8, 1 * 16 },
"kHz"
};
NumberField field_bw {
{ 15 * 8, 1 * 8 },
3,
{ 1, 150 },
1,
' '
};
Text text_amp {
{ 11 * 8, 3 * 8, 5 * 8, 1 * 16 },
"Amp:"
};
NumberField field_amp {
{ 16 * 8, 3 * 8 },
2,
{ 0, 14 },
14,
' '
};
Button button_start {
{ 21 * 8, 1 * 8, 9 * 8, 32 },
"START"
};
FrequencyStepView field_frequency_step {
{ 10 * 8 - 4, 1 * 8 },
};
void on_tuning_frequency_changed(rf::Frequency f);
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
void on_tx_gain_changed(int32_t tx_gain);
void on_tx_amp_changed(bool rf_amp);
void update_gainlevel_styles(void);
};
} /* namespace ui */
#endif/*__UI_TRANSMITTER_H__*/

View File

@ -0,0 +1,251 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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 "ui_tv.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include "string_format.hpp"
#include <cmath>
#include <array>
namespace ui {
namespace tv {
/* TimeScopeView******************************************************/
TimeScopeView::TimeScopeView(
const Rect parent_rect
) : View { parent_rect }
{
set_focusable(true);
add_children({
//&labels,
//&field_frequency,
&waveform
});
/*field_frequency.on_change = [this](int32_t) {
set_dirty();
};
field_frequency.set_value(10);*/
}
void TimeScopeView::paint(Painter& painter) {
const auto r = screen_rect();
painter.fill_rectangle(r, Color::black());
// Cursor
/*
const Rect r_cursor {
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
1, cursor_band_height
};
painter.fill_rectangle(
r_cursor,
Color::red()
);*/
}
void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) {
for (size_t i = 0; i < spectrum->db.size(); i++)
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
waveform.set_dirty();
}
/* TVView *********************************************************/
void TVView::on_show() {
clear();
const auto screen_r = screen_rect();
display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void TVView::on_hide() {
/* TODO: Clear region to eliminate brief flash of content at un-shifted
* position?
*/
display.scroll_disable();
}
void TVView::paint(Painter& painter) {
// Do nothing.
(void)painter;
}
void TVView::on_adjust_xcorr(uint8_t xcorr){
x_correction = xcorr;
}
void TVView::on_channel_spectrum(
const ChannelSpectrum& spectrum
) {
//portapack has limitations
// 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256)
// 3.memory size (for ui::Color, the buffer size
//spectrum.db[i] is 256 long
//768x625 ->128x625 ->128x312 -> 128x104
//originally @6MHz sample rate, the PAL should be 768x625
//I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2)
//the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line
//However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer.
//So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame.
//then the resolution is changed to 128x312
//128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large
//according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted.
//So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height.
//I was expecting to see 1/3 height of original video.
//Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128
//Each call back gives me 256 samples which is exactly 2 lines. What a coincidence!
//After some experiment, I did some improvements.
//1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window.
//So I made the height twice simply by painting 2 identical lines in the place of original lines
//2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually
//3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value.
//I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically
//however this is useless until now.
for(size_t i=0; i<256; i++)
{
//video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]];
video_buffer_int[i+count*256] = 255 - spectrum.db[i];
}
count = count + 1;
if (count == 52 -1)
{
ui::Color line_buffer[128];
Coord line;
uint32_t bmp_px;
/*for (line = 0; line < 104; line++)
{
for (bmp_px = 0; bmp_px < 128; bmp_px++)
{
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]];
}
display.render_line({ 0, line + 100 }, 128, line_buffer);
}*/
for (line = 0; line < 208; line=line+2)
{
for (bmp_px = 0; bmp_px < 128; bmp_px++)
{
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]];
}
display.render_line({ 0, line + 100 }, 128, line_buffer);
display.render_line({ 0, line + 101 }, 128, line_buffer);
}
count = 0;
}
}
void TVView::clear() {
display.fill_rectangle(
screen_rect(),
Color::black()
);
}
/* TVWidget *******************************************************/
TVWidget::TVWidget() {
add_children({
&tv_view,
&field_xcorr
});
field_xcorr.set_value(10);
}
void TVWidget::on_show() {
baseband::spectrum_streaming_start();
}
void TVWidget::on_hide() {
baseband::spectrum_streaming_stop();
}
void TVWidget::show_audio_spectrum_view(const bool show) {
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
if (show) {
audio_spectrum_view = std::make_unique<TimeScopeView>(audio_spectrum_view_rect);
add_child(audio_spectrum_view.get());
update_widgets_rect();
} else {
audio_spectrum_update = false;
remove_child(audio_spectrum_view.get());
audio_spectrum_view.reset();
update_widgets_rect();
}
}
void TVWidget::update_widgets_rect() {
if (audio_spectrum_view) {
tv_view.set_parent_rect(tv_reduced_rect);
} else {
tv_view.set_parent_rect(tv_normal_rect);
}
tv_view.on_show();
}
void TVWidget::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
update_widgets_rect();
}
void TVWidget::paint(Painter& painter) {
// TODO:
(void)painter;
}
void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
tv_view.on_channel_spectrum(spectrum);
tv_view.on_adjust_xcorr(field_xcorr.value());
sampling_rate = spectrum.sampling_rate;
}
void TVWidget::on_audio_spectrum() {
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
}
} /* namespace tv */
} /* namespace ui */

View File

@ -0,0 +1,176 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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 __UI_TV_H__
#define __UI_TV_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
namespace ui {
namespace tv {
class TimeScopeView : public View {
public:
TimeScopeView(const Rect parent_rect);
void paint(Painter& painter) override;
void on_audio_spectrum(const AudioSpectrum* spectrum);
private:
static constexpr int cursor_band_height = 4;
int16_t audio_spectrum[128] { 0 };
/*Labels labels {
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
};*/
/*
NumberField field_frequency {
{ 0 * 8, 0 * 16 },
5,
{ 0, 48000 },
48000 / 240,
' '
};*/
Waveform waveform {
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
audio_spectrum,
128,
0,
false,
Color::white()
};
};
class TVView : public Widget {
public:
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void on_adjust_xcorr(uint8_t xcorr);
//ui::Color video_buffer[13312];
uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction
uint32_t count=0;
uint8_t x_correction=0;
private:
void clear();
};
class TVWidget : public View {
public:
std::function<void(int32_t offset)> on_select { };
TVWidget();
TVWidget(const TVWidget&) = delete;
TVWidget(TVWidget&&) = delete;
TVWidget& operator=(const TVWidget&) = delete;
TVWidget& operator=(TVWidget&&) = delete;
void on_show() override;
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void show_audio_spectrum_view(const bool show);
void paint(Painter& painter) override;
NumberField field_xcorr {
{ 0 * 8, 0 * 16 },
5,
{ 0, 128 },
1,
' '
};
private:
void update_widgets_rect();
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
TVView tv_view { };
ChannelSpectrumFIFO* channel_fifo { nullptr };
AudioSpectrum* audio_spectrum_data { nullptr };
bool audio_spectrum_update { false };
std::unique_ptr<TimeScopeView> audio_spectrum_view { };
int sampling_rate { 0 };
int32_t cursor_position { 0 };
ui::Rect tv_normal_rect { };
ui::Rect tv_reduced_rect { };
MessageHandlerRegistration message_handler_channel_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->channel_fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_audio_spectrum {
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
this->audio_spectrum_data = message.data;
this->audio_spectrum_update = true;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->channel_fifo ) {
ChannelSpectrum channel_spectrum;
while( channel_fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
if (this->audio_spectrum_update) {
this->audio_spectrum_update = false;
this->on_audio_spectrum();
}
}
};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void on_audio_spectrum();
};
} /* namespace tv */
} /* namespace ui */
#endif/*__UI_TV_H__*/