From fba5b507ad8f8960cdf6bcecbea3c0c462a29a99 Mon Sep 17 00:00:00 2001 From: furrtek Date: Thu, 3 Aug 2017 19:06:59 +0100 Subject: [PATCH] Made a GeoPos widget for lon/lat/alt entry and display (APRS...) Cleaned up the GeoMap view, can be used as input --- firmware/application/main.cpp | 5 +- firmware/application/ui_adsb_tx.cpp | 54 ++++--- firmware/application/ui_adsb_tx.hpp | 48 ++----- firmware/application/ui_geomap.cpp | 214 ++++++++++++++++++++-------- firmware/application/ui_geomap.hpp | 109 ++++++++++---- firmware/application/ui_tabview.hpp | 4 - firmware/common/adsb.cpp | 2 +- firmware/common/adsb.hpp | 5 +- firmware/common/ui.cpp | 5 + firmware/common/ui.hpp | 4 + 10 files changed, 285 insertions(+), 165 deletions(-) diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index f17fe906b..06f54ffe4 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -24,19 +24,20 @@ // Gimp image > indexed colors (16), then "xxd -i *.bmp" //TEST: ADS-B tx manchester encoder, velocity and squawk frames -//TEST: Mic tx //TEST: Menuview refresh, seems to blink a lot //TEST: Check AFSK transmit end, skips last bits ? //TEST: Imperial in whipcalc +//BUG: ADSB transmit baseband code works only if stuck in a loop (txdone message makes everything go nuts) //BUG: CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay //BUG: REPLAY See what's wrong with quality (format, or need for interpolation filter ?) -//TODO: REPLAY Convert C16 to C8 on M0 core //BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one //BUG: SCANNER Multiple slices //BUG: REPLAY freezes when SD card not present //BUG: RDS doesn't stop baseband when stopping tx ? +//TODO: REPLAY Convert C16 to C8 on M0 core +//TODO: Use TabView //TODO: De bruijn sequence scanner for encoders //TODO: Make freqman refresh simpler (use previous black rectangle method) //TODO: Merge AFSK and TONES procs ? diff --git a/firmware/application/ui_adsb_tx.cpp b/firmware/application/ui_adsb_tx.cpp index 4294682ef..3bc377b4d 100644 --- a/firmware/application/ui_adsb_tx.cpp +++ b/firmware/application/ui_adsb_tx.cpp @@ -22,7 +22,6 @@ #include "ui_adsb_tx.hpp" #include "ui_alphanum.hpp" -#include "ui_geomap.hpp" #include "manchester.hpp" #include "string_format.hpp" @@ -43,10 +42,6 @@ Compass::Compass( { } -Point Compass::polar_to_point(uint32_t angle, uint32_t distance) { - return Point(sin_f32(DEG_TO_RAD(angle) + (pi / 2)) * distance, -sin_f32(DEG_TO_RAD(angle)) * distance); -} - void Compass::set_value(uint32_t new_value) { Point center = screen_pos() + Point(32, 32); @@ -103,40 +98,35 @@ ADSBPositionView::ADSBPositionView(NavigationView& nav) { set_type("position"); add_children({ - &labels_position, - &field_altitude, - &field_lat_degrees, - &field_lat_minutes, - &field_lat_seconds, - &field_lon_degrees, - &field_lon_minutes, - &field_lon_seconds, + &geopos, &button_set_map }); - field_altitude.set_value(36000); - field_lat_degrees.set_value(0); - field_lat_minutes.set_value(0); - field_lat_seconds.set_value(0); - field_lon_degrees.set_value(0); - field_lon_minutes.set_value(0); - field_lon_seconds.set_value(0); + geopos.set_altitude(36000); button_set_map.on_select = [this, &nav](Button&) { - nav.push(GeoMapView::Mode::SET); + nav.push( + geopos.altitude(), + geopos.lat(), + geopos.lon(), + [this](int32_t altitude, float lat, float lon) { + geopos.set_altitude(altitude); + geopos.set_lat(lat); + geopos.set_lon(lon); + }); }; } void ADSBPositionView::collect_frames(const uint32_t ICAO_address, std::vector& frame_list) { ADSBFrame temp_frame; - encode_frame_pos(temp_frame, ICAO_address, field_altitude.value(), - field_lat_degrees.value(), field_lon_degrees.value(), 0); + encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), + geopos.lat(), geopos.lon(), 0); frame_list.emplace_back(temp_frame); - encode_frame_pos(temp_frame, ICAO_address, field_altitude.value(), - field_lat_degrees.value(), field_lon_degrees.value(), 1); + encode_frame_pos(temp_frame, ICAO_address, geopos.altitude(), + geopos.lat(), geopos.lon(), 1); frame_list.emplace_back(temp_frame); } @@ -223,10 +213,17 @@ ADSBTxView::~ADSBTxView() { void ADSBTxView::generate_frames() { const uint32_t ICAO_address = sym_icao.value_hex_u64(); + /* This scheme kinda sucks. Each "tab"'s collect_frames method + * is called to generate its related frame(s). Getting values + * from each widget of each tab would be better ? + * */ view_position.collect_frames(ICAO_address, frames); view_callsign.collect_frames(ICAO_address, frames); view_speed.collect_frames(ICAO_address, frames); view_squawk.collect_frames(ICAO_address, frames); + + // DEBUG: Show how many frames were generated + text_frame.set(to_string_dec_uint(frames.size()) + " frame(s)."); //memset(bin_ptr, 0, 240); @@ -352,14 +349,11 @@ ADSBTxView::ADSBTxView( view_speed.set_parent_rect(view_rect); view_squawk.set_parent_rect(view_rect); - sym_icao.on_change = [this]() { - generate_frames(); - }; - tx_view.on_start = [this]() { start_tx(); tx_view.set_transmitting(true); - rotate_frames(); + // Disable for DEBUG + //rotate_frames(); }; tx_view.on_stop = [this]() { diff --git a/firmware/application/ui_adsb_tx.hpp b/firmware/application/ui_adsb_tx.hpp index 88f4831c4..c911ef1dd 100644 --- a/firmware/application/ui_adsb_tx.hpp +++ b/firmware/application/ui_adsb_tx.hpp @@ -23,9 +23,9 @@ #include "ui.hpp" #include "adsb.hpp" #include "utility.hpp" -#include "sine_table.hpp" #include "ui_textentry.hpp" #include "ui_widget.hpp" +#include "ui_geomap.hpp" #include "ui_tabview.hpp" #include "ui_navigation.hpp" #include "ui_transmitter.hpp" @@ -47,8 +47,6 @@ public: void paint(Painter&) override; private: - Point polar_to_point(uint32_t angle, uint32_t distance); - const range_t range { 0, 359 }; uint32_t value_ { 0 }; }; @@ -83,38 +81,8 @@ public: void collect_frames(const uint32_t ICAO_address, std::vector& frame_list); private: - Labels labels_position { - { { 2 * 8, 2 * 16 }, "Alt: feet", Color::light_grey() }, - { { 2 * 8, 4 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font - { { 2 * 8, 5 * 16 }, "Lon: * ' \"", Color::light_grey() }, - }; - - NumberField field_altitude { - { 7 * 8, 2 * 16 }, - 5, - { -1000, 50000 }, - 250, - ' ' - }; - - NumberField field_lat_degrees { - { 7 * 8, 4 * 16 }, 4, { -90, 90 }, 1, ' ' - }; - NumberField field_lat_minutes { - { 12 * 8, 4 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - NumberField field_lat_seconds { - { 15 * 8, 4 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - - NumberField field_lon_degrees { - { 7 * 8, 5 * 16 }, 4, { -180, 180 }, 1, ' ' - }; - NumberField field_lon_minutes { - { 12 * 8, 5 * 16 }, 2, { 0, 59 }, 1, ' ' - }; - NumberField field_lon_seconds { - { 15 * 8, 5 * 16 }, 2, { 0, 59 }, 1, ' ' + GeoPos geopos { + { 0, 2 * 16 } }; Button button_set_map { @@ -194,13 +162,13 @@ public: std::string title() const override { return "ADS-B transmit"; }; private: - enum tx_modes { + /*enum tx_modes { IDLE = 0, SINGLE, SEQUENCE - }; + };*/ - const float plane_lats[12] = { + /*const float plane_lats[12] = { 0, -1, -2, @@ -227,9 +195,9 @@ private: -1, -1, -1 - }; + };*/ - tx_modes tx_mode = IDLE; + //tx_modes tx_mode = IDLE; NavigationView& nav_; std::vector frames { }; diff --git a/firmware/application/ui_geomap.cpp b/firmware/application/ui_geomap.cpp index a276458b9..0b064d6d2 100644 --- a/firmware/application/ui_geomap.cpp +++ b/firmware/application/ui_geomap.cpp @@ -23,8 +23,6 @@ #include "ui_geomap.hpp" #include "adsb.hpp" -//#include "string_format.hpp" -#include "sine_table_int8.hpp" #include "portapack.hpp" #include @@ -34,9 +32,80 @@ using namespace portapack; namespace ui { +GeoPos::GeoPos( + const Point pos +) { + set_parent_rect({pos, {30 * 8, 3 * 16}}); + + add_children({ + &labels_position, + &field_altitude, + &field_lat_degrees, + &field_lat_minutes, + &field_lat_seconds, + &field_lon_degrees, + &field_lon_minutes, + &field_lon_seconds + }); + + // Defaults + set_altitude(0); + set_lat(0); + set_lon(0); + + const auto changed = [this](int32_t) { + if (on_change) + on_change(); + }; + + 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; +} + +void GeoPos::focus() { + field_altitude.focus(); +} + +void GeoPos::set_altitude(int32_t altitude) { + field_altitude.set_value(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); +} + +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); +} + +float GeoPos::lat() { + return field_lat_degrees.value() + (field_lat_minutes.value() / 60.0) + (field_lat_seconds.value() / 3600.0); +}; + +float GeoPos::lon() { + return field_lon_degrees.value() + (field_lon_minutes.value() / 60.0) + (field_lon_seconds.value() / 3600.0); +}; + +int32_t GeoPos::altitude() { + return field_altitude.value(); +}; + +void GeoPos::set_read_only(bool v) { + set_focusable(~v); +}; + void GeoMapView::focus() { if (!file_error) { - field_xpos.focus(); + geopos.focus(); move_map(); } else nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr); @@ -46,61 +115,58 @@ GeoMapView::~GeoMapView() { } -Point GeoMapView::polar_to_point(const uint8_t angle, const uint32_t size) { - return { (Coord)(sine_table_i8[(angle + 64) & 0xFF] * size) >> 7, (Coord)(sine_table_i8[angle] * size) >> 7 }; -} - -void GeoMapView::draw_bearing(const Point origin, const uint8_t angle, uint32_t size, const Color color) { +void GeoMapView::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) { Point arrow_a, arrow_b, arrow_c; - arrow_a = polar_to_point(angle, size) + origin; - arrow_b = polar_to_point(angle + 128 - 16, size) + origin; - arrow_c = polar_to_point(angle + 128 + 16, size) + origin; - display.draw_line(arrow_a, arrow_b, color); - display.draw_line(arrow_b, arrow_c, color); - display.draw_line(arrow_c, arrow_a, color); - - size--; - arrow_a = polar_to_point(angle, size) + origin; - arrow_b = polar_to_point(angle + 128 - 16, size) + origin; - arrow_c = polar_to_point(angle + 128 + 16, size) + origin; - display.draw_line(arrow_a, arrow_b, color); - display.draw_line(arrow_b, arrow_c, color); - display.draw_line(arrow_c, arrow_a, color); + for (size_t thickness = 0; thickness < 3; thickness++) { + arrow_a = polar_to_point(angle, size) + origin; + arrow_b = polar_to_point(angle + 180 - 30, size) + origin; + arrow_c = polar_to_point(angle + 180 + 30, size) + origin; + + display.draw_line(arrow_a, arrow_b, color); + display.draw_line(arrow_b, arrow_c, color); + display.draw_line(arrow_c, arrow_a, color); + + size--; + } } void GeoMapView::move_map() { Coord line; int32_t x_pos, y_pos; - std::array map_buffer; + 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 - 120 + ((((field_xpos.value() * map_center_x) << 8) / 180) >> 8); - y_pos = map_center_y - 144 + ((((field_ypos.value() * map_center_y) << 8) / 90) >> 8); + 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 - 240)) - x_pos = map_width - 240; - if (y_pos > (map_height + 288)) - y_pos = map_height - 288; + 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 < 288; line++) { + for (line = 0; line < map_rect.height(); line++) { map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1)); - map_file.read(map_buffer.data(), 240 * 2); - display.draw_pixels({ 0, 32 + line, 240, 1 }, map_buffer); + 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); } - draw_bearing({ 120, 32 + 144 }, field_angle.value(), 16, Color::red()); - - //display.fill_rectangle({ 120-16, 176-1, 32, 2 }, Color::red()); - //display.fill_rectangle({ 120-1, 176-16, 2, 32 }, Color::red()); + 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()); + } } -GeoMapView::GeoMapView( - NavigationView& nav, - Mode mode -) : nav_ (nav), - mode_ (mode) -{ +void GeoMapView::setup() { auto result = map_file.open("ADSB/world_map.bin"); if (result.is_valid()) { file_error = true; @@ -113,24 +179,60 @@ GeoMapView::GeoMapView( map_center_x = map_width >> 1; map_center_y = map_height >> 1; - add_children({ - &field_xpos, - &field_ypos - }); + add_child(&geopos); - if (mode_ == SHOW) { - add_child(&field_angle); - field_angle.on_change = [this](int32_t) { - move_map(); - }; - } + geopos.set_altitude(altitude_); + geopos.set_lat(lat_); + geopos.set_lon(lon_); - field_xpos.on_change = [this](int32_t) { - move_map(); - }; - field_ypos.on_change = [this](int32_t) { + geopos.on_change = [this]() { move_map(); }; } +// Display mode +GeoMapView::GeoMapView( + NavigationView& nav, + std::string* tag, + int32_t altitude, + float lat, + float lon, + float angle +) : nav_ (nav), + tag_ (tag), + altitude_ (altitude), + lat_ (lat), + lon_ (lon), + angle_ (angle) +{ + mode_ = DISPLAY; + setup(); + + geopos.set_read_only(true); +} + +// Prompt mode +GeoMapView::GeoMapView( + NavigationView& nav, + int32_t altitude, + float lat, + float lon, + const std::function on_done +) : nav_ (nav), + altitude_ (altitude), + lat_ (lat), + lon_ (lon) +{ + mode_ = PROMPT; + setup(); + + add_child(&button_ok); + + button_ok.on_select = [this, on_done, &nav](Button&) { + if (on_done) + on_done(altitude_, lat_, lon_); + nav.pop(); + }; +} + } /* namespace ui */ diff --git a/firmware/application/ui_geomap.hpp b/firmware/application/ui_geomap.hpp index d37ad5324..32e75acfd 100644 --- a/firmware/application/ui_geomap.hpp +++ b/firmware/application/ui_geomap.hpp @@ -29,14 +29,70 @@ namespace ui { -class GeoMapView : public View { +class GeoPos : public View { public: - enum Mode { - SHOW, - SET + std::function on_change { }; + + GeoPos(const Point pos); + + void focus() override; + + void set_read_only(bool v); + void set_altitude(int32_t altitude); + void set_lat(float lat); + void set_lon(float lon); + float lat(); + float lon(); + int32_t altitude(); + +private: + bool read_only { false }; + + Labels labels_position { + { { 2 * 8, 0 * 16 }, "Alt: feet", Color::light_grey() }, + { { 2 * 8, 1 * 16 }, "Lat: * ' \"", Color::light_grey() }, // No ° symbol in 8x16 font + { { 2 * 8, 2 * 16 }, "Lon: * ' \"", Color::light_grey() }, }; - GeoMapView(NavigationView& nav, Mode mode); + NumberField field_altitude { + { 7 * 8, 0 * 16 }, + 5, + { -1000, 50000 }, + 250, + ' ' + }; + + NumberField field_lat_degrees { + { 7 * 8, 1 * 16 }, 4, { -90, 90 }, 1, ' ' + }; + NumberField field_lat_minutes { + { 12 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' ' + }; + NumberField field_lat_seconds { + { 15 * 8, 1 * 16 }, 2, { 0, 59 }, 1, ' ' + }; + + NumberField field_lon_degrees { + { 7 * 8, 2 * 16 }, 4, { -180, 180 }, 1, ' ' + }; + NumberField field_lon_minutes { + { 12 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' ' + }; + NumberField field_lon_seconds { + { 15 * 8, 2 * 16 }, 2, { 0, 59 }, 1, ' ' + }; +}; + +class GeoMapView : public View { +public: + GeoMapView(NavigationView& nav, std::string* tag, int32_t altitude, float lat, float lon, float angle); + GeoMapView(NavigationView& nav, int32_t altitude, float lat, float lon, const std::function on_done); + + GeoMapView(const GeoMapView&) = delete; + GeoMapView(GeoMapView&&) = delete; + GeoMapView& operator=(const GeoMapView&) = delete; + GeoMapView& operator=(GeoMapView&&) = delete; + ~GeoMapView(); void focus() override; @@ -44,42 +100,37 @@ public: std::string title() const override { return "Map view"; }; private: + enum Mode { + DISPLAY, + PROMPT + }; + + const Dim banner_height = 3 * 16; NavigationView& nav_; + const std::function on_done { }; Mode 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(); - Point polar_to_point(const uint8_t angle, const uint32_t size); - void draw_bearing(const Point origin, const uint8_t angle, uint32_t size, const Color color); + void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color); - Labels labels { - { { 0 * 8, 0 * 8 }, "Test", Color::light_grey() } + GeoPos geopos { + { 0, 0 } }; - NumberField field_xpos { - { 0, 0 }, - 4, - { -180, 180 }, - 1, - '0' - }; - NumberField field_ypos { - { 6 * 8, 0 }, - 4, - { -90, 90 }, - 1, - '0' - }; - NumberField field_angle { - { 12 * 8, 0 }, - 3, - { 0, 255 }, - 1, - '0' + Button button_ok { + { 20 * 8, 8, 8 * 8, 2 * 16 }, + "OK" }; }; diff --git a/firmware/application/ui_tabview.hpp b/firmware/application/ui_tabview.hpp index 6919ddec3..550a4e519 100644 --- a/firmware/application/ui_tabview.hpp +++ b/firmware/application/ui_tabview.hpp @@ -27,8 +27,6 @@ #include "ui_widget.hpp" #include "ui_navigation.hpp" #include "ui_painter.hpp" -#include "ui_receiver.hpp" -#include "ui_font_fixed_8x16.hpp" namespace ui { @@ -53,8 +51,6 @@ private: class TabView : public View { public: - //std::function on_change { }; - struct TabDef { std::string text; ui::Color color; diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index 629631bbc..388966a89 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -161,7 +161,7 @@ int cpr_N(float lat, int is_odd) { // ... and ... // if (lon > 180) lon -= 360; -void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t altitude, +void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, const float latitude, const float longitude, const uint32_t time_parity) { uint32_t altitude_coded; diff --git a/firmware/common/adsb.hpp b/firmware/common/adsb.hpp index 6c3c8a3e4..8e348bd2e 100644 --- a/firmware/common/adsb.hpp +++ b/firmware/common/adsb.hpp @@ -24,14 +24,13 @@ #define __ADSB_H__ #include "adsb_frame.hpp" +#include "ui.hpp" #include #include namespace adsb { -#define DEG_TO_RAD(d) (d * (2 * pi) / 360.0) - enum downlink_format { DF_ADSB = 17, DF_EHS_SQUAWK = 21 @@ -70,7 +69,7 @@ const float adsb_lat_lut[58] = { void make_frame_adsb(ADSBFrame& frame, const uint32_t ICAO_address); void encode_frame_id(ADSBFrame& frame, const uint32_t ICAO_address, const std::string& callsign); -void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t altitude, +void encode_frame_pos(ADSBFrame& frame, const uint32_t ICAO_address, const int32_t altitude, const float latitude, const float longitude, const uint32_t time_parity); void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, const float angle, const int32_t v_rate); diff --git a/firmware/common/ui.cpp b/firmware/common/ui.cpp index 06255c2de..f1011463f 100644 --- a/firmware/common/ui.cpp +++ b/firmware/common/ui.cpp @@ -20,6 +20,7 @@ */ #include "ui.hpp" +#include "sine_table.hpp" #include @@ -69,4 +70,8 @@ Rect& Rect::operator-=(const Point& p) { return *this; } +Point polar_to_point(float angle, uint32_t distance) { + return Point(sin_f32(DEG_TO_RAD(angle) + (pi / 2)) * distance, -sin_f32(DEG_TO_RAD(angle)) * distance); +} + } /* namespace ui */ diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index 0a5ebd428..e2113bcfa 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -26,6 +26,8 @@ namespace ui { +#define DEG_TO_RAD(d) (d * (2 * pi) / 360.0) + using Coord = int16_t; using Dim = int16_t; @@ -326,6 +328,8 @@ struct TouchEvent { Type type; }; +Point polar_to_point(float angle, uint32_t distance); + } /* namespace ui */ #endif/*__UI_H__*/