Replay of IQ files ! :D

Added icons and colors for commonly used files in Fileman
Fileman can filter by file extension
Bugfix: Fileman doesn't crash anymore on renaming long file names
Updated binary
This commit is contained in:
furrtek 2017-12-07 00:58:25 +00:00
parent 3221992ad1
commit b38adf3769
23 changed files with 403 additions and 445 deletions

View File

@ -195,7 +195,7 @@ set(CPPSRC
ui_rds.cpp ui_rds.cpp
ui_receiver.cpp ui_receiver.cpp
ui_record_view.cpp ui_record_view.cpp
ui_replay_view.cpp # ui_replay_view.cpp
ui_rssi.cpp ui_rssi.cpp
ui_scanner.cpp ui_scanner.cpp
# ui_script.cpp # ui_script.cpp

View File

@ -367,6 +367,28 @@ static constexpr Bitmap bitmap_record {
{ 16, 16 }, bitmap_record_data { 16, 16 }, bitmap_record_data
}; };
static constexpr uint8_t bitmap_icon_file_text_data[] = {
0x00, 0x20,
0x00, 0x30,
0x00, 0x38,
0x00, 0x38,
0x00, 0x34,
0x00, 0x32,
0x00, 0x31,
0x80, 0x30,
0xC0, 0x30,
0xE0, 0x3F,
0x30, 0x30,
0x18, 0x30,
0x0C, 0x30,
0x0E, 0x78,
0x1F, 0xFC,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_file_text {
{ 16, 16 }, bitmap_icon_file_text_data
};
static constexpr uint8_t bitmap_icon_ais_data[] = { static constexpr uint8_t bitmap_icon_ais_data[] = {
0x00, 0x01, 0x00, 0x01,
0x80, 0x01, 0x80, 0x01,
@ -449,6 +471,28 @@ static constexpr Bitmap bitmap_icon_nuoptix {
{ 16, 16 }, bitmap_icon_nuoptix_data { 16, 16 }, bitmap_icon_nuoptix_data
}; };
static constexpr uint8_t bitmap_icon_file_iq_data[] = {
0x98, 0x00,
0x24, 0x06,
0xA4, 0x08,
0x34, 0x10,
0xB8, 0x20,
0x20, 0x20,
0x80, 0x00,
0xD5, 0x55,
0x80, 0x00,
0x02, 0x70,
0x82, 0x20,
0x04, 0x20,
0x88, 0x20,
0x30, 0x70,
0x80, 0x00,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_file_iq {
{ 16, 16 }, bitmap_icon_file_iq_data
};
static constexpr uint8_t bitmap_icon_closecall_data[] = { static constexpr uint8_t bitmap_icon_closecall_data[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x10,
@ -1751,6 +1795,28 @@ static constexpr Bitmap bitmap_bulb_off {
{ 24, 24 }, bitmap_bulb_off_data { 24, 24 }, bitmap_bulb_off_data
}; };
static constexpr uint8_t bitmap_icon_file_image_data[] = {
0x00, 0x00,
0xFF, 0xFF,
0x01, 0x80,
0x01, 0x80,
0x89, 0x80,
0xC1, 0x81,
0xE1, 0xA3,
0xB1, 0xB3,
0x89, 0xDC,
0x07, 0x8C,
0x01, 0x90,
0x01, 0x80,
0xAB, 0x82,
0xFF, 0xD5,
0xFF, 0xFF,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_file_image {
{ 16, 16 }, bitmap_icon_file_image_data
};
} /* namespace ui */ } /* namespace ui */

View File

@ -233,13 +233,14 @@ space_info space(const path& p);
} /* namespace filesystem */ } /* namespace filesystem */
} /* namespace std */ } /* namespace std */
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension);
std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory);
std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern);
void delete_file(const std::filesystem::path& file_path); void delete_file(const std::filesystem::path& file_path);
void rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name); void rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
uint32_t make_new_directory(const std::filesystem::path& dir_path); uint32_t make_new_directory(const std::filesystem::path& dir_path);
std::vector<std::filesystem::path> scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension);
std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::path& directory);
std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern);
/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */ /* Values added to FatFs FRESULT enum, values outside the FRESULT data type */
static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected."); static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected.");

View File

@ -27,13 +27,14 @@
//TEST: Check AFSK transmit end, skips last bits ? //TEST: Check AFSK transmit end, skips last bits ?
//TEST: Imperial in whipcalc //TEST: Imperial in whipcalc
//BUG: Crash on rename file with long filename
//BUG: Auto backlight off doesn't work anymore //BUG: Auto backlight off doesn't work anymore
//BUG: CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay //BUG: (Workaround ok) CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay
//BUG: REPLAY See what's wrong with quality (format, or need for interpolation filter ?)
//BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one //BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one
//BUG: SCANNER Multiple slices //BUG: SCANNER Multiple slices
//TODO: Display file creation/modification date in FileLoadView
//TODO: Display recording frequency in Replay (from associated .txt file, if present)
//TODO: Clean up ReplayThread
//TODO: Cap Wav viewer position //TODO: Cap Wav viewer position
//TODO: Adapt wav viewer position step //TODO: Adapt wav viewer position step
//TODO: Use unit_auto_scale //TODO: Use unit_auto_scale
@ -80,10 +81,7 @@ Continuous (Fox-oring)
// Old or low-priority stuff: // Old or low-priority stuff:
//TODO: Bodet :) //TODO: Bodet :)
//TODO: Analog TV tx with camcorder font character generator //TODO: Analog TV tx with camcorder font character generator
//TODO: Show address/data bit fields in OOK TX
//TODO: Scan for OOK TX //TODO: Scan for OOK TX
//TODO: Script engine ?
//TODO: AFSK receiver
//TODO: Check more OOK encoders //TODO: Check more OOK encoders
//BUG (fixed ?): No audio in about when shown second time //BUG (fixed ?): No audio in about when shown second time
//TODO: Show MD5 mismatches for modules not found, etc... //TODO: Show MD5 mismatches for modules not found, etc...

