mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-13 23:27:40 +00:00
Moved SPainter and GPSSim to ext app (#1721)
* Moved GPSSim * Moved spainter
This commit is contained in:
12
firmware/application/external/external.cmake
vendored
12
firmware/application/external/external.cmake
vendored
@@ -44,6 +44,16 @@ set(EXTCPPSRC
|
||||
#lcr
|
||||
external/jammer/main.cpp
|
||||
external/jammer/ui_jammer.cpp
|
||||
|
||||
#gpssim
|
||||
external/gpssim/main.cpp
|
||||
external/gpssim/gps_sim_app.cpp
|
||||
|
||||
#spainter
|
||||
external/spainter/main.cpp
|
||||
external/spainter/ui_spectrum_painter.cpp
|
||||
external/spainter/ui_spectrum_painter_text.cpp
|
||||
external/spainter/ui_spectrum_painter_image.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -58,4 +68,6 @@ set(EXTAPPLIST
|
||||
lge
|
||||
lcr
|
||||
jammer
|
||||
gpssim
|
||||
spainter
|
||||
)
|
||||
|
18
firmware/application/external/external.ld
vendored
18
firmware/application/external/external.ld
vendored
@@ -28,6 +28,8 @@ MEMORY
|
||||
ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k
|
||||
ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k
|
||||
ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k
|
||||
ram_external_app_gpssim(rwx) : org = 0xEEF40000, len = 32k
|
||||
ram_external_app_spainter(rwx) : org = 0xEEF50000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -88,18 +90,30 @@ SECTIONS
|
||||
} > ram_external_app_lge
|
||||
|
||||
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_lcr.application_information));
|
||||
*(*ui*external_app*lcr*);
|
||||
} > ram_external_app_lcr
|
||||
|
||||
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_jammer.application_information));
|
||||
*(*ui*external_app*jammer*);
|
||||
} > ram_external_app_jammer
|
||||
|
||||
.external_app_gpssim : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_gpssim.application_information));
|
||||
*(*ui*external_app*gpssim*);
|
||||
} > ram_external_app_gpssim
|
||||
|
||||
|
||||
.external_app_spainter : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_spainter.application_information));
|
||||
*(*ui*external_app*spainter*);
|
||||
} > ram_external_app_spainter
|
||||
|
||||
}
|
||||
|
215
firmware/application/external/gpssim/gps_sim_app.cpp
vendored
Normal file
215
firmware/application/external/gpssim/gps_sim_app.cpp
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "gps_sim_app.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
void GpsSimAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
file_path = fs::path(u"/") + new_file_path;
|
||||
File::Size file_size{};
|
||||
|
||||
{ // Get the size of the data file.
|
||||
File data_file;
|
||||
auto error = data_file.open(file_path);
|
||||
if (error) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
file_size = data_file.size();
|
||||
}
|
||||
|
||||
// Get original record frequency if available.
|
||||
auto metadata_path = get_metadata_path(file_path);
|
||||
auto metadata = read_metadata_file(metadata_path);
|
||||
|
||||
if (metadata) {
|
||||
field_frequency.set_value(metadata->center_frequency);
|
||||
transmitter_model.set_sampling_rate(metadata->sample_rate);
|
||||
}
|
||||
|
||||
// UI Fixup.
|
||||
text_sample_rate.set(unit_auto_scale(transmitter_model.sampling_rate(), 3, 1) + "Hz");
|
||||
progressbar.set_max(file_size);
|
||||
text_filename.set(truncate(file_path.filename().string(), 12));
|
||||
|
||||
auto duration = ms_duration(file_size, transmitter_model.sampling_rate(), 2);
|
||||
text_duration.set(to_string_time_ms(duration));
|
||||
|
||||
// TODO: fix in UI framework with 'try_focus()'?
|
||||
// Hack around focus getting called by ctor before parent is set.
|
||||
if (parent())
|
||||
button_play.focus();
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_tx_progress(const uint32_t progress) {
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
|
||||
void GpsSimAppView::focus() {
|
||||
button_open.focus();
|
||||
}
|
||||
|
||||
void GpsSimAppView::file_error() {
|
||||
nav_.display_modal("Error", "File read error.");
|
||||
}
|
||||
|
||||
bool GpsSimAppView::is_active() const {
|
||||
return (bool)replay_thread;
|
||||
}
|
||||
|
||||
void GpsSimAppView::toggle() {
|
||||
if (is_active()) {
|
||||
stop(false);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void GpsSimAppView::start() {
|
||||
stop(false);
|
||||
|
||||
std::unique_ptr<stream::Reader> reader;
|
||||
|
||||
auto p = std::make_unique<FileReader>();
|
||||
auto open_error = p->open(file_path);
|
||||
if (open_error.is_valid()) {
|
||||
file_error();
|
||||
} else {
|
||||
reader = std::move(p);
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
button_play.set_bitmap(&bitmap_stop);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size, buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
}
|
||||
|
||||
transmitter_model.enable();
|
||||
}
|
||||
|
||||
void GpsSimAppView::stop(const bool do_loop) {
|
||||
if (is_active())
|
||||
replay_thread.reset();
|
||||
|
||||
if (do_loop && check_loop.value()) {
|
||||
start();
|
||||
} else {
|
||||
transmitter_model.disable();
|
||||
button_play.set_bitmap(&bitmap_play);
|
||||
}
|
||||
|
||||
ready_signal = false;
|
||||
}
|
||||
|
||||
void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
|
||||
if (return_code == ReplayThread::END_OF_FILE) {
|
||||
stop(true);
|
||||
} else if (return_code == ReplayThread::READ_ERROR) {
|
||||
stop(false);
|
||||
file_error();
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
}
|
||||
|
||||
GpsSimAppView::GpsSimAppView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
// baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&button_open,
|
||||
&text_filename,
|
||||
&text_sample_rate,
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&tx_view, // now it handles previous rfgain, rfamp.
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
this->toggle();
|
||||
};
|
||||
|
||||
button_open.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".C8");
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
on_file_changed(new_file_path);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
GpsSimAppView::~GpsSimAppView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_hide() {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
if (is_active())
|
||||
stop(false);
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
} /* namespace ui::external_app::gpssim */
|
148
firmware/application/external/gpssim/gps_sim_app.hpp
vendored
Normal file
148
firmware/application/external/gpssim/gps_sim_app.hpp
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2020 Shao
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GPS_SIM_APP_HPP__
|
||||
#define __GPS_SIM_APP_HPP__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
class GpsSimAppView : public View {
|
||||
public:
|
||||
GpsSimAppView(NavigationView& nav);
|
||||
~GpsSimAppView();
|
||||
|
||||
void on_hide() override;
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "GPS Sim TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
1575420000 /* frequency */,
|
||||
15000000 /* bandwidth */,
|
||||
2600000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_gps", app_settings::Mode::TX};
|
||||
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
const size_t read_size{16384};
|
||||
const size_t buffer_count{3};
|
||||
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
void toggle();
|
||||
void start();
|
||||
void stop(const bool do_loop);
|
||||
bool is_active() const;
|
||||
void set_ready();
|
||||
void handle_replay_thread_done(const uint32_t return_code);
|
||||
void file_error();
|
||||
|
||||
std::filesystem::path file_path{};
|
||||
std::unique_ptr<ReplayThread> replay_thread{};
|
||||
bool ready_signal{false};
|
||||
|
||||
Button button_open{
|
||||
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
|
||||
"Open file"};
|
||||
|
||||
Text text_filename{
|
||||
{11 * 8, 0 * 16, 12 * 8, 16},
|
||||
"-"};
|
||||
Text text_sample_rate{
|
||||
{24 * 8, 0 * 16, 6 * 8, 16},
|
||||
"-"};
|
||||
|
||||
Text text_duration{
|
||||
{11 * 8, 1 * 16, 6 * 8, 16},
|
||||
"-"};
|
||||
ProgressBar progressbar{
|
||||
{18 * 8, 1 * 16, 12 * 8, 16}};
|
||||
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, 2 * 16},
|
||||
nav_};
|
||||
|
||||
TransmitterView2 tx_view{
|
||||
{11 * 8, 2 * 16},
|
||||
/*short_ui*/ true};
|
||||
|
||||
Checkbox check_loop{
|
||||
{21 * 8, 2 * 16},
|
||||
4,
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
ImageButton button_play{
|
||||
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||
&bitmap_play,
|
||||
Color::green(),
|
||||
Color::black()};
|
||||
|
||||
spectrum::WaterfallView waterfall{};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_error{
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
this->handle_replay_thread_done(message.return_code);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
}
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress);
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
|
||||
#endif /*__GPS_SIM_APP_HPP__*/
|
82
firmware/application/external/gpssim/main.cpp
vendored
Normal file
82
firmware/application/external/gpssim/main.cpp
vendored
Normal 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 "gps_sim_app.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<GpsSimAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::gpssim
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_gpssim.application_information"), used)) application_information_t _application_information_gpssim = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::gpssim::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "GPSSim",
|
||||
/*.bitmap_data = */ {
|
||||
0xC0,
|
||||
0x07,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x1F,
|
||||
0x78,
|
||||
0x3E,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x30,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x70,
|
||||
0x1E,
|
||||
0x70,
|
||||
0x1F,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0xC0,
|
||||
0x07,
|
||||
0x80,
|
||||
0x03,
|
||||
0x20,
|
||||
0x09,
|
||||
0x50,
|
||||
0x14,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
82
firmware/application/external/spainter/main.cpp
vendored
Normal file
82
firmware/application/external/spainter/main.cpp
vendored
Normal 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_spectrum_painter.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<SpectrumPainterView>();
|
||||
}
|
||||
} // namespace ui::external_app::spainter
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_spainter.application_information"), used)) application_information_t _application_information_spainter = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::spainter::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "S.Painter",
|
||||
/*.bitmap_data = */ {
|
||||
0xFE,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xBF,
|
||||
0xFE,
|
||||
0xBF,
|
||||
0x00,
|
||||
0x80,
|
||||
0x80,
|
||||
0xFF,
|
||||
0x80,
|
||||
0x00,
|
||||
0x80,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_spainter */ {'P', 'S', 'P', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
202
firmware/application/external/spainter/ui_spectrum_painter.cpp
vendored
Normal file
202
firmware/application/external/spainter/ui_spectrum_painter.cpp
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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_spectrum_painter.hpp"
|
||||
|
||||
#include "bmp.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "file.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumPainterView::SpectrumPainterView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
// baseband::run_image(spi_flash::image_tag_spectrum_painter);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&tab_view,
|
||||
&input_image,
|
||||
&input_text,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&field_rfgain,
|
||||
&field_rfamp,
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&option_bandwidth,
|
||||
&field_duration,
|
||||
&field_pause,
|
||||
});
|
||||
|
||||
Rect view_rect = {0, 3 * 8, 240, 80};
|
||||
input_image.set_parent_rect(view_rect);
|
||||
input_text.set_parent_rect(view_rect);
|
||||
|
||||
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
tx_gain = transmitter_model.tx_gain();
|
||||
field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
|
||||
field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file.
|
||||
tx_gain = v;
|
||||
transmitter_model.set_tx_gain(tx_gain);
|
||||
};
|
||||
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
|
||||
field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file.
|
||||
rf_amp = (bool)v;
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
};
|
||||
|
||||
input_image.on_input_avaliable = [this]() {
|
||||
image_input_avaliable = true;
|
||||
};
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
if (tx_active == false) {
|
||||
tx_mode = tab_view.selected();
|
||||
|
||||
if (tx_mode == 0 && image_input_avaliable == false)
|
||||
return;
|
||||
/* By experimental test measurement, we got a good painted spectrum quality when selecting
|
||||
a BW GUI App range from 100k ... 2M aprox. In that range , the best TX LPF filter = 1M75 (the min)
|
||||
With this min TX LPF filter , we minimized the harmonics aliasing multipicture overlapped.
|
||||
So original fw 1.7.4., was already selecting the best setting (1M75) */
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Already tested, is the best setting for this App.
|
||||
transmitter_model.enable();
|
||||
|
||||
if (persistent_memory::stealth_mode()) {
|
||||
DisplaySleepMessage message;
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
|
||||
button_play.set_bitmap(&bitmap_stop);
|
||||
|
||||
if (tx_mode == 0) {
|
||||
tx_current_max_lines = input_image.get_height();
|
||||
tx_current_width = input_image.get_width();
|
||||
} else {
|
||||
tx_current_max_lines = input_text.get_height();
|
||||
tx_current_width = input_text.get_width();
|
||||
}
|
||||
|
||||
progressbar.set_max(tx_current_max_lines);
|
||||
progressbar.set_value(0);
|
||||
|
||||
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, false, option_bandwidth.selected_index_value());
|
||||
} else {
|
||||
stop_tx();
|
||||
}
|
||||
};
|
||||
|
||||
option_bandwidth.on_change = [this](size_t, ui::OptionsField::value_t value) {
|
||||
baseband::set_spectrum_painter_config(tx_current_width, tx_current_max_lines, true, value);
|
||||
};
|
||||
|
||||
field_duration.set_value(10);
|
||||
field_pause.set_value(5);
|
||||
}
|
||||
|
||||
void SpectrumPainterView::start_tx() {
|
||||
tx_current_line = 0;
|
||||
tx_active = true;
|
||||
tx_timestamp_start = chTimeNow();
|
||||
}
|
||||
|
||||
void SpectrumPainterView::stop_tx() {
|
||||
button_play.set_bitmap(&bitmap_play);
|
||||
transmitter_model.disable();
|
||||
tx_active = false;
|
||||
tx_current_line = 0;
|
||||
}
|
||||
|
||||
void SpectrumPainterView::frame_sync() {
|
||||
if (tx_active) {
|
||||
if (fifo->is_empty()) {
|
||||
int32_t sequence_duration = (field_duration.value() + (check_loop.value() ? field_pause.value() : 0)) * 1000;
|
||||
int32_t sequence_time = tx_time_elapsed() % sequence_duration;
|
||||
bool is_pausing = sequence_time > field_duration.value() * 1000;
|
||||
|
||||
if (is_pausing) {
|
||||
fifo->in(std::vector<uint8_t>(tx_current_width));
|
||||
} else {
|
||||
auto current_time_line = sequence_time * tx_current_max_lines / (field_duration.value() * 1000);
|
||||
|
||||
if (tx_current_line > current_time_line && !check_loop.value()) {
|
||||
fifo->in(std::vector<uint8_t>(tx_current_width));
|
||||
stop_tx();
|
||||
return;
|
||||
}
|
||||
|
||||
tx_current_line = current_time_line;
|
||||
progressbar.set_value(current_time_line);
|
||||
|
||||
if (tx_mode == 0) {
|
||||
std::vector<uint8_t> line = input_image.get_line(current_time_line);
|
||||
fifo->in(line);
|
||||
} else {
|
||||
std::vector<uint8_t> line = input_text.get_line(current_time_line);
|
||||
fifo->in(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpectrumPainterView::~SpectrumPainterView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void SpectrumPainterView::focus() {
|
||||
tab_view.focus();
|
||||
}
|
||||
|
||||
void SpectrumPainterView::paint(Painter& painter) {
|
||||
View::paint(painter);
|
||||
|
||||
size_t c;
|
||||
Point pos = {0, screen_pos().y() + 8 + footer_location};
|
||||
|
||||
for (c = 0; c < 20; c++) {
|
||||
painter.draw_bitmap(
|
||||
pos,
|
||||
bitmap_stripes,
|
||||
ui::Color(191, 191, 0),
|
||||
ui::Color::black());
|
||||
if (c != 9)
|
||||
pos += {24, 0};
|
||||
else
|
||||
pos = {0, screen_pos().y() + 8 + footer_location + 32 + 8};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::spainter
|
167
firmware/application/external/spainter/ui_spectrum_painter.hpp
vendored
Normal file
167
firmware/application/external/spainter/ui_spectrum_painter.hpp
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "capture_app.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include "ui_spectrum_painter_image.hpp"
|
||||
#include "ui_spectrum_painter_text.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumPainterView : public View {
|
||||
public:
|
||||
SpectrumPainterView(NavigationView& nav);
|
||||
~SpectrumPainterView();
|
||||
|
||||
SpectrumPainterView(const SpectrumPainterView&) = delete;
|
||||
SpectrumPainterView(SpectrumPainterView&&) = delete;
|
||||
SpectrumPainterView& operator=(const SpectrumPainterView&) = delete;
|
||||
SpectrumPainterView& operator=(SpectrumPainterView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
std::string title() const override { return "Spec.Painter"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_painter", app_settings::Mode::TX};
|
||||
|
||||
bool image_input_avaliable{false};
|
||||
bool tx_active{false};
|
||||
uint32_t tx_mode{0};
|
||||
uint16_t tx_current_line{0};
|
||||
uint16_t tx_current_max_lines{0};
|
||||
uint16_t tx_current_width{0};
|
||||
systime_t tx_timestamp_start{0};
|
||||
|
||||
inline uint32_t tx_time_elapsed() {
|
||||
auto now = chTimeNow();
|
||||
return now - tx_timestamp_start;
|
||||
}
|
||||
|
||||
int32_t tx_gain{47};
|
||||
bool rf_amp{false};
|
||||
|
||||
SpectrumInputImageView input_image{nav_};
|
||||
SpectrumInputTextView input_text{nav_};
|
||||
|
||||
std::array<View*, 2> input_views{{&input_image, &input_text}};
|
||||
|
||||
TabView tab_view{
|
||||
{"Image", Color::white(), input_views[0]},
|
||||
{"Text", Color::white(), input_views[1]}};
|
||||
|
||||
static constexpr int32_t footer_location = 15 * 16 + 8;
|
||||
ProgressBar progressbar{
|
||||
{4, footer_location - 16, 240 - 8, 16}};
|
||||
|
||||
Labels labels{
|
||||
{{10 * 8, footer_location + 1 * 16}, "GAIN A:", Color::light_grey()},
|
||||
{{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Color::light_grey()},
|
||||
};
|
||||
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, footer_location + 1 * 16},
|
||||
nav_};
|
||||
|
||||
NumberField field_rfgain{
|
||||
{14 * 8, footer_location + 1 * 16},
|
||||
2,
|
||||
{0, 47},
|
||||
1,
|
||||
' '};
|
||||
|
||||
NumberField field_rfamp{
|
||||
{19 * 8, footer_location + 1 * 16},
|
||||
2,
|
||||
{0, 14},
|
||||
14,
|
||||
' '};
|
||||
|
||||
Checkbox check_loop{
|
||||
{21 * 8, footer_location + 1 * 16},
|
||||
4,
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
|
||||
ImageButton button_play{
|
||||
{28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16},
|
||||
&bitmap_play,
|
||||
Color::green(),
|
||||
Color::black()};
|
||||
|
||||
OptionsField option_bandwidth{
|
||||
{4 * 8, footer_location + 2 * 16},
|
||||
5,
|
||||
{}};
|
||||
|
||||
NumberField field_duration{
|
||||
{13 * 8, footer_location + 2 * 16},
|
||||
3,
|
||||
{1, 999},
|
||||
1,
|
||||
' '};
|
||||
|
||||
NumberField field_pause{
|
||||
{19 * 8, footer_location + 2 * 16},
|
||||
2,
|
||||
{0, 99},
|
||||
1,
|
||||
' '};
|
||||
|
||||
SpectrumPainterFIFO* fifo{nullptr};
|
||||
void start_tx();
|
||||
void frame_sync();
|
||||
void stop_tx();
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::SpectrumPainterBufferResponseConfigure,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const SpectrumPainterBufferConfigureResponseMessage*>(p);
|
||||
this->fifo = message->fifo;
|
||||
this->start_tx();
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_sample{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->frame_sync();
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::spainter
|
257
firmware/application/external/spainter/ui_spectrum_painter_image.cpp
vendored
Normal file
257
firmware/application/external/spainter/ui_spectrum_painter_image.cpp
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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_spectrum_painter_image.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "bmp.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
|
||||
add_children({&button_load_image});
|
||||
|
||||
button_load_image.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".bmp");
|
||||
|
||||
constexpr auto data_directory = u"SPECTRUM";
|
||||
ensure_directory(data_directory);
|
||||
open_view->push_dir(data_directory);
|
||||
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
this->file = new_file_path.string();
|
||||
painted = false;
|
||||
this->set_dirty();
|
||||
this->on_input_avaliable();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
SpectrumInputImageView::~SpectrumInputImageView() {
|
||||
}
|
||||
|
||||
void SpectrumInputImageView::focus() {
|
||||
button_load_image.focus();
|
||||
}
|
||||
|
||||
bool SpectrumInputImageView::drawBMP_scaled(const ui::Rect r, const std::string file) {
|
||||
File bmpimage;
|
||||
size_t file_pos = 0;
|
||||
uint16_t pointer = 0;
|
||||
int16_t px = 0, py, zoom_factor = 0;
|
||||
bmp_header_t bmp_header;
|
||||
char buffer[257];
|
||||
ui::Color line_buffer[240];
|
||||
|
||||
auto result = bmpimage.open(file);
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
bmpimage.seek(file_pos);
|
||||
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header));
|
||||
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature
|
||||
(bmp_header.planes == 1) && // Seems always to be 1
|
||||
(bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (bmp_header.bpp) {
|
||||
case 16:
|
||||
file_pos = 0x36;
|
||||
memset(buffer, 0, 16);
|
||||
bmpimage.read(buffer, 16);
|
||||
if (buffer[1] == 0x7C)
|
||||
type = 3; // A1R5G5B5
|
||||
else
|
||||
type = 0; // R5G6B5
|
||||
break;
|
||||
case 24:
|
||||
type = 1;
|
||||
break;
|
||||
case 32:
|
||||
default:
|
||||
type = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
width = bmp_header.width;
|
||||
height = bmp_header.height;
|
||||
|
||||
data_start = file_pos = bmp_header.image_data;
|
||||
|
||||
while (r.width() < (width >> zoom_factor) || r.height() < (height >> zoom_factor)) {
|
||||
zoom_factor++;
|
||||
}
|
||||
|
||||
py = height + 16;
|
||||
|
||||
while (1) {
|
||||
while (px < width) {
|
||||
bmpimage.seek(file_pos);
|
||||
memset(buffer, 0, 257);
|
||||
read_size = bmpimage.read(buffer, 256);
|
||||
if (read_size.is_error())
|
||||
return false; // Read error
|
||||
|
||||
pointer = 0;
|
||||
while (pointer < 256) {
|
||||
if (pointer + 4 > 256)
|
||||
break;
|
||||
switch (type) {
|
||||
case 0: // R5G6B5
|
||||
if ((((1 << zoom_factor) - 1) & px) == 0x00)
|
||||
line_buffer[px >> zoom_factor] = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
|
||||
|
||||
pointer += 2;
|
||||
file_pos += 2;
|
||||
break;
|
||||
|
||||
case 3: // A1R5G5B5
|
||||
if ((((1 << zoom_factor) - 1) & px) == 0x00)
|
||||
line_buffer[px >> zoom_factor] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
|
||||
|
||||
pointer += 2;
|
||||
file_pos += 2;
|
||||
break;
|
||||
|
||||
case 1: // 24
|
||||
default:
|
||||
if ((((1 << zoom_factor) - 1) & px) == 0x00)
|
||||
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
pointer += 3;
|
||||
file_pos += 3;
|
||||
break;
|
||||
|
||||
case 2: // 32
|
||||
if ((((1 << zoom_factor) - 1) & px) == 0x00)
|
||||
line_buffer[px >> zoom_factor] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
pointer += 4;
|
||||
file_pos += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
px++;
|
||||
if (px >= width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_size.value() != 256)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((((1 << zoom_factor) - 1) & py) == 0x00)
|
||||
portapack::display.render_line({r.left(), r.top() + (py >> zoom_factor)}, px >> zoom_factor, line_buffer);
|
||||
|
||||
px = 0;
|
||||
py--;
|
||||
|
||||
if (read_size.value() < 256 || py < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t SpectrumInputImageView::get_width() {
|
||||
return this->width;
|
||||
}
|
||||
|
||||
uint16_t SpectrumInputImageView::get_height() {
|
||||
return this->height;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SpectrumInputImageView::get_line(uint16_t y) {
|
||||
File bmpimage;
|
||||
bmpimage.open(this->file);
|
||||
|
||||
// seek to line
|
||||
uint32_t line_size = width * (type == 2 ? 4 : (type == 1 ? 3 : 2));
|
||||
uint32_t line_offset = y * line_size;
|
||||
bmpimage.seek(data_start + line_offset);
|
||||
|
||||
// allocate memory and read
|
||||
auto buffer = new uint8_t[line_size];
|
||||
auto bytes_read = bmpimage.read(buffer, line_size);
|
||||
|
||||
// greyscale
|
||||
auto grey_buffer = new uint8_t[width];
|
||||
int pointer = 0;
|
||||
for (uint16_t px = 0; px < width; px++) {
|
||||
ui::Color color;
|
||||
switch (type) {
|
||||
case 0: // R5G6B5
|
||||
pointer = px * 2;
|
||||
color = ui::Color(((uint16_t)buffer[pointer] & 0x1F) | ((uint16_t)buffer[pointer] & 0xE0) << 1 | ((uint16_t)buffer[pointer + 1] & 0x7F) << 9);
|
||||
break;
|
||||
|
||||
case 3: // A1R5G5B5
|
||||
pointer = px * 2;
|
||||
color = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
|
||||
break;
|
||||
|
||||
case 1: // 24
|
||||
default:
|
||||
pointer = px * 3;
|
||||
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
break;
|
||||
|
||||
case 2: // 32
|
||||
pointer = px * 4;
|
||||
color = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
break;
|
||||
}
|
||||
|
||||
grey_buffer[px] = color.to_greyscale();
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
std::vector<uint8_t> values(width);
|
||||
for (int i = 0; i < width; i++) {
|
||||
values[i] = grey_buffer[i];
|
||||
}
|
||||
|
||||
delete[] grey_buffer;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
void SpectrumInputImageView::paint(Painter& painter) {
|
||||
painter.fill_rectangle(
|
||||
{{0, 40}, {240, 204}},
|
||||
style().background);
|
||||
|
||||
if (!painted) {
|
||||
// This is very slow for big pictures. Do only once.
|
||||
this->drawBMP_scaled({{0, 40}, {240, 160}}, this->file);
|
||||
painted = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::spainter
|
66
firmware/application/external/spainter/ui_spectrum_painter_image.hpp
vendored
Normal file
66
firmware/application/external/spainter/ui_spectrum_painter_image.hpp
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputImageView : public View {
|
||||
public:
|
||||
SpectrumInputImageView(NavigationView& nav);
|
||||
~SpectrumInputImageView();
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter&) override;
|
||||
|
||||
uint16_t get_width();
|
||||
uint16_t get_height();
|
||||
std::vector<uint8_t> get_line(uint16_t);
|
||||
|
||||
std::function<void()> on_input_avaliable{};
|
||||
|
||||
private:
|
||||
bool painted{false};
|
||||
std::string file{""};
|
||||
uint16_t width{0};
|
||||
uint16_t height{0};
|
||||
uint8_t type{0};
|
||||
uint32_t data_start{0};
|
||||
|
||||
Button button_load_image{
|
||||
{0 * 8, 11 * 16 - 4, 30 * 8, 28},
|
||||
"Load Image ..."};
|
||||
|
||||
bool drawBMP_scaled(const ui::Rect r, const std::string file);
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::spainter
|
108
firmware/application/external/spainter/ui_spectrum_painter_text.cpp
vendored
Normal file
108
firmware/application/external/spainter/ui_spectrum_painter_text.cpp
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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_spectrum_painter_text.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "bmp.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
|
||||
add_children({&text_message_0,
|
||||
&text_message_1,
|
||||
&text_message_2,
|
||||
&text_message_3,
|
||||
&text_message_4,
|
||||
&text_message_5,
|
||||
&text_message_6,
|
||||
&text_message_7,
|
||||
&text_message_8,
|
||||
&text_message_9,
|
||||
&button_message});
|
||||
|
||||
button_message.on_select = [this, &nav](Button&) {
|
||||
this->on_set_text(nav);
|
||||
};
|
||||
}
|
||||
|
||||
SpectrumInputTextView::~SpectrumInputTextView() {
|
||||
}
|
||||
|
||||
void SpectrumInputTextView::on_set_text(NavigationView& nav) {
|
||||
text_prompt(nav, buffer, 300);
|
||||
}
|
||||
|
||||
void SpectrumInputTextView::focus() {
|
||||
button_message.focus();
|
||||
}
|
||||
|
||||
void SpectrumInputTextView::paint(Painter& painter) {
|
||||
message = buffer;
|
||||
for (uint32_t i = 0; i < text_message.size(); i++) {
|
||||
if (message.length() > i * 30)
|
||||
text_message[i]->set(message.substr(i * 30, 30));
|
||||
else
|
||||
text_message[i]->set("");
|
||||
}
|
||||
|
||||
painter.fill_rectangle(
|
||||
{{0, 40}, {240, 204}},
|
||||
style().background);
|
||||
}
|
||||
|
||||
constexpr uint32_t pixel_repeat = 32;
|
||||
uint16_t SpectrumInputTextView::get_width() {
|
||||
return 16 * pixel_repeat;
|
||||
}
|
||||
|
||||
uint16_t SpectrumInputTextView::get_height() {
|
||||
return this->message.length() * 8;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
|
||||
auto character_position = y / 8;
|
||||
auto character = this->message[character_position];
|
||||
auto glyph_data = ui::font::fixed_8x16.glyph(character).pixels();
|
||||
|
||||
auto line_in_character = y % 8;
|
||||
std::vector<uint8_t> data(16 * pixel_repeat);
|
||||
|
||||
for (uint32_t index = 0; index < 16; index++) {
|
||||
auto glyph_byte = index;
|
||||
auto glyph_bit = line_in_character;
|
||||
uint8_t glyph_pixel = (glyph_data[glyph_byte] & (1 << glyph_bit)) >> glyph_bit;
|
||||
|
||||
for (uint32_t j = 0; j < pixel_repeat; j++)
|
||||
data[index * pixel_repeat + j] = glyph_pixel * 255;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
} // namespace ui::external_app::spainter
|
112
firmware/application/external/spainter/ui_spectrum_painter_text.hpp
vendored
Normal file
112
firmware/application/external/spainter/ui_spectrum_painter_text.hpp
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputTextView : public View {
|
||||
public:
|
||||
SpectrumInputTextView(NavigationView& nav);
|
||||
~SpectrumInputTextView();
|
||||
|
||||
void focus() override;
|
||||
void paint(Painter&) override;
|
||||
|
||||
uint16_t get_width();
|
||||
uint16_t get_height();
|
||||
std::vector<uint8_t> get_line(uint16_t);
|
||||
|
||||
private:
|
||||
std::string buffer{"PORTAPACK"};
|
||||
std::string message{};
|
||||
void on_set_text(NavigationView& nav);
|
||||
|
||||
Text text_message_0{
|
||||
{0 * 8, 0 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_1{
|
||||
{0 * 8, 1 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_2{
|
||||
{0 * 8, 2 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_3{
|
||||
{0 * 8, 3 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_4{
|
||||
{0 * 8, 4 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_5{
|
||||
{0 * 8, 5 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_6{
|
||||
{0 * 8, 6 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_7{
|
||||
{0 * 8, 7 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_8{
|
||||
{0 * 8, 8 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
Text text_message_9{
|
||||
{0 * 8, 9 * 16, 30 * 8, 16},
|
||||
""};
|
||||
|
||||
std::array<Text*, 10> text_message{{
|
||||
&text_message_0,
|
||||
&text_message_1,
|
||||
&text_message_2,
|
||||
&text_message_3,
|
||||
&text_message_4,
|
||||
&text_message_5,
|
||||
&text_message_6,
|
||||
&text_message_7,
|
||||
&text_message_8,
|
||||
&text_message_9,
|
||||
}};
|
||||
|
||||
Button button_message{
|
||||
{0 * 8, 11 * 16 - 4, 30 * 8, 28},
|
||||
"Set message"};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::spainter
|
Reference in New Issue
Block a user