mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-15 19:13:00 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86d4b17257 | |||
| f6a437f7fb | |||
| 3db8a5ac5b | |||
| 266a626acf | |||
| 411e2fb93a |
@@ -1 +1 @@
|
||||
v1.7.3
|
||||
v1.7.4
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.7.4
|
||||
v1.8.0
|
||||
|
||||
@@ -156,6 +156,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
|
||||
File f;
|
||||
auto path = get_settings_path(std::string{store_name});
|
||||
|
||||
make_new_directory(SETTINGS_DIR);
|
||||
auto error = f.create(path);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include "max283x.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
// Folder to store app settings, pmem_fileflag, and date_fileflag
|
||||
#define SETTINGS_DIR u"/SETTINGS"
|
||||
|
||||
// Bring in the string_view literal.
|
||||
using std::literals::operator""sv;
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
&text_samples,
|
||||
&text_max,
|
||||
&field_cutoff,
|
||||
&field_amplify,
|
||||
&button_trim,
|
||||
});
|
||||
|
||||
@@ -72,6 +73,11 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
refresh_ui();
|
||||
};
|
||||
|
||||
field_amplify.set_value(1); // 1X is default (no amplification)
|
||||
field_amplify.on_change = [this](int32_t) {
|
||||
refresh_ui();
|
||||
};
|
||||
|
||||
button_trim.on_select = [this](Button&) {
|
||||
if (trim_capture()) {
|
||||
profile_capture();
|
||||
@@ -133,7 +139,18 @@ void IQTrimView::focus() {
|
||||
void IQTrimView::refresh_ui() {
|
||||
field_path.set_text(path_.filename().string());
|
||||
text_samples.set(to_string_dec_uint(info_->sample_count));
|
||||
text_max.set(to_string_dec_uint(info_->max_power));
|
||||
|
||||
// show max power after amplification applied
|
||||
uint64_t power_amp = field_amplify.value() * field_amplify.value() * field_amplify.value() * field_amplify.value();
|
||||
text_max.set(to_string_dec_uint(info_->max_power * power_amp));
|
||||
|
||||
// show max power in red if amplification is too high, causing clipping
|
||||
uint32_t clipping_limit = (fs::capture_file_sample_size(path_) == sizeof(complex8_t)) ? 0x80 : 0x8000;
|
||||
if ((field_amplify.value() * info_->max_iq) > clipping_limit)
|
||||
text_max.set_style(&Styles::red);
|
||||
else
|
||||
text_max.set_style(&Styles::light_grey);
|
||||
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
@@ -191,7 +208,7 @@ bool IQTrimView::trim_capture() {
|
||||
}
|
||||
|
||||
progress_ui.show_trimming();
|
||||
trimmed = iq::trim_capture_with_range(path_, trim_range, progress_ui.get_callback());
|
||||
trimmed = iq::trim_capture_with_range(path_, trim_range, progress_ui.get_callback(), field_amplify.value());
|
||||
progress_ui.clear();
|
||||
|
||||
if (!trimmed)
|
||||
|
||||
@@ -107,6 +107,8 @@ class IQTrimView : public View {
|
||||
{{0 * 8, 9 * 16}, "Max Pwr:", Color::light_grey()},
|
||||
{{0 * 8, 10 * 16}, "Cutoff :", Color::light_grey()},
|
||||
{{12 * 8, 10 * 16}, "%", Color::light_grey()},
|
||||
{{0 * 8, 12 * 16}, "Amplify:", Color::light_grey()},
|
||||
{{10 * 8, 12 * 16}, "x", Color::light_grey()},
|
||||
};
|
||||
|
||||
TextField field_path{
|
||||
@@ -135,7 +137,7 @@ class IQTrimView : public View {
|
||||
"0"};
|
||||
|
||||
Text text_max{
|
||||
{9 * 8, 9 * 16, 10 * 8, 1 * 16},
|
||||
{9 * 8, 9 * 16, 20 * 8, 1 * 16},
|
||||
"0"};
|
||||
|
||||
NumberField field_cutoff{
|
||||
@@ -145,6 +147,13 @@ class IQTrimView : public View {
|
||||
1,
|
||||
' '};
|
||||
|
||||
NumberField field_amplify{
|
||||
{9 * 8, 12 * 16},
|
||||
1,
|
||||
{1, 9},
|
||||
1,
|
||||
' '};
|
||||
|
||||
Button button_trim{
|
||||
{20 * 8, 16 * 16, 8 * 8, 2 * 16},
|
||||
"Trim"};
|
||||
|
||||
@@ -60,7 +60,7 @@ class POCSAGTXView : public View {
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
466175000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -36,6 +36,13 @@ uint32_t power(T value) {
|
||||
return (real * real) + (imag * imag);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint32_t iq_max(T value) {
|
||||
auto real = abs(value.real());
|
||||
auto imag = abs(value.imag());
|
||||
return (real > imag) ? real : imag;
|
||||
}
|
||||
|
||||
/* Collects capture file metadata and sample power buckets. */
|
||||
template <typename T>
|
||||
Optional<CaptureInfo> profile_capture(
|
||||
@@ -51,7 +58,8 @@ Optional<CaptureInfo> profile_capture(
|
||||
.file_size = f.size(),
|
||||
.sample_count = f.size() / sizeof(T),
|
||||
.sample_size = sizeof(T),
|
||||
.max_power = 0};
|
||||
.max_power = 0,
|
||||
.max_iq = 0};
|
||||
|
||||
auto profile_samples = buckets.size * samples_per_bucket;
|
||||
auto sample_interval = info.sample_count / profile_samples;
|
||||
@@ -67,8 +75,11 @@ Optional<CaptureInfo> profile_capture(
|
||||
if (*result != info.sample_size)
|
||||
break; // EOF
|
||||
|
||||
auto mag_squared = power(value);
|
||||
auto max_iq = iq_max(value);
|
||||
if (max_iq > info.max_iq)
|
||||
info.max_iq = max_iq;
|
||||
|
||||
auto mag_squared = power(value);
|
||||
if (mag_squared > info.max_power)
|
||||
info.max_power = mag_squared;
|
||||
|
||||
@@ -133,13 +144,50 @@ TrimRange compute_trim_range(
|
||||
info.sample_size};
|
||||
}
|
||||
|
||||
void amplify_iq_buffer(uint8_t* buffer, uint32_t length, uint32_t amplification, uint8_t sample_size) {
|
||||
uint32_t mult_count = length / sample_size / 2;
|
||||
|
||||
switch (sample_size) {
|
||||
case sizeof(complex16_t): {
|
||||
int16_t* buf_ptr = (int16_t*)buffer;
|
||||
for (uint32_t i = 0; i < mult_count; i++) {
|
||||
int32_t val = *buf_ptr * amplification;
|
||||
if (val > 0x7FFF)
|
||||
val = 0x7FFF;
|
||||
else if (val < -0x7FFF)
|
||||
val = -0x7FFF;
|
||||
*buf_ptr++ = val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case sizeof(complex8_t): {
|
||||
int8_t* buf_ptr = (int8_t*)buffer;
|
||||
for (uint32_t i = 0; i < mult_count; i++) {
|
||||
int32_t val = *buf_ptr * amplification;
|
||||
if (val > 0x7F)
|
||||
val = 0x7F;
|
||||
else if (val < -0x7F)
|
||||
val = -0x7F;
|
||||
*buf_ptr++ = val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool trim_capture_with_range(
|
||||
const fs::path& path,
|
||||
TrimRange range,
|
||||
const std::function<void(uint8_t)>& on_progress) {
|
||||
const std::function<void(uint8_t)>& on_progress,
|
||||
const uint32_t amplification) {
|
||||
constexpr size_t buffer_size = std::filesystem::max_file_block_size;
|
||||
uint8_t buffer[buffer_size];
|
||||
auto temp_path = path + u"-tmp";
|
||||
auto sample_size = fs::capture_file_sample_size(path);
|
||||
|
||||
// end_sample is the first sample to _not_ include.
|
||||
auto start_byte = range.start_sample * range.sample_size;
|
||||
@@ -168,6 +216,9 @@ bool trim_capture_with_range(
|
||||
auto remaining = length - processed;
|
||||
auto to_write = std::min(remaining, *result);
|
||||
|
||||
if (amplification > 1)
|
||||
amplify_iq_buffer(buffer, to_write, amplification, sample_size);
|
||||
|
||||
result = dst->write(buffer, to_write);
|
||||
if (result.is_error()) return false;
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ struct CaptureInfo {
|
||||
uint64_t sample_count;
|
||||
uint8_t sample_size;
|
||||
uint32_t max_power;
|
||||
uint32_t max_iq;
|
||||
};
|
||||
|
||||
/* Holds sample average power by bucket. */
|
||||
@@ -82,11 +83,19 @@ TrimRange compute_trim_range(
|
||||
const PowerBuckets& buckets,
|
||||
uint8_t cutoff_percent);
|
||||
|
||||
/* Multiplies samples in an IQ buffer by amplification value */
|
||||
void amplify_iq_buffer(
|
||||
uint8_t* buffer,
|
||||
uint32_t length,
|
||||
uint32_t amplification,
|
||||
uint8_t sample_size);
|
||||
|
||||
/* Trims the capture file with the specified range. */
|
||||
bool trim_capture_with_range(
|
||||
const std::filesystem::path& path,
|
||||
TrimRange range,
|
||||
const std::function<void(uint8_t)>& on_progress);
|
||||
const std::function<void(uint8_t)>& on_progress,
|
||||
const uint32_t amplification);
|
||||
|
||||
} // namespace iq
|
||||
|
||||
|
||||
@@ -110,16 +110,18 @@ void BtnGridView::clear() {
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
menu_items.push_back(item);
|
||||
if (!blacklisted_app(item))
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::add_item(GridItem new_item) {
|
||||
menu_items.push_back(new_item);
|
||||
|
||||
update_items();
|
||||
if (!blacklisted_app(new_item)) {
|
||||
menu_items.push_back(new_item);
|
||||
update_items();
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::update_items() {
|
||||
@@ -232,4 +234,30 @@ bool BtnGridView::on_encoder(const EncoderEvent event) {
|
||||
return set_highlighted(highlighted_item + event);
|
||||
}
|
||||
|
||||
/* BlackList ******************************************************/
|
||||
|
||||
std::unique_ptr<char> blacklist_ptr{};
|
||||
size_t blacklist_len{};
|
||||
|
||||
void load_blacklist() {
|
||||
File f;
|
||||
|
||||
auto error = f.open(BLACKLIST);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
blacklist_ptr = std::unique_ptr<char>(new char[f.size()]);
|
||||
if (f.read(blacklist_ptr.get(), f.size()))
|
||||
blacklist_len = f.size();
|
||||
}
|
||||
|
||||
bool BtnGridView::blacklisted_app(GridItem new_item) {
|
||||
std::string app_name = new_item.text;
|
||||
|
||||
if (blacklist_len < app_name.size())
|
||||
return false;
|
||||
|
||||
return std::search(blacklist_ptr.get(), blacklist_ptr.get() + blacklist_len, app_name.begin(), app_name.end()) < blacklist_ptr.get() + blacklist_len;
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// file used for listing apps to hide from menu
|
||||
#define BLACKLIST u"/SETTINGS/blacklist"
|
||||
|
||||
namespace ui {
|
||||
|
||||
struct GridItem {
|
||||
@@ -47,6 +50,8 @@ struct GridItem {
|
||||
// TODO: Prevent default-constructed GridItems.
|
||||
};
|
||||
|
||||
void load_blacklist();
|
||||
|
||||
class BtnGridView : public View {
|
||||
public:
|
||||
BtnGridView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false);
|
||||
@@ -70,6 +75,7 @@ class BtnGridView : public View {
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
bool blacklisted_app(GridItem new_item);
|
||||
|
||||
private:
|
||||
int rows_{3};
|
||||
|
||||
@@ -161,6 +161,8 @@ SystemStatusView::SystemStatusView(
|
||||
|
||||
rtc_battery_workaround();
|
||||
|
||||
ui::load_blacklist();
|
||||
|
||||
if (pmem::should_use_sdcard_for_pmem()) {
|
||||
pmem::load_persistent_settings_from_file();
|
||||
}
|
||||
@@ -393,6 +395,7 @@ void SystemStatusView::rtc_battery_workaround() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
make_new_directory(SETTINGS_DIR);
|
||||
make_new_file(DATE_FILEFLAG);
|
||||
|
||||
year = 1980;
|
||||
@@ -572,8 +575,8 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"ADS-B", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
|
||||
{"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
|
||||
{"ADS-B TX", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
|
||||
{"APRS TX", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
|
||||
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
|
||||
{"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }},
|
||||
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
|
||||
@@ -583,7 +586,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{"Morse", ui::Color::green(), &bitmap_icon_morse, [&nav]() { nav.push<MorseView>(); }},
|
||||
// { "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push<NuoptixView>(); }},
|
||||
{"OOK", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<EncodersView>(); }},
|
||||
{"POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGTXView>(); }},
|
||||
{"POCSAG TX", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGTXView>(); }},
|
||||
{"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push<RDSView>(); }},
|
||||
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
|
||||
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||
|
||||
@@ -322,7 +322,7 @@ void RecordView::trim_capture() {
|
||||
auto trim_range = iq::compute_trim_range(*info, power_buckets, 7);
|
||||
|
||||
trim_ui.show_trimming();
|
||||
iq::trim_capture_with_range(trim_path, trim_range, trim_ui.get_callback());
|
||||
iq::trim_capture_with_range(trim_path, trim_range, trim_ui.get_callback(), 1);
|
||||
}
|
||||
|
||||
trim_ui.clear();
|
||||
|
||||
@@ -868,6 +868,8 @@ bool should_use_sdcard_for_pmem() {
|
||||
|
||||
int save_persistent_settings_to_file() {
|
||||
File outfile;
|
||||
|
||||
make_new_directory(SETTINGS_DIR);
|
||||
auto error = outfile.create(PMEM_SETTING_FILE);
|
||||
if (error)
|
||||
return false;
|
||||
@@ -912,7 +914,9 @@ bool debug_dump() {
|
||||
painter.draw_string({0, 320 - 16}, ui::Styles::red, "ERROR DUMPING " + filename.filename().string() + " !");
|
||||
return false;
|
||||
}
|
||||
pmem_dump_file.write_line("FW version " VERSION_STRING);
|
||||
pmem_dump_file.write_line("FW version: " VERSION_STRING);
|
||||
pmem_dump_file.write_line("Ext APPS version req'd: 0x" + to_string_hex(VERSION_MD5));
|
||||
pmem_dump_file.write_line("GCC version: " + to_string_dec_int(__GNUC__) + "." + to_string_dec_int(__GNUC_MINOR__) + "." + to_string_dec_int(__GNUC_PATCHLEVEL__));
|
||||
|
||||
// write persistent memory
|
||||
pmem_dump_file.write_line("\n[Persistent Memory]");
|
||||
|
||||
Reference in New Issue
Block a user