portapack-mayhem/firmware/application/ui/ui_transmitter.cpp
Kyle Reed fca373d936
Add Remote App & UI updates. (#1451)
* Alpha order sub-menus
* WIP Getting Remote types outlined
* WIP building UI
* WIP adding RemoteButton control
* WIP Fix build
* WIP Basic editing support
* Border on the active button
* Make TxView2 sane
* Add easier RGB color creation from uint32
* Center some button icons
* WIP Remote - main UI
* WIP main UI mostly working, can send
* Add 'join' utility
* WIP save/load
* Pre-alloc buttons to prevent focus dangling
* Alpha order settings/debug pages
* Add UI for picking capture and set frequency
* WIP Getting really close now
* Fix path for init name
* Some fit & finish
2023-09-18 23:22:46 +02:00

261 lines
7.3 KiB
C++

/*
* Copyright (C) 2014 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.
*/
// TODO: Consolidate and make TX Widgets instead like ui_receiver.
#include "ui_transmitter.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "string_format.hpp"
#include "max2837.hpp"
using namespace portapack;
#define POWER_THRESHOLD_HIGH 47
#define POWER_THRESHOLD_MED 38
#define POWER_THRESHOLD_LOW 17
namespace ui {
/* Gets a style indicating total TX gain level. */
static const Style* get_style_for_gain(uint8_t tot_gain) {
if (tot_gain > POWER_THRESHOLD_HIGH) return &Styles::red;
if (tot_gain > POWER_THRESHOLD_MED)
return &Styles::orange;
if (tot_gain > POWER_THRESHOLD_LOW)
return &Styles::yellow;
return nullptr; // Uses default.
}
/* TransmitterView *******************************************************/
void TransmitterView::paint(Painter& painter) {
size_t c;
Point pos = {0, screen_pos().y()};
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() + 32 + 8};
}
}
void TransmitterView::on_target_frequency_changed(rf::Frequency f) {
transmitter_model.set_target_frequency(f);
}
void TransmitterView::on_channel_bandwidth_changed(uint32_t channel_bandwidth) {
// TODO: this doesn't actually affect the radio through the model.
transmitter_model.set_channel_bandwidth(channel_bandwidth);
}
void TransmitterView::on_tx_gain_changed(int32_t tx_gain) {
transmitter_model.set_tx_gain(tx_gain);
update_gainlevel_styles();
}
void TransmitterView::on_tx_amp_changed(bool rf_amp) {
transmitter_model.set_rf_amp(rf_amp);
update_gainlevel_styles();
}
void TransmitterView::update_gainlevel_styles() {
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
auto style = get_style_for_gain(tot_gain);
field_gain.set_style(style);
text_gain.set_style(style);
field_amp.set_style(style);
text_amp.set_style(style);
}
void TransmitterView::set_transmitting(const bool transmitting) {
if (transmitting) {
button_start.set_text("STOP");
button_start.set_style(&style_stop);
} else {
button_start.set_text("START");
button_start.set_style(&style_start);
}
transmitting_ = transmitting;
}
void TransmitterView::on_show() {
field_frequency.set_value(transmitter_model.target_frequency());
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
update_gainlevel_styles();
}
void TransmitterView::focus() {
button_start.focus();
}
TransmitterView::TransmitterView(
const Coord y,
const uint64_t frequency_step,
const uint32_t channel_bandwidth,
const bool lock)
: lock_{lock} {
set_parent_rect({0, y, 30 * 8, 6 * 8});
add_children({
&field_frequency,
&field_frequency_step,
&text_gain,
&field_gain,
&button_start,
&text_amp,
&field_amp,
});
set_transmitting(false);
if (lock_) {
field_frequency.set_focusable(false);
field_frequency.set_style(&style_locked);
} else {
// TODO: Make a widget.
if (channel_bandwidth) {
add_children({&text_bw,
&field_bw});
field_bw.on_change = [this](int32_t v) {
on_channel_bandwidth_changed(v * 1000);
};
field_bw.set_value(channel_bandwidth);
}
}
field_frequency.on_change = [this](rf::Frequency f) {
on_target_frequency_changed(f);
};
field_frequency.on_edit = [this]() {
if (on_edit_frequency)
on_edit_frequency();
};
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v);
this->field_frequency.set_step(v);
};
// TODO: Shouldn't be a ctor parameter because it doesn't work with app settings.
field_frequency_step.set_by_value(frequency_step);
field_gain.on_change = [this](uint32_t tx_gain) {
on_tx_gain_changed(tx_gain);
};
field_amp.on_change = [this](uint32_t rf_amp) {
on_tx_amp_changed((bool)rf_amp);
};
button_start.on_select = [this](Button&) {
if (transmitting_) {
if (on_stop)
on_stop();
} else {
if (on_start)
on_start();
}
};
}
TransmitterView::~TransmitterView() {
// TODO: Does this make sense? Seems wrong to have
// what's basically a widget control the radio.
audio::output::stop();
transmitter_model.disable();
baseband::shutdown();
}
/* TransmitterView2 *******************************************************/
TransmitterView2::TransmitterView2(Point pos, bool short_ui) {
// There are two modes, short and !short
// Short: "G:XX A:YY"
// !Short: "Gain:XX Amp:YY"
Dim width = short_ui ? (9 * 8) : (14 * 8);
set_parent_rect({pos, {width, 16}});
add_children({
&text_labels,
&field_gain,
&field_amp,
});
// Set up controls depending UI mode.
text_labels.set(short_ui ? "G: A:" : "Gain: Amp:");
text_labels.set_parent_rect(
short_ui
? Rect{0 * 8, 0 * 16, 7 * 8, 1 * 16}
: Rect{0 * 8, 0 * 16, 12 * 8, 1 * 16});
field_gain.set_parent_rect(
short_ui
? Rect{2 * 8, 0 * 16, 2 * 8, 1 * 16}
: Rect{5 * 8, 0 * 16, 2 * 8, 1 * 16});
field_amp.set_parent_rect(
short_ui
? Rect{7 * 8, 0 * 16, 2 * 8, 1 * 16}
: Rect{12 * 8, 0 * 16, 2 * 8, 1 * 16});
field_gain.set_value(transmitter_model.tx_gain());
field_gain.on_change = [this](uint32_t tx_gain) {
transmitter_model.set_tx_gain(tx_gain);
update_gainlevel_styles();
};
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
field_amp.on_change = [this](uint32_t rf_amp) {
transmitter_model.set_rf_amp(rf_amp > 0);
update_gainlevel_styles();
};
update_gainlevel_styles();
}
void TransmitterView2::update_gainlevel_styles() {
int8_t tot_gain = transmitter_model.tx_gain() + (transmitter_model.rf_amp() ? 14 : 0);
auto style = get_style_for_gain(tot_gain);
text_labels.set_style(style);
field_gain.set_style(style);
field_amp.set_style(style);
}
} /* namespace ui */