GeoMap and Jammer clean up

Jammer ranges can now be set with center and width
GeoMap can be moved with touch
GeoMap negative coordinates bugfix
Replay app throws error if no files found instead of crashing
This commit is contained in:
furrtek 2017-08-12 07:07:21 +01:00
parent 1a6d80cd10
commit cb880258fb
7 changed files with 499 additions and 304 deletions

View File

@ -102,7 +102,10 @@ void ReplayAppView::on_hide() {
}*/
void ReplayAppView::focus() {
field_frequency.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) {

View File

@ -45,7 +45,7 @@ public:
void focus() override;
std::string title() const override { return "Replay (broken)"; };
std::string title() const override { return "Replay (BETA)"; };
private:
NavigationView& nav_;

View File

@ -86,7 +86,7 @@ private:
};
Button button_set_map {
{ 7 * 8, 7 * 16, 14 * 8, 2 * 16 },
{ 8 * 8, 6 * 16, 14 * 8, 2 * 16 },
"Set from map"
};
};

View File

@ -22,7 +22,6 @@
#include "ui_geomap.hpp"
#include "adsb.hpp"
#include "portapack.hpp"
#include <cstring>
@ -53,18 +52,23 @@ GeoPos::GeoPos(
set_lat(0);
set_lon(0);
const auto changed = [this](int32_t) {
if (on_change)
on_change();
const auto changed_fn = [this](int32_t) {
if (on_change && report_change)
on_change(altitude(), lat(), lon());
};
field_altitude.on_change = changed;
field_lat_degrees.on_change = changed;
field_lat_minutes.on_change = changed;
field_lat_seconds.on_change = changed;
field_lon_degrees.on_change = changed;
field_lon_minutes.on_change = changed;
field_lon_seconds.on_change = changed;
field_altitude.on_change = changed_fn;
field_lat_degrees.on_change = changed_fn;
field_lat_minutes.on_change = changed_fn;
field_lat_seconds.on_change = changed_fn;
field_lon_degrees.on_change = changed_fn;
field_lon_minutes.on_change = changed_fn;
field_lon_seconds.on_change = changed_fn;
}
// Stupid hack to avoid an event loop
void GeoPos::set_report_change(bool v) {
report_change = v;
}
void GeoPos::focus() {
@ -77,14 +81,14 @@ void GeoPos::set_altitude(int32_t altitude) {
void GeoPos::set_lat(float lat) {
field_lat_degrees.set_value(lat);
field_lat_minutes.set_value((uint32_t)(lat / (1.0 / 60)) % 60);
field_lat_seconds.set_value((uint32_t)(lat / (1.0 / 3600)) % 60);
field_lat_minutes.set_value((uint32_t)abs(lat / (1.0 / 60)) % 60);
field_lat_seconds.set_value((uint32_t)abs(lat / (1.0 / 3600)) % 60);
}
void GeoPos::set_lon(float lon) {
field_lon_degrees.set_value(lon);
field_lon_minutes.set_value((uint32_t)(lon / (1.0 / 60)) % 60);
field_lon_seconds.set_value((uint32_t)(lon / (1.0 / 3600)) % 60);
field_lon_minutes.set_value((uint32_t)abs(lon / (1.0 / 60)) % 60);
field_lon_seconds.set_value((uint32_t)abs(lon / (1.0 / 3600)) % 60);
}
float GeoPos::lat() {
@ -103,19 +107,102 @@ void GeoPos::set_read_only(bool v) {
set_focusable(~v);
};
void GeoMapView::focus() {
if (!file_error) {
geopos.focus();
move_map();
} else
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
GeoMap::GeoMap(
Rect parent_rect
) : Widget { parent_rect }
{
//set_focusable(true);
}
GeoMapView::~GeoMapView() {
void GeoMap::paint(Painter& painter) {
Coord line;
std::array<ui::Color, 240> map_line_buffer;
//Color border;
const auto r = screen_rect();
// Ony redraw map if it moved by at least 1 pixel
if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) {
for (line = 0; line < r.height(); line++) {
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
map_file.read(map_line_buffer.data(), r.width() << 1);
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
}
prev_x_pos = x_pos;
prev_y_pos = y_pos;
}
if (mode_ == PROMPT) {
// Cross
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
} else {
draw_bearing({ 120, 32 + 144 }, angle_, 16, Color::red());
}
/*if (has_focus() || highlighted())
border = style().foreground;
else
border = Color::grey();
painter.draw_rectangle(
{ r.location().x(), r.location().y(), r.size().width(), r.size().height() },
border
);*/
}
void GeoMapView::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) {
bool GeoMap::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
set_highlighted(true);
if (on_move) {
Point p = event.point - screen_rect().center();
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio);
return true;
}
}
return false;
}
void GeoMap::move(const float lon, const float lat) {
lon_ = lon;
lat_ = lat;
Rect map_rect = screen_rect();
// Map is in Equidistant "Plate Carrée" projection
x_pos = map_center_x - (map_rect.width() / 2) + (lon_ / lon_ratio);
y_pos = map_center_y - (map_rect.height() / 2) + (lat_ / lat_ratio);
// Cap position
if (x_pos > (map_width - map_rect.width()))
x_pos = map_width - map_rect.width();
if (y_pos > (map_height + map_rect.height()))
y_pos = map_height - map_rect.height();
}
bool GeoMap::init() {
auto result = map_file.open("ADSB/world_map.bin");
if (result.is_valid()) {
return false;
}
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
map_center_x = map_width >> 1;
map_center_y = map_height >> 1;
lon_ratio = 180.0 / map_center_x;
lat_ratio = 90.0 / map_center_y;
return true;
}
void GeoMap::set_mode(GeoMapMode mode) {
mode_ = mode;
}
void GeoMap::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) {
Point arrow_a, arrow_b, arrow_c;
for (size_t thickness = 0; thickness < 3; thickness++) {
@ -131,62 +218,43 @@ void GeoMapView::draw_bearing(const Point origin, const uint32_t angle, uint32_t
}
}
void GeoMapView::move_map() {
Coord line;
int32_t x_pos, y_pos;
std::array<ui::Color, 240> map_line_buffer;
auto r = screen_rect();
Rect map_rect = { r.left(), r.top() + banner_height, r.width(), r.height() - banner_height };
altitude_ = geopos.altitude();
lat_ = geopos.lat();
lon_ = geopos.lon();
// Map is in Equidistant "Plate Carrée" projection
x_pos = map_center_x - (map_rect.width() / 2) + ((lat_ * map_center_x) / 180);
y_pos = map_center_y - (map_rect.height() / 2) + ((lon_ * map_center_y) / 90);
if (x_pos > (map_width - map_rect.width()))
x_pos = map_width - map_rect.width();
if (y_pos > (map_height + map_rect.height()))
y_pos = map_height - map_rect.height();
for (line = 0; line < map_rect.height(); line++) {
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
map_file.read(map_line_buffer.data(), map_rect.width() << 1);
display.draw_pixels({ 0, map_rect.top() + line, map_rect.width(), 1 }, map_line_buffer);
}
if (mode_ == PROMPT) {
display.fill_rectangle({ map_rect.center() - Point(16, 1), { 32, 2 } }, Color::red());
display.fill_rectangle({ map_rect.center() - Point(1, 16), { 2, 32 } }, Color::red());
} else {
draw_bearing({ 120, 32 + 144 }, angle_, 16, Color::red());
}
void GeoMapView::focus() {
if (!file_error) {
geopos.focus();
} else
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
}
void GeoMapView::setup() {
auto result = map_file.open("ADSB/world_map.bin");
if (result.is_valid()) {
file_error = true;
return;
}
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
map_center_x = map_width >> 1;
map_center_y = map_height >> 1;
add_child(&geopos);
add_children({
&geopos,
&geomap
});
geopos.set_altitude(altitude_);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.on_change = [this]() {
move_map();
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
altitude_ = altitude;
lat_ = lat;
lon_ = lon;
geomap.move(lon_, lat_);
geomap.set_dirty();
};
geomap.on_move = [this](float move_x, float move_y) {
lon_ += move_x;
lat_ += move_y;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lon(lon_);
geopos.set_lat(lat_);
geopos.set_report_change(true);
geomap.move(lon_, lat_);
geomap.set_dirty();
};
}
@ -206,8 +274,15 @@ GeoMapView::GeoMapView(
angle_ (angle)
{
mode_ = DISPLAY;
file_error = !geomap.init();
if (file_error) return;
setup();
geomap.set_mode(mode_);
geomap.move(lon_, lat_);
geopos.set_read_only(true);
}
@ -224,10 +299,16 @@ GeoMapView::GeoMapView(
lon_ (lon)
{
mode_ = PROMPT;
setup();
file_error = !geomap.init();
if (file_error) return;
setup();
add_child(&button_ok);
geomap.set_mode(mode_);
geomap.move(lon_, lat_);
button_ok.on_select = [this, on_done, &nav](Button&) {
if (on_done)
on_done(altitude_, lat_, lon_);

View File

@ -29,9 +29,14 @@
namespace ui {
enum GeoMapMode {
DISPLAY,
PROMPT
};
class GeoPos : public View {
public:
std::function<void(void)> on_change { };
std::function<void(int32_t, float, float)> on_change { };
GeoPos(const Point pos);
@ -41,12 +46,15 @@ public:
void set_altitude(int32_t altitude);
void set_lat(float lat);
void set_lon(float lon);
int32_t altitude();
float lat();
float lon();
int32_t altitude();
void set_report_change(bool v);
private:
bool read_only { false };
bool report_change { true };
Labels labels_position {
{ { 2 * 8, 0 * 16 }, "Alt: feet", Color::light_grey() },
@ -83,6 +91,35 @@ private:
};
};
class GeoMap : public Widget {
public:
std::function<void(float, float)> on_move { };
GeoMap(Rect parent_rect);
void paint(Painter& painter) override;
bool on_touch(const TouchEvent event) override;
bool init();
void set_mode(GeoMapMode mode);
void move(const float lon, const float lat);
private:
void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color);
GeoMapMode mode_ { };
File map_file { };
uint16_t map_width { }, map_height { };
int32_t map_center_x { }, map_center_y { };
float lon_ratio { }, lat_ratio { };
int32_t x_pos { }, y_pos { };
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
float lat_ { };
float lon_ { };
float angle_ { };
};
class GeoMapView : public View {
public:
GeoMapView(NavigationView& nav, std::string* tag, int32_t altitude, float lat, float lon, float angle);
@ -93,41 +130,35 @@ public:
GeoMapView& operator=(const GeoMapView&) = delete;
GeoMapView& operator=(GeoMapView&&) = delete;
~GeoMapView();
void focus() override;
std::string title() const override { return "Map view"; };
private:
enum Mode {
DISPLAY,
PROMPT
};
NavigationView& nav_;
void setup();
const std::function<void(int32_t, float, float)> on_done { };
const Dim banner_height = 3 * 16;
NavigationView& nav_;
const std::function<void(int32_t, float, float)> on_done { };
Mode mode_ { };
GeoMapMode mode_ { };
std::string* tag_ { };
int32_t altitude_ { };
float lat_ { };
float lon_ { };
float angle_ { };
File map_file { };
bool file_error { false };
uint16_t map_width { }, map_height { };
int32_t map_center_x { }, map_center_y { };
void setup();
void move_map();
void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color);
bool file_error { };
GeoPos geopos {
{ 0, 0 }
};
GeoMap geomap {
{ 0, banner_height, 240, 320 - 16 - banner_height }
};
Button button_ok {
{ 20 * 8, 8, 8 * 8, 2 * 16 },
"OK"

View File

@ -26,12 +26,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace ui {
@ -39,10 +33,90 @@ namespace ui {
void RangeView::focus() {
check_enabled.focus();
}
extern constexpr jammer_range_t RangeView::range_presets[];
extern constexpr Style RangeView::style_info;
void RangeView::update_min(rf::Frequency f) {
// Change everything except max
frequency_range.min = f;
button_min.set_text(to_string_short_freq(f));
center = (frequency_range.min + frequency_range.max) / 2;
width = abs(frequency_range.max - frequency_range.min);
button_center.set_text(to_string_short_freq(center));
button_width.set_text(to_string_short_freq(width));
}
void RangeView::update_max(rf::Frequency f) {
// Change everything except min
frequency_range.max = f;
button_max.set_text(to_string_short_freq(f));
center = (frequency_range.min + frequency_range.max) / 2;
width = abs(frequency_range.max - frequency_range.min);
button_center.set_text(to_string_short_freq(center));
button_width.set_text(to_string_short_freq(width));
}
void RangeView::update_center(rf::Frequency f) {
// Change min/max/center, keep width
center = f;
button_center.set_text(to_string_short_freq(center));
rf::Frequency min = center - (width / 2);
rf::Frequency max = min + width;
frequency_range.min = min;
button_min.set_text(to_string_short_freq(min));
frequency_range.max = max;
button_max.set_text(to_string_short_freq(max));
}
void RangeView::update_width(uint32_t w) {
// Change min/max/width, keep center
width = w;
button_width.set_text(to_string_short_freq(width));
rf::Frequency min = center - (width / 2);
rf::Frequency max = min + width;
frequency_range.min = min;
button_min.set_text(to_string_short_freq(min));
frequency_range.max = max;
button_max.set_text(to_string_short_freq(max));
}
void RangeView::paint(Painter&) {
// Draw lines and arrows
Rect r;
Point p;
Coord c;
r = button_center.screen_rect();
p = r.center() + Point(0, r.height() / 2);
display.draw_line(p, p + Point(0, 10), Color::grey());
r = button_width.screen_rect();
c = r.top() + (r.height() / 2);
p = {r.left() - 64, c};
display.draw_line({r.left(), c}, p, Color::grey());
display.draw_line(p, p + Point(10, -10), Color::grey());
display.draw_line(p, p + Point(10, 10), Color::grey());
p = {r.right() + 64, c};
display.draw_line({r.right(), c}, p, Color::grey());
display.draw_line(p, p + Point(-10, -10), Color::grey());
display.draw_line(p, p + Point(-10, 10), Color::grey());
}
RangeView::RangeView(NavigationView& nav) {
hidden(true);
@ -52,90 +126,58 @@ RangeView::RangeView(NavigationView& nav) {
&options_preset,
&button_min,
&button_max,
&text_info
&button_center,
&button_width
});
check_enabled.set_value(false);
check_enabled.on_select = [this](Checkbox&, bool v) {
frequency_range.enabled = v;
};
button_min.on_select = [this, &nav](Button& button) {
rf::Frequency * value_ptr;
value_ptr = &frequency_range.min;
auto new_view = nav.push<FrequencyKeypadView>(*value_ptr);
new_view->on_changed = [this, value_ptr, &button](rf::Frequency f) {
*value_ptr = f;
update_button(button, f);
update_range();
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.min);
new_view->on_changed = [this, &button](rf::Frequency f) {
update_min(f);
};
//update_button(button, f);
};
button_max.on_select = [this, &nav](Button& button) {
rf::Frequency * value_ptr;
value_ptr = &frequency_range.max;
auto new_view = nav.push<FrequencyKeypadView>(*value_ptr);
new_view->on_changed = [this, value_ptr, &button](rf::Frequency f) {
*value_ptr = f;
update_button(button, f);
update_range();
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
new_view->on_changed = [this, &button](rf::Frequency f) {
update_max(f);
};
//update_button(button, f);
};
text_info.set_style(&style_info);
button_center.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
new_view->on_changed = [this, &button](rf::Frequency f) {
update_center(f);
};
//update_button(button, f);
};
options_preset.set_selected_index(8); // ISM 868
button_width.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
new_view->on_changed = [this, &button](rf::Frequency f) {
update_width(f);
};
//update_button(button, f);
};
options_preset.on_change = [this](size_t, OptionsField::value_t v) {
frequency_range.min = range_presets[v].min;
frequency_range.max = range_presets[v].max;
update_min(range_presets[v].min);
update_max(range_presets[v].max);
check_enabled.set_value(true);
update_button(button_min, frequency_range.min);
update_button(button_max, frequency_range.max);
update_range();
};
}
void RangeView::update_button(Button& button, rf::Frequency f) {
std::string label;
options_preset.set_selected_index(11); // ISM 868
auto f_mhz = to_string_dec_int(f / 1000000, 4);
auto f_hz100 = to_string_dec_int((f / 1000) % 1000, 3, '0');
label = f_mhz + "." + f_hz100 + "M";
button.set_text(label);
}
void RangeView::update_range() {
std::string label;
rf::Frequency center, bw_khz;
center = (frequency_range.min + frequency_range.max) / 2;
bw_khz = abs(frequency_range.max - frequency_range.min) / 1000;
label = "C:" + to_string_short_freq(center) + "M W:";
if (bw_khz < 1000) {
label += to_string_dec_int(bw_khz, 3) + "kHz";
} else {
bw_khz /= 1000;
label += to_string_dec_int(bw_khz, 3) + "MHz";
}
while (label.length() < 23)
label += " ";
text_info.set(label);
check_enabled.set_value(false);
}
void JammerView::focus() {
@ -153,6 +195,104 @@ void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
text_range_number.set(to_string_dec_uint(range, 2));
}
}
void JammerView::set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration) {
jammer_channels[i].enabled = true;
jammer_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = center;
jammer_channels[i].duration = 30720 * duration;
}
extern constexpr Style JammerView::style_val;
extern constexpr Style JammerView::style_cancel;
void JammerView::start_tx() {
uint32_t c, i = 0;
size_t num_channels;
rf::Frequency start_freq, range_bw, range_bw_sub, ch_width;
bool out_of_ranges = false;
size_t hop_value = options_hop.selected_index_value();
// Disable all channels by default
for (c = 0; c < JAMMER_MAX_CH; c++)
jammer_channels[c].enabled = false;
// Generate jamming channels with JAMMER_MAX_CH maximum width
// Convert ranges min/max to center/bw
for (size_t r = 0; r < 3; r++) {
if (range_views[r]->frequency_range.enabled) {
range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min);
// Get lower bound
if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max)
start_freq = range_views[r]->frequency_range.min;
else
start_freq = range_views[r]->frequency_range.max;
if (range_bw >= JAMMER_CH_WIDTH) {
// Split range in multiple channels
num_channels = 0;
range_bw_sub = range_bw;
do {
range_bw_sub -= JAMMER_CH_WIDTH;
num_channels++;
} while (range_bw_sub >= JAMMER_CH_WIDTH);
ch_width = range_bw / num_channels;
for (c = 0; c < num_channels; c++) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
break;
}
set_jammer_channel(i, ch_width, start_freq + (ch_width / 2) + (ch_width * c), hop_value);
i++;
}
} else {
// Range fits in a single channel
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
} else {
set_jammer_channel(i, range_bw, start_freq + (range_bw / 2), hop_value);
i++;
}
}
}
}
if (!out_of_ranges && i) {
text_range_total.set("/" + to_string_dec_uint(i, 2));
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.set_sampling_rate(3072000U);
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(3500000U);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
} else {
if (out_of_ranges)
nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz.");
else
nav_.display_modal("Error", "No range enabled.");
}
}
void JammerView::stop_tx() {
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
transmitter_model.disable();
radio::disable();
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
jamming = false;
}
JammerView::JammerView(
NavigationView& nav
@ -161,20 +301,6 @@ JammerView::JammerView(
Rect view_rect = { 0, 3 * 8, 240, 80 };
baseband::run_image(portapack::spi_flash::image_tag_jammer);
static constexpr Style style_val {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
static constexpr Style style_cancel {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
add_children({
&tab_view,
&view_range_a,
@ -193,94 +319,16 @@ JammerView::JammerView(
view_range_b.set_parent_rect(view_rect);
view_range_c.set_parent_rect(view_rect);
options_type.set_selected_index(2); // Sweep
options_type.set_selected_index(3); // Rand CW
options_speed.set_selected_index(3); // 10kHz
options_hop.set_selected_index(1); // 50ms
button_transmit.set_style(&style_val);
button_transmit.on_select = [this, &nav, jammer_channels](Button&) {
uint32_t c, i = 0;
size_t num_channels;
rf::Frequency start_freq, range_bw, range_bw_sub, ch_width;
bool out_of_ranges = false;
if (jamming) {
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
transmitter_model.disable();
radio::disable();
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
jamming = false;
} else {
// Disable all ranges by default
for (c = 0; c < JAMMER_MAX_CH; c++)
jammer_channels[c].enabled = false;
// Generate jamming "channels", maximum: JAMMER_MAX_CH
// Convert ranges min/max to center/bw
for (size_t r = 0; r < 3; r++) {
if (range_views[r]->frequency_range.enabled) {
range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min);
// Sort
if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max)
start_freq = range_views[r]->frequency_range.min;
else
start_freq = range_views[r]->frequency_range.max;
if (range_bw >= JAMMER_CH_WIDTH) {
num_channels = 0;
range_bw_sub = range_bw;
do {
range_bw_sub -= JAMMER_CH_WIDTH;
num_channels++;
} while (range_bw_sub >= JAMMER_CH_WIDTH);
ch_width = range_bw / num_channels;
for (c = 0; c < num_channels; c++) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
break;
}
jammer_channels[i].enabled = true;
jammer_channels[i].width = (ch_width * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = start_freq + (ch_width / 2) + (ch_width * c);
jammer_channels[i].duration = 30720 * options_hop.selected_index_value();
i++;
}
} else {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
} else {
jammer_channels[i].enabled = true;
jammer_channels[i].width = (range_bw * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = start_freq + (range_bw / 2);
jammer_channels[i].duration = 30720 * options_hop.selected_index_value();
i++;
}
}
}
}
if (!out_of_ranges) {
text_range_total.set("/" + to_string_dec_uint(i, 2));
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.set_sampling_rate(3072000U);
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(3500000U);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
} else {
nav.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz.");
}
}
button_transmit.on_select = [this](Button&) {
if (jamming)
stop_tx();
else
start_tx();
};
}

View File

@ -38,12 +38,18 @@ public:
RangeView(NavigationView& nav);
void focus() override;
void paint(Painter&) override;
jammer_range_t frequency_range { };
jammer_range_t frequency_range { false, 0, 0 };
private:
void update_button(Button& button, rf::Frequency f);
void update_range();
void update_min(rf::Frequency f);
void update_max(rf::Frequency f);
void update_center(rf::Frequency f);
void update_width(uint32_t w);
uint32_t width { };
rf::Frequency center { };
static constexpr Style style_info {
.font = font::fixed_8x16,
@ -55,17 +61,17 @@ private:
// GSM900 Orange
{ true, 935000000, 945000000 }, // BW:10M
// GSM1800 Orange
{ false, 1808000000, 1832000000 }, // BW:24M
{ true, 1808000000, 1832000000 }, // BW:24M
// GSM900 SFR
{ true, 950000000, 960000000 }, // BW:10M
// GSM1800 SFR
{ false, 1832000000, 1853000000 }, // BW:21M
{ true, 1832000000, 1853000000 }, // BW:21M
// GSM900 Bouygues
{ true, 925000000, 935000000 }, // BW:10M
// GSM1800 Bouygues
{ false, 1858000000, 1880000000 }, // BW:22M
{ true, 1858000000, 1880000000 }, // BW:22M
// GSM900 Free
{ true, 945000000, 950000000 }, // BW:5M
@ -88,7 +94,7 @@ private:
// GPS L1
{ true, 1575420000 - 500000, 1575420000 + 500000 }, // BW: 1MHz
// GPS L2
{ false, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz
{ true, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz
// WLAN 2.4G CH1
{ true, 2412000000 - 11000000, 2412000000 + 11000000}, // BW: 22MHz
@ -119,20 +125,22 @@ private:
};
Labels labels {
{ { 2 * 8, 5 * 8 }, "Preset:", Color::light_grey() },
{ { 5 * 8, 9 * 8 }, "Start:", Color::light_grey() },
{ { 6 * 8, 13 * 8 }, "Stop:", Color::light_grey() },
{ { 1 * 8, 4 * 8 }, "Preset:", Color::light_grey() },
{ { 2 * 8, 9 * 8 + 4 }, "Start", Color::light_grey() },
{ { 23 * 8, 9 * 8 + 4 }, "Stop", Color::light_grey() },
{ { 12 * 8, 6 * 8 }, "Center", Color::light_grey() },
{ { 12 * 8 + 4, 14 * 8 }, "Width", Color::light_grey() }
};
Checkbox check_enabled {
{ 44, 1 * 8 },
{ 7 * 8, 4 },
12,
"Enable range",
false
};
OptionsField options_preset {
{ 9 * 8, 5 * 8 },
{ 9 * 8, 4 * 8 },
19,
{
{ "GSM900 Orange FR", 0 },
@ -141,41 +149,44 @@ private:
{ "GSM1800 SFR FR", 3 },
{ "GSM900 Bouygues FR", 4 },
{ "GSM1800 Bouygues FR", 5 },
{ "GSM Free FR ", 6 },
{ "GSM-R FR ", 7 },
{ "DECT ", 8 },
{ "Optifib ", 9 },
{ "ISM 433 ", 10 },
{ "ISM 868 ", 11 },
{ "GPS L1 ", 12 },
{ "GPS L2 ", 13 },
{ "WLAN 2.4G CH1 ", 14 },
{ "WLAN 2.4G CH2 ", 15 },
{ "WLAN 2.4G CH3 ", 16 },
{ "WLAN 2.4G CH4 ", 17 },
{ "WLAN 2.4G CH5 ", 18 },
{ "WLAN 2.4G CH6 ", 19 },
{ "WLAN 2.4G CH7 ", 20 },
{ "WLAN 2.4G CH8 ", 21 },
{ "WLAN 2.4G CH9 ", 22 },
{ "WLAN 2.4G CH10 ", 23 },
{ "WLAN 2.4G CH11 ", 24 },
{ "WLAN 2.4G CH12 ", 25 },
{ "WLAN 2.4G CH13 ", 26 }
{ "GSM Free FR", 6 },
{ "GSM-R FR", 7 },
{ "DECT", 8 },
{ "Optifib", 9 },
{ "ISM 433", 10 },
{ "ISM 868", 11 },
{ "GPS L1", 12 },
{ "GPS L2", 13 },
{ "WLAN 2.4G CH1", 14 },
{ "WLAN 2.4G CH2", 15 },
{ "WLAN 2.4G CH3", 16 },
{ "WLAN 2.4G CH4", 17 },
{ "WLAN 2.4G CH5", 18 },
{ "WLAN 2.4G CH6", 19 },
{ "WLAN 2.4G CH7", 20 },
{ "WLAN 2.4G CH8", 21 },
{ "WLAN 2.4G CH9", 22 },
{ "WLAN 2.4G CH10", 23 },
{ "WLAN 2.4G CH11", 24 },
{ "WLAN 2.4G CH12", 25 },
{ "WLAN 2.4G CH13", 26 }
}
};
Button button_min {
{ 13 * 8, 4 * 16, 120, 28 },
{ 0 * 8, 6 * 16, 11 * 8, 28 },
""
};
Button button_max {
{ 13 * 8, 6 * 16, 120, 28 },
{ 19 * 8, 6 * 16, 11 * 8, 28 },
""
};
Text text_info {
{ 3 * 8, 8 * 16, 25 * 8, 16 },
Button button_center {
{ 76, 4 * 16, 11 * 8, 28 },
""
};
Button button_width {
{ 76, 8 * 16, 11 * 8, 28 },
""
};
};
@ -185,6 +196,11 @@ public:
JammerView(NavigationView& nav);
~JammerView();
JammerView(const JammerView&) = delete;
JammerView(JammerView&&) = delete;
JammerView& operator=(const JammerView&) = delete;
JammerView& operator=(JammerView&&) = delete;
void focus() override;
std::string title() const override { return "Jammer"; };
@ -192,10 +208,25 @@ public:
private:
NavigationView& nav_;
void start_tx();
void stop_tx();
void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
void on_retune(const rf::Frequency freq, const uint32_t range);
JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
bool jamming { false };
static constexpr Style style_val {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
static constexpr Style style_cancel {
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
RangeView view_range_a { nav_ };
RangeView view_range_b { nav_ };
RangeView view_range_c { nav_ };
@ -216,20 +247,21 @@ private:
OptionsField options_type {
{ 9 * 8, 12 * 16 },
5,
8,
{
{ "FSK ", 0 },
{ "Tone ", 1 },
{ "Sweep", 2 }
{ "Rand FSK", 0 },
{ "FM tone", 1 },
{ "CW sweep", 2 },
{ "Rand CW", 3 },
}
};
Text text_range_number {
{ 18 * 8, 12 * 16, 2 * 8, 16 },
{ 22 * 8, 12 * 16, 2 * 8, 16 },
"--"
};
Text text_range_total {
{ 20 * 8, 12 * 16, 3 * 8, 16 },
{ 24 * 8, 12 * 16, 3 * 8, 16 },
"/--"
};
@ -260,7 +292,7 @@ private:
};
Button button_transmit {
{ 1 * 8, 16 * 16, 80, 48 },
{ 9 * 8, 16 * 16, 96, 48 },
"START"
};