mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2024-12-04 23:45:26 +00:00
parent
a27881ecd6
commit
b4da86d491
@ -281,6 +281,9 @@ set(CPPSRC
|
||||
apps/ui_level.cpp
|
||||
apps/tpms_app.cpp
|
||||
apps/tpms_app.cpp
|
||||
apps/ui_spectrum_painter.cpp
|
||||
apps/ui_spectrum_painter_image.cpp
|
||||
apps/ui_spectrum_painter_text.cpp
|
||||
protocols/aprs.cpp
|
||||
protocols/ax25.cpp
|
||||
protocols/bht.cpp
|
||||
|
@ -29,6 +29,23 @@
|
||||
#include "ui_record_view.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#define BW_OPTIONS \
|
||||
{ " 8k5", 8500 }, \
|
||||
{ " 11k", 11000 }, \
|
||||
{ " 16k", 16000 }, \
|
||||
{ " 25k", 25000 }, \
|
||||
{ " 50k", 50000 }, \
|
||||
{ " 100k", 100000 }, \
|
||||
{ " 250k", 250000 }, \
|
||||
{ " 500k", 500000 }, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
|
||||
{ " 600k", 600000 }, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
|
||||
{ " 750k", 750000 }, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \
|
||||
{ "1100k", 1100000 }, \
|
||||
{ "1750k", 1750000 }, \
|
||||
{ "2000k", 2000000 }, \
|
||||
{ "2500k", 2500000 }, \
|
||||
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
|
||||
|
||||
namespace ui {
|
||||
|
||||
class CaptureAppView : public View {
|
||||
@ -88,21 +105,7 @@ private:
|
||||
{ 5 * 8, 1 * 16 },
|
||||
5,
|
||||
{
|
||||
{ " 8k5", 8500 },
|
||||
{ " 11k ", 11000 },
|
||||
{ " 16k ", 16000 },
|
||||
{ " 25k ", 25000 },
|
||||
{ " 50k ", 50000 },
|
||||
{ "100k ", 100000 },
|
||||
{ "250k ", 250000 },
|
||||
{ "500k ", 500000 }, // Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.
|
||||
{ "600k ", 600000 }, // That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec)
|
||||
{ "750k ", 750000 }, // From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size)
|
||||
{ "1100k", 1100000 },
|
||||
{ "1750k", 1750000 },
|
||||
{ "2000k", 2000000 },
|
||||
{ "2500k", 2500000 },
|
||||
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
|
||||
BW_OPTIONS
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "Fileman"; };
|
||||
void push_dir(const std::filesystem::path& path);
|
||||
|
||||
protected:
|
||||
static constexpr size_t max_filename_length = 64;
|
||||
@ -83,7 +84,6 @@ protected:
|
||||
std::filesystem::path get_selected_full_path() const;
|
||||
const fileman_entry& get_selected_entry() const;
|
||||
|
||||
void push_dir(const std::filesystem::path& path);
|
||||
void pop_dir();
|
||||
void refresh_list();
|
||||
void reload_current();
|
||||
|
233
firmware/application/apps/ui_spectrum_painter.cpp
Normal file
233
firmware/application/apps/ui_spectrum_painter.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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 "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 {
|
||||
|
||||
SpectrumPainterView::SpectrumPainterView(
|
||||
NavigationView& nav
|
||||
) : nav_ (nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_spectrum_painter);
|
||||
|
||||
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);
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(5000);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
tx_gain = 10;
|
||||
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;
|
||||
portapack::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;
|
||||
portapack::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;
|
||||
|
||||
//Enable Bias Tee if selected
|
||||
radio::set_antenna_bias(portapack::get_antenna_bias());
|
||||
|
||||
radio::enable({
|
||||
portapack::receiver_model.tuning_frequency(),
|
||||
3072000U,
|
||||
1750000,
|
||||
rf::Direction::Transmit,
|
||||
rf_amp,
|
||||
static_cast<int8_t>(portapack::receiver_model.lna()),
|
||||
static_cast<int8_t>(portapack::receiver_model.vga())
|
||||
});
|
||||
|
||||
if (portapack::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);
|
||||
portapack::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() {
|
||||
portapack::transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void SpectrumPainterView::focus() {
|
||||
tab_view.focus();
|
||||
}
|
||||
|
||||
void SpectrumPainterView::on_target_frequency_changed(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
}
|
||||
|
||||
void SpectrumPainterView::set_target_frequency(const rf::Frequency new_value) {
|
||||
portapack::persistent_memory::set_tuned_frequency(new_value);
|
||||
}
|
||||
|
||||
rf::Frequency SpectrumPainterView::target_frequency() const {
|
||||
return portapack::persistent_memory::tuned_frequency();
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
177
firmware/application/apps/ui_spectrum_painter.hpp
Normal file
177
firmware/application/apps/ui_spectrum_painter.hpp
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 "capture_app.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include "ui_spectrum_painter_image.hpp"
|
||||
#include "ui_spectrum_painter_text.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
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:
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
rf::Frequency target_frequency() const;
|
||||
|
||||
NavigationView& nav_;
|
||||
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() },
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0 * 8, footer_location + 1 * 16 },
|
||||
};
|
||||
|
||||
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,
|
||||
"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,
|
||||
{
|
||||
BW_OPTIONS
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
265
firmware/application/apps/ui_spectrum_painter_image.cpp
Normal file
265
firmware/application/apps/ui_spectrum_painter_image.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
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";
|
||||
if (std::filesystem::is_directory(data_directory) == false) {
|
||||
if (make_new_directory(data_directory).ok())
|
||||
open_view->push_dir(data_directory);
|
||||
}
|
||||
else
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
67
firmware/application/apps/ui_spectrum_painter_image.hpp
Normal file
67
firmware/application/apps/ui_spectrum_painter_image.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
111
firmware/application/apps/ui_spectrum_painter_text.cpp
Normal file
111
firmware/application/apps/ui_spectrum_painter_text.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 "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"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
124
firmware/application/apps/ui_spectrum_painter_text.hpp
Normal file
124
firmware/application/apps/ui_spectrum_painter_text.hpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -299,6 +299,11 @@ void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t d
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw){
|
||||
SpectrumPainterBufferConfigureRequestMessage message { width, height, update, bw };
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
static bool baseband_image_running = false;
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag) {
|
||||
|
@ -91,6 +91,7 @@ void set_rds_data(const uint16_t message_length);
|
||||
void set_spectrum(const size_t sampling_rate, const size_t trigger);
|
||||
void set_siggen_tone(const uint32_t tone);
|
||||
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
|
||||
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
|
||||
void request_beep();
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "ui_whipcalc.hpp"
|
||||
#include "ui_flash_utility.hpp"
|
||||
#include "ui_sd_over_usb.hpp"
|
||||
#include "ui_spectrum_painter.hpp"
|
||||
|
||||
//#include "acars_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
@ -596,6 +597,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{ "TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push<LCRView>(); } },
|
||||
{ "TouchTune", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push<TouchTunesView>(); } },
|
||||
{ "Playlist", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push<PlaylistView>(); } },
|
||||
{ "S.Painter", ui::Color::orange(), &bitmap_icon_morse, [&nav](){ nav.push<SpectrumPainterView>(); } },
|
||||
//{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } },
|
||||
});
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ set(CPPSRC
|
||||
debug.cpp
|
||||
${COMMON}/gcc.cpp
|
||||
${COMMON}/performance_counter.cpp
|
||||
${COMMON}/random.cpp
|
||||
tone_gen.cpp
|
||||
)
|
||||
|
||||
@ -463,6 +464,13 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PSIG siggen)
|
||||
|
||||
### Spectrum painter
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_spectrum_painter.cpp
|
||||
)
|
||||
DeclareTargets(PSPT spectrum_painter)
|
||||
|
||||
### SSTV TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
|
178
firmware/baseband/proc_spectrum_painter.cpp
Normal file
178
firmware/baseband/proc_spectrum_painter.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 "proc_spectrum_painter.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "dsp_fft.hpp"
|
||||
#include "random.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// TODO move to class members SpectrumPainterProcessor
|
||||
complex16_t *current_line_data = nullptr;
|
||||
complex16_t * volatile next_line_data = nullptr;
|
||||
uint32_t current_line_index = 0;
|
||||
uint32_t current_line_width = 0;
|
||||
int32_t current_bw = 0;
|
||||
|
||||
std::vector<uint8_t> fifo_data[1 << SpectrumPainterBufferConfigureResponseMessage::fifo_k] { };
|
||||
SpectrumPainterFIFO fifo { fifo_data, SpectrumPainterBufferConfigureResponseMessage::fifo_k };
|
||||
|
||||
int max_val = 127;
|
||||
|
||||
// This is called at 3072000/2048 = 1500Hz
|
||||
void SpectrumPainterProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
for (uint32_t i = 0; i < buffer.count; i++) {
|
||||
if (current_line_data != nullptr) {
|
||||
auto data = current_line_data[(current_line_index++ * current_bw / 3072 ) % current_line_width];
|
||||
buffer.p[i] = {(int8_t) data.real(), (int8_t) data.imag()};
|
||||
}
|
||||
else
|
||||
buffer.p[i] = {0, 0};
|
||||
}
|
||||
|
||||
// collect new data
|
||||
if (next_line_data != nullptr) {
|
||||
if (current_line_data != nullptr)
|
||||
delete current_line_data;
|
||||
|
||||
current_line_data = next_line_data;
|
||||
next_line_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
WORKING_AREA(thread_wa, 4096);
|
||||
|
||||
void SpectrumPainterProcessor::run() {
|
||||
int ui = 0;
|
||||
init_genrand(22267);
|
||||
|
||||
while (true) {
|
||||
if (fifo.is_empty() == false && next_line_data == nullptr) {
|
||||
std::vector<uint8_t> data;
|
||||
fifo.out(data);
|
||||
|
||||
auto picture_width = data.size();
|
||||
|
||||
auto fft_width = picture_width * 2;
|
||||
auto qu = fft_width/4;
|
||||
|
||||
complex16_t *v = new complex16_t[fft_width];
|
||||
complex16_t *tmp = new complex16_t[fft_width];
|
||||
|
||||
for (uint32_t fft_index = 0; fft_index < fft_width; fft_index++) {
|
||||
if (fft_index < qu) {
|
||||
}
|
||||
else if (fft_index < qu*3) {
|
||||
//TODO: Improve index handling
|
||||
auto image_index = fft_index-qu;
|
||||
|
||||
auto bin_power = data[image_index]; // 0 to 255
|
||||
auto bin_phase = genrand_int31(); // 0 to 255
|
||||
|
||||
// rotate by random angle
|
||||
auto phase_cos = (sine_table_i8[((int)(bin_phase + 0x40)) & 0xFF]); // -127 to 127
|
||||
auto phase_sin = (sine_table_i8[((int)(bin_phase)) & 0xFF]);
|
||||
|
||||
auto real = (int16_t)((int16_t)phase_cos * bin_power / 255); // -127 to 127
|
||||
auto imag = (int16_t)((int16_t)phase_sin * bin_power / 255); // -127 to 127
|
||||
|
||||
auto fftshift_index = 0;
|
||||
if (fft_index < qu*2) // first half (fft_index = qu; fft_index < qu*2)
|
||||
fftshift_index = fft_index + 2*qu; // goes to back
|
||||
else // 2nd half (fft_index = qu*2; fft_index < qu*3)
|
||||
fftshift_index = fft_index - 2*qu; // goes to front
|
||||
|
||||
v[fftshift_index] = {real, imag};
|
||||
}
|
||||
}
|
||||
|
||||
ifft<complex16_t>(v, fft_width, tmp);
|
||||
|
||||
// normalize
|
||||
volatile int32_t maximum = 1;
|
||||
for (uint32_t i = 0; i < fft_width; i++) {
|
||||
if (v[i].real() > maximum)
|
||||
maximum = v[i].real();
|
||||
if (v[i].real() < -maximum)
|
||||
maximum = -v[i].real();
|
||||
if (v[i].imag() > maximum)
|
||||
maximum = v[i].imag();
|
||||
if (v[i].imag() < -maximum)
|
||||
maximum = -v[i].imag();
|
||||
}
|
||||
|
||||
if (maximum == 1) { // a black line
|
||||
for (uint32_t i = 0; i < fft_width; i++)
|
||||
v[i] = {0, 0};
|
||||
}
|
||||
else {
|
||||
for (uint32_t i = 0; i < fft_width; i++) {
|
||||
v[i] = { (int8_t)((int32_t)v[i].real() * 120 / maximum), (int8_t)((int32_t)v[i].imag() * 120 / maximum)};
|
||||
}
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
next_line_data = v;
|
||||
ui++;
|
||||
}
|
||||
else {
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumPainterProcessor::on_message(const Message* const msg) {
|
||||
|
||||
switch(msg->id) {
|
||||
case Message::ID::SpectrumPainterBufferRequestConfigure:
|
||||
{
|
||||
const auto message = *reinterpret_cast<const SpectrumPainterBufferConfigureRequestMessage*>(msg);
|
||||
current_line_width = message.width;
|
||||
current_bw = message.bw / 500;
|
||||
|
||||
if (message.update == false) {
|
||||
SpectrumPainterBufferConfigureResponseMessage response { &fifo };
|
||||
shared_memory.application_queue.push(response);
|
||||
|
||||
if (configured == false) {
|
||||
thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
|
||||
NORMALPRIO, SpectrumPainterProcessor::fn,
|
||||
this
|
||||
);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<SpectrumPainterProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
46
firmware/baseband/proc_spectrum_painter.hpp
Normal file
46
firmware/baseband/proc_spectrum_painter.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "portapack_shared_memory.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
class SpectrumPainterProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const p) override;
|
||||
void run();
|
||||
|
||||
private:
|
||||
bool configured { false };
|
||||
BasebandThread baseband_thread { 3072000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
Thread* thread {nullptr};
|
||||
|
||||
protected:
|
||||
static msg_t fn(void* arg) {
|
||||
auto obj = static_cast<SpectrumPainterProcessor*>(arg);
|
||||
obj->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
@ -20,3 +20,4 @@
|
||||
*/
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
#include "complex.hpp"
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "complex.hpp"
|
||||
#include "hal.h"
|
||||
#include "utility.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
|
||||
namespace std {
|
||||
/* https://github.com/AE9RB/fftbench/blob/master/cxlr.hpp
|
||||
@ -135,4 +136,45 @@ void fft_c_preswapped(std::array<T, N>& data, const size_t from, const size_t to
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ifft(v,N):
|
||||
[0] If N==1 then return.
|
||||
[1] For k = 0 to N/2-1, let ve[k] = v[2*k]
|
||||
[2] Compute ifft(ve, N/2);
|
||||
[3] For k = 0 to N/2-1, let vo[k] = v[2*k+1]
|
||||
[4] Compute ifft(vo, N/2);
|
||||
[5] For m = 0 to N/2-1, do [6] through [9]
|
||||
[6] Let w.real() = cos(2*PI*m/N)
|
||||
[7] Let w.imag() = sin(2*PI*m/N)
|
||||
[8] Let v[m] = ve[m] + w*vo[m]
|
||||
[9] Let v[m+N/2] = ve[m] - w*vo[m]
|
||||
*/
|
||||
template<typename T>
|
||||
void ifft( T *v, int n, T *tmp )
|
||||
{
|
||||
if(n>1) {
|
||||
int k,m;
|
||||
T z, w, *vo, *ve;
|
||||
ve = tmp; vo = tmp+n/2;
|
||||
for(k=0; k<n/2; k++) {
|
||||
ve[k] = v[2*k];
|
||||
vo[k] = v[2*k+1];
|
||||
}
|
||||
ifft( ve, n/2, v ); /* FFT on even-indexed elements of v[] */
|
||||
ifft( vo, n/2, v ); /* FFT on odd-indexed elements of v[] */
|
||||
for(m=0; m<n/2; m++) {
|
||||
w.real(sine_table_i8[((int)(m/(double)n * 0x100 + 0x40)) & 0xFF]);
|
||||
w.imag(sine_table_i8[((int)(m/(double)n * 0x100)) & 0xFF]);
|
||||
|
||||
z.real((w.real()*vo[m].real() - w.imag()*vo[m].imag())/127); /* Re(w*vo[m]) */
|
||||
z.imag((w.real()*vo[m].imag() + w.imag()*vo[m].real())/127); /* Im(w*vo[m]) */
|
||||
v[ m ].real(ve[m].real() + z.real());
|
||||
v[ m ].imag(ve[m].imag() + z.imag());
|
||||
v[m+n/2].real(ve[m].real() - z.real());
|
||||
v[m+n/2].imag(ve[m].imag() - z.imag());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endif/*__DSP_FFT_H__*/
|
||||
|
@ -489,8 +489,8 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::string file) {
|
||||
if(pointer + 4 > 256)
|
||||
break;
|
||||
switch(type) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 0: // R5G6B5
|
||||
case 3: // A1R5G5B5
|
||||
if(!type)
|
||||
line_buffer[px] = ui::Color((uint16_t)buffer[pointer] | ((uint16_t)buffer[pointer + 1] << 8));
|
||||
else
|
||||
@ -498,13 +498,13 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::string file) {
|
||||
pointer += 2;
|
||||
file_pos += 2;
|
||||
break;
|
||||
case 1:
|
||||
case 1: // 24
|
||||
default:
|
||||
line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
pointer += 3;
|
||||
file_pos += 3;
|
||||
break;
|
||||
case 2:
|
||||
case 2: // 32
|
||||
line_buffer[px] = ui::Color(buffer[pointer + 2], buffer[pointer + 1], buffer[pointer]);
|
||||
pointer += 4;
|
||||
file_pos += 4;
|
||||
|
@ -109,6 +109,8 @@ public:
|
||||
AudioSpectrum = 52,
|
||||
APRSPacket = 53,
|
||||
APRSRxConfigure = 54,
|
||||
SpectrumPainterBufferRequestConfigure = 55,
|
||||
SpectrumPainterBufferResponseConfigure = 56,
|
||||
MAX
|
||||
};
|
||||
|
||||
@ -1143,4 +1145,40 @@ public:
|
||||
uint32_t return_code;
|
||||
};
|
||||
|
||||
class SpectrumPainterBufferConfigureRequestMessage : public Message {
|
||||
public:
|
||||
constexpr SpectrumPainterBufferConfigureRequestMessage(
|
||||
uint16_t width,
|
||||
uint16_t height,
|
||||
bool update,
|
||||
int32_t bw
|
||||
) : Message { ID::SpectrumPainterBufferRequestConfigure },
|
||||
width { width },
|
||||
height { height },
|
||||
update { update },
|
||||
bw { bw }
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
bool update;
|
||||
int32_t bw;
|
||||
};
|
||||
|
||||
using SpectrumPainterFIFO = FIFO<std::vector<uint8_t>>;
|
||||
class SpectrumPainterBufferConfigureResponseMessage : public Message {
|
||||
public:
|
||||
static constexpr size_t fifo_k = 2;
|
||||
|
||||
constexpr SpectrumPainterBufferConfigureResponseMessage(
|
||||
SpectrumPainterFIFO* fifo
|
||||
) : Message { ID::SpectrumPainterBufferResponseConfigure },
|
||||
fifo { fifo }
|
||||
{
|
||||
}
|
||||
|
||||
SpectrumPainterFIFO* fifo { nullptr };
|
||||
};
|
||||
|
||||
#endif/*__MESSAGE_H__*/
|
||||
|
@ -68,7 +68,7 @@ struct SharedMemory {
|
||||
uint8_t volatile request_m4_performance_counter{ 0 };
|
||||
uint8_t volatile m4_cpu_usage{ 0 };
|
||||
uint16_t volatile m4_stack_usage{ 0 };
|
||||
uint16_t volatile m4_heap_usage{ 0 };
|
||||
uint32_t volatile m4_heap_usage{ 0 };
|
||||
uint16_t volatile m4_buffer_missed{ 0 };
|
||||
};
|
||||
|
||||
|
86
firmware/common/random.cpp
Normal file
86
firmware/common/random.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 "random.hpp"
|
||||
|
||||
static unsigned long state[N]; /* the array for the state vector */
|
||||
static int rnd_left = 1;
|
||||
static int rnd_init = 0;
|
||||
static unsigned long *rnd_next;
|
||||
|
||||
/* initializes state[N] with a seed */
|
||||
void init_genrand(unsigned long s)
|
||||
{
|
||||
int j;
|
||||
state[0] = s & 0xffffffffUL;
|
||||
|
||||
for (j = 1; j < N; j++)
|
||||
{
|
||||
state[j] = (1812433253UL * (state[j - 1] ^ (state[j - 1] >> 30)) + j);
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array state[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
state[j] &= 0xffffffffUL; /* for >32 bit machines */
|
||||
}
|
||||
|
||||
rnd_left = 1;
|
||||
rnd_init = 1;
|
||||
}
|
||||
|
||||
static void next_state(void)
|
||||
{
|
||||
unsigned long *p = state;
|
||||
int j;
|
||||
|
||||
/* if init_genrand() has not been called, */
|
||||
/* a default initial seed is used */
|
||||
if (rnd_init == 0)
|
||||
init_genrand(5489UL);
|
||||
|
||||
rnd_left = N;
|
||||
rnd_next = state;
|
||||
|
||||
for (j = N - M + 1; --j; p++)
|
||||
*p = p[M] ^ TWIST(p[0], p[1]);
|
||||
|
||||
for (j = M; --j; p++)
|
||||
*p = p[M - N] ^ TWIST(p[0], p[1]);
|
||||
|
||||
*p = p[M - N] ^ TWIST(p[0], state[0]);
|
||||
}
|
||||
|
||||
long genrand_int31(void)
|
||||
{
|
||||
unsigned long y;
|
||||
|
||||
if (--rnd_left == 0)
|
||||
next_state();
|
||||
y = *rnd_next++;
|
||||
|
||||
/* Tempering */
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680UL;
|
||||
y ^= (y << 15) & 0xefc60000UL;
|
||||
y ^= (y >> 18);
|
||||
|
||||
return (long)(y >> 1);
|
||||
}
|
35
firmware/common/random.hpp
Normal file
35
firmware/common/random.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/* Period parameters */
|
||||
#define N 624
|
||||
#define M 397
|
||||
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
|
||||
#define UMASK 0x80000000UL /* most significant w-r bits */
|
||||
#define LMASK 0x7fffffffUL /* least significant r bits */
|
||||
#define MIXBITS(u, v) (((u)&UMASK) | ((v)&LMASK))
|
||||
#define TWIST(u, v) ((MIXBITS(u, v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
|
||||
|
||||
/* initializes state[N] with a seed */
|
||||
extern void init_genrand(unsigned long s);
|
||||
extern long genrand_int31(void);
|
@ -104,6 +104,7 @@ constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_replay { 'P', 'R', 'E', 'P' };
|
||||
constexpr image_tag_t image_tag_gps { 'P', 'G', 'P', 'S' };
|
||||
constexpr image_tag_t image_tag_siggen { 'P', 'S', 'I', 'G' };
|
||||
constexpr image_tag_t image_tag_spectrum_painter { 'P', 'S', 'P', 'T' };
|
||||
constexpr image_tag_t image_tag_sstv_tx { 'P', 'S', 'T', 'X' };
|
||||
constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
|
||||
constexpr image_tag_t image_tag_flash_utility { 'P', 'F', 'U', 'T' };
|
||||
|
@ -57,6 +57,16 @@ struct Color {
|
||||
)}
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t to_greyscale() {
|
||||
uint32_t r = ((v >> 11) & 31U) << 3;
|
||||
uint32_t g = ((v >> 5) & 63U) << 2;
|
||||
uint32_t b = (v & 31U) << 3;
|
||||
|
||||
uint32_t grey = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
|
||||
return (uint8_t)grey;
|
||||
}
|
||||
|
||||
Color operator-() const {
|
||||
return (v ^ 0xffff);
|
||||
|
Loading…
Reference in New Issue
Block a user