mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-24 04:27:42 +00:00
Merge branch 'v1.2' into add-heading-to-geomap
This commit is contained in:
@@ -214,7 +214,7 @@ set(CPPSRC
|
||||
ui/ui_tabview.cpp
|
||||
ui/ui_textentry.cpp
|
||||
ui/ui_transmitter.cpp
|
||||
apps/ui_about.cpp
|
||||
apps/ui_about_simple.cpp
|
||||
apps/ui_adsb_rx.cpp
|
||||
apps/ui_adsb_tx.cpp
|
||||
apps/ui_afsk_rx.cpp
|
||||
|
75
firmware/application/apps/ui_about_simple.cpp
Normal file
75
firmware/application/apps/ui_about_simple.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "ui_about_simple.hpp"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
AboutView::AboutView(NavigationView &nav)
|
||||
{
|
||||
add_children({&console, &button_ok});
|
||||
|
||||
button_ok.on_select = [&nav](Button &) {
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
console.writeln("\x1B\x07List of contributors:\x1B\x10");
|
||||
console.writeln("");
|
||||
}
|
||||
|
||||
void AboutView::update()
|
||||
{
|
||||
if (++timer > 200)
|
||||
{
|
||||
timer = 0;
|
||||
|
||||
switch (++frame)
|
||||
{
|
||||
case 1:
|
||||
// TODO: Generate this automatically from github
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c
|
||||
console.writeln("\x1B\x06Mayhem:\x1B\x10");
|
||||
console.writeln("eried,euquiq,gregoryfenton");
|
||||
console.writeln("johnelder,jwetzell,nnemanjan00");
|
||||
console.writeln("N0vaPixel,klockee,jamesshao8");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
|
||||
console.writeln("\x1B\x06Havoc:\x1B\x10");
|
||||
console.writeln("furrtek,mrmookie,notpike");
|
||||
console.writeln("mjwaxios,ImDroided,Giorgiofox");
|
||||
console.writeln("F4GEV,z4ziggy,xmycroftx");
|
||||
console.writeln("troussos,silascutler");
|
||||
console.writeln("nickbouwhuis,msoose,leres");
|
||||
console.writeln("joakar,dhoetger,clem-42");
|
||||
console.writeln("brianlechthaler,ZeroChaos-...");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c
|
||||
console.writeln("\x1B\x06PortaPack:\x1B\x10");
|
||||
console.writeln("jboone,argilo");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// https://github.com/mossmann/hackrf/graphs/contributors
|
||||
console.writeln("\x1B\x06HackRF:\x1B\x10");
|
||||
console.writeln("mossmann,dominicgs,bvernoux");
|
||||
console.writeln("bgamari,schneider42,miek");
|
||||
console.writeln("willcode,hessu,Sec42");
|
||||
console.writeln("yhetti,ckuethe,smunaut");
|
||||
console.writeln("wishi,mrbubble62,scateu...");
|
||||
console.writeln("");
|
||||
frame = 0; // Loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::focus()
|
||||
{
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
40
firmware/application/apps/ui_about_simple.hpp
Normal file
40
firmware/application/apps/ui_about_simple.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef __UI_ABOUT_SIMPLE_H__
|
||||
#define __UI_ABOUT_SIMPLE_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class AboutView : public View
|
||||
{
|
||||
public:
|
||||
AboutView(NavigationView &nav);
|
||||
void focus() override;
|
||||
std::string title() const override { return "About"; };
|
||||
int32_t timer{180};
|
||||
short frame{0};
|
||||
|
||||
private:
|
||||
void update();
|
||||
|
||||
Console console{
|
||||
{0, 10, 240, 240}};
|
||||
|
||||
Button button_ok{
|
||||
{240/3, 270, 240/3, 24},
|
||||
"OK",
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_update{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message *const) {
|
||||
this->update();
|
||||
}};
|
||||
};
|
||||
} // namespace ui
|
||||
|
||||
#endif /*__UI_ABOUT_SIMPLE_H__*/
|
@@ -92,31 +92,37 @@ FileManBaseView::FileManBaseView(
|
||||
) : nav_ (nav),
|
||||
extension_filter { filter }
|
||||
{
|
||||
load_directory_contents(current_path);
|
||||
|
||||
if (!entry_list.size())
|
||||
empty_root = true;
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&text_current,
|
||||
&button_exit
|
||||
});
|
||||
|
||||
menu_view.on_left = [&nav, this]() {
|
||||
load_directory_contents(get_parent_dir());
|
||||
refresh_list();
|
||||
};
|
||||
|
||||
button_exit.on_select = [this, &nav](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
};
|
||||
|
||||
if (!sdcIsCardInserted(&SDCD1)) {
|
||||
empty_root=true;
|
||||
text_current.set("NO SD CARD!");
|
||||
} else {
|
||||
load_directory_contents(current_path);
|
||||
if (!entry_list.size())
|
||||
{
|
||||
empty_root = true;
|
||||
text_current.set("EMPTY SD CARD!");
|
||||
} else {
|
||||
menu_view.on_left = [&nav, this]() {
|
||||
load_directory_contents(get_parent_dir());
|
||||
refresh_list();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileManBaseView::focus() {
|
||||
if (empty_root) {
|
||||
button_exit.focus();
|
||||
nav_.display_modal("Error", "No files in root.", ABORT, nullptr);
|
||||
} else {
|
||||
menu_view.focus();
|
||||
}
|
||||
@@ -190,7 +196,6 @@ void FileManBaseView::refresh_list() {
|
||||
nav_.pop();
|
||||
});
|
||||
}
|
||||
|
||||
FileSaveView::FileSaveView(
|
||||
NavigationView& nav
|
||||
) : FileManBaseView(nav)
|
||||
@@ -244,7 +249,11 @@ FileLoadView::FileLoadView(
|
||||
|
||||
void FileManagerView::on_rename(NavigationView& nav) {
|
||||
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
|
||||
rename_file(get_selected_path(), buffer);
|
||||
std::string destination_path = current_path.string();
|
||||
if (destination_path.back() != '/')
|
||||
destination_path += '/';
|
||||
destination_path = destination_path + buffer;
|
||||
rename_file(get_selected_path(), destination_path);
|
||||
load_directory_contents(current_path);
|
||||
refresh_list();
|
||||
});
|
||||
@@ -271,57 +280,59 @@ FileManagerView::FileManagerView(
|
||||
NavigationView& nav
|
||||
) : FileManBaseView(nav, "")
|
||||
{
|
||||
on_refresh_widgets = [this](bool v) {
|
||||
refresh_widgets(v);
|
||||
};
|
||||
|
||||
add_children({
|
||||
&menu_view,
|
||||
&labels,
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_new_dir,
|
||||
&button_delete
|
||||
});
|
||||
|
||||
menu_view.on_highlight = [this]() {
|
||||
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
|
||||
};
|
||||
|
||||
refresh_list();
|
||||
|
||||
on_select_entry = [this]() {
|
||||
if (entry_list[menu_view.highlighted_index()].is_directory) {
|
||||
load_directory_contents(get_selected_path());
|
||||
refresh_list();
|
||||
} else
|
||||
button_rename.focus();
|
||||
};
|
||||
|
||||
button_new_dir.on_select = [this, &nav](Button&) {
|
||||
name_buffer.clear();
|
||||
if (!empty_root) {
|
||||
on_refresh_widgets = [this](bool v) {
|
||||
refresh_widgets(v);
|
||||
};
|
||||
|
||||
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
|
||||
make_new_directory(current_path.string() + '/' + buffer);
|
||||
load_directory_contents(current_path);
|
||||
refresh_list();
|
||||
add_children({
|
||||
&menu_view,
|
||||
&labels,
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_new_dir,
|
||||
&button_delete
|
||||
});
|
||||
};
|
||||
|
||||
button_rename.on_select = [this, &nav](Button&) {
|
||||
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
|
||||
on_rename(nav);
|
||||
};
|
||||
|
||||
button_delete.on_select = [this, &nav](Button&) {
|
||||
// Use display_modal ?
|
||||
nav.push<ModalMessageView>("Delete", "Delete " + entry_list[menu_view.highlighted_index()].entry_path.filename().string() + "\nAre you sure?", YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice)
|
||||
on_delete();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
menu_view.on_highlight = [this]() {
|
||||
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
|
||||
};
|
||||
|
||||
refresh_list();
|
||||
|
||||
on_select_entry = [this]() {
|
||||
if (entry_list[menu_view.highlighted_index()].is_directory) {
|
||||
load_directory_contents(get_selected_path());
|
||||
refresh_list();
|
||||
} else
|
||||
button_rename.focus();
|
||||
};
|
||||
|
||||
button_new_dir.on_select = [this, &nav](Button&) {
|
||||
name_buffer.clear();
|
||||
|
||||
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
|
||||
make_new_directory(current_path.string() + '/' + buffer);
|
||||
load_directory_contents(current_path);
|
||||
refresh_list();
|
||||
});
|
||||
};
|
||||
|
||||
button_rename.on_select = [this, &nav](Button&) {
|
||||
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
|
||||
on_rename(nav);
|
||||
};
|
||||
|
||||
button_delete.on_select = [this, &nav](Button&) {
|
||||
// Use display_modal ?
|
||||
nav.push<ModalMessageView>("Delete", "Delete " + entry_list[menu_view.highlighted_index()].entry_path.filename().string() + "\nAre you sure?", YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice)
|
||||
on_delete();
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ SondeView::SondeView(NavigationView& nav) {
|
||||
});
|
||||
|
||||
field_frequency.set_value(target_frequency_);
|
||||
field_frequency.set_step(10000);
|
||||
field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
field_frequency.set_value(f);
|
||||
@@ -86,11 +86,11 @@ SondeView::SondeView(NavigationView& nav) {
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
nav.push<GeoMapView>(
|
||||
"",
|
||||
altitude,
|
||||
sonde_id,
|
||||
gps_info.alt,
|
||||
GeoPos::alt_unit::METERS,
|
||||
latitude,
|
||||
longitude,
|
||||
gps_info.lat,
|
||||
gps_info.lon,
|
||||
999); //set a dummy heading out of range to draw a cross...probably not ideal?
|
||||
};
|
||||
|
||||
@@ -113,16 +113,15 @@ void SondeView::on_packet(const sonde::Packet& packet) {
|
||||
//const auto hex_formatted = packet.symbols_formatted();
|
||||
|
||||
text_signature.set(packet.type_string());
|
||||
text_serial.set(packet.serial_number());
|
||||
sonde_id = packet.serial_number(); //used also as tag on the geomap
|
||||
text_serial.set(sonde_id);
|
||||
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V");
|
||||
|
||||
gps_info = packet.get_GPS_data();
|
||||
|
||||
altitude = packet.GPS_altitude();
|
||||
latitude = packet.GPS_latitude();
|
||||
longitude = packet.GPS_longitude();
|
||||
|
||||
geopos.set_altitude(altitude);
|
||||
geopos.set_lat(latitude);
|
||||
geopos.set_lon(longitude);
|
||||
geopos.set_altitude(gps_info.alt);
|
||||
geopos.set_lat(gps_info.lat);
|
||||
geopos.set_lon(gps_info.lon);
|
||||
|
||||
if (logger && logging) {
|
||||
logger->on_packet(packet);
|
||||
|
@@ -65,11 +65,10 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<SondeLogger> logger { };
|
||||
uint32_t target_frequency_ { 402000000 };
|
||||
uint32_t target_frequency_ { 402700000 };
|
||||
bool logging { false };
|
||||
int32_t altitude { 0 };
|
||||
float latitude { 0 };
|
||||
float longitude { 0 };
|
||||
sonde::GPS_data gps_info;
|
||||
std::string sonde_id;
|
||||
|
||||
Labels labels {
|
||||
{ { 0 * 8, 2 * 16 }, "Signature:", Color::light_grey() },
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,15 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::set_arrow_enabled(bool new_value) {
|
||||
if(new_value){
|
||||
add_child(&arrow_more);
|
||||
}
|
||||
else{
|
||||
remove_child(&arrow_more);
|
||||
}
|
||||
};
|
||||
|
||||
void BtnGridView::on_tick_second() {
|
||||
if (more && blink)
|
||||
arrow_more.set_foreground(Color::white());
|
||||
|
@@ -62,6 +62,7 @@ public:
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void set_arrow_enabled(bool new_value);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
|
@@ -120,8 +120,8 @@ void FrequencyScale::paint(Painter& painter) {
|
||||
|
||||
if (_blink) {
|
||||
const Rect r_cursor {
|
||||
120 + cursor_position, r.bottom() - filter_band_height,
|
||||
2, filter_band_height
|
||||
118 + cursor_position, r.bottom() - filter_band_height,
|
||||
5, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_cursor,
|
||||
|
@@ -30,7 +30,7 @@
|
||||
#include "bmp_modal_warning.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "ui_about_simple.hpp"
|
||||
#include "ui_adsb_rx.hpp"
|
||||
#include "ui_adsb_tx.hpp"
|
||||
#include "ui_afsk_rx.hpp"
|
||||
@@ -106,6 +106,7 @@ SystemStatusView::SystemStatusView(
|
||||
&backdrop,
|
||||
&button_back,
|
||||
&title,
|
||||
&button_title,
|
||||
&button_speaker,
|
||||
&button_stealth,
|
||||
//&button_textentry,
|
||||
@@ -138,6 +139,10 @@ SystemStatusView::SystemStatusView(
|
||||
if (this->on_back)
|
||||
this->on_back();
|
||||
};
|
||||
|
||||
button_title.on_select = [this](ImageButton&) {
|
||||
this->on_title();
|
||||
};
|
||||
|
||||
button_speaker.on_select = [this](ImageButton&) {
|
||||
this->on_speaker();
|
||||
@@ -194,8 +199,23 @@ void SystemStatusView::refresh() {
|
||||
}
|
||||
|
||||
void SystemStatusView::set_back_enabled(bool new_value) {
|
||||
button_back.set_foreground(new_value ? Color::white() : Color::dark_grey());
|
||||
button_back.set_focusable(new_value);
|
||||
|
||||
if(new_value){
|
||||
add_child(&button_back);
|
||||
}
|
||||
else{
|
||||
remove_child(&button_back);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::set_title_image_enabled(bool new_value) {
|
||||
|
||||
if(new_value){
|
||||
add_child(&button_title);
|
||||
}
|
||||
else{
|
||||
remove_child(&button_title);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::set_title(const std::string new_value) {
|
||||
@@ -282,6 +302,39 @@ void SystemStatusView::on_camera() {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::on_title() {
|
||||
if(nav_.is_top())
|
||||
nav_.push<AboutView>();
|
||||
else
|
||||
nav_.pop();
|
||||
}
|
||||
|
||||
/* Information View *****************************************************/
|
||||
|
||||
InformationView::InformationView(
|
||||
NavigationView& nav
|
||||
) : nav_ (nav)
|
||||
{
|
||||
static constexpr Style style_infobar {
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 33, 33},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
add_children({
|
||||
&backdrop,
|
||||
&version,
|
||||
<ime
|
||||
});
|
||||
|
||||
version.set_style(&style_infobar);
|
||||
ltime.set_style(&style_infobar);
|
||||
ltime.set_seconds_enabled(true);
|
||||
ltime.set_date_enabled(false);
|
||||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
/* Navigation ************************************************************/
|
||||
|
||||
bool NavigationView::is_top() const {
|
||||
@@ -383,7 +436,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
||||
add_items({
|
||||
//{ "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } },
|
||||
{ "ADS-B", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.push<ADSBRxView>(); }, },
|
||||
{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
|
||||
//{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
|
||||
{ "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push<AISAppView>(); } },
|
||||
{ "AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav](){ nav.push<AFSKRxView>(); } },
|
||||
{ "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push<BTLERxView>(); } },
|
||||
@@ -392,7 +445,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
||||
{ "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push<AnalogTvView>(); } },
|
||||
{ "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
|
||||
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
|
||||
{ "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
|
||||
{ "Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
|
||||
{ "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push<TPMSAppView>(); } },
|
||||
/*{ "APRS", ui::Color::dark_grey(), &bitmap_icon_aprs, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
@@ -478,6 +531,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
//{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push<AboutView>(); } }
|
||||
});
|
||||
set_max_rows(2); // allow wider buttons
|
||||
set_arrow_enabled(false);
|
||||
//set_highlighted(1); // Startup selection
|
||||
}
|
||||
|
||||
@@ -498,6 +552,7 @@ SystemView::SystemView(
|
||||
set_style(&style_default);
|
||||
|
||||
constexpr ui::Dim status_view_height = 16;
|
||||
constexpr ui::Dim info_view_height = 16;
|
||||
|
||||
add_child(&status_view);
|
||||
status_view.set_parent_rect({
|
||||
@@ -513,11 +568,30 @@ SystemView::SystemView(
|
||||
{ 0, status_view_height },
|
||||
{ parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
|
||||
});
|
||||
|
||||
add_child(&info_view);
|
||||
info_view.set_parent_rect({
|
||||
{0, 19 * 16},
|
||||
{ parent_rect.width(), info_view_height }
|
||||
});
|
||||
|
||||
navigation_view.on_view_changed = [this](const View& new_view) {
|
||||
|
||||
if(!this->navigation_view.is_top()){
|
||||
remove_child(&info_view);
|
||||
}
|
||||
else{
|
||||
add_child(&info_view);
|
||||
}
|
||||
|
||||
this->status_view.set_back_enabled(!this->navigation_view.is_top());
|
||||
this->status_view.set_title_image_enabled(this->navigation_view.is_top());
|
||||
this->status_view.set_title(new_view.title());
|
||||
this->status_view.set_dirty();
|
||||
|
||||
};
|
||||
|
||||
|
||||
// portapack::persistent_memory::set_playdead_sequence(0x8D1);
|
||||
|
||||
// Initial view
|
||||
@@ -530,6 +604,9 @@ SystemView::SystemView(
|
||||
|
||||
if (portapack::persistent_memory::config_splash())
|
||||
navigation_view.push<BMPView>();
|
||||
status_view.set_back_enabled(false);
|
||||
status_view.set_title_image_enabled(true);
|
||||
status_view.set_dirty();
|
||||
//else
|
||||
// navigation_view.push<SystemMenuView>();
|
||||
|
||||
|
@@ -106,10 +106,11 @@ public:
|
||||
SystemStatusView(NavigationView& nav);
|
||||
|
||||
void set_back_enabled(bool new_value);
|
||||
void set_title_image_enabled(bool new_value);
|
||||
void set_title(const std::string new_value);
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "MAYHEM v1.1.1"; // TODO: Move the version somewhere
|
||||
static constexpr auto default_title = "";
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -130,6 +131,13 @@ private:
|
||||
default_title,
|
||||
};
|
||||
|
||||
ImageButton button_title {
|
||||
{2, 0, 80, 16},
|
||||
&bitmap_titlebar_image,
|
||||
Color::white(),
|
||||
Color::dark_grey()
|
||||
};
|
||||
|
||||
ImageButton button_speaker {
|
||||
{ 17 * 8, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_icon_speaker_mute,
|
||||
@@ -188,6 +196,7 @@ private:
|
||||
void on_bias_tee();
|
||||
//void on_textentry();
|
||||
void on_camera();
|
||||
void on_title();
|
||||
void refresh();
|
||||
|
||||
MessageHandlerRegistration message_handler_refresh {
|
||||
@@ -199,6 +208,29 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
class InformationView : public View {
|
||||
public:
|
||||
InformationView(NavigationView& nav);
|
||||
|
||||
private:
|
||||
static constexpr auto version_string = "v1.1.1";
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop {
|
||||
{ 0, 0 * 16, 240, 16 },
|
||||
{33, 33, 33}
|
||||
};
|
||||
|
||||
Text version {
|
||||
{2, 0, 11 * 8, 16},
|
||||
version_string
|
||||
};
|
||||
|
||||
LiveDateTime ltime {
|
||||
{174, 0, 8 * 8, 16}
|
||||
};
|
||||
};
|
||||
|
||||
class BMPView : public View {
|
||||
public:
|
||||
BMPView(NavigationView& nav);
|
||||
@@ -253,6 +285,7 @@ public:
|
||||
|
||||
private:
|
||||
SystemStatusView status_view { navigation_view };
|
||||
InformationView info_view { navigation_view };
|
||||
NavigationView navigation_view { };
|
||||
Context& context_;
|
||||
};
|
||||
|
@@ -140,7 +140,8 @@ private:
|
||||
}
|
||||
};
|
||||
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_fsk_4800_Vaisala {
|
||||
{ 0b00001000011011010101001110001000, 32, 1 },
|
||||
{ 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet)
|
||||
//{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes.
|
||||
{ },
|
||||
{ 320 * 8 },
|
||||
[this](const baseband::Packet& packet) {
|
||||
|
@@ -22,9 +22,25 @@
|
||||
|
||||
#include "sonde_packet.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include <cstring>
|
||||
//#include <complex>
|
||||
|
||||
namespace sonde {
|
||||
|
||||
//Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c
|
||||
#define MASK_LEN 64
|
||||
#define pos_FrameNb 0x37 //0x03B // 2 byte
|
||||
#define pos_SondeID 0x39 //0x03D // 8 byte
|
||||
#define pos_Voltage 0x041 //0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v
|
||||
#define pos_CalData 0x04E //0x052 // 1 byte, counter 0x00..0x32
|
||||
#define pos_GPSweek 0x091 //0x095 // 2 byte
|
||||
#define pos_GPSTOW 0x093 //0x097 // 4 byte
|
||||
#define pos_GPSecefX 0x110 //0x114 // 4 byte
|
||||
#define pos_GPSecefY 0x114 //0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop)
|
||||
#define pos_GPSecefZ 0x118 //0x11C // 4 byte (same as Y)
|
||||
|
||||
#define PI 3.1415926535897932384626433832795 //3.1416 //(3.1415926535897932384626433832795)
|
||||
|
||||
Packet::Packet(
|
||||
const baseband::Packet& packet,
|
||||
const Type type
|
||||
@@ -60,37 +76,65 @@ Packet::Type Packet::type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
/*uint8_t Packet::vaisala_descramble(const uint32_t pos) {
|
||||
return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63];
|
||||
};*/
|
||||
//euquiq here:
|
||||
//RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame")
|
||||
//The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp)
|
||||
|
||||
uint32_t Packet::GPS_altitude() const {
|
||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
||||
return (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
|
||||
else if (type_ == Type::Vaisala_RS41_SG) {
|
||||
/*uint32_t altitude_ecef = 0;
|
||||
for (uint32_t i = 0; i < 4; i++)
|
||||
altitude_ecef = (altitude_ecef << 8) + vaisala_descramble(0x11C + i);*/
|
||||
// TODO: and a bunch of maths (see ecef2elli() from RS1729)
|
||||
return 0;
|
||||
} else
|
||||
return 0; // Unknown
|
||||
}
|
||||
|
||||
float Packet::GPS_latitude() const {
|
||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
||||
return reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||
//else if (type_ == Type::Vaisala_RS41_SG)
|
||||
// return vaisala_descramble();
|
||||
else
|
||||
return 0; // Unknown
|
||||
}
|
||||
uint8_t Packet::vaisala_descramble(const uint32_t pos) const {
|
||||
//return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63];
|
||||
// packet_[i]; its a bit; packet_.size the total (should be 2560 bits)
|
||||
uint8_t value = 0;
|
||||
for (uint8_t i = 0; i < 8; i++)
|
||||
value = (value << 1) | packet_[(pos * 8) + (7 -i)]; //get the byte from the bits collection
|
||||
|
||||
float Packet::GPS_longitude() const {
|
||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
||||
return reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||
else
|
||||
return 0; // Unknown
|
||||
//packetReader reader { packet_ }; //This works just as above.
|
||||
//value = reader.read(pos * 8,8);
|
||||
//shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature
|
||||
uint32_t mask_pos = pos + 4;
|
||||
value = value ^ vaisala_mask[mask_pos % MASK_LEN]; //descramble with the xor pseudorandom table
|
||||
return value;
|
||||
};
|
||||
|
||||
GPS_data Packet::get_GPS_data() const {
|
||||
GPS_data result;
|
||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) {
|
||||
|
||||
result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
|
||||
result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||
result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||
|
||||
} else if (type_ == Type::Vaisala_RS41_SG) {
|
||||
|
||||
uint8_t XYZ_bytes[4];
|
||||
int32_t XYZ; // 32bit
|
||||
double_t X[3];
|
||||
for (int32_t k = 0; k < 3; k++) { //Get X,Y,Z ECEF position from GPS
|
||||
for (int32_t i = 0; i < 4; i++) //each one is 4 bytes (32 bits)
|
||||
XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4*k) + i);
|
||||
memcpy(&XYZ, XYZ_bytes, 4);
|
||||
X[k] = XYZ / 100.0;
|
||||
}
|
||||
|
||||
double_t a = 6378137.0;
|
||||
double_t b = 6356752.31424518;
|
||||
double_t e = sqrt( (a*a - b*b) / (a*a) );
|
||||
double_t ee = sqrt( (a*a - b*b) / (b*b) );
|
||||
|
||||
double_t lam = atan2( X[1] , X[0] );
|
||||
double_t p = sqrt( X[0]*X[0] + X[1]*X[1] );
|
||||
double_t t = atan2( X[2]*a , p*b );
|
||||
double_t phi = atan2( X[2] + ee*ee * b * sin(t)*sin(t)*sin(t) ,
|
||||
p - e*e * a * cos(t)*cos(t)*cos(t) );
|
||||
|
||||
double_t R = a / sqrt( 1 - e*e*sin(phi)*sin(phi) );
|
||||
|
||||
result.alt = p / cos(phi) - R;
|
||||
result.lat = phi*180/PI;
|
||||
result.lon = lam*180/PI;
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Packet::battery_voltage() const {
|
||||
@@ -98,8 +142,13 @@ uint32_t Packet::battery_voltage() const {
|
||||
return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150;
|
||||
else if (type_ == Type::Meteomodem_M2K2)
|
||||
return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8
|
||||
else
|
||||
else if (type_ == Type::Vaisala_RS41_SG) {
|
||||
uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; //byte 69 = voltage * 10 (check if this value needs to be multiplied)
|
||||
return voltage;
|
||||
}
|
||||
else {
|
||||
return 0; // Unknown
|
||||
}
|
||||
}
|
||||
|
||||
std::string Packet::type_string() const {
|
||||
@@ -127,12 +176,33 @@ std::string Packet::serial_number() const {
|
||||
to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) +
|
||||
to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0');
|
||||
|
||||
} else
|
||||
} else if(type() == Type::Vaisala_RS41_SG) {
|
||||
std::string serial_id = "";
|
||||
uint8_t achar;
|
||||
for (uint8_t i=0; i<8; i++) { //euquiq: Serial ID is 8 bytes long, each byte a char
|
||||
achar = vaisala_descramble(pos_SondeID + i);
|
||||
if (achar < 32 || achar > 126) return "?"; //Maybe there are ids with less than 8 bytes and this is not OK.
|
||||
serial_id += (char)achar;
|
||||
}
|
||||
return serial_id;
|
||||
} else
|
||||
return "?";
|
||||
}
|
||||
|
||||
FormattedSymbols Packet::symbols_formatted() const {
|
||||
return format_symbols(decoder_);
|
||||
if (type() == Type::Vaisala_RS41_SG) { //Euquiq: now we distinguish different types
|
||||
uint32_t bytes = packet_.size() / 8; //Need the byte amount, which if full, it SHOULD be 320 size() should return 2560
|
||||
std::string hex_data;
|
||||
std::string hex_error;
|
||||
hex_data.reserve(bytes * 2); //2 hexa chars per byte
|
||||
hex_error.reserve(1);
|
||||
for (uint32_t i=0; i < bytes; i++) //log will show the packet starting on the last 4 bytes from signature 93DF1A60
|
||||
hex_data += to_string_hex(vaisala_descramble(i),2);
|
||||
return { hex_data, hex_error };
|
||||
|
||||
} else {
|
||||
return format_symbols(decoder_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Packet::crc_ok() const {
|
||||
|
@@ -32,6 +32,12 @@
|
||||
|
||||
namespace sonde {
|
||||
|
||||
struct GPS_data {
|
||||
uint32_t alt { 0 };
|
||||
float lat { 0 };
|
||||
float lon { 0 };
|
||||
};
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
enum class Type : uint32_t {
|
||||
@@ -41,7 +47,7 @@ public:
|
||||
Meteomodem_M2K2 = 3,
|
||||
Vaisala_RS41_SG = 4,
|
||||
};
|
||||
|
||||
|
||||
Packet(const baseband::Packet& packet, const Type type);
|
||||
|
||||
size_t length() const;
|
||||
@@ -56,9 +62,7 @@ public:
|
||||
std::string serial_number() const;
|
||||
uint32_t battery_voltage() const;
|
||||
|
||||
uint32_t GPS_altitude() const;
|
||||
float GPS_latitude() const;
|
||||
float GPS_longitude() const;
|
||||
GPS_data get_GPS_data() const;
|
||||
|
||||
FormattedSymbols symbols_formatted() const;
|
||||
|
||||
@@ -75,17 +79,20 @@ private:
|
||||
0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23,
|
||||
0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1
|
||||
};
|
||||
|
||||
GPS_data ecef_to_gps() const;
|
||||
|
||||
//uint8_t vaisala_descramble(const uint32_t pos);
|
||||
uint8_t vaisala_descramble(uint32_t pos) const;
|
||||
|
||||
const baseband::Packet packet_;
|
||||
const BiphaseMDecoder decoder_;
|
||||
const FieldReader<BiphaseMDecoder, BitRemapNone> reader_bi_m;
|
||||
Type type_;
|
||||
|
||||
using packetReader = FieldReader<baseband::Packet, BitRemapByteReverse>; //baseband::Packet instead of BiphaseMDecoder
|
||||
bool crc_ok_M10() const;
|
||||
};
|
||||
|
||||
} /* namespace sonde */
|
||||
|
||||
#endif/*__SONDE_PACKET_H__*/
|
||||
#endif/*__SONDE_PACKET_H__*/
|
@@ -409,10 +409,26 @@ void Labels::paint(Painter& painter) {
|
||||
|
||||
void LiveDateTime::on_tick_second() {
|
||||
rtcGetTime(&RTCD1, &datetime);
|
||||
text = "";
|
||||
|
||||
text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " " +
|
||||
to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0');
|
||||
if(date_enabled){
|
||||
text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " ";
|
||||
}
|
||||
|
||||
text = text + to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0');
|
||||
|
||||
if(seconds_enabled){
|
||||
text += ":";
|
||||
|
||||
if(init_delay==0)
|
||||
text += to_string_dec_uint(datetime.second(), 2, '0');
|
||||
else
|
||||
{
|
||||
// Placeholder while the seconds are not updated
|
||||
text += "XX";
|
||||
init_delay--;
|
||||
}
|
||||
}
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
@@ -444,6 +460,14 @@ void LiveDateTime::paint(Painter& painter) {
|
||||
);
|
||||
}
|
||||
|
||||
void LiveDateTime::set_date_enabled(bool new_value){
|
||||
this->date_enabled = new_value;
|
||||
}
|
||||
|
||||
void LiveDateTime::set_seconds_enabled(bool new_value) {
|
||||
this->seconds_enabled = new_value;
|
||||
}
|
||||
|
||||
/* BigFrequency **********************************************************/
|
||||
|
||||
BigFrequency::BigFrequency(
|
||||
@@ -625,7 +649,8 @@ void Console::write(std::string message) {
|
||||
|
||||
void Console::writeln(std::string message) {
|
||||
write(message);
|
||||
crlf();
|
||||
write("\n");
|
||||
//crlf();
|
||||
}
|
||||
|
||||
void Console::paint(Painter&) {
|
||||
|
@@ -243,7 +243,10 @@ public:
|
||||
~LiveDateTime();
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
|
||||
void set_seconds_enabled(bool new_value);
|
||||
void set_date_enabled(bool new_value);
|
||||
|
||||
std::string& string() {
|
||||
return text;
|
||||
}
|
||||
@@ -251,6 +254,10 @@ public:
|
||||
private:
|
||||
void on_tick_second();
|
||||
|
||||
uint16_t init_delay = 4;
|
||||
bool date_enabled = true;
|
||||
bool seconds_enabled = false;
|
||||
|
||||
rtc::RTC datetime { };
|
||||
SignalToken signal_token_tick_second { };
|
||||
std::string text { };
|
||||
|
BIN
firmware/graphics/titlebar_image.png
Normal file
BIN
firmware/graphics/titlebar_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
Reference in New Issue
Block a user