From cb880258fb8bdfc3651a4e731c95bb154bee34f9 Mon Sep 17 00:00:00 2001 From: furrtek Date: Sat, 12 Aug 2017 07:07:21 +0100 Subject: [PATCH] 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 --- firmware/application/replay_app.cpp | 5 +- firmware/application/replay_app.hpp | 2 +- firmware/application/ui_adsb_tx.hpp | 2 +- firmware/application/ui_geomap.cpp | 225 +++++++++++------ firmware/application/ui_geomap.hpp | 69 +++-- firmware/application/ui_jammer.cpp | 378 ++++++++++++++++------------ firmware/application/ui_jammer.hpp | 122 +++++---- 7 files changed, 499 insertions(+), 304 deletions(-) diff --git a/firmware/application/replay_app.cpp b/firmware/application/replay_app.cpp index 0627acb8..b54e2bb3 100644 --- a/firmware/application/replay_app.cpp +++ b/firmware/application/replay_app.cpp @@ -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) { diff --git a/firmware/application/replay_app.hpp b/firmware/application/replay_app.hpp index f8ae50ba..0404350a 100644 --- a/firmware/application/replay_app.hpp +++ b/firmware/application/replay_app.hpp @@ -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_; diff --git a/firmware/application/ui_adsb_tx.hpp b/firmware/application/ui_adsb_tx.hpp index c911ef1d..ab5bd97f 100644 --- a/firmware/application/ui_adsb_tx.hpp +++ b/firmware/application/ui_adsb_tx.hpp @@ -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" }; }; diff --git a/firmware/application/ui_geomap.cpp b/firmware/application/ui_geomap.cpp index 0b064d6d..bb22faaf 100644 --- a/firmware/application/ui_geomap.cpp +++ b/firmware/application/ui_geomap.cpp @@ -22,7 +22,6 @@ #include "ui_geomap.hpp" -#include "adsb.hpp" #include "portapack.hpp" #include @@ -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 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 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_); diff --git a/firmware/application/ui_geomap.hpp b/firmware/application/ui_geomap.hpp index 32e75acf..96ad8398 100644 --- a/firmware/application/ui_geomap.hpp +++ b/firmware/application/ui_geomap.hpp @@ -29,9 +29,14 @@ namespace ui { +enum GeoMapMode { + DISPLAY, + PROMPT +}; + class GeoPos : public View { public: - std::function on_change { }; + std::function 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 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 on_done { }; const Dim banner_height = 3 * 16; - NavigationView& nav_; - const std::function 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" diff --git a/firmware/application/ui_jammer.cpp b/firmware/application/ui_jammer.cpp index 8b2265bd..69b2194f 100644 --- a/firmware/application/ui_jammer.cpp +++ b/firmware/application/ui_jammer.cpp @@ -26,12 +26,6 @@ #include "baseband_api.hpp" #include "string_format.hpp" -#include "portapack_shared_memory.hpp" -#include "portapack_persistent_memory.hpp" - -#include -#include - 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(*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(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(*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(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(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(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(); }; } diff --git a/firmware/application/ui_jammer.hpp b/firmware/application/ui_jammer.hpp index 86416acc..29d20bdc 100644 --- a/firmware/application/ui_jammer.hpp +++ b/firmware/application/ui_jammer.hpp @@ -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" };