Compare commits

...

22 Commits

Author SHA1 Message Date
jLynx 572776bb9e Update past_version.txt 2024-09-23 17:17:47 +12:00
jLynx 073c4ed9f2 Update version.txt 2024-09-23 17:17:38 +12:00
zxkmm 42233e62df transparent splash logo & fix tab widget color glitch (#2256) 2024-09-22 19:40:54 +02:00
zxkmm 9390317a75 add shift feat for protcol view tool (#2258)
* add shift

* add shift

* format

* add padding (wrote by AI)

* layout fine tune

* remove debug thing

* edit per request

* remove torjan (jk)

* int 16 should be good enough for it
2024-09-22 15:30:31 +02:00
Erwin Ried 89b51095bf New H4M version (#2259) 2024-09-21 16:15:17 +02:00
Totoo 9438c9a574 SSTV TX to ext (#2254)
* SSTV TX to ext
2024-09-20 21:41:44 +08:00
jLynx 7feef9d38b Learn ic fix (#2253)
* WIP

* Fixed merge

* Added test code

* WIP

* Clean up

* add reset learned params

* ui fix

* ui fix2

* Updated func

* Fixed english

* WIP

* WIP testing

* Added new debug app

* Got new app for debug

* Got new app for debug

* Got one full page showing

* Got app working with all reg

* Got app working with all reg

* Got full hex showing

* Fixed dp

* Fixed dp

* Moved entities

* Enabled apps again

* SHow battery debug if ic

* WIP

* Refactored further

* WIP

* Refactor and clean up

* Refactor and clean up

* fix warning, add tte/ttf, add cycles counter.

* wip

* morse tx to ext app

* fix morse crash

* fix ui

* Updated wording

* WIP

* WIP

* Updated to display hours and minutes

---------

Co-authored-by: HTotoo <ttotoo@gmail.com>
2024-09-20 09:59:17 +00:00
zxkmm 582bb02a75 add bitmap arr to file (or reverse) converter (#2248)
* add covert tool

* fix for generator

* fix for generator
2024-09-20 09:56:33 +02:00
Totoo bfa7f3ca6b Wardrive map improv (#2251)
Added paginator.
Added Flipper SUB file support.
2024-09-16 21:59:28 +02:00
Mark Thompson 21facd9cc3 Options to select 314.9 or 315MHz TPMS freq (#2252) 2024-09-15 23:20:04 -05:00
zxkmm d30234aaa6 reword of #2158 (#2249) 2024-09-11 19:11:11 +02:00
gullradriel d45a17236e USB Serial setfreq support (#2246) 2024-09-09 20:01:25 +02:00
zxkmm 281e52d143 fine tune the invert logic (#2243) 2024-09-08 17:38:40 +02:00
Totoo 1f78646054 only inv or fakebrightness (#2240) 2024-09-08 09:35:09 +02:00
zxkmm 31c844b48e fix black bg of those bitmap when we have themes enabled (#2239) 2024-09-07 17:39:50 +02:00
Totoo 87069f11e5 invert display option (#2232)
* invert display option

* text fix, format code
2024-09-07 10:58:57 +02:00
Totoo e6afd7744d SubghzD & Weather improvments (#2237)
* Weather restructure

* Added Solight TE44 protocol

* Add disabled Marantec24, and GangQi

* More opt in subghzd
2024-09-06 20:23:11 +02:00
Dongping Guo (Donny) 76763b9bab Add BLE RX filter options (#2236) 2024-09-06 20:22:00 +02:00
Totoo 02b75f567a Setfreq usb command (#2235)
* SetFreq usb command for rx apps
* code format, better check
2024-09-04 20:26:31 +02:00
Erwin Ried a2c4fefe34 Slighty smaller video on readme 2024-09-03 09:27:22 +02:00
Erwin Ried 121352b149 Fully remove references to bountysource + ltt video (#2233)
-Permanently remove the references to the bountysource scammers
-Added LTT video about the device
2024-09-02 21:47:11 +08:00
Totoo 0ae7768f20 Battery info switcher (#2230) 2024-08-28 11:32:24 +02:00
157 changed files with 2930 additions and 1695 deletions
+1 -1
View File
@@ -1 +1 @@
v2.0.0
v2.0.1
+1 -1
View File
@@ -1 +1 @@
v2.0.1
v2.0.2
+5 -11
View File
@@ -17,9 +17,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="512">](https://grabify.link/9UZGEW)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="700">](https://grabify.link/X4D5TF)
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="254">](https://grabify.link/C0J6ZR)
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="230">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="230">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="230">](https://grabify.link/C0J6ZR)
# Frequently Asked Questions
@@ -27,12 +27,12 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/VPMPSL), featuring numerous improvements and accessories. Sold by our friends at OpenSourceSDRLab. Join their giveaways on discord (check the badge on top).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/7T28JP), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/HTDXG5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of one of their [PortaPack H2 bundle](https://grabify.link/T328XL) or [only metal case kits](https://grabify.link/LG0OUY), you can support them too by ordering.
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
![image](https://user-images.githubusercontent.com/1091420/214579017-9ad970b9-0917-48f6-a550-588226d3f89b.png)
@@ -55,13 +55,7 @@ Consider that the hardware and firmware has been created and maintain by a [lot]
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
## What if I really want something specific?
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md).
<del>You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose) or [choosing](https://github.com/portapack-mayhem/mayhem-firmware/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).</del>
<del>Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).</del>
Bountysource has not been reliable lately, so until this changes, please **DO NOT** post a bounty there. Go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md). Alternatively, go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
## What if I need help?
First, check the [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose).
+4 -2
View File
@@ -206,6 +206,7 @@ set(CPPSRC
irq_rtc.cpp
log_file.cpp
metadata_file.cpp
flipper_subfile.cpp
portapack.cpp
usb_serial_shell.cpp
usb_serial_shell_filesystem.cpp
@@ -291,6 +292,7 @@ set(CPPSRC
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_debug_battery.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
apps/ui_fileman.cpp
@@ -305,7 +307,7 @@ set(CPPSRC
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
# apps/ui_morse.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
@@ -325,7 +327,7 @@ set(CPPSRC
# apps/ui_spectrum_painter_text.cpp
# apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
# apps/ui_sstvtx.cpp #moved to ext
apps/ui_standalone_view.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
@@ -435,4 +435,7 @@ void AnalogAudioView::handle_coded_squelch(uint32_t value) {
text_ctcss.set(tone_key_string_by_value(value, text_ctcss.parent_rect().width() / 8));
}
void AnalogAudioView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
@@ -247,12 +247,21 @@ class AnalogAudioView : public View {
void handle_coded_squelch(uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} /* namespace ui */
+38 -9
View File
@@ -428,6 +428,7 @@ BLERxView::BLERxView(NavigationView& nav)
&text_found_count,
&check_serial_log,
&button_filter,
&options_filter,
&button_save_list,
&button_clear_list,
&button_switch,
@@ -499,6 +500,7 @@ BLERxView::BLERxView(NavigationView& nav)
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
// update the include_name instance variable value of each entry in recent entries
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
@@ -525,8 +527,14 @@ BLERxView::BLERxView(NavigationView& nav)
handle_entries_sort(v);
};
options_filter.on_change = [this](size_t index, int32_t v) {
filter_index = (uint8_t)index;
handle_filter_options(v);
};
options_channel.set_selected_index(channel_index, true);
options_sort.set_selected_index(sort_index, true);
options_filter.set_selected_index(filter_index, true);
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
@@ -716,10 +724,11 @@ void BLERxView::on_data(BlePacketData* packet) {
updateEntry(packet, entry, (ADV_PDU_TYPE)packet->type);
// Add entries if they meet the criteria.
auto value = filter;
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// auto value = filter;
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// });
handle_filter_options(options_filter.selected_index());
handle_entries_sort(options_sort.selected_index());
@@ -756,12 +765,13 @@ void BLERxView::on_data(BlePacketData* packet) {
void BLERxView::on_filter_change(std::string value) {
// New filter? Reset list from recent entries.
if (filter != value) {
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos) && (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
// });
filter = value;
handle_filter_options(options_filter.selected_index());
}
filter = value;
}
void BLERxView::on_file_changed(const std::filesystem::path& new_file_path) {
@@ -852,6 +862,24 @@ void BLERxView::handle_entries_sort(uint8_t index) {
recent_entries_view.set_dirty();
}
void BLERxView::handle_filter_options(uint8_t index) {
auto value = filter;
switch (index) {
case 0: // filter by Data
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
break;
case 1: // filter by MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF)
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
});
break;
default:
break;
}
}
void BLERxView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height};
@@ -914,6 +942,7 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
// Subtract 1 because type is part of the length.
for (int i = 0; i < length - 1; i++) {
// parse the name of bluetooth device: 0x08->Shortened Local Name; 0x09->Complete Local Name
if (type == 0x08 || type == 0x09) {
decoded_data += (char)advertiseData->Data[currentByte];
}
+24 -15
View File
@@ -203,6 +203,7 @@ class BLERxView : public View {
void file_error();
void on_timer();
void handle_entries_sort(uint8_t index);
void handle_filter_options(uint8_t index);
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type);
NavigationView& nav_;
@@ -214,6 +215,7 @@ class BLERxView : public View {
uint8_t channel_index{0};
uint8_t sort_index{0};
uint8_t filter_index{0};
std::string filter{};
bool logging{false};
bool serial_logging{false};
@@ -227,6 +229,7 @@ class BLERxView : public View {
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
{"filter_index"sv, &filter_index},
// disabled to always start without USB serial activated until we can make it non blocking if not connected
// {"serial_log"sv, &serial_logging},
{"name"sv, &name_enable},
@@ -255,13 +258,13 @@ class BLERxView : public View {
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
static constexpr auto header_height = 4 * 16;
static constexpr auto header_height = 9 * 8;
static constexpr auto switch_button_height = 3 * 16;
OptionsField options_channel{
{0 * 8, 0 * 8},
5,
{{"Ch.37 ", 37},
{{"Ch.37", 37},
{"Ch.38", 38},
{"Ch.39", 39},
{"Auto", 40}}};
@@ -286,10 +289,10 @@ class BLERxView : public View {
{24 * 8, 5, 6 * 8, 4}};
Labels label_sort{
{{0 * 8, 3 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
{{0 * 8, 2 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_sort{
{5 * 8, 3 * 8},
{5 * 8, 2 * 8},
4,
{{"MAC", 0},
{"Hits", 1},
@@ -298,40 +301,46 @@ class BLERxView : public View {
{"Name", 4}}};
Button button_filter{
{11 * 8, 3 * 8, 4 * 8, 16},
"Filter"};
{11 * 8, 2 * 8, 7 * 8, 16},
"Filter:"};
OptionsField options_filter{
{18 * 8 + 2, 2 * 8},
4,
{{"Data", 0},
{"MAC", 1}}};
Checkbox check_log{
{17 * 8, 3 * 8},
{10 * 8, 4 * 8 + 2},
3,
"Log",
true};
Checkbox check_name{
{23 * 8, 3 * 8},
{0 * 8, 4 * 8 + 2},
3,
"Name",
true};
Button button_find{
{0 * 8, 6 * 8, 4 * 8, 16},
{0 * 8, 7 * 8 - 2, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 6 * 8}, "Found:", Theme::getInstance()->fg_light->foreground}};
{{5 * 8, 7 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}};
Text text_found_count{
{11 * 8, 3 * 16, 20 * 8, 16},
{11 * 8, 7 * 8 - 2, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{17 * 8, 3 * 16 - 2},
{18 * 8 + 2, 4 * 8 + 2},
7,
"USB Log",
true};
Console console{
{0, 4 * 16, 240, 240}};
// Console console{
// {0, 10 * 8, 240, 240}};
Button button_clear_list{
{2 * 8, 320 - (16 + 32), 7 * 8, 32},
@@ -371,7 +380,7 @@ class BLERxView : public View {
[this](const Message* const) {
this->on_timer();
}};
};
}; /* BLERxView */
} /* namespace ui */
@@ -128,4 +128,8 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
@@ -108,6 +108,15 @@ class CaptureAppView : public View {
3};
spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui */
+4
View File
@@ -178,4 +178,8 @@ void ERTAppView::on_show_list() {
recent_entries_view.focus();
}
void ERTAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+8
View File
@@ -176,6 +176,14 @@ class ERTAppView : public View {
this->on_packet(packet);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void on_packet(const ert::Packet& packet);
void on_show_list();
};
+4
View File
@@ -321,6 +321,10 @@ void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) {
widget_frames.set_sync(stats->has_sync);
}
void POCSAGAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void BaudIndicator::paint(Painter& painter) {
auto p = screen_pos();
char top = '-';
+9
View File
@@ -292,6 +292,15 @@ class POCSAGAppView : public View {
Console console{
{0, 2 * 16 + 6, screen_width, screen_height - 54}};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::POCSAGPacket,
[this](Message* const p) {
+4
View File
@@ -122,6 +122,10 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
receiver_model.enable();
}
void APRSRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void APRSRxView::on_packet(const APRSPacketMessage* message) {
std::string str_console = "\x1B";
+8
View File
@@ -185,6 +185,7 @@ class APRSRxView : public View {
std::string title() const override { return "APRS RX"; };
void on_packet(const APRSPacketMessage* message);
void on_freqchg(int64_t freq);
private:
void on_data(uint32_t value, bool is_data);
@@ -271,6 +272,13 @@ class APRSRXView : public View {
this->view_stream.on_packet(message);
this->view_table.on_pkt(message);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->view_stream.on_freqchg(message->freq);
}};
};
} /* namespace ui */
+69 -2
View File
@@ -49,6 +49,10 @@ void BattinfoView::update_result() {
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
text_cycles.set("-");
text_ttef.set("-");
text_method.set("-");
text_warn.set("");
return;
}
bool uichg = false;
@@ -72,11 +76,59 @@ void BattinfoView::update_result() {
text_current.set(to_string_dec_int(current) + " mA");
text_charge.set(current >= 0 ? "Charging" : "Discharging");
labels_opt.hidden(false);
text_ttef.hidden(false);
} else {
if (!labels_opt.hidden()) uichg = true;
labels_opt.hidden(true);
text_current.hidden(true);
text_charge.hidden(true);
text_cycles.hidden(true);
text_ttef.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
text_cycles.hidden(false);
uint16_t cycles = battery::BatteryManagement::get_cycles();
if (cycles < 2)
text_warn.set("SoC improves after 2 cycles");
else
text_warn.set("");
text_cycles.set(to_string_dec_uint(cycles));
} else {
text_cycles.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
text_ttef.hidden(false);
float ttef = 0;
if (current <= 0) {
ttef = battery::BatteryManagement::get_tte();
} else {
ttef = battery::BatteryManagement::get_ttf();
}
// Convert ttef to hours and minutes
uint8_t hours = static_cast<uint8_t>(ttef);
uint8_t minutes = static_cast<uint8_t>((ttef - hours) * 60 + 0.5); // +0.5 for rounding
// Create the formatted string
std::string formatted_time;
if (hours > 0) {
formatted_time += to_string_dec_uint(hours) + "h ";
}
formatted_time += to_string_dec_uint(minutes) + "m";
text_ttef.set(formatted_time);
} else {
text_ttef.hidden(true);
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_PERCENT) == battery::BatteryManagement::BATT_VALID_PERCENT) {
text_method.set("IC");
button_mode.set_text("Volt");
} else {
text_method.set("Voltage");
button_mode.set_text("IC");
}
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
@@ -92,12 +144,27 @@ BattinfoView::BattinfoView(NavigationView& nav)
&text_voltage,
&text_current,
&text_charge,
&button_exit});
&text_method,
&button_mode,
&button_exit,
&text_cycles,
&text_warn,
&text_ttef});
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
button_mode.on_select = [this, &nav](Button&) {
if (button_mode.text() == "IC") {
battery::BatteryManagement::set_calc_override(false);
persistent_memory::set_ui_override_batt_calc(false);
button_mode.set_text("Volt");
} else {
battery::BatteryManagement::set_calc_override(true);
persistent_memory::set_ui_override_batt_calc(true);
button_mode.set_text("IC");
}
};
update_result();
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
}
+28 -5
View File
@@ -53,11 +53,17 @@ class BattinfoView : public View {
Labels labels{
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 3 * 16}, "Method:", Theme::getInstance()->fg_light->foreground},
};
Labels labels_opt{
{{2 * 8, 3 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 4 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
@@ -65,12 +71,29 @@ class BattinfoView : public View {
Text text_voltage{
{13 * 8, 2 * 16, 10 * 16, 16},
"-"};
Text text_current{
Text text_method{
{13 * 8, 3 * 16, 10 * 16, 16},
"-"};
Text text_charge{
Text text_current{
{13 * 8, 4 * 16, 10 * 16, 16},
"-"};
Text text_charge{
{13 * 8, 5 * 16, 10 * 16, 16},
"-"};
Text text_ttef{
{13 * 8, 6 * 16, 10 * 16, 16},
"-"};
Text text_cycles{
{13 * 8, 7 * 16, 10 * 16, 16},
"-"};
Text text_warn{
{2 * 8, 8 * 16, 30 * 8, 2 * 16},
""};
Button button_mode{
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
"Volt"};
Button button_exit{
{72, 17 * 16, 96, 32},
+6
View File
@@ -36,6 +36,7 @@
#include "ui_font_fixed_8x16.hpp"
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_battery.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -511,6 +512,11 @@ void DebugMenuView::on_populate() {
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
add_item(
{"Battery", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_batt_icon, [this]() { nav_.push<BatteryCapacityView>(); }});
}
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
add_item(gridItem);
};
@@ -0,0 +1,104 @@
#include "ui_debug_battery.hpp"
#include "string_format.hpp"
namespace ui {
BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) {
if (index < battery::max17055::MAX17055::entries_count) {
return battery::max17055::MAX17055::entries[index];
}
return {"", 0, "", 0, false, "", false, 0, false, false, false, 0, false};
}
BatteryCapacityView::BatteryCapacityView(NavigationView& nav) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
name_texts[i].set_parent_rect({0 * 8, static_cast<int>((i + 1) * 16), 8 * 8, 16});
addr_texts[i].set_parent_rect({9 * 8, static_cast<int>((i + 1) * 16), 4 * 8, 16});
hex_texts[i].set_parent_rect({14 * 8, static_cast<int>((i + 1) * 16), 6 * 8, 16});
value_texts[i].set_parent_rect({21 * 8, static_cast<int>((i + 1) * 16), 10 * 8, 16});
add_child(&name_texts[i]);
add_child(&addr_texts[i]);
add_child(&hex_texts[i]);
add_child(&value_texts[i]);
}
add_children({&labels, &page_text, &button_done});
button_done.on_select = [&nav](Button&) { nav.pop(); };
populate_page(0);
update_page_text();
}
void BatteryCapacityView::focus() {
button_done.focus();
}
bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
int32_t new_page = current_page + delta;
if (new_page >= 0 && new_page < ((int32_t)battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
current_page = new_page;
populate_page(current_page * ENTRIES_PER_PAGE);
update_page_text();
}
return true;
}
void BatteryCapacityView::update_values() {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = current_page * ENTRIES_PER_PAGE + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
uint16_t raw_value = battery::BatteryManagement::read_register(entry.address);
hex_texts[i].set("0x" + to_string_hex(raw_value, 4));
float scaled_value;
if (entry.is_signed) {
int16_t signed_value = static_cast<int16_t>(raw_value);
scaled_value = signed_value * entry.scalar;
} else {
scaled_value = raw_value * entry.scalar;
}
// Format the value with appropriate decimal places
std::string formatted_value;
if (entry.resolution > 0) {
formatted_value = to_string_decimal(scaled_value, std::min(entry.resolution, 3));
} else {
formatted_value = to_string_dec_int(scaled_value); // Show up to 3 decimal places
}
value_texts[i].set(formatted_value + " " + entry.unit);
}
}
}
void BatteryCapacityView::populate_page(int start_index) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = start_index + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
name_texts[i].set(entry.name);
addr_texts[i].set("0x" + to_string_hex(entry.address, 2));
name_texts[i].hidden(false);
addr_texts[i].hidden(false);
hex_texts[i].hidden(false);
value_texts[i].hidden(false);
} else {
name_texts[i].hidden(true);
addr_texts[i].hidden(true);
hex_texts[i].hidden(true);
value_texts[i].hidden(true);
}
}
update_values();
}
void BatteryCapacityView::update_page_text() {
int total_pages = (battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
page_text.set("Page " + to_string_dec_uint(current_page + 1) + "/" + to_string_dec_uint(total_pages));
}
} // namespace ui
@@ -0,0 +1,48 @@
#ifndef __UI_DEBUG_BATTERY_HPP__
#define __UI_DEBUG_BATTERY_HPP__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "battery.hpp"
#include "max17055.hpp"
namespace ui {
class BatteryCapacityView : public View {
public:
BatteryCapacityView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery Registers"; }
bool on_encoder(const EncoderEvent delta) override;
using RegisterEntry = battery::max17055::RegisterEntry;
private:
static RegisterEntry get_entry(size_t index);
Labels labels{
{{0 * 8, 0 * 16}, "Reg", Theme::getInstance()->fg_yellow->foreground},
{{9 * 8, 0 * 16}, "Addr", Theme::getInstance()->fg_yellow->foreground},
{{14 * 8, 0 * 16}, "Hex", Theme::getInstance()->fg_yellow->foreground},
{{21 * 8, 0 * 16}, "Value", Theme::getInstance()->fg_yellow->foreground},
};
std::array<Text, 16> name_texts = {};
std::array<Text, 16> addr_texts = {};
std::array<Text, 16> hex_texts = {};
std::array<Text, 16> value_texts = {};
Text page_text{{144, 284, 80, 16}, "Page 1/1"};
Button button_done{{16, 280, 96, 24}, "Done"};
void update_values();
void populate_page(int start_index);
void update_page_text();
int current_page = 0;
static constexpr int ENTRIES_PER_PAGE = 16;
};
} // namespace ui
#endif // __UI_DEBUG_BATTERY_HPP__
+5
View File
@@ -323,4 +323,9 @@ void LevelView::handle_coded_squelch(const uint32_t value) {
text_ctcss.set(" ");
}
void LevelView::on_freqchg(int64_t freq) {
receiver_model.set_target_frequency(freq);
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
}
} /* namespace ui */
+9
View File
@@ -188,6 +188,15 @@ class LevelView : public View {
void handle_coded_squelch(const uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
@@ -561,6 +561,17 @@ GlassView::GlassView(
update_display_beep();
}
void GlassView::on_freqchg(int64_t freq) {
int64_t half_range = abs(field_frequency_max.value() - field_frequency_max.value()) / 2;
if (half_range < 1) {
half_range = 1;
}
range_presets.set_selected_index(0); // Manual
update_min(freq - half_range);
update_max(freq + half_range);
on_range_changed();
}
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
return iq_phase_calibration_value;
}
@@ -109,6 +109,7 @@ class GlassView : public View {
std::string label{};
};
void on_freqchg(int64_t freq);
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
std::vector<preset_entry> presets_db{};
void manage_beep_audio();
@@ -307,6 +308,7 @@ class GlassView : public View {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
@@ -317,6 +319,13 @@ class GlassView : public View {
}
}
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} // namespace ui
#endif
+53 -4
View File
@@ -774,31 +774,44 @@ void SetConfigModeView::focus() {
button_save.focus();
}
/* SetFakeBrightnessView ************************************/
/* SetDisplayView ************************************/
SetFakeBrightnessView::SetFakeBrightnessView(NavigationView& nav) {
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetFakeBrightnessView::focus() {
void SetDisplayView::focus() {
button_save.focus();
}
@@ -948,6 +961,41 @@ void SetThemeView::focus() {
options.focus();
}
/* SetBatteryView ************************************/
SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BATT_MAX17055) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
send_system_refresh();
nav.pop();
};
button_reset.on_select = [&nav, this](Button&) {
if (battery::BatteryManagement::reset_learned())
nav.display_modal("Reset", "Battery parameters reset");
else
nav.display_modal("Error", "Error parameter reset");
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetBatteryView::focus() {
button_cancel.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav)
@@ -973,11 +1021,12 @@ void SettingsMenuView::on_populate() {
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
//{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetFakeBrightnessView>(); }},
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
}
} /* namespace ui */
+43 -3
View File
@@ -711,13 +711,13 @@ class SetConfigModeView : public View {
using portapack::persistent_memory::fake_brightness_level_options;
class SetFakeBrightnessView : public View {
class SetDisplayView : public View {
public:
SetFakeBrightnessView(NavigationView& nav);
SetDisplayView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Brightness"; };
std::string title() const override { return "Display"; };
private:
Labels labels{
@@ -739,6 +739,11 @@ class SetFakeBrightnessView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
23,
"Invert colors (For IPS)"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -888,6 +893,41 @@ class SetThemeView : public View {
};
};
class SetBatteryView : public View {
public:
SetBatteryView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery"; };
private:
int32_t selected = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
Labels labels2{
{{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Checkbox checkbox_overridebatt{
{2 * 8, 4 * 16},
23,
"Override"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 8 * 16, 12 * 8, 32},
"Reset",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
+4
View File
@@ -187,4 +187,8 @@ void SondeView::on_packet(const sonde::Packet& packet) {
}
}
void SondeView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+8
View File
@@ -195,6 +195,14 @@ class SondeView : public View {
const auto message = static_cast<const OrientationDataMessage*>(p);
this->on_orientation(message);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
+21 -1
View File
@@ -212,7 +212,10 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "Somify Keytis";
case FPS_SOMIFY_TELIS:
return "Somify Telis";
case FPS_GANGQI:
return "GangQi";
case FPS_MARANTEC24:
return "Marantec24";
case FPS_Invalid:
default:
return "Unknown";
@@ -224,6 +227,10 @@ std::string SubGhzDView::pad_string_with_spaces(int snakes) {
return paddedStr;
}
void SubGhzDView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
template <>
void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
@@ -713,5 +720,18 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
serial = dataa & 0xFFFFFF; // address}
return;
}
if (entry_.sensorType == FPS_GANGQI) {
btn = 0; // parser needs some time i think in flipper side.
cnt = (uint8_t)(entry_.data >> 32);
serial = (entry_.data & 0xFFFFFFFF);
return;
}
if (entry_.sensorType == FPS_MARANTEC24) {
serial = (entry_.data >> 4);
btn = entry_.data & 0xf;
return;
}
}
} // namespace ui
+8
View File
@@ -127,6 +127,14 @@ class SubGhzDView : public View {
}};
SubGhzDRecentEntriesView recent_entries_view{columns, recent};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::SubGhzDData,
[this](Message* const p) {
+292 -1
View File
@@ -26,6 +26,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "../baseband/fprotos/fprotogeneral.hpp"
using namespace portapack;
using namespace ui;
@@ -138,7 +139,8 @@ void WeatherView::on_tick_second() {
}
void WeatherView::on_data(const WeatherDataMessage* data) {
WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
WeatherRecentEntry key = process_data(data);
// WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
@@ -209,6 +211,8 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
return "Acurite5in1";
case FPW_EmosE601x:
return "EmosE601x";
case FPW_SolightTE44:
return "SolightTE44";
case FPW_Invalid:
default:
return "Unknown";
@@ -220,6 +224,10 @@ std::string WeatherView::pad_string_with_spaces(int snakes) {
return paddedStr;
}
void WeatherView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
template <>
void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
const Entry& entry,
@@ -250,4 +258,287 @@ void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
painter.draw_string(target_rect.location(), style, line);
}
#define LACROSSE_TX_MSG_TYPE_TEMP 0x00
#define LACROSSE_TX_MSG_TYPE_HUM 0x0E
WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) {
WeatherRecentEntry ret = {};
ret.sensorType = data->sensorType;
int16_t i16 = 0;
uint16_t u16 = 0;
uint64_t u64 = 0;
uint8_t u8 = 0;
uint8_t channel_3021[] = {3, 0, 2, 1};
float flo = 0.0;
switch (data->sensorType) {
case FPW_NexusTH:
ret.id = (data->decode_data >> 28) & 0xFF;
ret.battery_low = !((data->decode_data >> 27) & 1);
ret.channel = ((data->decode_data >> 24) & 0x03) + 1;
if (!((data->decode_data >> 23) & 1)) {
ret.temp = (float)((data->decode_data >> 12) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 12) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = data->decode_data & 0xFF;
if (ret.humidity > 95)
ret.humidity = 95;
else if (ret.humidity < 20)
ret.humidity = 20;
break;
case FPW_Acurite592TXR:
u8 = ((data->decode_data >> 54) & 0x03);
ret.channel = channel_3021[u8];
ret.id = (data->decode_data >> 40) & 0x3FFF;
ret.battery_low = !((data->decode_data >> 38) & 1);
ret.humidity = (data->decode_data >> 24) & 0x7F;
u16 = ((data->decode_data >> 9) & 0xF80) | ((data->decode_data >> 8) & 0x7F);
ret.temp = ((float)(u16)-1000) / 10.0f;
break;
case FPW_Acurite606TX:
ret.id = (data->decode_data >> 24) & 0xFF;
ret.battery_low = (data->decode_data >> 23) & 1;
if (!((data->decode_data >> 19) & 1)) {
ret.temp = (float)((data->decode_data >> 8) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 8) & 0x07FF) + 1) / -10.0f;
}
break;
case FPW_Acurite609TX:
ret.id = (data->decode_data >> 32) & 0xFF;
ret.battery_low = (data->decode_data >> 31) & 1;
ret.channel = WS_NO_CHANNEL;
// Temperature in Celsius is encoded as a 12 bit integer value
// multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
// negative values are recovered by sign extend from int16_t.
i16 = (int16_t)(((data->decode_data >> 12) & 0xf000) | ((data->decode_data >> 16) << 4));
ret.temp = (i16 >> 4) * 0.1f;
ret.humidity = (data->decode_data >> 8) & 0xff;
break;
case FPW_Ambient:
id = (data->decode_data >> 32) & 0xFF;
ret.battery_low = (data->decode_data >> 31) & 1;
ret.channel = ((data->decode_data >> 28) & 0x07) + 1;
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data->decode_data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
ret.humidity = (data->decode_data >> 8) & 0xFF;
break;
case FPW_AuriolAhfl:
ret.id = data->decode_data >> 34;
ret.battery_low = (data->decode_data >> 33) & 1;
// btn = (data >> 32) & 1;
ret.channel = ((data->decode_data >> 30) & 0x3) + 1;
if (!((data->decode_data >> 29) & 1)) {
ret.temp = (float)((data->decode_data >> 18) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 18) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 11) & 0x7F;
break;
case FPW_AuriolTH:
ret.id = (data->decode_data >> 31) & 0xFF;
ret.battery_low = ((data->decode_data >> 30) & 1);
ret.channel = ((data->decode_data >> 25) & 0x03) + 1;
if (!((data->decode_data >> 23) & 1)) {
ret.temp = (float)((data->decode_data >> 13) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 13) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 1) & 0x7F;
break;
case FPW_GTWT02:
ret.id = (data->decode_data >> 29) & 0xFF;
ret.battery_low = (data->decode_data >> 28) & 1;
// btn = (data->decode_data >> 27) & 1;
ret.channel = ((data->decode_data >> 25) & 0x3) + 1;
if (!((data->decode_data >> 24) & 1)) {
ret.temp = (float)((data->decode_data >> 13) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 13) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 6) & 0x7F;
if (ret.humidity <= 10) // actually the sensors sends 10 below working range of 20%
ret.humidity = 0;
else if (ret.humidity > 90) // actually the sensors sends 110 above working range of 90%
ret.humidity = 100;
break;
case FPW_GTWT03:
ret.id = data->decode_data >> 33;
ret.humidity = (data->decode_data >> 25) & 0xFF;
if (ret.humidity <= 10) { // actually the sensors sends 10 below working range of 20%
ret.humidity = 0;
} else if (ret.humidity > 95) { // actually the sensors sends 110 above working range of 90%
ret.humidity = 100;
}
ret.battery_low = (data->decode_data >> 24) & 1;
// (data->decode_data >> 23) & 1;
ret.channel = ((data->decode_data >> 21) & 0x03) + 1;
if (!((data->decode_data >> 20) & 1)) {
ret.temp = (float)((data->decode_data >> 9) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 9) & 0x07FF) + 1) / -10.0f;
}
break;
case FPW_INFACTORY:
ret.id = data->decode_data >> 32;
ret.battery_low = (data->decode_data >> 26) & 1;
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data->decode_data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
ret.humidity = (((data->decode_data >> 8) & 0x0F) * 10) + ((data->decode_data >> 4) & 0x0F); // BCD, 'A0'=100%rH
ret.channel = data->decode_data & 0x03;
break;
case FPW_LACROSSETX:
u8 = (data->decode_data >> 32) & 0x0F;
ret.id = (((data->decode_data >> 28) & 0x0F) << 3) | (((data->decode_data >> 24) & 0x0F) >> 1);
flo = (float)((data->decode_data >> 20) & 0x0F) * 10.0f + (float)((data->decode_data >> 16) & 0x0F) + (float)((data->decode_data >> 12) & 0x0F) * 0.1f;
if (u8 == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051
ret.temp = flo - 50.0f;
ret.humidity = WS_NO_HUMIDITY;
} else if (u8 == LACROSSE_TX_MSG_TYPE_HUM) {
// ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard
ret.humidity = (uint8_t)flo;
}
break;
case FPW_LACROSSETX141thbv2:
ret.id = data->decode_data >> 32;
ret.battery_low = (data->decode_data >> 31) & 1;
// btn = (data->decode_data >> 30) & 1;
ret.channel = ((data->decode_data >> 28) & 0x03) + 1;
ret.temp = ((float)((data->decode_data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
ret.humidity = (data->decode_data >> 8) & 0xFF;
break;
case FPW_OREGON2:
i16 = ((data->decode_data >> 4) & 0xF) * 10 + (((data->decode_data >> 4) >> 4) & 0xF);
i16 *= 10;
i16 += (data->decode_data >> 12) & 0xF;
if (data->decode_data & 0xF) i16 = -i16;
ret.temp = (float)i16 / 10.0;
// todo fix missing parts.
break;
case FPW_OREGON3:
// todo check
ret.humidity = ((data->decode_data >> 4) & 0xF) * 10 + (((data->decode_data >> 4) >> 4) & 0xF);
i16 = (((data->decode_data >> 12) >> 4) & 0xF) * 10 + ((((data->decode_data >> 12) >> 4) >> 4) & 0xF); // ws_oregon3_bcd_decode_short((data->decode_data >> 12) >> 4);
i16 *= 10;
i16 += ((data->decode_data >> 12) >> 12) & 0xF;
if ((data->decode_data >> 12) & 0xF) i16 = -i16;
ret.temp = (float)i16 / 10.0;
break;
case FPW_OREGONv1:
u64 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data, 32);
id = u64 & 0xFF;
ret.channel = ((u64 >> 6) & 0x03) + 1;
ret.temp = ((u64 >> 8) & 0x0F) * 0.1f + ((u64 >> 12) & 0x0F) + ((u64 >> 16) & 0x0F) * 10.0f;
if (!((u64 >> 21) & 1)) {
ret.temp = ret.temp;
} else {
ret.temp = -ret.temp;
}
ret.battery_low = !((u64 >> 23) & 1ULL);
break;
case FPW_THERMOPROTX4:
ret.id = (data->decode_data >> 25) & 0xFF;
ret.battery_low = (data->decode_data >> 24) & 1;
// btn = (data->decode_data >> 23) & 1;
ret.channel = ((data->decode_data >> 21) & 0x03) + 1;
if (!((data->decode_data >> 20) & 1)) {
ret.temp = (float)((data->decode_data >> 9) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 9) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 1) & 0xFF;
break;
case FPW_TX_8300:
ret.humidity = (((data->decode_data >> 28) & 0x0F) * 10) + ((data->decode_data >> 24) & 0x0F);
if (!((data->decode_data >> 22) & 0x03))
ret.battery_low = 0;
else
ret.battery_low = 1;
ret.channel = (data->decode_data >> 20) & 0x03;
ret.id = (data->decode_data >> 12) & 0x7F;
ret.temp = ((data->decode_data >> 8) & 0x0F) * 10.0f + ((data->decode_data >> 4) & 0x0F) +
(data->decode_data & 0x0F) * 0.1f;
if (!((data->decode_data >> 19) & 1)) {
ret.temp = ret.temp;
} else {
ret.temp = -ret.temp;
}
break;
case FPW_WENDOX_W6726:
ret.id = (data->decode_data >> 24) & 0xFF;
ret.battery_low = (data->decode_data >> 6) & 1;
if (((data->decode_data >> 23) & 1)) {
ret.temp = (float)(((data->decode_data >> 14) & 0x1FF) + 12) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 14) & 0x1FF) + 1 - 12) / -10.0f;
}
if (ret.temp < -50.0f) {
ret.temp = -50.0f;
} else if (ret.temp > 70.0f) {
ret.temp = 70.0f;
}
break;
case FPW_Acurite986:
ret.id = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 24, 8);
ret.id = (id << 8) | FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 16, 8);
ret.battery_low = (data->decode_data >> 14) & 1;
ret.channel = ((data->decode_data >> 15) & 1) + 1;
i16 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 32, 8);
if (i16 & 0x80) {
i16 = -(i16 & 0x7F);
}
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)i16);
break;
case FPW_KEDSUM:
ret.id = data->decode_data >> 32;
if ((data->decode_data >> 30) & 0x3) {
ret.battery_low = 0;
} else {
ret.battery_low = 1;
}
ret.channel = ((data->decode_data >> 28) & 0x3) + 1;
u16 = ((data->decode_data >> 16) & 0x0f) << 8 | ((data->decode_data >> 20) & 0x0f) << 4 | ((data->decode_data >> 24) & 0x0f);
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)u16 - 900.0f) / 10.0f);
ret.humidity = ((data->decode_data >> 8) & 0x0f) << 4 | ((data->decode_data >> 12) & 0x0f);
break;
case FPW_Acurite5in1:
u8 = ((data->decode_data >> 62) & 0x03);
ret.channel = channel_3021[u8];
ret.id = (data->decode_data >> 48) & 0x3FFF;
ret.battery_low = !((data->decode_data >> 46) & 1);
ret.humidity = (data->decode_data >> 8) & 0x7F;
u16 = ((data->decode_data >> (24 - 7)) & 0x780) | ((data->decode_data >> 16) & 0x7F);
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)(u16)-400) / 10.0f);
break;
case FPW_EmosE601x:
ret.id = 0xffff; //(upper_decode_data >> 24) & 0xff; //todo maybe with oregon, and +64bit usage/message
ret.battery_low = (data->decode_data >> 10) & 1;
i16 = (data->decode_data >> 40) & 0xfff;
/* Handle signed data */
if (i16 & 0x800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
ret.humidity = (data->decode_data >> 32) & 0xff;
ret.channel = (data->decode_data >> 52) & 0x03;
break;
case FPW_SolightTE44:
ret.id = (data->decode_data >> 28) & 0xff;
ret.battery_low = !(data->decode_data >> 27) & 0x01;
ret.channel = ((data->decode_data >> 24) & 0x03) + 1;
i16 = (data->decode_data >> 12) & 0x0fff;
/* Handle signed data */
if (i16 & 0x0800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
break;
case FPW_Invalid:
default:
break;
}
return ret;
}
} // namespace ui
@@ -96,7 +96,7 @@ class WeatherView : public View {
private:
void on_tick_second();
void on_data(const WeatherDataMessage* data);
WeatherRecentEntry process_data(const WeatherDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
@@ -151,6 +151,14 @@ class WeatherView : public View {
}};
WeatherRecentEntriesView recent_entries_view{columns, recent};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::WeatherData,
[this](Message* const p) {
+319 -313
View File
@@ -1,321 +1,327 @@
// converted by bmp_to_hex_cpp_arr.py at the firmware/tools dir
// fake transparent px should be the exact color rgb dec(41,24,22), then use true for the last arg when drawing bmp with the draw func
const unsigned char splash_bmp[] = {
0x42, 0x4d, 0xf4, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
0x42, 0x4d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x32, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x82, 0x0e,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x0e,
0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x0f, 0x00,
0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf6, 0x00, 0x00, 0x14,
0xc9, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x16, 0x18, 0x29, 0x00, 0xfd, 0xfd,
0xfd, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0xe7, 0xe7, 0xe7, 0x00, 0xda, 0xda,
0xda, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xba, 0xba,
0xba, 0x00, 0xad, 0xad, 0xad, 0x00, 0x96, 0x96, 0x96, 0x00, 0x5c, 0x5c,
0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee,
0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0x00, 0x18,
0xee, 0x44, 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d,
0x08, 0xee, 0x00, 0x14, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0xea, 0x44, 0x44,
0x44, 0xde, 0x12, 0xee, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0xee,
0x00, 0x08, 0xc4, 0x44, 0x45, 0xce, 0x10, 0xee, 0x00, 0x08, 0x34, 0x44,
0x44, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x44, 0x44, 0x6d, 0xee, 0xee,
0xee, 0xe3, 0xd5, 0x00, 0x1a, 0x44, 0x00, 0x1c, 0x45, 0xce, 0xee, 0x44,
0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, 0x08, 0xee,
0x00, 0x0a, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x34,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98,
0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xed, 0x79, 0x99,
0x99, 0x73, 0x10, 0xee, 0x00, 0x0a, 0xed, 0x47, 0x99, 0x95, 0xae, 0x00,
0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c,
0xee, 0xee, 0xee, 0xb4, 0x56, 0x89, 0x18, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98,
0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x34, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99,
0x99, 0x99, 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xb9, 0x99, 0x99, 0x8d, 0x10, 0xee, 0x00, 0x0a, 0xe7, 0x69, 0x99, 0x74,
0xde, 0x00, 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xee, 0xea, 0x47, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, 0x99, 0x99,
0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99,
0x99, 0x99, 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xd7, 0x99, 0x99, 0x98, 0x3e, 0x00, 0x0e, 0xee, 0x00, 0x08, 0xd4, 0x89,
0x99, 0x5a, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xee, 0xb4, 0x79, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, 0x99, 0x99,
0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x12, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00,
0x08, 0x99, 0x00, 0x1c, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0xee, 0xeb, 0x99, 0x99, 0x99, 0xce, 0x0c, 0xee, 0x00, 0x0a, 0xe3, 0x56,
0x99, 0x97, 0x4d, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xed, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16,
0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00,
0x08, 0x99, 0x00, 0x10, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x10, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5,
0x0a, 0x99, 0x00, 0x1c, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0xee, 0xed, 0x79, 0x99, 0x99, 0x73, 0x0c, 0xee, 0x00, 0x0a, 0xec, 0x48,
0x99, 0x95, 0xae, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xe7, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14,
0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, 0x0a, 0x99,
0x00, 0x10, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, 0x97,
0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xb9, 0x99, 0x99, 0x9d, 0x00, 0x0c, 0xee, 0x00, 0x0a, 0x34, 0x69,
0x99, 0x74, 0xde, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44,
0x00, 0x2c, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99,
0x97, 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x38, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59,
0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94,
0x3e, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x99, 0x74, 0x0c, 0x44, 0x00, 0x06,
0x99, 0x99, 0x4b, 0x00, 0x0c, 0xee, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee,
0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, 0x99, 0x95, 0xbc,
0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x30, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0x34, 0x79, 0x99, 0x84,
0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xeb, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x00, 0x0a,
0xed, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0xee, 0x00, 0x08, 0x34, 0x99,
0x99, 0x9d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6,
0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde,
0xee, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3,
0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95,
0xde, 0xee, 0xc4, 0x99, 0x99, 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee,
0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xed, 0x79, 0x00, 0x14, 0x99,
0x02, 0x94, 0x02, 0xce, 0x0a, 0xee, 0x00, 0x0c, 0x35, 0x59, 0x99, 0x99,
0x98, 0x3e, 0x0e, 0xee, 0x00, 0x0a, 0x34, 0x99, 0x99, 0x99, 0x74, 0x00,
0x16, 0x44, 0x00, 0x10, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x9d,
0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xc4, 0x99, 0x99,
0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99,
0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94,
0x3e, 0xee, 0xee, 0xee, 0xc9, 0x00, 0x14, 0x99, 0x02, 0x74, 0x02, 0x3e,
0x08, 0xee, 0x00, 0x0e, 0xe3, 0x84, 0x89, 0x99, 0x99, 0x99, 0xa3, 0x00,
0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x0e, 0x7c, 0xee, 0xd6, 0x99,
0x99, 0x99, 0x64, 0x00, 0x18, 0x44, 0x00, 0x2e, 0x45, 0xce, 0xee, 0x49,
0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99,
0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32,
0xee, 0x49, 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37,
0x99, 0x99, 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee,
0x37, 0x00, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0xee, 0x02, 0xeb, 0x02, 0x48,
0x08, 0x99, 0x02, 0x9b, 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, 0x99, 0x99,
0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32,
0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec,
0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee,
0xec, 0x00, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x02, 0xd4,
0x02, 0x79, 0x0a, 0x99, 0x02, 0xde, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec,
0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x48, 0xee, 0x49, 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e,
0xee, 0xe3, 0x79, 0x99, 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99, 0x99,
0x94, 0xce, 0x08, 0xee, 0x00, 0x12, 0xed, 0x46, 0x99, 0x98, 0x59, 0x99,
0x99, 0x99, 0x8d, 0x00, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, 0xee, 0xe3, 0x79, 0x99,
0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a,
0xee, 0x49, 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee,
0xa9, 0x99, 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee,
0x00, 0x16, 0xc9, 0x99, 0x99, 0x9c, 0xee, 0xee, 0x3b, 0x99, 0x99, 0x74,
0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0x35, 0x59, 0x99, 0x95, 0x7d, 0x99,
0x99, 0x99, 0x9a, 0x3e, 0x0a, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, 0xa9, 0x99,
0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a,
0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, 0x4d, 0xee, 0xee, 0xee,
0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee,
0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xc4, 0x89, 0x99, 0x4c,
0x08, 0xee, 0x00, 0x16, 0xe3, 0x84, 0x89, 0x99, 0x64, 0x3e, 0xd9, 0x99,
0x99, 0x99, 0xa3, 0x00, 0x0a, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x64,
0x16, 0x44, 0x00, 0x0c, 0x99, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x20, 0x99,
0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98,
0x4d, 0xee, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94,
0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69,
0x99, 0x96, 0x5e, 0xee, 0xee, 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49,
0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0xec, 0x99, 0x99, 0x99,
0xbe, 0xe3, 0x46, 0x99, 0x96, 0x43, 0x08, 0xee, 0x00, 0x16, 0xeb, 0x48,
0x99, 0x97, 0x4d, 0xee, 0xed, 0x99, 0x99, 0x99, 0x9c, 0x00, 0x0a, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x39, 0x99,
0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x54, 0x00, 0x1c, 0x44, 0x00, 0x2c,
0xae, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x5e, 0xee, 0xee,
0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x2a, 0xee, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xde, 0xee,
0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x3e, 0x00,
0x08, 0xee, 0x00, 0x14, 0xe3, 0x79, 0x99, 0x99, 0x8d, 0xea, 0x59, 0x99,
0x84, 0xde, 0x08, 0xee, 0x00, 0x18, 0xd4, 0x79, 0x99, 0x84, 0xbe, 0xee,
0xee, 0xb9, 0x99, 0x99, 0x99, 0xde, 0x08, 0xee, 0x00, 0x08, 0x34, 0x99,
0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6,
0x99, 0x99, 0x5d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x98, 0xd5,
0x79, 0x99, 0x94, 0xde, 0xee, 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73,
0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x99,
0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99,
0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x0a, 0xee, 0x00, 0x32, 0xc9, 0x99,
0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65, 0x3e, 0xee, 0xee, 0xee, 0xed, 0x46,
0x99, 0x98, 0x48, 0x3e, 0xee, 0xee, 0x3a, 0x99, 0x99, 0x99, 0x83, 0x00,
0x08, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x93, 0x00, 0x20, 0xee,
0x00, 0x28, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee,
0xee, 0xe3, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x10, 0xee, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee,
0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, 0x79, 0x99, 0x84, 0x3e, 0x0a, 0xee,
0x00, 0x10, 0x37, 0x99, 0x99, 0x99, 0x86, 0x99, 0x98, 0x4d, 0x08, 0xee,
0x00, 0x2a, 0x35, 0x59, 0x99, 0x95, 0x53, 0xee, 0xee, 0xee, 0xe3, 0x89,
0x99, 0x99, 0x9a, 0x3e, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00,
0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, 0xee, 0x36, 0x99, 0x99,
0x99, 0x54, 0x18, 0x44, 0x00, 0x14, 0x45, 0xce, 0xee, 0x49, 0x99, 0x99,
0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99,
0x79, 0x99, 0x84, 0x3e, 0x00, 0x00, 0x02, 0xee, 0x02, 0x59, 0x08, 0x99,
0x02, 0x97, 0x02, 0x4d, 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75,
0x0c, 0xee, 0x02, 0xec, 0x0a, 0x99, 0x00, 0x16, 0x96, 0x53, 0xee, 0xee,
0xee, 0xe3, 0x84, 0x89, 0x99, 0x64, 0xde, 0x00, 0x08, 0xee, 0x00, 0x18,
0xd9, 0x99, 0x99, 0x99, 0xbe, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d,
0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe6, 0x20, 0x99,
0x00, 0x08, 0x97, 0xae, 0xee, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d,
0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x02, 0xee, 0x00, 0x00,
0x02, 0xee, 0x02, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee,
0x02, 0xec, 0x0a, 0x99, 0x02, 0x5b, 0x0c, 0xee, 0x02, 0xe3, 0x02, 0x89,
0x08, 0x99, 0x00, 0x14, 0x84, 0xde, 0xee, 0xee, 0xee, 0xeb, 0x48, 0x99,
0x97, 0x4c, 0x0a, 0xee, 0x00, 0x18, 0xed, 0x99, 0x99, 0x99, 0x9d, 0xee,
0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xed, 0x89, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xae,
0xee, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, 0x02, 0xec,
0x0a, 0x99, 0x02, 0x5b, 0x02, 0xee, 0x00, 0x00, 0x02, 0xee, 0x02, 0xd7,
0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x0e, 0xe3, 0x79,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd8, 0x99,
0x99, 0x99, 0x98, 0x48, 0x08, 0xee, 0x00, 0x0a, 0xd4, 0x79, 0x99, 0x84,
0xbe, 0x00, 0x0c, 0xee, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98, 0xde, 0xee,
0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xe3, 0x20, 0x99, 0x00, 0x08, 0x97, 0xae, 0xee, 0xd7,
0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x10, 0xe3, 0x79,
0x99, 0x99, 0x99, 0x96, 0x4d, 0xee, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xec,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd9, 0x99,
0x99, 0x99, 0x74, 0xce, 0x0e, 0xee, 0x00, 0x1e, 0xed, 0x99, 0x99, 0x99,
0x85, 0x53, 0xee, 0xee, 0xee, 0xed, 0x46, 0x99, 0x99, 0x57, 0x3e, 0x00,
0x0c, 0xee, 0x00, 0x16, 0x39, 0x99, 0x99, 0x99, 0xa3, 0xee, 0xee, 0x34,
0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0xee, 0xee, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xec,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xd9, 0x99,
0x99, 0x99, 0x74, 0xce, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xe3,
0xc8, 0x99, 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0a, 0xed, 0x79,
0x99, 0x76, 0x4b, 0x00, 0x12, 0xee, 0x00, 0x1a, 0xc7, 0x99, 0x96, 0x45,
0xde, 0xee, 0xee, 0xee, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00, 0x0e, 0xee,
0x00, 0x16, 0xe3, 0x89, 0x99, 0x99, 0x9a, 0x3e, 0xee, 0x34, 0x99, 0x99,
0x6d, 0x00, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xee,
0xe3, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xe3, 0xc8, 0x99,
0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xed, 0x79, 0x99, 0x76,
0x4b, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xee, 0xee, 0xed, 0x84,
0x44, 0xc3, 0x12, 0xee, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xde, 0x12, 0xee,
0x00, 0x1a, 0xed, 0xa4, 0x44, 0xd3, 0xee, 0xee, 0xee, 0xe3, 0xc8, 0x44,
0x44, 0x44, 0xde, 0x00, 0x10, 0xee, 0x00, 0x14, 0xd4, 0x44, 0x44, 0x44,
0xae, 0xee, 0x38, 0x44, 0x44, 0x4d, 0x16, 0xee, 0x00, 0x12, 0x38, 0x44,
0x44, 0x4c, 0xee, 0xee, 0xee, 0xed, 0xc4, 0x00, 0x1c, 0x44, 0x00, 0x0e,
0xae, 0xee, 0xee, 0xed, 0x84, 0x44, 0xc3, 0x00, 0x12, 0xee, 0x00, 0x0c,
0xdc, 0x44, 0x4a, 0xde, 0xee, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x2e, 0xe0, 0x00,
0x13, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00,
0x00, 0x26, 0x1e, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0x00, 0x00,
0x13, 0xe3, 0x00, 0x00, 0x1e, 0xe2, 0x00, 0x00, 0x2e, 0x00, 0x24, 0xee,
0x00, 0x06, 0xe2, 0x00, 0x02, 0x00, 0x08, 0xee, 0x00, 0x50, 0xe2, 0x00,
0x00, 0x00, 0x01, 0xee, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xee,
0xe2, 0x00, 0x02, 0xee, 0xee, 0xe0, 0x00, 0x13, 0xe2, 0x00, 0x02, 0xee,
0x00, 0x01, 0x3e, 0xee, 0xee, 0xee, 0x30, 0x00, 0x1e, 0xe1, 0x00, 0x02,
0xee, 0x30, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x30, 0x00, 0x1e, 0xe1, 0x00,
0x02, 0xee, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11,
0x03, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x10, 0x10, 0x00, 0x00,
0x00, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x03,
0xe3, 0x01, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06,
0xe2, 0x01, 0x11, 0x00, 0x08, 0xee, 0x00, 0x68, 0x10, 0x10, 0x00, 0x11,
0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01,
0x11, 0xee, 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10,
0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x00,
0x10, 0x00, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee,
0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01,
0x10, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, 0xe2, 0x01,
0x11, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x31, 0x11, 0x0e,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee,
0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, 0x3e, 0xee,
0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x00,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00,
0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x2e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x12, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01,
0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x00, 0x2e, 0x20, 0xee, 0x00, 0x06,
0xe2, 0x01, 0x12, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x30,
0x11, 0x0e, 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0xee, 0xe2, 0x01,
0x11, 0xee, 0xee, 0xe0, 0x11, 0x13, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x11,
0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01,
0x10, 0x00, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02,
0xee, 0x00, 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x11, 0x00, 0x11,
0x03, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x1e, 0xee, 0x01, 0x08, 0x11,
0x00, 0x10, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x10, 0x2e, 0x20, 0xee,
0x00, 0x60, 0xe2, 0x01, 0x11, 0x10, 0x00, 0x03, 0xee, 0x11, 0x10, 0x2e,
0x30, 0x11, 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x02, 0xee, 0xee, 0xe2,
0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01,
0x11, 0x22, 0x22, 0x23, 0xee, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3,
0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13,
0xee, 0x00, 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x00, 0x11, 0x11,
0x03, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x1e, 0xee, 0x01, 0x11, 0x00,
0x01, 0x11, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x00, 0x00, 0x2e, 0x20, 0xee,
0x00, 0x60, 0xe2, 0x01, 0x10, 0x01, 0x11, 0x12, 0x3e, 0x11, 0x10, 0x2e,
0x30, 0x11, 0x0e, 0xe1, 0x11, 0x10, 0x10, 0x11, 0x1e, 0xee, 0xee, 0xe2,
0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x10, 0x10, 0x11, 0x11, 0xee, 0x01,
0x11, 0x01, 0x00, 0x01, 0x2e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0xe3,
0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x2e,
0xee, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe1, 0x11,
0x03, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x01, 0x11, 0x03,
0xe3, 0x11, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60,
0xe2, 0x01, 0x11, 0xee, 0x11, 0x10, 0x3e, 0x11, 0x10, 0x2e, 0x30, 0x11,
0x0e, 0xe1, 0x11, 0x01, 0x22, 0x11, 0x12, 0xee, 0xee, 0xe2, 0x01, 0x11,
0xee, 0xee, 0xe0, 0x11, 0x02, 0x22, 0x11, 0x11, 0xee, 0x01, 0x10, 0x22,
0x21, 0x11, 0x1e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x10,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0xee, 0x00,
0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01,
0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, 0xe2, 0x01,
0x11, 0xee, 0x01, 0x10, 0x3e, 0x11, 0x11, 0x2e, 0x30, 0x11, 0x0e, 0xe1,
0x11, 0x12, 0xe3, 0x11, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, 0xee,
0xe0, 0x11, 0x13, 0xe2, 0x11, 0x11, 0xee, 0x01, 0x11, 0x3e, 0x21, 0x11,
0x1e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x08, 0xee,
0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, 0x00, 0x00,
0x00, 0x54, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, 0x11, 0x12,
0xe3, 0x01, 0x00, 0xee, 0x11, 0x11, 0x2e, 0xee, 0xee, 0xee, 0xe1, 0x11,
0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, 0x10, 0x1e,
0xe2, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x2e, 0x1c, 0xee, 0x00, 0x60,
0xe2, 0x01, 0x12, 0xee, 0x01, 0x00, 0x3e, 0x11, 0x11, 0x22, 0x20, 0x11,
0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x12,
0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, 0x11, 0x22,
0x10, 0x11, 0x1e, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x11,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00,
0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe2,
0x11, 0x11, 0x00, 0x01, 0x02, 0xee, 0x21, 0x11, 0x10, 0x00, 0x00, 0x1e,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x08, 0x11, 0x00, 0x08,
0x12, 0xee, 0xe3, 0x21, 0x08, 0x11, 0x02, 0x10, 0x02, 0x2e, 0x1c, 0xee,
0x00, 0x76, 0xe2, 0x01, 0x11, 0x10, 0x01, 0x02, 0x3e, 0x11, 0x11, 0x11,
0x01, 0x10, 0x0e, 0xe1, 0x11, 0x11, 0x10, 0x11, 0x00, 0xee, 0x10, 0x00,
0x01, 0x11, 0x10, 0x1e, 0xe0, 0x11, 0x11, 0x10, 0x11, 0x01, 0xee, 0x01,
0x11, 0x11, 0x01, 0x10, 0x1e, 0x30, 0x11, 0x11, 0x00, 0x10, 0x02, 0xe3,
0x01, 0x11, 0x10, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02,
0xee, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x1e, 0xe1, 0x00,
0x03, 0xee, 0x20, 0x00, 0x00, 0x00, 0x1e, 0xee, 0xe2, 0x00, 0x08, 0x00,
0x00, 0x12, 0x0e, 0xe2, 0x00, 0x02, 0xe3, 0x10, 0x00, 0xee, 0x10, 0x00,
0x08, 0x00, 0x00, 0x08, 0x02, 0xee, 0xee, 0x30, 0x0a, 0x00, 0x02, 0x2e,
0x1c, 0xee, 0x02, 0xe2, 0x08, 0x00, 0x00, 0x12, 0x03, 0xee, 0xe2, 0x00,
0x00, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, 0x00, 0x06, 0x1e, 0xee,
0x20, 0x00, 0x08, 0x00, 0x00, 0x1e, 0x0e, 0xee, 0x10, 0x00, 0x00, 0x00,
0x2e, 0xee, 0x10, 0x00, 0x00, 0x00, 0x02, 0xee, 0xe3, 0x00, 0x08, 0x00,
0x00, 0x06, 0x2e, 0xee, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x31,
0x00, 0x0e, 0xe2, 0x00, 0x02, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x01};
unsigned int splash_bmp_len = 3828;
0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0x00, 0x08,
0x33, 0x44, 0x44, 0x47, 0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00,
0x0a, 0x33, 0x00, 0x0a, 0x44, 0x44, 0x48, 0x33, 0x3a, 0x00, 0x06, 0x44,
0x02, 0xd3, 0x12, 0x33, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0x33,
0x00, 0x08, 0xc4, 0x44, 0x45, 0xc3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x44,
0x44, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x44, 0x44, 0x6d, 0x08, 0x33,
0x02, 0xd5, 0x1a, 0x44, 0x00, 0x0c, 0x45, 0xc3, 0x33, 0x44, 0x44, 0x47,
0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00, 0x0a, 0x33, 0x00, 0x08,
0x44, 0x44, 0x48, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00,
0x08, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x3d, 0x79, 0x99, 0x99,
0x73, 0x00, 0x10, 0x33, 0x00, 0x0a, 0x3d, 0x47, 0x99, 0x95, 0xa3, 0x00,
0x08, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x06, 0x33, 0x00, 0x06, 0xb4, 0x56, 0x89, 0x00, 0x18, 0x99, 0x00, 0x0e,
0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a,
0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00, 0x08, 0x33, 0x00, 0x08, 0x49, 0x99,
0x94, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x12,
0x49, 0x99, 0x94, 0x33, 0x33, 0xb9, 0x99, 0x99, 0x8d, 0x00, 0x10, 0x33,
0x00, 0x0a, 0x37, 0x69, 0x99, 0x74, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x08,
0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x3a, 0x47,
0x1c, 0x99, 0x00, 0x0e, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x08,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x33, 0xd7, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x00, 0x08, 0xd4, 0x89, 0x99, 0x5a, 0x0a, 0x33,
0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x7c, 0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33,
0xb4, 0x79, 0x1c, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99,
0x02, 0x73, 0x06, 0x33, 0x00, 0x0c, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3b,
0x06, 0x99, 0x02, 0xc3, 0x0e, 0x33, 0x00, 0x08, 0x56, 0x99, 0x97, 0x4d,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x3d, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99, 0x02, 0x73,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d,
0x06, 0x33, 0x00, 0x14, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3d, 0x79, 0x99,
0x99, 0x73, 0x0c, 0x33, 0x00, 0x0a, 0x3c, 0x48, 0x99, 0x95, 0xa3, 0x00,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x37, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d, 0x06, 0x33,
0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x1c, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89, 0x99, 0x99, 0x97,
0x06, 0x33, 0x00, 0x06, 0x49, 0x99, 0x94, 0x00, 0x06, 0x33, 0x00, 0x08,
0xb9, 0x99, 0x99, 0x9d, 0x0c, 0x33, 0x00, 0x0a, 0x34, 0x69, 0x99, 0x74,
0xd3, 0x00, 0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xa3, 0x10, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44, 0x00, 0x1e,
0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89,
0x99, 0x99, 0x97, 0x00, 0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59,
0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94,
0x06, 0x33, 0x02, 0xd7, 0x06, 0x99, 0x02, 0x74, 0x0c, 0x44, 0x00, 0x06,
0x99, 0x99, 0x4b, 0x00, 0x0c, 0x33, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xa3,
0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59, 0x99, 0x95, 0xbc,
0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x34, 0x79, 0x99, 0x84,
0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33, 0x49, 0x99, 0x94, 0x06, 0x33,
0x02, 0x3b, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x00, 0x0a,
0x3d, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x9d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x6d, 0x00, 0x20, 0x33, 0x00, 0x28, 0x49, 0x99, 0x95, 0xd3,
0x33, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33, 0x79, 0x99, 0x99, 0x8d, 0x33,
0x33, 0x49, 0x99, 0x94, 0x06, 0x33, 0x02, 0x3d, 0x02, 0x79, 0x14, 0x99,
0x02, 0x94, 0x02, 0xc3, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x02, 0x34, 0x06, 0x99, 0x02, 0x74, 0x16, 0x44,
0x00, 0x10, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x9d, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33,
0x79, 0x99, 0x99, 0x8d, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0xc9, 0x14, 0x99, 0x02, 0x74, 0x0c, 0x33, 0x02, 0x84, 0x02, 0x89,
0x06, 0x99, 0x02, 0xa3, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0x33, 0xd6, 0x00, 0x06, 0x99, 0x02, 0x64, 0x18, 0x44, 0x00, 0x2e,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99,
0x97, 0x43, 0x33, 0x37, 0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94,
0x08, 0x33, 0x02, 0x37, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0x33, 0x02, 0x3b,
0x02, 0x48, 0x08, 0x99, 0x02, 0x9b, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99, 0x97, 0x43, 0x33, 0x37,
0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3,
0x33, 0x3c, 0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0x3c, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x02, 0xd4,
0x02, 0x79, 0x0a, 0x99, 0x02, 0xd3, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3, 0x33, 0x3c,
0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00,
0x06, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94,
0x0a, 0x33, 0x00, 0x16, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99,
0x99, 0x94, 0xc3, 0x00, 0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98,
0x59, 0x00, 0x06, 0x99, 0x02, 0x8d, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00, 0x06, 0x33,
0x00, 0x12, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99,
0x6a, 0x00, 0x06, 0x33, 0x00, 0x10, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0xc9, 0x99, 0x99, 0x9c, 0x33, 0x33,
0x3b, 0x99, 0x99, 0x74, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x7d, 0x00, 0x06, 0x99, 0x02, 0x9a, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99, 0x6a, 0x00, 0x06, 0x33,
0x00, 0x12, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98,
0x4d, 0x00, 0x06, 0x33, 0x00, 0x10, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x33, 0x33,
0xc4, 0x89, 0x99, 0x4c, 0x0a, 0x33, 0x00, 0x0c, 0x84, 0x89, 0x99, 0x64,
0x33, 0xd9, 0x06, 0x99, 0x02, 0xa3, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x64, 0x16, 0x44, 0x06, 0x99, 0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00,
0x20, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48,
0x99, 0x98, 0x4d, 0x00, 0x06, 0x33, 0x00, 0x12, 0xd7, 0x99, 0x99, 0x97,
0xd3, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49,
0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x53, 0x00, 0x06, 0x33, 0x02, 0x38,
0x06, 0x99, 0x00, 0x08, 0xc3, 0x49, 0x99, 0x94, 0x0a, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x00, 0x0c, 0xb3, 0x33, 0x46, 0x99, 0x96, 0x43, 0x08, 0x33,
0x00, 0x0e, 0x3b, 0x48, 0x99, 0x97, 0x4d, 0x33, 0x3d, 0x00, 0x06, 0x99,
0x02, 0x9c, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33,
0x00, 0x12, 0x39, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x54, 0x00,
0x1c, 0x44, 0x00, 0x14, 0xa3, 0x33, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99,
0x96, 0x53, 0x06, 0x33, 0x02, 0x38, 0x06, 0x99, 0x00, 0x0a, 0xc3, 0x49,
0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x98,
0xd5, 0x79, 0x99, 0x94, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x10, 0x3d, 0x89,
0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x0c, 0x33, 0x00, 0x12, 0x79, 0x99,
0x99, 0x8d, 0x3a, 0x59, 0x99, 0x84, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x10,
0xd4, 0x79, 0x99, 0x84, 0xb3, 0x33, 0x33, 0xb9, 0x06, 0x99, 0x02, 0xd3,
0x08, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x5d, 0x00, 0x20, 0x33,
0x00, 0x10, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xd3, 0x06, 0x33,
0x00, 0x12, 0x3d, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74,
0x0a, 0x33, 0x00, 0x0e, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x00,
0x0c, 0x33, 0x00, 0x10, 0xc9, 0x99, 0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65,
0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98, 0x48, 0x00, 0x06, 0x33,
0x02, 0x3a, 0x06, 0x99, 0x02, 0x83, 0x08, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x93, 0x00, 0x20, 0x33, 0x00, 0x0e, 0x49, 0x99, 0x99, 0xa6,
0x99, 0x99, 0x74, 0x00, 0x0a, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x9c,
0x59, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99,
0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33, 0x02, 0xd8, 0x06, 0x99, 0x00, 0x06,
0x79, 0x99, 0x84, 0x00, 0x0c, 0x33, 0x02, 0x37, 0x06, 0x99, 0x00, 0x08,
0x86, 0x99, 0x98, 0x4d, 0x08, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x53, 0x00, 0x08, 0x33, 0x00, 0x08, 0x89, 0x99, 0x99, 0x9a, 0x08, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x06, 0x99, 0x02, 0x54, 0x18, 0x44, 0x00, 0x14,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33,
0x02, 0xd8, 0x06, 0x99, 0x00, 0x08, 0x79, 0x99, 0x84, 0x33, 0x00, 0x00,
0x02, 0x33, 0x02, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33,
0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x0c, 0x33, 0x02, 0x3c, 0x0a, 0x99,
0x02, 0x96, 0x02, 0x53, 0x08, 0x33, 0x00, 0x0a, 0x84, 0x89, 0x99, 0x64,
0xd3, 0x00, 0x08, 0x33, 0x02, 0xd9, 0x06, 0x99, 0x02, 0xb3, 0x06, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x59,
0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33, 0x02, 0x36, 0x0a, 0x99,
0x02, 0x75, 0x02, 0x33, 0x00, 0x00, 0x02, 0x33, 0x02, 0xa8, 0x08, 0x99,
0x02, 0x95, 0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b,
0x0e, 0x33, 0x02, 0x89, 0x08, 0x99, 0x02, 0x84, 0x02, 0xd3, 0x06, 0x33,
0x00, 0x0a, 0x3b, 0x48, 0x99, 0x97, 0x4c, 0x00, 0x0a, 0x33, 0x02, 0x3d,
0x06, 0x99, 0x02, 0x9d, 0x06, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d,
0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x3d, 0x89, 0x00,
0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0xa8, 0x08, 0x99, 0x02, 0x95,
0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b, 0x02, 0x33,
0x00, 0x00, 0x02, 0x33, 0x02, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33,
0x02, 0x79, 0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd8,
0x06, 0x99, 0x02, 0x98, 0x02, 0x48, 0x08, 0x33, 0x00, 0x0a, 0xd4, 0x79,
0x99, 0x84, 0xb3, 0x00, 0x0c, 0x33, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98,
0xd3, 0x33, 0x33, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0c,
0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3,
0x33, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33, 0x02, 0x79, 0x06, 0x99,
0x00, 0x06, 0x96, 0x4d, 0x33, 0x00, 0x00, 0x00, 0x02, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x02, 0x74, 0x02, 0xc3, 0x0e, 0x33, 0x02, 0x3d, 0x06, 0x99, 0x02, 0x85,
0x02, 0x53, 0x06, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x99, 0x57, 0x00,
0x0e, 0x33, 0x02, 0x39, 0x06, 0x99, 0x00, 0x0e, 0xa3, 0x33, 0x33, 0x34,
0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x33, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x00, 0x06, 0x74, 0xc3, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x33, 0x33,
0xc8, 0x99, 0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0a, 0x3d, 0x79,
0x99, 0x76, 0x4b, 0x00, 0x12, 0x33, 0x00, 0x0a, 0xc7, 0x99, 0x96, 0x45,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00,
0x10, 0x33, 0x00, 0x14, 0x89, 0x99, 0x99, 0x9a, 0x33, 0x33, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x06, 0x33,
0x02, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xa3, 0x33, 0x33, 0xc8, 0x99,
0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0e, 0x3d, 0x79, 0x99, 0x76,
0x4b, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x33, 0x33, 0x3d, 0x84,
0x44, 0xc3, 0x12, 0x33, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xd3, 0x12, 0x33,
0x00, 0x08, 0x3d, 0xa4, 0x44, 0xd3, 0x08, 0x33, 0x02, 0xc8, 0x06, 0x44,
0x02, 0xd3, 0x10, 0x33, 0x02, 0xd4, 0x06, 0x44, 0x00, 0x0c, 0xa3, 0x33,
0x38, 0x44, 0x44, 0x4d, 0x16, 0x33, 0x00, 0x08, 0x38, 0x44, 0x44, 0x4c,
0x06, 0x33, 0x02, 0x3d, 0x02, 0xc4, 0x1c, 0x44, 0x00, 0x0e, 0xa3, 0x33,
0x33, 0x3d, 0x84, 0x44, 0xc3, 0x00, 0x12, 0x33, 0x00, 0x0c, 0xdc, 0x44,
0x4a, 0xd3, 0x33, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0x00, 0x1e, 0x33, 0x20, 0x00, 0x23, 0x30, 0x00, 0x13, 0x31,
0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x26,
0x13, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x00, 0x00, 0x13, 0x33,
0x00, 0x00, 0x13, 0x32, 0x00, 0x00, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x00, 0x02, 0x00, 0x08, 0x33, 0x02, 0x32, 0x06, 0x00, 0x00, 0x30,
0x01, 0x33, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x33, 0x32, 0x00,
0x02, 0x33, 0x33, 0x30, 0x00, 0x13, 0x32, 0x00, 0x02, 0x33, 0x00, 0x01,
0x08, 0x33, 0x00, 0x10, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33, 0x30,
0x08, 0x00, 0x00, 0x10, 0x23, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33,
0x00, 0x00, 0x00, 0x20, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x10, 0x10, 0x06, 0x00, 0x00, 0x26,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x03, 0x33,
0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x10, 0x10, 0x00, 0x11,
0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33, 0x32, 0x01,
0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x10, 0x00, 0x06, 0x00, 0x00, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11,
0x02, 0x33, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00,
0x06, 0x33, 0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x11, 0x13, 0x33, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33,
0x00, 0x06, 0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x11, 0x10,
0x23, 0x31, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33,
0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33,
0x01, 0x10, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02,
0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10,
0x23, 0x00, 0x06, 0x33, 0x00, 0x28, 0x31, 0x11, 0x12, 0x33, 0x01, 0x00,
0x33, 0x01, 0x11, 0x11, 0x00, 0x01, 0x12, 0x33, 0x32, 0x01, 0x11, 0x11,
0x00, 0x23, 0x20, 0x33, 0x00, 0x06, 0x32, 0x01, 0x12, 0x00, 0x08, 0x33,
0x00, 0x38, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33,
0x01, 0x00, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x13,
0x32, 0x01, 0x11, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22,
0x33, 0x20, 0x11, 0x11, 0x00, 0x11, 0x03, 0x31, 0x11, 0x11, 0x00, 0x01,
0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x10, 0x31, 0x11,
0x11, 0x00, 0x01, 0x13, 0x33, 0x01, 0x08, 0x11, 0x00, 0x10, 0x12, 0x33,
0x32, 0x01, 0x11, 0x11, 0x10, 0x23, 0x20, 0x33, 0x00, 0x60, 0x32, 0x01,
0x11, 0x10, 0x00, 0x03, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x22, 0x01, 0x02, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33,
0x30, 0x11, 0x12, 0x21, 0x01, 0x11, 0x33, 0x01, 0x11, 0x22, 0x22, 0x23,
0x33, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10, 0x08, 0x33,
0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13, 0x33, 0x00, 0x00, 0x00,
0x00, 0x22, 0x33, 0x20, 0x11, 0x00, 0x11, 0x11, 0x03, 0x31, 0x11, 0x00,
0x01, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x28,
0x31, 0x11, 0x00, 0x01, 0x11, 0x13, 0x33, 0x01, 0x11, 0x00, 0x01, 0x11,
0x12, 0x33, 0x32, 0x01, 0x11, 0x00, 0x00, 0x23, 0x20, 0x33, 0x00, 0x60,
0x32, 0x01, 0x10, 0x01, 0x11, 0x12, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x10, 0x10, 0x11, 0x13, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x10, 0x10, 0x11, 0x11, 0x33, 0x01, 0x11, 0x01,
0x00, 0x01, 0x23, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x31, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x01, 0x11, 0x03,
0x33, 0x11, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x01, 0x22, 0x11, 0x12, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x02, 0x22, 0x11, 0x11, 0x33, 0x01, 0x10, 0x22,
0x21, 0x11, 0x13, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x13,
0x33, 0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x01, 0x10, 0x33, 0x11, 0x11, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x12, 0x33, 0x11, 0x10, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x13, 0x32, 0x11, 0x11, 0x33, 0x01, 0x11, 0x33,
0x21, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11, 0x11, 0x23, 0x00, 0x06, 0x33,
0x00, 0x2c, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x11,
0x00, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x23,
0x1c, 0x33, 0x00, 0x60, 0x32, 0x01, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11,
0x11, 0x22, 0x20, 0x11, 0x03, 0x31, 0x11, 0x12, 0x22, 0x01, 0x10, 0x33,
0x33, 0x32, 0x01, 0x12, 0x33, 0x33, 0x30, 0x11, 0x12, 0x21, 0x01, 0x11,
0x33, 0x01, 0x11, 0x22, 0x10, 0x11, 0x13, 0x30, 0x11, 0x12, 0x21, 0x11,
0x02, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x38, 0x33, 0x20, 0x11, 0x13,
0x30, 0x11, 0x03, 0x32, 0x11, 0x11, 0x00, 0x01, 0x02, 0x33, 0x21, 0x11,
0x10, 0x00, 0x00, 0x13, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x08, 0x11, 0x00, 0x08, 0x12, 0x33, 0x33, 0x21, 0x08, 0x11, 0x02, 0x10,
0x02, 0x23, 0x1c, 0x33, 0x00, 0x0e, 0x32, 0x01, 0x11, 0x10, 0x01, 0x02,
0x33, 0x00, 0x06, 0x11, 0x00, 0x62, 0x01, 0x10, 0x03, 0x31, 0x11, 0x11,
0x10, 0x11, 0x00, 0x33, 0x10, 0x00, 0x01, 0x11, 0x10, 0x13, 0x30, 0x11,
0x11, 0x10, 0x11, 0x01, 0x33, 0x01, 0x11, 0x11, 0x01, 0x10, 0x13, 0x30,
0x11, 0x11, 0x00, 0x10, 0x02, 0x33, 0x01, 0x11, 0x10, 0x00, 0x00, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12,
0x33, 0x20, 0x00, 0x13, 0x31, 0x00, 0x03, 0x33, 0x20, 0x00, 0x06, 0x00,
0x00, 0x06, 0x13, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x12, 0x03, 0x32,
0x00, 0x02, 0x33, 0x10, 0x00, 0x33, 0x10, 0x00, 0x08, 0x00, 0x00, 0x08,
0x02, 0x33, 0x33, 0x30, 0x0a, 0x00, 0x02, 0x23, 0x1c, 0x33, 0x02, 0x32,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x32, 0x00, 0x06, 0x00, 0x00, 0x06,
0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x06, 0x13, 0x33, 0x20, 0x00,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06,
0x23, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06, 0x02, 0x33, 0x33, 0x00,
0x08, 0x00, 0x00, 0x06, 0x23, 0x33, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10,
0x23, 0x31, 0x00, 0x03, 0x32, 0x00, 0x02, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x01};
unsigned int splash_new__new_t_len = 3854;
+4
View File
@@ -143,6 +143,10 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
}
}
void AFSKRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
AFSKRxView::~AFSKRxView() {
audio::output::stop();
receiver_model.disable();
+9
View File
@@ -117,6 +117,15 @@ class AFSKRxView : public View {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::afsk_rx
@@ -202,4 +202,8 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
receiver_model.enable();
}
void AnalogTvView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} // namespace ui::external_app::analogtv
@@ -108,6 +108,15 @@ class AnalogTvView : public View {
void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::analogtv
+1 -1
View File
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_blespam.application_information"), use
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'B', 'T', 'T'},
/*.m4_app_tag = portapack::spi_flash::image_tag_btle_tx */ {'P', 'B', 'T', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+10
View File
@@ -88,6 +88,14 @@ set(EXTCPPSRC
#adsbtx
external/adsbtx/main.cpp
external/adsbtx/ui_adsb_tx.cpp
#morse_tx
external/morse_tx/main.cpp
external/morse_tx/ui_morse.cpp
#sstvtx
external/sstvtx/main.cpp
external/sstvtx/ui_sstvtx.cpp
)
set(EXTAPPLIST
@@ -112,4 +120,6 @@ set(EXTAPPLIST
tpmsrx
protoview
adsbtx
morse_tx
sstvtx
)
+16
View File
@@ -44,6 +44,8 @@ MEMORY
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k
ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k
ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k
ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k
}
SECTIONS
@@ -176,4 +178,18 @@ SECTIONS
} > ram_external_app_adsbtx
.external_app_morse_tx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_morse_tx.application_information));
*(*ui*external_app*morse_tx*);
} > ram_external_app_morse_tx
.external_app_sstvtx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_sstvtx.application_information));
*(*ui*external_app*sstvtx*);
} > ram_external_app_sstvtx
}
@@ -103,4 +103,8 @@ void FoxhuntRxView::on_orientation(const OrientationDataMessage* msg) {
geomap.update_my_orientation(msg->angle, true);
}
void FoxhuntRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} // namespace ui::external_app::foxhunt_rx
@@ -103,6 +103,14 @@ class FoxhuntRxView : public View {
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
float my_lat = 200;
float my_lon = 200;
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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.hpp"
#include "ui_morse.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::morse_tx {
void initialize_app(ui::NavigationView& nav) {
nav.push<MorseView>();
}
} // namespace ui::external_app::morse_tx
extern "C" {
__attribute__((section(".external_app.app_morse_tx.application_information"), used)) application_information_t _application_information_morse_tx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::morse_tx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Morse",
/*.bitmap_data = */ {
0x00,
0x00,
0xFE,
0x7F,
0xFF,
0xFF,
0xBB,
0xD0,
0xFF,
0xFF,
0xFF,
0xFF,
0x0B,
0xE1,
0xFF,
0xFF,
0xFF,
0xFF,
0xEB,
0xD0,
0xFF,
0xFF,
0xFE,
0x7F,
0x70,
0x00,
0x30,
0x00,
0x10,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -36,7 +36,7 @@ using namespace portapack;
using namespace morse;
using namespace hackrf::one;
namespace ui {
namespace ui::external_app::morse_tx {
static WORKING_AREA(ookthread_wa, 256);
@@ -194,7 +194,8 @@ void MorseView::set_foxhunt(size_t i) {
MorseView::MorseView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_tones);
// baseband::run_image(portapack::spi_flash::image_tag_tones);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&labels,
&checkbox_foxhunt,
@@ -284,4 +285,4 @@ MorseView::MorseView(
};
}
} /* namespace ui */
} // namespace ui::external_app::morse_tx
@@ -39,7 +39,7 @@
using namespace morse;
namespace ui {
namespace ui::external_app::morse_tx {
class MorseView : public View {
public:
@@ -179,6 +179,6 @@ class MorseView : public View {
}};
};
} /* namespace ui */
} // namespace ui::external_app::morse_tx
#endif /*__MORSE_TX_H__*/
+4
View File
@@ -125,6 +125,10 @@ void NRFRxView::on_data(uint32_t value, bool is_data) {
}
}
void NRFRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
NRFRxView::~NRFRxView() {
audio::output::stop();
receiver_model.disable();
+9
View File
@@ -89,6 +89,15 @@ class NRFRxView : public View {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui::external_app::nrf_rx */
+26 -5
View File
@@ -51,6 +51,7 @@ ProtoView::ProtoView(NavigationView& nav)
&field_frequency,
&labels,
&options_zoom,
&number_shift,
&button_reset,
&waveform,
&waveform2,
@@ -63,6 +64,11 @@ ProtoView::ProtoView(NavigationView& nav)
draw();
draw2();
};
number_shift.on_change = [this](int32_t value) {
waveform_shift = value;
draw();
draw2();
};
button_reset.on_select = [this](Button&) {
reset();
};
@@ -74,6 +80,8 @@ ProtoView::ProtoView(NavigationView& nav)
void ProtoView::reset() {
cnt = 0;
number_shift.set_value(0);
waveform_shift = 0;
for (uint16_t i = 0; i < MAXSIGNALBUFFER; i++) time_buffer[i] = 0;
needCntReset = false;
draw();
@@ -126,9 +134,19 @@ void ProtoView::draw() {
drawcnt = 0;
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
for (uint16_t i = 0; i < MAXSIGNALBUFFER; ++i) {
state = time_buffer[i] >= 0;
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
// add empty data for padding (so you can shift left/nagetive)
for (int32_t i = 0;
i < ((waveform_shift > 0) ? 0 : -waveform_shift) && drawcnt < MAXDRAWCNT; // this is for shift nagetive (left side)
++i) {
waveform_buffer[drawcnt++] = 0;
}
for (uint16_t i = ((waveform_shift < 0) ? -waveform_shift : 0); // this is for shift positive aka right side
i < MAXSIGNALBUFFER && drawcnt < MAXDRAWCNT; // prevent out of ranging
++i) {
uint16_t buffer_index = (i + waveform_shift + MAXSIGNALBUFFER) % MAXSIGNALBUFFER;
state = time_buffer[buffer_index] >= 0;
int32_t timeabs = state ? time_buffer[buffer_index] : -1 * time_buffer[buffer_index];
int32_t timesize = timeabs / zoom;
if (timesize == 0) {
remain += timeabs;
@@ -145,9 +163,8 @@ void ProtoView::draw() {
}
remain = 0;
lmax = 0;
for (int32_t ii = 0; ii < timesize; ++ii) {
for (int32_t ii = 0; ii < timesize && drawcnt < MAXDRAWCNT; ++ii) {
waveform_buffer[drawcnt++] = state;
if (drawcnt >= MAXDRAWCNT) return;
}
}
}
@@ -186,6 +203,10 @@ void ProtoView::on_data(const ProtoViewDataMessage* message) {
draw2();
}
void ProtoView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
ProtoView::~ProtoView() {
audio::output::stop();
receiver_model.disable();
+25 -8
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -76,7 +75,8 @@ class ProtoView : public View {
{0 * 8, 0 * 16},
nav_};
Labels labels{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Shift: ", Theme::getInstance()->fg_light->foreground}};
OptionsField options_zoom{
{7 * 8, 1 * 16},
@@ -92,12 +92,19 @@ class ProtoView : public View {
{"500", 500},
{"1000", 1000}}};
NumberField number_shift{
{7 * 8, 2 * 16},
5,
{-MAXSIGNALBUFFER, MAXSIGNALBUFFER},
1,
' '};
Button button_reset{
{screen_width - 12 * 8, 1 * 16, 96, 24},
LanguageHelper::currentMessages[LANG_RESET]};
Waveform waveform{
{0, 5 * 8, 240, 50},
{0, 8 * 8, 240, 50},
waveform_buffer,
0,
0,
@@ -105,7 +112,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform2{
{0, 5 * 8 + 55, 240, 50},
{0, 8 * 8 + 55, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF],
0,
0,
@@ -113,7 +120,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform3{
{0, 5 * 8 + 110, 240, 50},
{0, 8 * 8 + 110, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 2],
0,
0,
@@ -121,7 +128,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform4{
{0, 5 * 8 + 165, 240, 50},
{0, 8 * 8 + 165, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 3],
0,
0,
@@ -131,6 +138,7 @@ class ProtoView : public View {
bool needCntReset = false;
int16_t zoom = 1; // one value in ms
int16_t waveform_shift = 0;
uint16_t cnt = 0; // pointer to next element
uint16_t drawcnt = 0; // pointer to draw next element
@@ -157,8 +165,17 @@ class ProtoView : public View {
[this](const Message* const) {
this->on_timer();
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::protoview
#endif /*__UI_PROTOVIEW_H__*/
#endif /*__UI_PROTOVIEW_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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.hpp"
#include "ui_sstvtx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::sstvtx {
void initialize_app(ui::NavigationView& nav) {
nav.push<SSTVTXView>();
}
} // namespace ui::external_app::sstvtx
extern "C" {
__attribute__((section(".external_app.app_sstvtx.application_information"), used)) application_information_t _application_information_sstvtx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::sstvtx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "SSTV",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xFE,
0x7F,
0x03,
0xC0,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0x03,
0xC0,
0xFF,
0xFF,
0xFB,
0xD7,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_sstvtx */ {'P', 'S', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -31,7 +31,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::sstvtx {
void SSTVTXView::focus() {
if (file_error)
@@ -232,7 +232,8 @@ SSTVTXView::SSTVTXView(
// Maybe this could be merged with proc_tones ? Pretty much the same except lots
// of different tones (256+)
baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
// baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&labels,
&options_bitmaps,
@@ -283,4 +284,4 @@ SSTVTXView::SSTVTXView(
};
}
} /* namespace ui */
} // namespace ui::external_app::sstvtx
@@ -38,7 +38,7 @@
using namespace sstv;
namespace ui {
namespace ui::external_app::sstvtx {
class SSTVTXView : public View {
public:
@@ -110,6 +110,6 @@ class SSTVTXView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::sstvtx */
#endif /*__UI_SSTVTX_H__*/
+7 -6
View File
@@ -107,7 +107,7 @@ class TPMSAppView : public View {
private:
RxRadioState radio_state_{
314950000 /* frequency*/
314900000 /* frequency*/
,
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -147,20 +147,21 @@ class TPMSAppView : public View {
// "315 MHz" TPMS sensors transmit at either 314.9 or 315 MHz but we should pick up either
OptionsField options_band{
{0 * 8, 0 * 16},
3,
5,
{
{"315", 314950000},
{"434", 433920000},
{"314.9", 314900000},
{"315.0", 315000000},
{"433.9", 433920000},
}};
OptionsField options_pressure{
{5 * 8, 0 * 16},
{6 * 8, 0 * 16},
3,
{{"kPa", 0},
{"PSI", 1}}};
OptionsField options_temperature{
{9 * 8, 0 * 16},
{10 * 8, 0 * 16},
2,
{{STR_DEGREES_C, 0},
{STR_DEGREES_F, 1}}};
+80 -18
View File
@@ -23,6 +23,7 @@
#include "string_format.hpp"
#include "file_path.hpp"
#include "metadata_file.hpp"
#include "flipper_subfile.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -36,14 +37,14 @@ void WardriveMapView::focus() {
// needs to load on every map change, because won't store or draw all while marker is not in the current view.
// todo optimize this somehow
bool WardriveMapView::load_markers() {
uint16_t cnt = 0;
void WardriveMapView::load_markers() {
uint16_t displayed_cnt = 0;
uint16_t cnt = 0;
geomap.clear_markers();
// serach for files with geotag, and add it to geomap as marker with tag. limit to N bc of mem limit.
for (const auto& entry : std::filesystem::directory_iterator(captures_dir, u"*.txt")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (displayed_cnt > 30) break;
if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements
std::filesystem::path pth = captures_dir;
pth += u"/" + entry.path();
auto metadata_path = get_metadata_path(pth);
@@ -51,23 +52,65 @@ bool WardriveMapView::load_markers() {
if (metadata) {
if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
// skip nth
if (marker_start <= cnt) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
if (!markers_counted) marker_cntall++;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
cnt++;
}
}
}
}
return (cnt > 0);
// load flipper files too
for (const auto& entry : std::filesystem::directory_iterator(flippersub_dir, u"*.sub")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements
std::filesystem::path pth = flippersub_dir;
pth += u"/" + entry.path();
auto metadata = read_flippersub_file(pth);
if (metadata) {
if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) {
// skip nth
if (marker_start <= cnt) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
if (!markers_counted) marker_cntall++;
}
cnt++;
}
}
}
}
markers_counted = true;
// show / hide paginator buttons
btn_back.hidden((marker_start == 0) || (marker_cntall == 0));
btn_next.hidden(((marker_start + ui::GeoMap::NumMarkerListElements) >= marker_cntall) || (marker_cntall == 0));
// update text
text_info.set(to_string_dec_uint(marker_start + 1) + " - " + to_string_dec_uint(displayed_cnt + marker_start) + " / " + to_string_dec_uint(marker_cntall));
set_dirty();
return;
}
WardriveMapView::WardriveMapView(NavigationView& nav)
@@ -75,7 +118,9 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
add_children({&text_info,
&geomap,
&geopos,
&text_notfound});
&text_notfound,
&btn_back,
&btn_next});
geomap.set_mode(DISPLAY);
geomap.set_manual_panning(false);
@@ -100,21 +145,38 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
load_markers();
geomap.set_dirty();
};
text_notfound.hidden(true);
geomap.init();
// load markers
if (load_markers()) {
load_markers();
if (marker_cntall > 0) {
text_notfound.hidden(true);
geomap.set_dirty();
} else {
geomap.hidden(true);
geopos.hidden(true);
text_notfound.hidden(false);
text_info.hidden(true);
}
// never move this before the first load() bc that will mess load up
geomap.on_move = [this](float lon, float lat) {
(void)lon;
(void)lat;
load_markers();
};
btn_back.on_select = [this, &nav](Button&) {
if (marker_start - ui::GeoMap::NumMarkerListElements >= 0)
marker_start = marker_start - ui::GeoMap::NumMarkerListElements;
else
marker_start = 0;
load_markers();
};
btn_next.on_select = [this, &nav](Button&) {
if (marker_start + ui::GeoMap::NumMarkerListElements <= marker_cntall)
marker_start = marker_start + ui::GeoMap::NumMarkerListElements;
else
marker_start = marker_cntall - ui::GeoMap::NumMarkerListElements;
load_markers();
};
}
WardriveMapView::~WardriveMapView() {
+12 -10
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -20,10 +19,6 @@
* Boston, MA 02110-1301, USA.
*/
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
// Thanks for the work of the original creators!
// Saying thanks in the main view!
#ifndef __UI_WARDRIVEMAP_H__
#define __UI_WARDRIVEMAP_H__
@@ -50,22 +45,29 @@ class WardriveMapView : public View {
};
private:
const std::filesystem::path flippersub_dir = u"subghz";
NavigationView& nav_;
Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "All GEOTAG from CAPTURES"};
Text text_notfound{{0 * 8, 3 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"};
Text text_notfound{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
GeoPos geopos{
{0, 20},
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
GeoMap geomap{{0, 75, 240, 320 - 75}};
Button btn_back{{22 * 8, 0 * 8, 3 * 8, 16}, "<-"};
Button btn_next{{26 * 8, 0 * 8, 3 * 8, 16}, "->"};
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
bool load_markers(); // returns true if any exists, false if none.
void load_markers(); // returns true if any exists, false if none.
bool first_init = false;
bool first_init = false; // to center map to first marker, before callback is set
bool markers_counted = false; // to iterate all files on first, but only on first.
uint16_t marker_start = 0; // for paginator, this will be the first displayed
uint16_t marker_cntall = 0; // all geotagged marker count
MessageHandlerRegistration message_handler_gps{
Message::ID::GPSPosData,
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright (C) 2024 HTotoo
*
* 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 "flipper_subfile.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include <string_view>
namespace fs = std::filesystem;
using namespace std::literals;
const std::string_view filetype_name = "Filetype"sv;
const std::string_view frequency_name = "Frequency"sv;
const std::string_view latitude_name = "Latitute"sv;
const std::string_view longitude_name = "Longitude"sv;
const std::string_view protocol_name = "Protocol"sv;
const std::string_view preset_name = "Preset"sv;
const std::string_view te_name = "TE"sv; // only in BinRAW
/*
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Latitute: nan
Longitude: nan
Protocol: BinRAW
Bit: 1730
TE: 495
Bit_RAW: 1730
Data_RAW: 02 10 84
te: is the quantization interval, in us.
bit: all bit counts in file.
bit_raw: the bits stored in the next data_raw. this 2 can repeat
data_raw: is an encoded sequence of durations, where each bit in the sequence encodes one TE interval: 1 - high level (there is a carrier), 0 - low (no carrier). For example, TE=100, Bit_RAW=8, Data_RAW=0x37 => 0b00110111, that is, -200 200 -100 300 will be transmitted
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: 5832 -12188 130 -162
raw_data- positive: carrier for n time, negative: no carrier for n time. (us)
*/
Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
File f;
auto error = f.open(path);
if (error)
return {};
flippersub_metadata metadata{};
auto reader = FileLineReader(f);
for (const auto& line : reader) {
auto cols = split_string(line, ':');
if (cols.size() != 2)
continue; // Bad line.
if (cols[1].length() <= 1) continue;
std::string fixed = cols[1].data() + 1;
fixed = trim(fixed);
if (cols[0] == filetype_name) {
if (fixed != "Flipper SubGhz Key File" && fixed != "Flipper SubGhz RAW File") return {}; // not supported
} else if (cols[0] == frequency_name)
parse_int(fixed, metadata.center_frequency);
else if (cols[0] == latitude_name)
parse_float_meta(fixed, metadata.latitude);
else if (cols[0] == longitude_name)
parse_float_meta(fixed, metadata.longitude);
else if (cols[0] == protocol_name) {
if (fixed == "RAW") metadata.protocol = FLIPPER_PROTO_RAW;
if (fixed == "BinRAW") metadata.protocol = FLIPPER_PROTO_BINRAW;
} else if (cols[0] == te_name) {
metadata.te = atoi(fixed.c_str());
} else if (cols[0] == preset_name) {
if (fixed.find("FSK") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_2FSK;
} else if (fixed.find("Ook") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_OOK;
} else if (fixed.find("Custom") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_CUSTOM;
}
} else
continue;
}
if (metadata.center_frequency == 0) return {}; // Parse failed.
return metadata;
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 HTotoo
*
* 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 __FLIPPER_SUBFILE_HPP__
#define __FLIPPER_SUBFILE_HPP__
#include "metadata_file.hpp"
typedef enum : uint8_t {
FLIPPER_PROTO_UNSUPPORTED = 0,
FLIPPER_PROTO_RAW = 1,
FLIPPER_PROTO_BINRAW = 2
} FlipperProto;
typedef enum : uint8_t {
FLIPPER_PRESET_UNK = 0,
FLIPPER_PRESET_CUSTOM = 1,
FLIPPER_PRESET_OOK = 2,
FLIPPER_PRESET_2FSK = 3,
} FlipperPreset;
struct flippersub_metadata {
rf::Frequency center_frequency;
float latitude = 0;
float longitude = 0;
FlipperProto protocol = FLIPPER_PROTO_UNSUPPORTED;
FlipperPreset preset = FLIPPER_PRESET_UNK;
uint16_t te = 0;
};
Optional<flippersub_metadata> read_flippersub_file(const std::filesystem::path& path);
// Maybe sometime there will be a data part reader / converter
#endif // __FLIPPER_SUBFILE_HPP__
+3 -1
View File
@@ -535,6 +535,8 @@ init_status_t init() {
set_cpu_clock_speed();
if (persistent_memory::config_lcd_inverted_mode()) display.set_inverted(true);
if (lcd_fast_setup)
draw_splash_screen_icon(0, ui::bitmap_icon_memory);
@@ -586,7 +588,7 @@ init_status_t init() {
chThdSleepMilliseconds(10);
audio::init(portapack_audio_codec());
battery::BatteryManagement::init();
battery::BatteryManagement::init(persistent_memory::ui_override_batt_calc());
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
+1 -1
View File
@@ -52,7 +52,7 @@ void Tab::paint(Painter& painter) {
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, Theme::getInstance()->fg_light->foreground);
painter.draw_hline({rect.left(), rect.top()}, rect.width() - 9, Theme::getInstance()->bg_light->background);
painter.draw_bitmap(
{rect.right() - 8, rect.top()},
+9 -9
View File
@@ -55,7 +55,7 @@
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "ui_mictx.hpp"
#include "ui_morse.hpp"
// #include "ui_morse.hpp" //moved to ext
// #include "ui_nrf_rx.hpp" //moved to ext
// #include "ui_numbers.hpp"
// #include "ui_nuoptix.hpp"
@@ -74,7 +74,7 @@
#include "ui_sonde.hpp"
// #include "ui_spectrum_painter.hpp" //moved to ext app
#include "ui_ss_viewer.hpp"
#include "ui_sstvtx.hpp"
// #include "ui_sstvtx.hpp" //moved to ext
// #include "ui_test.hpp"
#include "ui_text_editor.hpp"
#include "ui_tone_search.hpp"
@@ -90,7 +90,7 @@
#include "ais_app.hpp"
#include "analog_audio_app.hpp"
// #include "analog_tv_app.hpp" //moved to ext
#include "ble_comm_app.hpp"
// #include "ble_comm_app.hpp"
#include "ble_rx_app.hpp"
#include "ble_tx_app.hpp"
#include "capture_app.hpp"
@@ -185,13 +185,13 @@ const NavigationView::AppList NavigationView::appList = {
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},
{"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory<BHTView>()},
{"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory<BLETxView>()},
{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory<MorseView>()},
//{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory<MorseView>()}, //moved to ext
//{"nuoptixdtmf", "Nuoptix DTMF", TX, ui::Color::green(), &bitmap_icon_nuoptix, new ViewFactory<NuoptixView>()},
{"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory<EncodersView>()},
{"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGTXView>()},
{"rdstx", "RDS", TX, ui::Color::green(), &bitmap_icon_rds, new ViewFactory<RDSView>()},
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
{"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory<SSTVTXView>()},
//{"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory<SSTVTXView>()}, //moved to ext
{"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory<TouchTunesView>()},
/* UTILITIES *************************************************************/
{"antennalength", "Antenna Length", UTILITIES, Color::green(), &bitmap_icon_tools_antenna, new ViewFactory<WhipCalcView>()},
@@ -413,7 +413,7 @@ void SystemStatusView::refresh() {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
if (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
uint8_t percent = battery::BatteryManagement::getPercent();
@@ -450,7 +450,7 @@ void SystemStatusView::refresh() {
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground(pmem::apply_fake_brightness() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@@ -1006,7 +1006,7 @@ BMPView::BMPView(NavigationView& nav) {
void BMPView::paint(Painter&) {
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, true);
}
/* NotImplementedView ****************************************************/
@@ -1072,7 +1072,7 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter& painter) {
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, true);
// Break lines.
auto lines = split_string(message_, '\n');
+18
View File
@@ -1147,6 +1147,23 @@ static void cmd_asyncmsg(BaseSequentialStream* chp, int argc, char* argv[]) {
chprintf(chp, usage);
}
}
static void cmd_setfreq(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: setfreq freq_in_hz\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
int64_t freq = atol(argv[0]);
if (freq <= 0) {
chprintf(chp, usage);
return;
}
// radio::set_tuning_frequency(freq); // sadly this doesn't update any widget, just change the frequency.
FreqChangeCommandMessage message{freq};
EventDispatcher::send_message(message);
chprintf(chp, "ok\r\n");
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
@@ -1180,6 +1197,7 @@ static const ShellCommand commands[] = {
{"settingsreset", cmd_settingsreset},
{"sendpocsag", cmd_sendpocsag},
{"asyncmsg", cmd_asyncmsg},
{"setfreq", cmd_setfreq},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
+6 -6
View File
@@ -480,12 +480,6 @@ set(MODE_CPPSRC
DeclareTargets(PSIG siggen)
### SSTV TX
set(MODE_CPPSRC
proc_sstvtx.cpp
)
DeclareTargets(PSTX sstvtx)
### Tones
@@ -650,6 +644,12 @@ set(MODE_CPPSRC
DeclareTargets(PPVW protoview)
### SSTV TX
set(MODE_CPPSRC
proc_sstvtx.cpp
)
DeclareTargets(PSTX sstvtx)
### TPMS
-1
View File
@@ -36,7 +36,6 @@ class FProtoSubGhzDBett : public FProtoSubGhzDBase {
(te_delta * 15)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// dip decoder needed
if (callback) callback(this);
-1
View File
@@ -53,7 +53,6 @@ class FProtoSubGhzDCame : public FProtoSubGhzDBase {
parser_step = CameDecoderStepFoundStartBit;
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == AIRFORCE_COUNT_BIT) ||
(decode_count_bit == PRASTEL_COUNT_BIT) || (decode_count_bit == CAME_24_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
// if flippa hacky, i hacky
sensorType = FPS_CAME;
@@ -43,7 +43,6 @@ class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -43,7 +43,6 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -58,7 +58,6 @@ class FProtoSubGhzDChambCode : public FProtoSubGhzDBase {
if (duration > te_short * 5) {
if (decode_count_bit >= min_count_bit_for_found) {
if (subghz_protocol_decoder_chamb_code_check_mask_and_parse()) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
-1
View File
@@ -62,7 +62,6 @@ class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
-1
View File
@@ -44,7 +44,6 @@ class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = DoitrandDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
-1
View File
@@ -72,7 +72,6 @@ class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
parser_step = DooyaDecoderStepFoundStartBit;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
-1
View File
@@ -43,7 +43,6 @@ class FProtoSubGhzDFaac : public FProtoSubGhzDBase {
if (duration >= ((uint32_t)te_short * 3 + te_delta)) {
parser_step = FaacSLHDecoderStepFoundPreambula;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// remark controller skipped
if (callback) callback(this);
+86
View File
@@ -0,0 +1,86 @@
#ifndef __FPROTO_GANGQI_H__
#define __FPROTO_GANGQI_H__
#include "subghzdbase.hpp"
typedef enum {
GangQiDecoderStepReset = 0,
GangQiDecoderStepSaveDuration,
GangQiDecoderStepCheckDuration,
} GangQiDecoderStep;
class FProtoSubGhzDGangqi : public FProtoSubGhzDBase {
public:
FProtoSubGhzDGangqi() {
sensorType = FPS_GANGQI;
te_short = 500;
te_long = 1200;
te_delta = 200;
min_count_bit_for_found = 34;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case GangQiDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
// Found GAP
decode_data = 0;
decode_count_bit = 0;
parser_step = GangQiDecoderStepSaveDuration;
}
break;
case GangQiDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = GangQiDecoderStepCheckDuration;
} else {
parser_step = GangQiDecoderStepReset;
}
break;
case GangQiDecoderStepCheckDuration:
if (!level) {
// Bit 0 is short and long timing
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = GangQiDecoderStepSaveDuration;
// Bit 1 is long and short timing
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = GangQiDecoderStepSaveDuration;
} else if (
// End of the key
DURATION_DIFF(duration, te_short * 4) <
te_delta) {
// Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
}
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
}
// If got 34 bits key reading is finished
if (decode_count_bit == min_count_bit_for_found) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = GangQiDecoderStepReset;
} else {
parser_step = GangQiDecoderStepReset;
}
} else {
parser_step = GangQiDecoderStepReset;
}
break;
}
}
};
#endif
-1
View File
@@ -44,7 +44,6 @@ class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = GateTXDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
-1
View File
@@ -49,7 +49,6 @@ class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
if (decode_count_bit ==
min_count_bit_for_found) {
if ((decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
+2 -5
View File
@@ -44,11 +44,8 @@ class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
if (data != decode_data) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
@@ -71,7 +71,6 @@ class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
uint16_t crc = decode_data & 0xFFFF;
if (crc == crc_calc) {
// the data is good. process it.
data = decode_data;
data_count_bit = decode_count_bit; // maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
if (callback) callback(this);
decode_data = 0;
@@ -36,7 +36,6 @@ class FProtoSubGhzDHoneywellWdb : public FProtoSubGhzDBase {
if (DURATION_DIFF(duration, te_short * 3) < te_delta) {
if ((decode_count_bit == min_count_bit_for_found) &&
((decode_data & 0x01) == FProtoGeneral::subghz_protocol_blocks_get_parity(decode_data >> 1, min_count_bit_for_found - 1))) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller has too much, should be done on ui side
if (callback) callback(this);
-1
View File
@@ -47,7 +47,6 @@ class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
parser_step = HormannDecoderStepFoundStartBit;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -24,7 +24,6 @@ class FProtoSubGhzDHormannBiSecure : public FProtoSubGhzDBase {
void subghz_protocol_decoder_hormann_bisecur_reset() {
parser_step = HormannBiSecurDecoderStepReset;
data = 0;
for (uint8_t i = 0; i < 22; ++i) dataa[i] = 0;
data_count_bit = 0;
manchester_saved_state = ManchesterStateStart1;
-1
View File
@@ -44,7 +44,6 @@ class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
parser_step = IDoDecoderStepFoundPreambula;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -66,7 +66,6 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
parser_step = IntertechnoV3DecoderStepStartSync;
if ((decode_count_bit == min_count_bit_for_found) ||
(decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
+4 -5
View File
@@ -57,11 +57,8 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
parser_step = KeeloqDecoderStepReset;
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 2)) {
if (data != decode_data) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
}
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
@@ -96,6 +93,8 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
@@ -50,7 +50,8 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
DURATION_DIFF(duration, te_short * 2) < te_delta * 2) {
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
decode_data = 0;
data_2 = 0;
// data_2 = 0;
// data = 0;
decode_count_bit = 0;
header_count = 0;
}
@@ -58,17 +59,17 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
case KingGates_stylo_4kDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_long * 3)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = data_2;
data_2 = decode_data; // TODO DATA2
if (decode_count_bit == min_count_bit_for_found) {
// data = data_2;
// data_2 = decode_data; // TODO DATA2
data_count_bit = decode_count_bit;
// decode_data = data;
if (callback) callback(this);
}
parser_step = KingGates_stylo_4kDecoderStepReset;
decode_data = 0;
data_2 = 0;
// data_2 = 0;
decode_count_bit = 0;
header_count = 0;
break;
@@ -101,7 +102,7 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
header_count = 0;
}
if (decode_count_bit == 53) {
data_2 = decode_data;
// data_2 = decode_data;
decode_data = 0;
}
} else {
@@ -113,7 +114,9 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
}
protected:
uint64_t data_2 = 0;
// uint64_t data_2 = 0;
// uint64_t data = 0;
uint16_t header_count = 0;
};
#endif
+5 -11
View File
@@ -79,23 +79,17 @@ class FProtoSubGhzDLegrand : public FProtoSubGhzDBase {
if (found) {
te += duration;
if (decode_count_bit <
min_count_bit_for_found) {
if (decode_count_bit < min_count_bit_for_found) {
parser_step = LegrandDecoderStepSaveDuration;
break;
}
// enough bits for a packet found, save it only if there was a previous packet
// with the same data
if (data && (data != decode_data)) {
te /= decode_count_bit * 4;
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) {
callback(this);
}
te /= decode_count_bit * 4;
data_count_bit = decode_count_bit;
if (callback) {
callback(this);
}
// fallthrough to reset, the next bit is expected to be a sync
// it also takes care of resetting the decoder state
-1
View File
@@ -54,7 +54,6 @@ class FProtoSubGhzDLinear : public FProtoSubGhzDBase {
subghz_protocol_blocks_add_bit(1);
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -49,11 +49,8 @@ class FProtoSubGhzDLinearDelta3 : public FProtoSubGhzDBase {
subghz_protocol_blocks_add_bit(0);
}
if (decode_count_bit == min_count_bit_for_found) {
if ((data == decode_data) && data) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
data_count_bit = decode_count_bit;
if (callback) callback(this);
parser_step = LinearD3DecoderStepSaveDuration;
}
break;
+1 -1
View File
@@ -90,7 +90,6 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
// Found stop bit
if ((decode_count_bit == min_count_bit_for_found) &&
subghz_protocol_magellan_check_crc()) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -129,6 +128,7 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
}
return crc;
}
uint16_t header_count = 0;
};
#endif
-1
View File
@@ -43,7 +43,6 @@ class FProtoSubGhzDMarantec : public FProtoSubGhzDBase {
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -0,0 +1,93 @@
#ifndef __FPROTO_MARANTEC24_H__
#define __FPROTO_MARANTEC24_H__
#include "subghzdbase.hpp"
typedef enum {
Marantec24DecoderStepReset = 0,
Marantec24DecoderStepSaveDuration,
Marantec24DecoderStepCheckDuration,
} Marantec24DecoderStep;
class FProtoSubGhzDMarantec24 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMarantec24() {
sensorType = FPS_MARANTEC24;
te_short = 800;
te_long = 1600;
te_delta = 200;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
// Key samples
// 101011000000010111001000 = AC05C8
// 101011000000010111000100 = AC05C4
// 101011000000010111001100 = AC05CC
// 101011000000010111000000 = AC05C0
switch (parser_step) {
case Marantec24DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
// Found GAP
decode_data = 0;
decode_count_bit = 0;
parser_step = Marantec24DecoderStepSaveDuration;
}
break;
case Marantec24DecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = Marantec24DecoderStepCheckDuration;
} else {
parser_step = Marantec24DecoderStepReset;
}
break;
case Marantec24DecoderStepCheckDuration:
if (!level) {
// Bit 0 is long and short x2 timing = 1600us HIGH (te_last) and 2400us LOW
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Marantec24DecoderStepSaveDuration;
// Bit 1 is short and long x2 timing = 800us HIGH (te_last) and 3200us LOW
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long * 2) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Marantec24DecoderStepSaveDuration;
} else if (
// End of the key
DURATION_DIFF(duration, te_long * 9) < te_delta * 4) {
// Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
subghz_protocol_blocks_add_bit(0);
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
subghz_protocol_blocks_add_bit(1);
}
// If got 24 bits key reading is finished
if (decode_count_bit == min_count_bit_for_found) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Marantec24DecoderStepReset;
} else {
parser_step = Marantec24DecoderStepReset;
}
} else {
parser_step = Marantec24DecoderStepReset;
}
break;
}
}
protected:
};
#endif
@@ -61,7 +61,6 @@ class FProtoSubGhzDMastercode : public FProtoSubGhzDBase {
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
-1
View File
@@ -48,7 +48,6 @@ class FProtoSubGhzDMegacode : public FProtoSubGhzDBase {
parser_step = MegaCodeDecoderStepReset;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
+1 -1
View File
@@ -67,7 +67,6 @@ class FProtoSubGhzDNeroSketch : public FProtoSubGhzDBase {
// Found stop bit
parser_step = NeroSketchDecoderStepReset;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -103,6 +102,7 @@ class FProtoSubGhzDNeroSketch : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
+1 -1
View File
@@ -82,7 +82,6 @@ class FProtoSubGhzDNeroRadio : public FProtoSubGhzDBase {
parser_step = NeroRadioDecoderStepReset;
if ((decode_count_bit == min_count_bit_for_found) ||
(decode_count_bit == min_count_bit_for_found + 1)) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
@@ -110,6 +109,7 @@ class FProtoSubGhzDNeroRadio : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
-1
View File
@@ -47,7 +47,6 @@ class FProtoSubGhzDNiceflo : public FProtoSubGhzDBase {
if (duration >= (te_short * 4)) {
parser_step = NiceFloDecoderStepFoundStartBit;
if (decode_count_bit >= min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
+3 -4
View File
@@ -56,10 +56,9 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
// Found STOP bit
parser_step = NiceFlorSDecoderStepReset;
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == NICE_ONE_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
decode_data = 0;
}
break;
} else {
@@ -86,8 +85,8 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
parser_step = NiceFlorSDecoderStepReset;
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
decode_data = 0;
// data = decode_data;
// decode_data = 0;
}
break;
}
@@ -45,7 +45,6 @@ class FProtoSubGhzDPhoenixV2 : public FProtoSubGhzDBase {
parser_step = Phoenix_V2DecoderStepFoundStartBit;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -49,7 +49,6 @@ class FProtoSubGhzDPowerSmart : public FProtoSubGhzDBase {
}
if ((decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) {
if (subghz_protocol_power_smart_chek_valid(decode_data)) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
@@ -42,7 +42,6 @@ class FProtoSubGhzDPrinceton : public FProtoSubGhzDBase {
if (duration >= ((uint32_t)te_long * 2)) {
parser_step = PrincetonDecoderStepSaveDuration;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
@@ -50,7 +50,6 @@ class FProtoSubGhzDSecPlusV2 : public FProtoSubGhzDBase {
} else if (
duration >= (te_long * 2UL + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (subghz_protocol_secplus_v2_check_packet()) {
// controller too big

Some files were not shown because too many files have changed in this diff Show More