View File

@ -23,6 +23,9 @@
#include "replay_app.hpp" #include "replay_app.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
@ -31,31 +34,132 @@ using namespace portapack;
namespace ui { namespace ui {
void ReplayAppView::set_ready() {
ready_signal = true;
}
void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) {
File bbd_file;
std::string str_duration = "";
file_path = new_file_path;
text_filename.set(new_file_path.string().substr(0, 18));
bbd_file.open("/" + new_file_path.string());
auto file_size = bbd_file.size();
auto duration = file_size / (2 * 2 * sampling_rate / 8);
progressbar.set_max(file_size);
if (duration >= 60)
str_duration = to_string_dec_uint(duration / 60) + "m";
text_duration.set(str_duration + to_string_dec_uint(duration % 60) + "s");
button_play.focus();
}
void ReplayAppView::on_tx_progress(const uint32_t progress) {
progressbar.set_value(progress);
}
void ReplayAppView::focus() {
button_open.focus();
}
bool ReplayAppView::is_active() const {
return (bool)replay_thread;
}
void ReplayAppView::toggle() {
if( is_active() ) {
stop();
} else {
start();
}
}
void ReplayAppView::start() {
stop();
std::unique_ptr<stream::Reader> reader;
auto p = std::make_unique<FileReader>();
auto open_error = p->open(file_path);
if( open_error.is_valid() ) {
handle_error(open_error.value());
} 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,
[]() {
ReplayThreadDoneMessage message { };
EventDispatcher::send_message(message);
},
[](File::Error error) {
ReplayThreadDoneMessage message { error.code() };
EventDispatcher::send_message(message);
}
);
}
radio::enable({
receiver_model.tuning_frequency(),
sampling_rate,
baseband_bandwidth,
rf::Direction::Transmit,
receiver_model.rf_amp(),
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga())
});
}
void ReplayAppView::stop() {
if( is_active() )
replay_thread.reset();
progressbar.set_value(0);
radio::disable();
button_play.set_bitmap(&bitmap_play);
}
void ReplayAppView::handle_replay_thread_done(const File::Error error) {
stop();
if( error.code() ) {
handle_error(error);
}
}
void ReplayAppView::handle_error(const File::Error error) {
nav_.display_modal("Error", error.what());
}
ReplayAppView::ReplayAppView( ReplayAppView::ReplayAppView(
NavigationView& nav NavigationView& nav
) : nav_ (nav) ) : nav_ (nav)
{ {
std::vector<std::filesystem::path> file_list;
// Search for files with the right extension
file_list = scan_root_files(u"/", u"*.C16");
if (!file_list.size()) {
file_error = true;
return;
}
baseband::run_image(portapack::spi_flash::image_tag_replay); baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({ add_children({
&field_frequency, &field_frequency,
&field_frequency_step, &field_frequency_step,
&field_rf_amp, &field_rf_amp,
&replay_view, &button_play,
&text_filename,
&text_duration,
&progressbar,
&button_open,
&waterfall, &waterfall,
}); });
replay_view.set_file_list(file_list);
field_frequency.set_value(target_frequency()); field_frequency.set_value(target_frequency());
field_frequency.set_step(receiver_model.frequency_step()); field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) { field_frequency.on_change = [this](rf::Frequency f) {
@ -75,9 +179,16 @@ ReplayAppView::ReplayAppView(
receiver_model.set_frequency_step(v); receiver_model.set_frequency_step(v);
this->field_frequency.set_step(v); this->field_frequency.set_step(v);
}; };
replay_view.on_error = [&nav](std::string message) { button_play.on_select = [this](ImageButton&) {
nav.display_modal("Error", message); this->toggle();
};
button_open.on_select = [this, &nav](Button&) {
auto new_view = nav.push<FileLoadView>(".C16");
new_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
};
}; };
} }
@ -100,13 +211,6 @@ void ReplayAppView::set_parent_rect(const Rect new_parent_rect) {
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
void ReplayAppView::focus() {
if (!file_error) {
field_frequency.focus();
} else
nav_.display_modal("No files", "No .C16 files in\nSD card root", ABORT, nullptr);
}
void ReplayAppView::on_target_frequency_changed(rf::Frequency f) { void ReplayAppView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f); set_target_frequency(f);
} }

