This commit is contained in:
furrtek 2017-11-02 17:11:26 +00:00
parent 6e7b2c751f
commit 2ec24c9fa8
11 changed files with 136 additions and 99 deletions

View File

@ -34,7 +34,7 @@
//TODO: Cap Wav viewer position
//TODO: Adapt wav viewer position step
//TODO: Optimize wav viewer refresh
//TODO: Use unit_auto_scale
//TODO: Remove make_bistream from encoders.cpp, too complex, stinks. bitstream_append should be enough.
//TODO: Continue work on proc_afskrx_corr, see python script (it works !)
//TODO: Super simple text file viewer
@ -59,10 +59,7 @@ Continuous (Fox-oring)
60s transmit, 240s space (Classic 1/5 min)
60s transmit, 360s space (Classic 1/7 min)
*/
//TODO: Use TransmitterView in TEDI/LCR, Numbers, ...
//TODO: FreqMan: Remove and rename categories
//TODO: Wav visualizer
//TODO: File browser view ?
//TODO: Mousejack ?
//TODO: Move frequencykeypad from ui_receiver to ui_widget (used everywhere)
//TODO: ADS-B draw trajectory + GPS coordinates + scale, and playback

View File

@ -170,3 +170,31 @@ std::string to_string_timestamp(const rtc::RTC& value) {
to_string_dec_uint(value.minute(), 2, '0') +
to_string_dec_uint(value.second(), 2, '0');
}
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision) {
const uint32_t powers_of_ten[5] = { 1, 10, 100, 1000, 10000 };
std::string string { "" };
uint32_t prefix_index = base_nano;
double integer_part;
double fractional_part;
precision = std::min((uint32_t)4, precision);
while (n > 1000) {
n /= 1000.0;
prefix_index++;
}
fractional_part = modf(n, &integer_part) * powers_of_ten[precision];
if (fractional_part < 0)
fractional_part = -fractional_part;
string = to_string_dec_int(integer_part);
if (precision)
string += '.' + to_string_dec_uint(fractional_part, precision, '0');
if (prefix_index != 3)
string += unit_prefix[prefix_index];
return string;
}

View File

@ -35,6 +35,8 @@ enum TimeFormat {
HM = 2
};
const char unit_prefix[7] { 'n', 'u', 'm', 0, 'k', 'M', 'G' };
// TODO: Allow l=0 to not fill/justify? Already using this way in ui_spectrum.hpp...
std::string to_string_bin(const uint32_t n, const uint8_t l = 0);
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = 0);
@ -47,4 +49,6 @@ std::string to_string_short_freq(const uint64_t f);
std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format = YMDHMS);
std::string to_string_timestamp(const rtc::RTC& value);
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision);
#endif/*__STRING_FORMAT_H__*/

View File

