mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 23:09:26 -05:00
Simple amplification option in IQ Trim app (#1506)
* Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload
This commit is contained in:
parent
f6a437f7fb
commit
86d4b17257
@ -40,6 +40,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
|||||||
&text_samples,
|
&text_samples,
|
||||||
&text_max,
|
&text_max,
|
||||||
&field_cutoff,
|
&field_cutoff,
|
||||||
|
&field_amplify,
|
||||||
&button_trim,
|
&button_trim,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -72,6 +73,11 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
|||||||
refresh_ui();
|
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&) {
|
button_trim.on_select = [this](Button&) {
|
||||||
if (trim_capture()) {
|
if (trim_capture()) {
|
||||||
profile_capture();
|
profile_capture();
|
||||||
@ -133,7 +139,18 @@ void IQTrimView::focus() {
|
|||||||
void IQTrimView::refresh_ui() {
|
void IQTrimView::refresh_ui() {
|
||||||
field_path.set_text(path_.filename().string());
|
field_path.set_text(path_.filename().string());
|
||||||
text_samples.set(to_string_dec_uint(info_->sample_count));
|
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();
|
set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +208,7 @@ bool IQTrimView::trim_capture() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress_ui.show_trimming();
|
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();
|
progress_ui.clear();
|
||||||
|
|
||||||
if (!trimmed)
|
if (!trimmed)
|
||||||
|
@ -107,6 +107,8 @@ class IQTrimView : public View {
|
|||||||
{{0 * 8, 9 * 16}, "Max Pwr:", Color::light_grey()},
|
{{0 * 8, 9 * 16}, "Max Pwr:", Color::light_grey()},
|
||||||
{{0 * 8, 10 * 16}, "Cutoff :", Color::light_grey()},
|
{{0 * 8, 10 * 16}, "Cutoff :", Color::light_grey()},
|
||||||
{{12 * 8, 10 * 16}, "%", 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{
|
TextField field_path{
|
||||||
@ -135,7 +137,7 @@ class IQTrimView : public View {
|
|||||||
"0"};
|
"0"};
|
||||||
|
|
||||||
Text text_max{
|
Text text_max{
|
||||||
{9 * 8, 9 * 16, 10 * 8, 1 * 16},
|
{9 * 8, 9 * 16, 20 * 8, 1 * 16},
|
||||||
"0"};
|
"0"};
|
||||||
|
|
||||||
NumberField field_cutoff{
|
NumberField field_cutoff{
|
||||||
@ -145,6 +147,13 @@ class IQTrimView : public View {
|
|||||||
1,
|
1,
|
||||||
' '};
|
' '};
|
||||||
|
|
||||||
|
NumberField field_amplify{
|
||||||
|
{9 * 8, 12 * 16},
|
||||||
|
1,
|
||||||
|
{1, 9},
|
||||||
|
1,
|
||||||
|
' '};
|
||||||
|
|
||||||
Button button_trim{
|
Button button_trim{
|
||||||
{20 * 8, 16 * 16, 8 * 8, 2 * 16},
|
{20 * 8, 16 * 16, 8 * 8, 2 * 16},
|
||||||
"Trim"};
|
"Trim"};
|
||||||
|
@ -36,6 +36,13 @@ uint32_t power(T value) {
|
|||||||
return (real * real) + (imag * imag);
|
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. */
|
/* Collects capture file metadata and sample power buckets. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Optional<CaptureInfo> profile_capture(
|
Optional<CaptureInfo> profile_capture(
|
||||||
@ -51,7 +58,8 @@ Optional<CaptureInfo> profile_capture(
|
|||||||
.file_size = f.size(),
|
.file_size = f.size(),
|
||||||
.sample_count = f.size() / sizeof(T),
|
.sample_count = f.size() / sizeof(T),
|
||||||
.sample_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 profile_samples = buckets.size * samples_per_bucket;
|
||||||
auto sample_interval = info.sample_count / profile_samples;
|
auto sample_interval = info.sample_count / profile_samples;
|
||||||
@ -67,8 +75,11 @@ Optional<CaptureInfo> profile_capture(
|
|||||||
if (*result != info.sample_size)
|
if (*result != info.sample_size)
|
||||||
break; // EOF
|
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)
|
if (mag_squared > info.max_power)
|
||||||
info.max_power = mag_squared;
|
info.max_power = mag_squared;
|
||||||
|
|
||||||
@ -133,13 +144,50 @@ TrimRange compute_trim_range(
|
|||||||
info.sample_size};
|
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(
|
bool trim_capture_with_range(
|
||||||
const fs::path& path,
|
const fs::path& path,
|
||||||
TrimRange range,
|
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;
|
constexpr size_t buffer_size = std::filesystem::max_file_block_size;
|
||||||
uint8_t buffer[buffer_size];
|
uint8_t buffer[buffer_size];
|
||||||
auto temp_path = path + u"-tmp";
|
auto temp_path = path + u"-tmp";
|
||||||
|
auto sample_size = fs::capture_file_sample_size(path);
|
||||||
|
|
||||||
// end_sample is the first sample to _not_ include.
|
// end_sample is the first sample to _not_ include.
|
||||||
auto start_byte = range.start_sample * range.sample_size;
|
auto start_byte = range.start_sample * range.sample_size;
|
||||||
@ -168,6 +216,9 @@ bool trim_capture_with_range(
|
|||||||
auto remaining = length - processed;
|
auto remaining = length - processed;
|
||||||
auto to_write = std::min(remaining, *result);
|
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);
|
result = dst->write(buffer, to_write);
|
||||||
if (result.is_error()) return false;
|
if (result.is_error()) return false;
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ struct CaptureInfo {
|
|||||||
uint64_t sample_count;
|
uint64_t sample_count;
|
||||||
uint8_t sample_size;
|
uint8_t sample_size;
|
||||||
uint32_t max_power;
|
uint32_t max_power;
|
||||||
|
uint32_t max_iq;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Holds sample average power by bucket. */
|
/* Holds sample average power by bucket. */
|
||||||
@ -82,11 +83,19 @@ TrimRange compute_trim_range(
|
|||||||
const PowerBuckets& buckets,
|
const PowerBuckets& buckets,
|
||||||
uint8_t cutoff_percent);
|
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. */
|
/* Trims the capture file with the specified range. */
|
||||||
bool trim_capture_with_range(
|
bool trim_capture_with_range(
|
||||||
const std::filesystem::path& path,
|
const std::filesystem::path& path,
|
||||||
TrimRange range,
|
TrimRange range,
|
||||||
const std::function<void(uint8_t)>& on_progress);
|
const std::function<void(uint8_t)>& on_progress,
|
||||||
|
const uint32_t amplification);
|
||||||
|
|
||||||
} // namespace iq
|
} // namespace iq
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ void RecordView::trim_capture() {
|
|||||||
auto trim_range = iq::compute_trim_range(*info, power_buckets, 7);
|
auto trim_range = iq::compute_trim_range(*info, power_buckets, 7);
|
||||||
|
|
||||||
trim_ui.show_trimming();
|
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();
|
trim_ui.clear();
|
||||||
|
Loading…
Reference in New Issue
Block a user