View File

@ -26,7 +26,7 @@
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_replay_view.hpp" #include "replay_thread.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include <string> #include <string>
@ -40,24 +40,40 @@ public:
~ReplayAppView(); ~ReplayAppView();
void on_hide() override; void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override; void set_parent_rect(const Rect new_parent_rect) override;
void focus() override; void focus() override;
std::string title() const override { return "Replay (BETA)"; }; std::string title() const override { return "Replay"; };
void start();
void stop();
bool is_active() const;
private: private:
NavigationView& nav_; NavigationView& nav_;
bool file_error { false }; static constexpr ui::Dim header_height = 3 * 16;
static constexpr ui::Dim header_height = 2 * 16; static constexpr uint32_t sampling_rate = 4000000;
static constexpr uint32_t baseband_bandwidth = 2500000;
const size_t read_size { 16384 };
const size_t buffer_count { 3 };
void on_file_changed(std::filesystem::path new_file_path);
void on_target_frequency_changed(rf::Frequency f); void on_target_frequency_changed(rf::Frequency f);
void on_tx_progress(const uint32_t progress);
rf::Frequency target_frequency() const;
void set_target_frequency(const rf::Frequency new_value); void set_target_frequency(const rf::Frequency new_value);
rf::Frequency target_frequency() const;
void toggle();
void set_ready();
void handle_replay_thread_done(const File::Error error);
void handle_error(const File::Error error);
std::filesystem::path file_path { };
std::unique_ptr<ReplayThread> replay_thread { };
bool ready_signal { false };
FrequencyField field_frequency { FrequencyField field_frequency {
{ 0 * 8, 0 * 16 }, { 0 * 8, 0 * 16 },
@ -71,12 +87,57 @@ private:
{ 16 * 8, 0 * 16 } { 16 * 8, 0 * 16 }
}; };
ReplayView replay_view { ImageButton button_play {
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 }, { 0 * 8, 1 * 16 + 8, 2 * 8, 1 * 16 },
16384, 3 &bitmap_play,
Color::green(),
Color::black()
};
Text text_filename {
{ 2 * 8, 1 * 16, 18 * 8, 16 },
"-"
};
Text text_duration {
{ 2 * 8, 2 * 16, 6 * 8, 16 },
"-"
};
ProgressBar progressbar {
{ 9 * 8, 2 * 16, 10 * 8, 16 }
};
Button button_open {
{ 20 * 8, 1 * 16, 10 * 8, 2 * 16 },
"Open file"
}; };
spectrum::WaterfallWidget waterfall { }; spectrum::WaterfallWidget 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.error);
}
};
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 */ } /* namespace ui */

View File