@ -62,11 +62,18 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
&field_frequency,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(462713300); // 162950000
update_freq(467225500); // 462713300
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
@ -82,7 +89,6 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
@ -106,6 +112,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable();
}
@ -114,6 +121,7 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
std::string str_byte = "";
if (is_data) {
// Colorize differently after message splits
str_console += (char)((console_color & 3) + 9);
//value = deframe_word(value);
@ -122,7 +130,7 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD
value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB
value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA
value &= 0x7F;
value &= 0x7F; // Ignore parity, which is the MSB now
if ((value >= 32) && (value < 127)) {
str_console += (char)value; // Printable
@ -139,6 +147,7 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
if (logger) str_log += str_byte;
if ((value != 0x7F) && (prev_value == 0x7F)) {
// Message split
console.writeln("");
console_color++;

View File

@ -26,6 +26,7 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "log_file.hpp"
#include "utility.hpp"
@ -91,8 +92,14 @@ private:
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 3 * 16, 240, 240 }
{ 0, 4 * 16, 240, 240 }
};
void update_freq(rf::Frequency f);

View File

@ -128,12 +128,8 @@ void EncodersConfigView::on_show() {
void EncodersConfigView::draw_waveform() {
size_t length = frame_fragments.length();
for (size_t n = 0; n < length; n++) {
if (frame_fragments[n] == '0')
waveform_buffer[n] = 0;
else
waveform_buffer[n] = 1;
}
for (size_t n = 0; n < length; n++)
waveform_buffer[n] = (frame_fragments[n] == '0') ? 0 : 1;
waveform.set_length(length);
waveform.set_dirty();

View File

@ -55,7 +55,7 @@ private:
//uint8_t scan_count;
//double scan_progress;
//unsigned int scan_index;
int8_t waveform_buffer[512];
int16_t waveform_buffer[512];
const encoder_def_t * encoder_def { };
//uint8_t enc_type = 0;

View File

@ -32,29 +32,34 @@ namespace ui {
void ViewWavView::update_scale(int32_t new_scale) {
scale = new_scale;
ns_per_pixel = (1000000000UL / wav_reader->sample_rate()) * scale;
field_pos_samples.set_step(scale);
refresh_waveform();
refresh_measurements();
}
void ViewWavView::refresh_waveform() {
int16_t sample;
for (size_t i = 0; i < 240; i++) {
wav_reader->data_seek(position + (i * scale));
wav_reader->read(&sample, 2);
waveform_buffer[i] = sample >> 8;
wav_reader->read(&waveform_buffer[i], sizeof(int16_t));
}
waveform.set_dirty();
// Window
uint64_t w_start = (position * 240) / wav_reader->sample_count();
uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240);
display.fill_rectangle({ 0, 10 * 16 + 1, 240, 16 }, Color::black());
display.fill_rectangle({ (Coord)w_start, 21 * 8, (Dim)w_width + 1, 8 }, Color::white());
display.draw_line({ 0, 10 * 16 + 1 }, { (Coord)w_start, 21 * 8 }, Color::white());
display.draw_line({ 239, 10 * 16 + 1 }, { (Coord)(w_start + w_width), 21 * 8 }, Color::white());
}
void ViewWavView::refresh_measurements() {
uint64_t span_ns = ns_per_pixel * abs(field_cursor_b.value() - field_cursor_a.value());
if (span_ns)
text_delta.set(to_string_dec_uint(span_ns / 1000) + "us (" + to_string_dec_uint(1000000000UL / span_ns) + "Hz)");
text_delta.set(unit_auto_scale(span_ns, 0, 3) + "s (" + to_string_dec_uint(1000000000UL / span_ns) + "Hz)");
else
text_delta.set("0us ?Hz");
//waveform.set_dirty();
set_dirty();
}
void ViewWavView::paint(Painter& painter) {
@ -62,22 +67,9 @@ void ViewWavView::paint(Painter& painter) {
painter.draw_hline({ 0, 6 * 16 - 1 }, 240, Color::grey());
painter.draw_hline({ 0, 10 * 16 }, 240, Color::grey());
// 0~127 to 0~15 color index
// Overall amplitude view, 0~127 to 0~255 color index
for (size_t i = 0; i < 240; i++)
painter.draw_vline({ (Coord)i, 11 * 16 }, 8, amplitude_colors[amplitude_buffer[i] >> 3]);
// Window
uint64_t w_start = (position * 240) / wav_reader->sample_count();
uint64_t w_width = (scale * 240) / (wav_reader->sample_count() / 240);
painter.fill_rectangle({ 0, 10 * 16 + 1, 240, 16 }, Color::black());
painter.fill_rectangle({ (Coord)w_start, 21 * 8, (Dim)w_width + 1, 8 }, Color::white());
display.draw_line({ 0, 10 * 16 + 1 }, { (Coord)w_start, 21 * 8 }, Color::white());
display.draw_line({ 239, 10 * 16 + 1 }, { (Coord)(w_start + w_width), 21 * 8 }, Color::white());
// Cursors
painter.fill_rectangle({ 0, 6 * 16 - 8, 240, 7 }, Color::black());
painter.draw_vline({ (Coord)field_cursor_a.value(), 11 * 8 }, 7, Color::cyan());
painter.draw_vline({ (Coord)field_cursor_b.value(), 11 * 8 }, 7, Color::magenta());
painter.draw_vline({ (Coord)i, 11 * 16 }, 8, spectrum_rgb2_lut[amplitude_buffer[i] << 1]);
}
void ViewWavView::on_pos_changed() {
@ -101,7 +93,7 @@ void ViewWavView::load_wav(std::filesystem::path file_path) {
text_filename.set(file_path.filename().string());
auto ms_duration = wav_reader->ms_duration();
text_duration.set(to_string_dec_uint(ms_duration / 1000) + "s" + to_string_dec_uint(ms_duration % 1000) + "ms");
text_duration.set(unit_auto_scale(ms_duration, 2, 3) + "s");
wav_reader->rewind();
@ -117,13 +109,7 @@ void ViewWavView::load_wav(std::filesystem::path file_path) {
for (size_t s = 0; s < subsampling_factor; s++) {
wav_reader->data_seek(((i * subsampling_factor) + s) * skip);
wav_reader->read(&sample, 2);
if (sample < 0)
sample = -sample;
sample >>= 8;
average += sample;
average += (abs(sample) >> 8);
}
amplitude_buffer[i] = average / subsampling_factor;
@ -135,18 +121,10 @@ void ViewWavView::load_wav(std::filesystem::path file_path) {
void ViewWavView::reset_controls() {
field_scale.set_value(1);
field_scale.on_change = [this](int32_t value) {
update_scale(value);
};
field_pos_seconds.set_value(0);
field_pos_seconds.on_change = [this](int32_t) {
on_pos_changed();
};
field_pos_samples.set_value(0);
field_pos_samples.on_change = [this](int32_t) {
on_pos_changed();
};
field_cursor_a.set_value(0);
field_cursor_b.set_value(0);
}
ViewWavView::ViewWavView(
@ -175,19 +153,30 @@ ViewWavView::ViewWavView(
auto open_view = nav.push<FileLoadView>();
open_view->on_changed = [this](std::filesystem::path file_path) {
load_wav(file_path);
field_pos_seconds.focus();
};
};
reset_controls();
field_scale.on_change = [this](int32_t value) {
update_scale(value);
};
field_pos_seconds.on_change = [this](int32_t) {
on_pos_changed();
};
field_pos_samples.on_change = [this](int32_t) {
on_pos_changed();
};
field_cursor_a.set_value(0);
field_cursor_a.on_change = [this](int32_t) {
refresh_waveform();
field_cursor_a.on_change = [this](int32_t v) {
waveform.set_cursor(0, v);
refresh_measurements();
};
field_cursor_b.set_value(0);
field_cursor_b.on_change = [this](int32_t) {
refresh_waveform();
field_cursor_b.on_change = [this](int32_t v) {
waveform.set_cursor(1, v);
refresh_measurements();
};
reset_controls();
}
void ViewWavView::focus() {

View File

@ -23,6 +23,7 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "io_wave.hpp"
#include "spectrum_color_lut.hpp"
namespace ui {
@ -41,32 +42,14 @@ private:
void update_scale(int32_t new_scale);
void refresh_waveform();
void refresh_measurements();
void on_pos_changed();
void load_wav(std::filesystem::path file_path);
void reset_controls();
const Color amplitude_colors[16] = {
{ 0x00, 0x3F, 0xB0 },
{ 0x00, 0x6D, 0xB5 },
{ 0x00, 0x9C, 0xBA },
{ 0x00, 0xBF, 0xB0 },
{ 0x00, 0xC5, 0x86 },
{ 0x00, 0xCA, 0x5A },
{ 0x00, 0xCF, 0x2A },
{ 0x06, 0xD4, 0x00 },
{ 0x3A, 0xDA, 0x00 },
{ 0x71, 0xDF, 0x00 },
{ 0xAA, 0xE4, 0x00 },
{ 0xE6, 0xE9, 0x00 },
{ 0xEF, 0xB9, 0x00 },
{ 0xF4, 0x83, 0x00 },
{ 0xF9, 0x4B, 0x00 },
{ 0xFF, 0x0F, 0x00 }
};
std::unique_ptr<WAVFileReader> wav_reader { };
int8_t waveform_buffer[240] { };
int16_t waveform_buffer[240] { };
uint8_t amplitude_buffer[240] { };
int32_t scale { 1 };
uint64_t ns_per_pixel { };

View File

@ -1465,7 +1465,7 @@ int32_t SymField::clip_value(const uint32_t index, const uint32_t value) {
Waveform::Waveform(
Rect parent_rect,
int8_t * data,
int16_t * data,
uint32_t length,
uint32_t offset,
bool digital,
@ -1480,6 +1480,16 @@ Waveform::Waveform(
//set_focusable(false);
}
void Waveform::set_cursor(const uint32_t i, const int16_t position) {
if (i < 2) {
if (position != cursors[i]) {
cursors[i] = position;
set_dirty();
}
show_cursors = true;
}
}
void Waveform::set_offset(const uint32_t new_offset) {
if (new_offset != offset_) {
offset_ = new_offset;
@ -1495,28 +1505,26 @@ void Waveform::set_length(const uint32_t new_length) {
}
void Waveform::paint(Painter& painter) {
uint32_t n, point_count;
size_t n;
Coord y, y_offset = screen_rect().location().y();
Coord prev_x = screen_rect().location().x(), prev_y;
float x, x_inc;
Dim h = screen_rect().size().height();
int8_t * data_start = data_ + offset_;
const float y_scale = (float)(h - 1) / 65536.0;
int16_t * data_start = data_ + offset_;
// Clear
painter.fill_rectangle(screen_rect(), Color::black());
if (!length_) return;
x_inc = (float)screen_rect().size().width() / length_;
point_count = length_;
const float y_scale = (float)(h - 1) / 256; // TODO: Make variable
if (!point_count) return;
if (digital_) {
// Digital waveform: each value is an horizontal line
x = 0;
h--;
for (n = 0; n < point_count; n++) {
for (n = 0; n < length_; n++) {
y = *(data_start++) ? h : 0;
if (n) {
@ -1532,9 +1540,9 @@ void Waveform::paint(Painter& painter) {
} else {
// Analog waveform: each value is a point's Y coordinate
x = prev_x + x_inc;
h = h / 2;
h /= 2;
prev_y = y_offset + h - (*(data_start++) * y_scale);
for (n = 1; n < point_count; n++) {
for (n = 1; n < length_; n++) {
y = y_offset + h - (*(data_start++) * y_scale);
display.draw_line( {prev_x, prev_y}, {(Coord)x, y}, color_);
@ -1543,6 +1551,17 @@ void Waveform::paint(Painter& painter) {
x += x_inc;
}
}
// Cursors
if (show_cursors) {
for (n = 0; n < 2; n++) {
painter.draw_vline(
Point(std::min(screen_rect().size().width(), (int)cursors[n]), y_offset),
screen_rect().size().height(),
cursor_colors[n]
);
}
}
}

View File

@ -598,7 +598,7 @@ private:
class Waveform : public Widget {
public:
Waveform(Rect parent_rect, int8_t * data, uint32_t length, uint32_t offset, bool digital, Color color);
Waveform(Rect parent_rect, int16_t * data, uint32_t length, uint32_t offset, bool digital, Color color);
Waveform(const Waveform&) = delete;
Waveform(Waveform&&) = delete;
@ -607,15 +607,20 @@ public:
void set_offset(const uint32_t new_offset);
void set_length(const uint32_t new_length);
void set_cursor(const uint32_t i, const int16_t position);
void paint(Painter& painter) override;
private:
int8_t * data_;
const Color cursor_colors[2] = { Color::cyan(), Color::magenta() };
int16_t * data_;
uint32_t length_;
uint32_t offset_;
bool digital_ { false };
Color color_;
int16_t cursors[2] { };
bool show_cursors { false };
};
class VuMeter : public Widget {