@ -41,10 +41,12 @@ ReplayThread::ReplayThread(
std::unique_ptr<stream::Reader> reader, std::unique_ptr<stream::Reader> reader,
size_t read_size, size_t read_size,
size_t buffer_count, size_t buffer_count,
bool* ready_signal,
std::function<void()> success_callback, std::function<void()> success_callback,
std::function<void(File::Error)> error_callback std::function<void(File::Error)> error_callback
) : config { read_size, buffer_count }, ) : config { read_size, buffer_count },
reader { std::move(reader) }, reader { std::move(reader) },
ready_sig { ready_signal },
success_callback { std::move(success_callback) }, success_callback { std::move(success_callback) },
error_callback { std::move(error_callback) } error_callback { std::move(error_callback) }
{ {
@ -79,7 +81,12 @@ Optional<File::Error> ReplayThread::run() {
StreamBuffer* prefill_buffer { nullptr }; StreamBuffer* prefill_buffer { nullptr };
// TESTING: Prefill // Wait for FIFOs to be allocated in baseband
// Wait for ui_replay_view to tell us that the buffers are ready (awful :( )
while (!(*ready_sig)) {
chThdSleep(100);
};
// While empty buffers fifo is not empty... // While empty buffers fifo is not empty...
while (!buffers.empty()) { while (!buffers.empty()) {
prefill_buffer = buffers.get_prefill(); prefill_buffer = buffers.get_prefill();
@ -87,7 +94,7 @@ Optional<File::Error> ReplayThread::run() {
if (prefill_buffer == nullptr) { if (prefill_buffer == nullptr) {
buffers.put_app(prefill_buffer); buffers.put_app(prefill_buffer);
} else { } else {
size_t blocks = prefill_buffer->capacity() / 512; size_t blocks = 16384 / 512;
for (size_t c = 0; c < blocks; c++) { for (size_t c = 0; c < blocks; c++) {
auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512); auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512);
@ -96,23 +103,23 @@ Optional<File::Error> ReplayThread::run() {
} }
} }
prefill_buffer->set_size(prefill_buffer->capacity()); prefill_buffer->set_size(16384);
buffers.put(prefill_buffer); buffers.put(prefill_buffer);
//if (!buffers.put(prefill_buffer)) for(;;) {};
} }
}; };
baseband::set_fifo_data(nullptr);
while( !chThdShouldTerminate() ) { while( !chThdShouldTerminate() ) {
auto buffer = buffers.get(); auto buffer = buffers.get();
size_t blocks = buffer->capacity() / 512; auto read_result = reader->read(buffer->data(), buffer->capacity());
if( read_result.is_error() ) {
for (size_t c = 0; c < blocks; c++) { return read_result.error();
auto read_result = reader->read(&((uint8_t*)buffer->data())[c * 512], 512); } else {
if( read_result.is_error() ) { if (read_result.value() == 0) {
return read_result.error(); return { };
} }
} }

View File

@ -40,6 +40,7 @@ public:
std::unique_ptr<stream::Reader> reader, std::unique_ptr<stream::Reader> reader,
size_t read_size, size_t read_size,
size_t buffer_count, size_t buffer_count,
bool* ready_signal,
std::function<void()> success_callback, std::function<void()> success_callback,
std::function<void(File::Error)> error_callback std::function<void(File::Error)> error_callback
); );
@ -57,6 +58,7 @@ public:
private: private:
ReplayConfig config; ReplayConfig config;
std::unique_ptr<stream::Reader> reader; std::unique_ptr<stream::Reader> reader;
bool* ready_sig;
std::function<void()> success_callback; std::function<void()> success_callback;
std::function<void(File::Error)> error_callback; std::function<void(File::Error)> error_callback;
Thread* thread { nullptr }; Thread* thread { nullptr };

View File

@ -32,14 +32,24 @@ namespace ui {
void FileManBaseView::load_directory_contents(const std::filesystem::path& dir_path) { void FileManBaseView::load_directory_contents(const std::filesystem::path& dir_path) {
current_path = dir_path; current_path = dir_path;
text_current.set(dir_path.string()); text_current.set(dir_path.string().substr(0, 30 - 8));
entry_list.clear(); entry_list.clear();
// List all directories and files, put directories up top auto filtering = (bool)extension_filter.size();
// List directories and files, put directories up top
for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*")) { for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) { if (std::filesystem::is_regular_file(entry.status())) {
entry_list.push_back({ entry.path(), (uint32_t)entry.size(), false }); if (entry.path().string().length()) {
auto entry_extension = entry.path().extension().string();
for (auto &c: entry_extension)
c = toupper(c);
if ((entry_extension == extension_filter) || !filtering)
entry_list.push_back({ entry.path(), (uint32_t)entry.size(), false });
}
} else if (std::filesystem::is_directory(entry.status())) { } else if (std::filesystem::is_directory(entry.status())) {
entry_list.insert(entry_list.begin(), { entry.path(), 0, true }); entry_list.insert(entry_list.begin(), { entry.path(), 0, true });
} }
@ -57,8 +67,10 @@ std::filesystem::path FileManBaseView::get_selected_path() {
} }
FileManBaseView::FileManBaseView( FileManBaseView::FileManBaseView(
NavigationView& nav NavigationView& nav,
) : nav_ (nav) std::string filter
) : nav_ (nav),
extension_filter { filter }
{ {
load_directory_contents(current_path); load_directory_contents(current_path);
@ -95,10 +107,6 @@ void FileManBaseView::focus() {
} }
void FileManBaseView::refresh_list() { void FileManBaseView::refresh_list() {
std::string size_str { };
uint32_t suffix_index;
size_t file_size;
if (!entry_list.size()) { if (!entry_list.size()) {
// Hide widgets, show warning // Hide widgets, show warning
if (on_refresh_widgets) if (on_refresh_widgets)
@ -111,9 +119,10 @@ void FileManBaseView::refresh_list() {
menu_view.clear(); menu_view.clear();
for (size_t n = 0; n < entry_list.size(); n++) { for (size_t n = 0; n < entry_list.size(); n++) {
auto entry_name = entry_list[n].entry_path.filename().string().substr(0, 20); auto entry = &entry_list[n];
auto entry_name = entry->entry_path.filename().string().substr(0, 20);
if (entry_list[n].is_directory) { if (entry->is_directory) {
menu_view.add_item({ menu_view.add_item({
entry_name, entry_name,
@ -127,8 +136,8 @@ void FileManBaseView::refresh_list() {
} else { } else {
file_size = entry_list[n].size; auto file_size = entry->size;
suffix_index = 0; size_t suffix_index = 0;
while (file_size >= 1024) { while (file_size >= 1024) {
file_size /= 1024; file_size /= 1024;
@ -137,12 +146,23 @@ void FileManBaseView::refresh_list() {
if (suffix_index > 4) if (suffix_index > 4)
suffix_index = 4; suffix_index = 4;
size_str = to_string_dec_uint(file_size) + suffix[suffix_index]; std::string size_str = to_string_dec_uint(file_size) + suffix[suffix_index];
auto entry_extension = entry->entry_path.extension().string();
for (auto &c: entry_extension)
c = toupper(c);
// Associate extension to icon and color
size_t c;
for (c = 0; c < file_types.size() - 1; c++) {
if (entry_extension == file_types[c].extension)
break;
}
menu_view.add_item({ menu_view.add_item({
entry_name + std::string(21 - entry_name.length(), ' ') + size_str, entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
ui::Color::white(), file_types[c].color,
&bitmap_icon_file, file_types[c].icon,
[this](){ [this](){
if (on_select_entry) if (on_select_entry)
on_select_entry(); on_select_entry();
@ -186,8 +206,9 @@ void FileLoadView::refresh_widgets(const bool v) {
} }
FileLoadView::FileLoadView( FileLoadView::FileLoadView(
NavigationView& nav NavigationView& nav,
) : FileManBaseView(nav) std::string filter
) : FileManBaseView(nav, filter)
{ {
on_refresh_widgets = [this](bool v) { on_refresh_widgets = [this](bool v) {
refresh_widgets(v); refresh_widgets(v);
@ -216,7 +237,7 @@ FileLoadView::FileLoadView(
} }
void FileManagerView::on_rename(NavigationView& nav) { void FileManagerView::on_rename(NavigationView& nav) {
text_prompt(nav, &name_buffer, 12, [this](std::string * buffer) { text_prompt(nav, &name_buffer, max_filename_length, [this](std::string * buffer) {
rename_file(get_selected_path(), *buffer); rename_file(get_selected_path(), *buffer);
load_directory_contents(current_path); load_directory_contents(current_path);
refresh_list(); refresh_list();
@ -244,7 +265,7 @@ FileManagerView::~FileManagerView() {
FileManagerView::FileManagerView( FileManagerView::FileManagerView(
NavigationView& nav NavigationView& nav
) : FileManBaseView(nav) ) : FileManBaseView(nav, "")
{ {
on_refresh_widgets = [this](bool v) { on_refresh_widgets = [this](bool v) {
refresh_widgets(v); refresh_widgets(v);
@ -271,7 +292,7 @@ FileManagerView::FileManagerView(
button_new_dir.on_select = [this, &nav](Button&) { button_new_dir.on_select = [this, &nav](Button&) {
name_buffer.clear(); name_buffer.clear();
text_prompt(nav, &name_buffer, 12, [this](std::string * buffer) { text_prompt(nav, &name_buffer, max_filename_length, [this](std::string * buffer) {
std::string path_str = *buffer; std::string path_str = *buffer;
make_new_directory(current_path.string() + '/' + path_str); make_new_directory(current_path.string() + '/' + path_str);
@ -281,10 +302,8 @@ FileManagerView::FileManagerView(
}; };
button_rename.on_select = [this, &nav](Button&) { button_rename.on_select = [this, &nav](Button&) {
if (!entry_list[menu_view.highlighted()].is_directory) { name_buffer = entry_list[menu_view.highlighted()].entry_path.filename().string().substr(0, max_filename_length);
name_buffer = entry_list[menu_view.highlighted()].entry_path.filename().string(); on_rename(nav);
on_rename(nav);
}
}; };
button_delete.on_select = [this, &nav](Button&) { button_delete.on_select = [this, &nav](Button&) {

View File

@ -39,7 +39,8 @@ struct fileman_entry {
class FileManBaseView : public View { class FileManBaseView : public View {
public: public:
FileManBaseView( FileManBaseView(
NavigationView& nav NavigationView& nav,
std::string filter
); );
void focus() override; void focus() override;
@ -52,13 +53,31 @@ public:
protected: protected:
NavigationView& nav_; NavigationView& nav_;
static constexpr size_t max_filename_length = 30 - 2;
const std::string suffix[5] = { "B", "kB", "MB", "GB", "??" }; const std::string suffix[5] = { "B", "kB", "MB", "GB", "??" };
struct file_assoc_t {
std::string extension;
const Bitmap* icon;
ui::Color color;
};
const std::vector<file_assoc_t> file_types = {
{ ".TXT", &bitmap_icon_file_text, ui::Color::white() },
{ ".PNG", &bitmap_icon_file_image, ui::Color::green() },
{ ".BMP", &bitmap_icon_file_image, ui::Color::green() },
{ ".C16", &bitmap_icon_file_iq, ui::Color::blue() },
{ ".WAV", &bitmap_icon_speaker, ui::Color::dark_magenta() },
{ "", &bitmap_icon_file, ui::Color::light_grey() }
};
bool empty_root { false }; bool empty_root { false };
std::function<void(void)> on_select_entry { nullptr }; std::function<void(void)> on_select_entry { nullptr };
std::function<void(bool)> on_refresh_widgets { nullptr }; std::function<void(bool)> on_refresh_widgets { nullptr };
std::vector<fileman_entry> entry_list { }; std::vector<fileman_entry> entry_list { };
std::filesystem::path current_path { u"" }; std::filesystem::path current_path { u"" };
std::string extension_filter { "" };
void change_category(int32_t category_id); void change_category(int32_t category_id);
void refresh_list(); void refresh_list();
@ -113,7 +132,7 @@ class FileLoadView : public FileManBaseView {
public: public:
std::function<void(std::filesystem::path)> on_changed { }; std::function<void(std::filesystem::path)> on_changed { };
FileLoadView(NavigationView& nav); FileLoadView(NavigationView& nav, std::string filter);
private: private:
void refresh_widgets(const bool v); void refresh_widgets(const bool v);

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_replay_view.hpp"
#include "portapack.hpp"
#include "message.hpp"
#include "portapack_shared_memory.hpp"
using namespace portapack;
#include "rtc_time.hpp"
#include "io_file.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include <cstdint>
namespace ui {
void ReplayView::on_file_changed(const uint32_t duration) {
std::string str_duration = "";
if (duration >= 60)
str_duration = to_string_dec_uint(duration / 60) + "m";
text_duration.set(str_duration + to_string_dec_uint(duration % 60) + "s");
}
ReplayView::ReplayView(
const Rect parent_rect,
const size_t read_size,
const size_t buffer_count
) : View { parent_rect },
read_size { read_size },
buffer_count { buffer_count }
{
add_children({
&rect_background,
&button_play,
&options_files,
&text_duration,
//&text_time_seek,
});
rect_background.set_parent_rect({ { 0, 0 }, size() });
options_files.on_change = [this](size_t, int32_t duration) {
this->on_file_changed(duration);
};
button_play.on_select = [this](ImageButton&) {
this->toggle();
};
/*signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};*/
}
ReplayView::~ReplayView() {
//rtc_time::signal_tick_second -= signal_token_tick_second;
}
void ReplayView::focus() {
options_files.focus();
}
void ReplayView::set_file_list(const std::vector<std::filesystem::path>& file_list) {
File bbd_file;
uint32_t duration;
for (const auto& file : file_list) {
bbd_file.open("/" + file.string());
duration = bbd_file.size() / (2 * 2 * sampling_rate / 8);
file_options.emplace_back(file.string().substr(0, 8), duration);
}
options_files.set_options(file_options);
options_files.set_selected_index(0); // First file
on_file_changed(file_options[0].second);
}
bool ReplayView::is_active() const {
return (bool)replay_thread;
}
void ReplayView::toggle() {
if( is_active() ) {
stop();
} else {
start();
}
}
void ReplayView::start() {
stop();
std::unique_ptr<stream::Reader> reader;
auto p = std::make_unique<FileReader>();
auto create_error = p->open(file_options[options_files.selected_index()].first + ".C16");
if( create_error.is_valid() ) {
handle_error(create_error.value());
} 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,
[]() {
ReplayThreadDoneMessage message { };
EventDispatcher::send_message(message);
},
[](File::Error error) {
ReplayThreadDoneMessage message { error.code() };
EventDispatcher::send_message(message);
}
);
}
update_status_display();
radio::enable({
receiver_model.tuning_frequency(),
sampling_rate,
2500000, //baseband_bandwidth,
rf::Direction::Transmit,
receiver_model.rf_amp(),
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga())
});
}
void ReplayView::stop() {
if( is_active() ) {
replay_thread.reset();
radio::disable();
button_play.set_bitmap(&bitmap_play);
}
update_status_display();
}
void ReplayView::on_tick_second() {
update_status_display();
}
void ReplayView::update_status_display() {
/*if( sampling_rate ) {
const auto space_info = std::filesystem::space("");
const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate * 4);
const uint32_t available_seconds = space_info.free / bytes_per_second;
const uint32_t seconds = available_seconds % 60;
const uint32_t available_minutes = available_seconds / 60;
const uint32_t minutes = available_minutes % 60;
const uint32_t hours = available_minutes / 60;
const std::string available_time =
to_string_dec_uint(hours, 3, ' ') + ":" +
to_string_dec_uint(minutes, 2, '0') + ":" +
to_string_dec_uint(seconds, 2, '0');
text_time_available.set(available_time);
}*/
}
void ReplayView::handle_replay_thread_done(const File::Error error) {
stop();
if( error.code() ) {
handle_error(error);
}
}
void ReplayView::handle_error(const File::Error error) {
if( on_error ) {
on_error(error.what());
}
}
} /* namespace ui */

View File

@ -1,119 +0,0 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_REPLAY_VIEW_H__
#define __UI_REPLAY_VIEW_H__
#include "ui_widget.hpp"
#include "replay_thread.hpp"
#include "signal.hpp"
#include "bitmap.hpp"
#include <cstddef>
#include <string>
#include <memory>
namespace ui {
class ReplayView : public View {
public:
std::function<void(std::string)> on_error { };
ReplayView(
const Rect parent_rect,
const size_t read_size,
const size_t buffer_count
);
~ReplayView();
void focus() override;
void set_file_list(const std::vector<std::filesystem::path>& file_list);
void start();
void stop();
bool is_active() const;
private:
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
static constexpr uint32_t sampling_rate = 4000000;
void toggle();
void on_file_changed(const uint32_t duration);
void on_tick_second();
void update_status_display();
void handle_replay_thread_done(const File::Error error);
void handle_error(const File::Error error);
const size_t read_size;
const size_t buffer_count;
//SignalToken signal_token_tick_second { };
options_t file_options { };
//std::filesystem:path file_path { };
Rectangle rect_background {
Color::black()
};
ImageButton button_play {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_play,
Color::green(),
Color::black()
};
OptionsField options_files {
{ 2 * 8, 0 * 8 },
8,
{ }
};
Text text_duration {
{ 11 * 8, 0 * 8, 12 * 8, 16 },
"-"
};
/*Text text_time_seek {
{ 18 * 8, 0 * 16, 9 * 8, 16 },
"",
};*/
std::unique_ptr<ReplayThread> replay_thread { };
MessageHandlerRegistration message_handler_replay_thread_error {
Message::ID::CaptureThreadDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
this->handle_replay_thread_done(message.error);
}
};
};
} /* namespace ui */
#endif/*__UI_REPLAY_VIEW_H__*/

View File

@ -150,7 +150,7 @@ ViewWavView::ViewWavView(
}); });
button_open.on_select = [this, &nav](Button&) { button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(); auto open_view = nav.push<FileLoadView>(".WAV");
open_view->on_changed = [this](std::filesystem::path file_path) { open_view->on_changed = [this](std::filesystem::path file_path) {
load_wav(file_path); load_wav(file_path);
field_pos_seconds.focus(); field_pos_seconds.focus();

View File

@ -22,6 +22,7 @@
#include "proc_replay.hpp" #include "proc_replay.hpp"
#include "sine_table_int8.hpp" #include "sine_table_int8.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp" #include "event_m4.hpp"
@ -31,16 +32,18 @@ ReplayProcessor::ReplayProcessor() {
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667 channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333 channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333
spectrum_interval_samples = (baseband_fs / 8) / spectrum_rate_hz; spectrum_interval_samples = baseband_fs / spectrum_rate_hz;
spectrum_samples = 0; spectrum_samples = 0;
channel_spectrum.set_decimation_factor(1); channel_spectrum.set_decimation_factor(1);
configured = false;
} }
void ReplayProcessor::execute(const buffer_c8_t& buffer) { void ReplayProcessor::execute(const buffer_c8_t& buffer) {
/* 4MHz, 2048 samples */ /* 4MHz, 2048 samples */
size_t pos = 0; if (!configured) return;
// File data is in C16 format, we need C8 // File data is in C16 format, we need C8
// File samplerate is 500kHz, we're at 4MHz // File samplerate is 500kHz, we're at 4MHz
@ -51,37 +54,28 @@ void ReplayProcessor::execute(const buffer_c8_t& buffer) {
// So 256 * 4 bytes per sample (C16) = 1024 bytes from the file // So 256 * 4 bytes per sample (C16) = 1024 bytes from the file
if( stream ) { if( stream ) {
const size_t bytes_to_read = sizeof(*buffer.p) * 2 * (buffer.count / 8); // *2 (C16), /8 (oversampling) should be == 1024 const size_t bytes_to_read = sizeof(*buffer.p) * 2 * (buffer.count / 8); // *2 (C16), /8 (oversampling) should be == 1024
const auto result = stream->read(iq_buffer.p, bytes_to_read); bytes_read += stream->read(iq_buffer.p, bytes_to_read);
} }
//feed_channel_stats(channel);
// Zero-stuff // Fill and "stretch"
for (size_t i = 0; i < buffer.count; i++) { for (size_t i = 0; i < buffer.count; i++) {
if (i & 3) {
// DEBUG: This works. Transmits a 1kHz tone
/*sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]);
tone_phase += (1000 * ((1ULL << 32) / baseband_fs));
// Do FM
delta = sample * 30000 * (0xFFFFFFULL / baseband_fs);
phase += delta;
sphase = phase + (64 << 24);
iq_buffer.p[i >> 3] = { (int16_t)(sine_table_i8[(sphase & 0xFF000000) >> 24]) << 8, (int16_t)(sine_table_i8[(phase & 0xFF000000) >> 24]) << 8 };
*/
/*if (i & 3)
buffer.p[i] = buffer.p[i - 1]; buffer.p[i] = buffer.p[i - 1];
else {*/ } else {
auto re_out = iq_buffer.p[i >> 3].real() >> 8; auto re_out = iq_buffer.p[i >> 3].real() >> 8;
auto im_out = iq_buffer.p[i >> 3].imag() >> 8; auto im_out = iq_buffer.p[i >> 3].imag() >> 8;
buffer.p[i] = { re_out, im_out }; buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };
//} }
} }
spectrum_samples += buffer.count; spectrum_samples += buffer.count;
if( spectrum_samples >= spectrum_interval_samples ) { if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples; spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(iq_buffer, channel_filter_pass_f, channel_filter_stop_f); channel_spectrum.feed(iq_buffer, channel_filter_pass_f, channel_filter_stop_f);
txprogress_message.progress = bytes_read; // Inform UI about progress
txprogress_message.done = false;
shared_memory.application_queue.push(txprogress_message);
} }
} }
@ -93,8 +87,15 @@ void ReplayProcessor::on_message(const Message* const message) {
break; break;
case Message::ID::ReplayConfig: case Message::ID::ReplayConfig:
configured = false;
bytes_read = 0;
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message)); replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
break; break;
// App has prefilled the buffers, we're ready to go now
case Message::ID::FIFOData:
configured = true;
break;
default: default:
break; break;
@ -104,6 +105,9 @@ void ReplayProcessor::on_message(const Message* const message) {
void ReplayProcessor::replay_config(const ReplayConfigMessage& message) { void ReplayProcessor::replay_config(const ReplayConfigMessage& message) {
if( message.config ) { if( message.config ) {
stream = std::make_unique<StreamOutput>(message.config); stream = std::make_unique<StreamOutput>(message.config);
// Tell application that the buffers and FIFO pointers are ready, prefill
shared_memory.application_queue.push(sig_message);
} else { } else {
stream.reset(); stream.reset();
} }

View File

@ -47,26 +47,29 @@ private:
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
std::array<complex16_t, 256> iq { }; // This fits in just right in allocated RAM - Too big ? std::array<complex16_t, 256> iq { };
const buffer_c16_t iq_buffer { const buffer_c16_t iq_buffer {
iq.data(), iq.data(),
iq.size() iq.size(),
baseband_fs / 8
}; };
uint32_t channel_filter_pass_f = 0; uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0; uint32_t channel_filter_stop_f = 0;
// DEBUG
//uint32_t tone_phase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 };
//int8_t sample { 0 };
std::unique_ptr<StreamOutput> stream { }; std::unique_ptr<StreamOutput> stream { };
SpectrumCollector channel_spectrum { }; SpectrumCollector channel_spectrum { };
size_t spectrum_interval_samples = 0; size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0; size_t spectrum_samples = 0;
bool configured { false };
uint32_t bytes_read { 0 };
void replay_config(const ReplayConfigMessage& message); void replay_config(const ReplayConfigMessage& message);
TXProgressMessage txprogress_message { };
RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
}; };
#endif/*__PROC_REPLAY_HPP__*/ #endif/*__PROC_REPLAY_HPP__*/

View File

@ -51,8 +51,6 @@ size_t StreamOutput::read(void* const data, const size_t length) {
// We need a full buffer... // We need a full buffer...
if( !fifo_buffers_full.out(active_buffer) ) { if( !fifo_buffers_full.out(active_buffer) ) {
// ...but none are available. Hole in transmission (inform app and stop ?) // ...but none are available. Hole in transmission (inform app and stop ?)
//active_buffer = nullptr;
//creg::m4txevent::assert();
break; break;
} }
} }

View File

@ -21,10 +21,6 @@
#include "buffer_exchange.hpp" #include "buffer_exchange.hpp"
// DEBUG:
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
BufferExchange* BufferExchange::obj { nullptr }; BufferExchange* BufferExchange::obj { nullptr };
BufferExchange::BufferExchange( BufferExchange::BufferExchange(

View File

@ -55,7 +55,6 @@ public:
return fifo_buffers_for_baseband->in(p); return fifo_buffers_for_baseband->in(p);
} }
// TESTING...
bool put_app(StreamBuffer* const p) { bool put_app(StreamBuffer* const p) {
return fifo_buffers_for_application->in(p); return fifo_buffers_for_application->in(p);
} }

View File

@ -70,49 +70,49 @@ struct Color {
return { 255, 0, 0 }; return { 255, 0, 0 };
} }
static constexpr Color dark_red() { static constexpr Color dark_red() {
return { 127, 0, 0 }; return { 191, 0, 0 };
} }
static constexpr Color orange() { static constexpr Color orange() {
return { 255, 175, 0 }; return { 255, 175, 0 };
} }
static constexpr Color dark_orange() { static constexpr Color dark_orange() {
return { 127, 88, 0 }; return { 191, 88, 0 };
} }
static constexpr Color yellow() { static constexpr Color yellow() {
return { 255, 255, 0 }; return { 255, 255, 0 };
} }
static constexpr Color dark_yellow() { static constexpr Color dark_yellow() {
return { 127, 127, 0 }; return { 191, 191, 0 };
} }
static constexpr Color green() { static constexpr Color green() {
return { 0, 255, 0 }; return { 0, 255, 0 };
} }
static constexpr Color dark_green() { static constexpr Color dark_green() {
return { 0, 127, 0 }; return { 0, 191, 0 };
} }
static constexpr Color blue() { static constexpr Color blue() {
return { 0, 0, 255 }; return { 0, 0, 255 };
} }
static constexpr Color dark_blue() { static constexpr Color dark_blue() {
return { 0, 0, 127 }; return { 0, 0, 191 };
} }
static constexpr Color cyan() { static constexpr Color cyan() {
return { 0, 255, 255 }; return { 0, 255, 255 };
} }
static constexpr Color dark_cyan() { static constexpr Color dark_cyan() {
return { 0, 127, 127 }; return { 0, 191, 191 };
} }
static constexpr Color magenta() { static constexpr Color magenta() {
return { 255, 0, 255 }; return { 255, 0, 255 };
} }
static constexpr Color dark_magenta() { static constexpr Color dark_magenta() {
return { 127, 0, 127 }; return { 191, 0, 191 };
} }
static constexpr Color white() { static constexpr Color white() {
@ -120,10 +120,10 @@ struct Color {
} }
static constexpr Color light_grey() { static constexpr Color light_grey() {
return { 127, 127, 127 }; return { 191, 191, 191 };
} }
static constexpr Color grey() { static constexpr Color grey() {
return { 91, 91, 91 }; return { 127, 127, 127 };
} }
static constexpr Color dark_grey() { static constexpr Color dark_grey() {
return { 63, 63, 63 }; return { 63, 63, 63 };

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.