mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-11-19 19:42:24 -05:00
Openstreetmap support (#2765)
This commit is contained in:
parent
e6d4081c06
commit
4dda7cfaa5
20 changed files with 447 additions and 162 deletions
|
|
@ -456,7 +456,10 @@ void AISAppView::on_tick_second() {
|
|||
++timer_seconds;
|
||||
if (timer_seconds % 10 == 0) {
|
||||
if (recent_entry_detail_view.hidden()) return;
|
||||
recent_entry_detail_view.update_map_markers(recent);
|
||||
if (got_new_packet) {
|
||||
got_new_packet = false;
|
||||
recent_entry_detail_view.update_map_markers(recent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +478,7 @@ void AISAppView::on_packet(const ais::Packet& packet) {
|
|||
if (logger) {
|
||||
logger->on_packet(packet);
|
||||
}
|
||||
|
||||
got_new_packet = true;
|
||||
auto& entry = ::on_packet(recent, packet.source_id());
|
||||
entry.update(packet);
|
||||
recent_entries_view.set_dirty();
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ class AISAppView : public View {
|
|||
};
|
||||
SignalToken signal_token_tick_second{};
|
||||
uint8_t timer_seconds = 0;
|
||||
bool got_new_packet{false}; // got any new packet since latest screen update?
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::AISPacket,
|
||||
|
|
|
|||
|
|
@ -635,14 +635,14 @@ void ADSBRxView::refresh_ui() {
|
|||
|
||||
if (details_view->map_active()) {
|
||||
// Is it time to clear and refresh the map's markers?
|
||||
if (ticks_since_marker_refresh >= MARKER_UPDATE_SECONDS) {
|
||||
if (ticks_since_marker_refresh >= (details_view->get_map_type() == MAP_TYPE_OSM ? MARKER_UPDATE_SECONDS_OSM : MARKER_UPDATE_SECONDS_BIN)) {
|
||||
map_needs_update = true;
|
||||
ticks_since_marker_refresh = 0;
|
||||
details_view->clear_map_markers();
|
||||
}
|
||||
} else {
|
||||
// Refresh map immediately once active.
|
||||
ticks_since_marker_refresh = MARKER_UPDATE_SECONDS;
|
||||
ticks_since_marker_refresh = MARKER_UPDATE_SECONDS_OSM; // this is the bigger, so set it to force
|
||||
}
|
||||
|
||||
// Process the entries list.
|
||||
|
|
|
|||
|
|
@ -64,8 +64,9 @@ namespace ui {
|
|||
#define VEL_AIR_SUBSONIC 3
|
||||
#define VEL_AIR_SUPERSONIC 4
|
||||
|
||||
#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames
|
||||
#define MARKER_UPDATE_SECONDS 5 // "other" map marker redraw interval
|
||||
#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames
|
||||
#define MARKER_UPDATE_SECONDS_OSM 10 // "other" map marker redraw interval for osm
|
||||
#define MARKER_UPDATE_SECONDS_BIN 5 // "other" map marker redraw interval for bin map
|
||||
|
||||
/* Thresholds (in seconds) that define the transition between ages. */
|
||||
struct ADSBAgeLimit {
|
||||
|
|
@ -277,6 +278,13 @@ class ADSBRxDetailsView : public View {
|
|||
|
||||
std::string title() const override { return "Details"; }
|
||||
|
||||
MapType get_map_type() {
|
||||
if (geomap_view_)
|
||||
return geomap_view_->get_map_type();
|
||||
else
|
||||
return MapType::MAP_TYPE_BIN; // default
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh_ui();
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
|
|
@ -388,7 +396,7 @@ class ADSBRxView : public View {
|
|||
|
||||
SignalToken signal_token_tick_second{};
|
||||
uint8_t tick_count = 0;
|
||||
uint16_t ticks_since_marker_refresh{MARKER_UPDATE_SECONDS};
|
||||
uint16_t ticks_since_marker_refresh{MARKER_UPDATE_SECONDS_OSM};
|
||||
|
||||
/* Max number of entries that can be updated in a single pass.
|
||||
* 16 is one screen of recent entries. */
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ static void _sumlr(void);
|
|||
#undef __O
|
||||
#undef __P
|
||||
|
||||
#define PI 3.141592653589793
|
||||
#include "mathdef.hpp"
|
||||
|
||||
static void _numinput(byte k);
|
||||
static double dpush(double d);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ int HALF_HEIGHT = 0;
|
|||
#define JOGGING_SPEED 1.2
|
||||
#define MAX_ENTITY_DISTANCE 200
|
||||
#define ITEM_COLLIDER_DIST 6
|
||||
#define PI 3.14159265358979323846
|
||||
#include "mathdef.hpp"
|
||||
#define GUN_WIDTH 30
|
||||
#define GUN_HEIGHT 40
|
||||
#define GUN_TARGET_POS 24
|
||||
|
|
|
|||
|
|
@ -158,9 +158,10 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
|
|||
}
|
||||
|
||||
// never move this before the first load() bc that will mess load up
|
||||
geomap.on_move = [this](float lon, float lat) {
|
||||
geomap.on_move = [this](float lon, float lat, bool absolute) {
|
||||
(void)lon;
|
||||
(void)lat;
|
||||
(void)absolute;
|
||||
load_markers();
|
||||
};
|
||||
btn_back.on_select = [this, &nav](Button&) {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ File::Result<bool> File::eof() {
|
|||
File::Result<File::Offset> File::seek(Offset new_position) {
|
||||
/* NOTE: Returns *old* position, not new position */
|
||||
const auto old_position = tell();
|
||||
const auto result = f_lseek(&f, new_position);
|
||||
const auto result = (old_position == new_position) ? FR_OK : f_lseek(&f, new_position);
|
||||
if (result != FR_OK) {
|
||||
return {static_cast<Error>(result)};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,21 +22,17 @@
|
|||
*/
|
||||
|
||||
#include "ui_geomap.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string_view>
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "complex.hpp"
|
||||
#include "ui_font_fixed_5x8.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit,
|
||||
|
|
@ -188,6 +184,7 @@ int32_t GeoPos::speed() {
|
|||
GeoMap::GeoMap(
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect}, markerListLen(0) {
|
||||
has_osm = use_osm = find_osm_file_tile();
|
||||
}
|
||||
|
||||
bool GeoMap::on_encoder(const EncoderEvent delta) {
|
||||
|
|
@ -201,6 +198,8 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
|
|||
map_zoom += (map_zoom >= MAP_ZOOM_RESOLUTION_LIMIT) ? map_zoom : 1;
|
||||
}
|
||||
}
|
||||
map_osm_zoom++;
|
||||
if (has_osm) set_osm_max_zoom();
|
||||
} else if (delta < 0) {
|
||||
if (map_zoom > -MAX_MAP_ZOOM_OUT) {
|
||||
if (map_zoom == 1) {
|
||||
|
|
@ -212,12 +211,18 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
|
|||
map_zoom--;
|
||||
}
|
||||
}
|
||||
if (map_osm_zoom > 0) map_osm_zoom--;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
map_visible = map_opened && (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT);
|
||||
zoom_pixel_offset = (map_visible && (map_zoom > 1)) ? (float)map_zoom / 2 : 0.0f;
|
||||
if (use_osm) {
|
||||
map_visible = true;
|
||||
zoom_pixel_offset = 0;
|
||||
} else {
|
||||
zoom_pixel_offset = (map_visible && (map_zoom > 1)) ? (float)map_zoom / 2 : 0.0f;
|
||||
}
|
||||
|
||||
// Trigger map redraw
|
||||
redraw_map = true;
|
||||
|
|
@ -225,7 +230,7 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
|
||||
void GeoMap::map_read_line_bin(ui::Color* buffer, uint16_t pixels) {
|
||||
if (map_zoom == 1) {
|
||||
map_file.read(buffer, pixels << 1);
|
||||
} else if (map_zoom > 1) {
|
||||
|
|
@ -264,39 +269,86 @@ void GeoMap::draw_markers(Painter& painter) {
|
|||
void GeoMap::draw_marker_item(Painter& painter, GeoMarker& item, const Color color, const Color fontColor, const Color backColor) {
|
||||
const auto r = screen_rect();
|
||||
const ui::Point itemPoint = item_rect_pixel(item);
|
||||
|
||||
if ((itemPoint.x() >= 0) && (itemPoint.x() < r.width()) &&
|
||||
(itemPoint.y() > 10) && (itemPoint.y() < r.height())) // Dont draw within symbol size of top
|
||||
{
|
||||
draw_marker(painter, {itemPoint.x(), itemPoint.y() + r.top()}, item.angle, item.tag, color, fontColor, backColor);
|
||||
draw_marker(painter, {itemPoint.x() + r.left(), itemPoint.y() + r.top()}, item.angle, item.tag, color, fontColor, backColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate screen position of item, adjusted for zoom factor.
|
||||
ui::Point GeoMap::item_rect_pixel(GeoMarker& item) {
|
||||
const auto r = screen_rect();
|
||||
const auto geomap_rect_half_width = r.width() / 2;
|
||||
const auto geomap_rect_half_height = r.height() / 2;
|
||||
|
||||
GeoPoint mapPoint = lat_lon_to_map_pixel(item.lat, item.lon);
|
||||
float x = mapPoint.x - x_pos;
|
||||
float y = mapPoint.y - y_pos;
|
||||
|
||||
if (map_zoom > 1) {
|
||||
x = x * map_zoom + zoom_pixel_offset;
|
||||
y = y * map_zoom + zoom_pixel_offset;
|
||||
} else if (map_zoom < 0) {
|
||||
x = x / (-map_zoom);
|
||||
y = y / (-map_zoom);
|
||||
if (!use_osm) {
|
||||
const auto r = screen_rect();
|
||||
const auto geomap_rect_half_width = r.width() / 2;
|
||||
const auto geomap_rect_half_height = r.height() / 2;
|
||||
GeoPoint mapPoint = lat_lon_to_map_pixel(item.lat, item.lon);
|
||||
float x = mapPoint.x - x_pos;
|
||||
float y = mapPoint.y - y_pos;
|
||||
if (map_zoom > 1) {
|
||||
x = x * map_zoom + zoom_pixel_offset;
|
||||
y = y * map_zoom + zoom_pixel_offset;
|
||||
} else if (map_zoom < 0) {
|
||||
x = x / (-map_zoom);
|
||||
y = y / (-map_zoom);
|
||||
}
|
||||
x += geomap_rect_half_width;
|
||||
y += geomap_rect_half_height;
|
||||
return {(int16_t)x, (int16_t)y};
|
||||
}
|
||||
|
||||
x += geomap_rect_half_width;
|
||||
y += geomap_rect_half_height;
|
||||
|
||||
// osm calculation
|
||||
double y = lat_to_pixel_y_tile(item.lat, map_osm_zoom) - viewport_top_left_py;
|
||||
double x = lon_to_pixel_x_tile(item.lon, map_osm_zoom) - viewport_top_left_px;
|
||||
return {(int16_t)x, (int16_t)y};
|
||||
}
|
||||
|
||||
// Converts latitude/longitude to pixel coordinates in map file.
|
||||
/**
|
||||
* @brief Converts longitude to a map tile's X-coordinate.
|
||||
* @param lon The longitude in degrees.
|
||||
* @param zoom The zoom level.
|
||||
* @return The X-coordinate of the tile.
|
||||
*/
|
||||
int GeoMap::lon2tile(double lon, int zoom) {
|
||||
return (int)floor((lon + 180.0) / 360.0 * pow(2.0, zoom));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts latitude to a map tile's Y-coordinate.
|
||||
* @param lat The latitude in degrees.
|
||||
* @param zoom The zoom level.
|
||||
* @return The Y-coordinate of the tile.
|
||||
*/
|
||||
int GeoMap::lat2tile(double lat, int zoom) {
|
||||
// Convert latitude from degrees to radians for trigonometric functions
|
||||
double lat_rad = lat * M_PI / 180.0;
|
||||
// Perform the Mercator projection calculation
|
||||
return (int)floor((1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * pow(2.0, zoom));
|
||||
}
|
||||
|
||||
void GeoMap::set_osm_max_zoom() {
|
||||
if (map_osm_zoom > 20) map_osm_zoom = 20;
|
||||
for (uint8_t i = map_osm_zoom; i > 0; i--) {
|
||||
int tile_x = lon2tile(lon_, i);
|
||||
int tile_y = lat2tile(lat_, i);
|
||||
std::string filename = "/OSM/" + to_string_dec_int(i) + "/" + to_string_dec_int(tile_x) + "/" + to_string_dec_int(tile_y) + ".bmp";
|
||||
std::filesystem::path file_path(filename);
|
||||
if (file_exists(file_path)) {
|
||||
map_osm_zoom = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
map_osm_zoom = 0; // should not happen
|
||||
}
|
||||
|
||||
// checks if the tile file presents or not. to determine if we got osm or not
|
||||
uint8_t GeoMap::find_osm_file_tile() {
|
||||
std::string filename = "/OSM/" + to_string_dec_int(0) + "/" + to_string_dec_int(0) + "/" + to_string_dec_int(0) + ".bmp";
|
||||
std::filesystem::path file_path(filename);
|
||||
if (file_exists(file_path)) return 1;
|
||||
return 0; // not found
|
||||
}
|
||||
|
||||
// Converts latitude/longitude to pixel coordinates in binary map file.
|
||||
// (Note that when map_zoom==1, one pixel in map file corresponds to 1 pixel on screen)
|
||||
GeoPoint GeoMap::lat_lon_to_map_pixel(float lat, float lon) {
|
||||
// Using WGS 84/Pseudo-Mercator projection
|
||||
|
|
@ -310,9 +362,7 @@ GeoPoint GeoMap::lat_lon_to_map_pixel(float lat, float lon) {
|
|||
}
|
||||
|
||||
// Draw grid in place of map (when zoom-in level is too high).
|
||||
void GeoMap::draw_map_grid() {
|
||||
const auto r = screen_rect();
|
||||
|
||||
void GeoMap::draw_map_grid(ui::Rect r) {
|
||||
// Grid spacing is just based on zoom at the moment, and centered on screen.
|
||||
// TODO: Maybe align with latitude/longitude seconds instead?
|
||||
int grid_spacing = map_zoom * 2;
|
||||
|
|
@ -332,60 +382,184 @@ void GeoMap::draw_map_grid() {
|
|||
}
|
||||
}
|
||||
|
||||
double GeoMap::tile_pixel_x_to_lon(int x, int zoom) {
|
||||
double map_width = pow(2.0, zoom) * TILE_SIZE;
|
||||
return (x / map_width * 360.0) - 180.0;
|
||||
}
|
||||
|
||||
double GeoMap::tile_pixel_y_to_lat(int y, int zoom) {
|
||||
double map_height = pow(2.0, zoom) * TILE_SIZE;
|
||||
double n = M_PI * (1.0 - 2.0 * y / map_height);
|
||||
return atan(sinh(n)) * 180.0 / M_PI;
|
||||
}
|
||||
|
||||
double GeoMap::lon_to_pixel_x_tile(double lon, int zoom) {
|
||||
return ((lon + 180.0) / 360.0) * pow(2.0, zoom) * TILE_SIZE;
|
||||
}
|
||||
|
||||
double GeoMap::lat_to_pixel_y_tile(double lat, int zoom) {
|
||||
double lat_rad = lat * M_PI / 180.0;
|
||||
double sin_lat = sin(lat_rad);
|
||||
return ((1.0 - log((1.0 + sin_lat) / (1.0 - sin_lat)) / (2.0 * M_PI)) / 2.0) * pow(2.0, zoom) * TILE_SIZE;
|
||||
}
|
||||
|
||||
bool GeoMap::draw_osm_file(int zoom, int tile_x, int tile_y, int relative_x, int relative_y) {
|
||||
const auto r = screen_rect();
|
||||
// Early exit if the tile is completely outside the viewport
|
||||
if (relative_x >= r.width() || relative_y >= r.height() ||
|
||||
relative_x + TILE_SIZE <= 0 || relative_y + TILE_SIZE <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BMPFile bmp{};
|
||||
bmp.open("/OSM/" + to_string_dec_int(zoom) + "/" + to_string_dec_int(tile_x) + "/" + to_string_dec_int(tile_y) + ".bmp", true);
|
||||
// 1. Define the source and destination areas, starting with the full tile.
|
||||
int src_x = 0;
|
||||
int src_y = 0;
|
||||
int dest_x = relative_x;
|
||||
int dest_y = relative_y;
|
||||
int clip_w = TILE_SIZE;
|
||||
int clip_h = TILE_SIZE;
|
||||
// 2. Clip left edge
|
||||
if (dest_x < 0) {
|
||||
src_x = -dest_x;
|
||||
clip_w += dest_x;
|
||||
dest_x = 0;
|
||||
}
|
||||
// 3. Clip top edge
|
||||
if (dest_y < 0) {
|
||||
src_y = -dest_y;
|
||||
clip_h += dest_y;
|
||||
dest_y = 0;
|
||||
}
|
||||
// 4. Clip right edge
|
||||
if (dest_x + clip_w > r.width()) {
|
||||
clip_w = r.width() - dest_x;
|
||||
}
|
||||
// 5. Clip bottom edge
|
||||
if (dest_y + clip_h > r.height()) {
|
||||
clip_h = r.height() - dest_y;
|
||||
}
|
||||
// 6. If clipping resulted in no visible area, we're done.
|
||||
if (clip_w <= 0 || clip_h <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bmp.is_loaded()) {
|
||||
// Draw an error rectangle using the calculated clipped dimensions
|
||||
ui::Rect error_rect{{dest_x + r.left(), dest_y + r.top()}, {clip_w, clip_h}};
|
||||
display.fill_rectangle(error_rect, Theme::getInstance()->bg_darkest->background);
|
||||
return false;
|
||||
}
|
||||
std::vector<ui::Color> line(clip_w);
|
||||
for (int y = 0; y < clip_h; ++y) {
|
||||
int source_row = src_y + y;
|
||||
int dest_row = dest_y + y;
|
||||
bmp.seek(src_x, source_row);
|
||||
for (int x = 0; x < clip_w; ++x) {
|
||||
bmp.read_next_px(line[x], true);
|
||||
}
|
||||
display.draw_pixels({dest_x + r.left(), dest_row + r.top(), clip_w, 1}, line);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
std::array<ui::Color, geomap_rect_width> map_line_buffer;
|
||||
int16_t zoom_seek_x, zoom_seek_y;
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel or the markers list was updated
|
||||
if (map_zoom <= 1) {
|
||||
// Zooming out, or no zoom
|
||||
const int min_diff = abs(map_zoom);
|
||||
if ((int)abs(x_pos - prev_x_pos) >= min_diff)
|
||||
redraw_map = true;
|
||||
else if ((int)abs(y_pos - prev_y_pos) >= min_diff)
|
||||
redraw_map = true;
|
||||
if (!use_osm) {
|
||||
// Ony redraw map if it moved by at least 1 pixel or the markers list was updated
|
||||
if (map_zoom <= 1) {
|
||||
// Zooming out, or no zoom
|
||||
const int min_diff = abs(map_zoom);
|
||||
if ((int)abs(x_pos - prev_x_pos) >= min_diff)
|
||||
redraw_map = true;
|
||||
else if ((int)abs(y_pos - prev_y_pos) >= min_diff)
|
||||
redraw_map = true;
|
||||
} else {
|
||||
// Zooming in; magnify position differences (utilizing zoom_pixel_offset)
|
||||
if ((int)(abs(x_pos - prev_x_pos) * map_zoom) >= 1)
|
||||
redraw_map = true;
|
||||
else if ((int)(abs(y_pos - prev_y_pos) * map_zoom) >= 1)
|
||||
redraw_map = true;
|
||||
}
|
||||
} else {
|
||||
// Zooming in; magnify position differences (utilizing zoom_pixel_offset)
|
||||
if ((int)(abs(x_pos - prev_x_pos) * map_zoom) >= 1)
|
||||
redraw_map = true;
|
||||
else if ((int)(abs(y_pos - prev_y_pos) * map_zoom) >= 1)
|
||||
redraw_map = true;
|
||||
// using osm; needs to be stricter with the redraws, it'll be checked on move
|
||||
}
|
||||
|
||||
if (redraw_map) {
|
||||
prev_x_pos = x_pos; // Note x_pos/y_pos pixel position in map file now correspond to screen rect CENTER pixel
|
||||
prev_y_pos = y_pos;
|
||||
redraw_map = false;
|
||||
|
||||
// Adjust starting corner position of map per zoom setting;
|
||||
// When zooming in the map should technically by shifted left & up by another map_zoom/2 pixels but
|
||||
// the map_read_line() function doesn't handle that yet so we're adjusting markers instead (see zoom_pixel_offset).
|
||||
if (map_zoom > 1) {
|
||||
zoom_seek_x = x_pos - (float)r.width() / (2 * map_zoom);
|
||||
zoom_seek_y = y_pos - (float)r.height() / (2 * map_zoom);
|
||||
} else {
|
||||
zoom_seek_x = x_pos - (r.width() * abs(map_zoom)) / 2;
|
||||
zoom_seek_y = y_pos - (r.height() * abs(map_zoom)) / 2;
|
||||
}
|
||||
|
||||
if (map_visible) {
|
||||
// Read from map file and display to zoomed scale
|
||||
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
|
||||
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
|
||||
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
|
||||
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
|
||||
map_read_line(map_line_buffer.data(), r.width());
|
||||
if (!use_osm) {
|
||||
prev_x_pos = x_pos; // Note x_pos/y_pos pixel position in map file now correspond to screen rect CENTER pixel
|
||||
prev_y_pos = y_pos;
|
||||
// Adjust starting corner position of map per zoom setting;
|
||||
// When zooming in the map should technically by shifted left & up by another map_zoom/2 pixels but
|
||||
// the map_read_line_bin() function doesn't handle that yet so we're adjusting markers instead (see zoom_pixel_offset).
|
||||
if (map_zoom > 1) {
|
||||
zoom_seek_x = x_pos - (float)r.width() / (2 * map_zoom);
|
||||
zoom_seek_y = y_pos - (float)r.height() / (2 * map_zoom);
|
||||
} else {
|
||||
zoom_seek_x = x_pos - (r.width() * abs(map_zoom)) / 2;
|
||||
zoom_seek_y = y_pos - (r.height() * abs(map_zoom)) / 2;
|
||||
}
|
||||
// Read from map file and display to zoomed scale
|
||||
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
|
||||
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
|
||||
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
|
||||
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
|
||||
map_read_line_bin(map_line_buffer.data(), r.width());
|
||||
for (uint16_t j = 0; j < duplicate_lines; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t j = 0; j < duplicate_lines; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
|
||||
} else {
|
||||
// display osm tiles
|
||||
// Convert center GPS to a global pixel coordinate
|
||||
double global_center_px = lon_to_pixel_x_tile(lon_, map_osm_zoom);
|
||||
double global_center_py = lat_to_pixel_y_tile(lat_, map_osm_zoom);
|
||||
|
||||
// Find the top-left corner of the screen (viewport) in global pixel coordinates
|
||||
viewport_top_left_px = global_center_px - (r.width() / 2.0);
|
||||
viewport_top_left_py = global_center_py - (r.height() / 2.0);
|
||||
|
||||
// Find the tile ID that contains the top-left corner of the viewport
|
||||
int start_tile_x = floor(viewport_top_left_px / TILE_SIZE);
|
||||
int start_tile_y = floor(viewport_top_left_py / TILE_SIZE);
|
||||
|
||||
// Calculate the crucial render offset.
|
||||
// This determines how much the first tile is shifted to align the map correctly.
|
||||
// This value will almost always be negative or zero.
|
||||
double render_offset_x = -(viewport_top_left_px - (start_tile_x * TILE_SIZE));
|
||||
double render_offset_y = -(viewport_top_left_py - (start_tile_y * TILE_SIZE));
|
||||
|
||||
// Determine how many tiles we need to draw to fill the screen
|
||||
int tiles_needed_x = (r.width() / TILE_SIZE) + 2;
|
||||
int tiles_needed_y = (r.height() / TILE_SIZE) + 2;
|
||||
|
||||
for (int y = 0; y < tiles_needed_y; ++y) {
|
||||
for (int x = 0; x < tiles_needed_x; ++x) {
|
||||
int current_tile_x = start_tile_x + x;
|
||||
int current_tile_y = start_tile_y + y;
|
||||
|
||||
// Calculate the final on-screen drawing position for this tile.
|
||||
// For the first tile (x=0, y=0), this will be the negative offset.
|
||||
int draw_pos_x = round(render_offset_x + x * TILE_SIZE);
|
||||
int draw_pos_y = round(render_offset_y + y * TILE_SIZE);
|
||||
if (!draw_osm_file(map_osm_zoom, current_tile_x, current_tile_y, draw_pos_x, draw_pos_y)) {
|
||||
// already blanked it.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// No map data or excessive zoom; just draw a grid
|
||||
draw_map_grid();
|
||||
draw_map_grid(r);
|
||||
}
|
||||
|
||||
// Draw crosshairs in center in manual panning mode
|
||||
if (manual_panning_) {
|
||||
display.fill_rectangle({r.center() - Point(16, 1) + Point(zoom_pixel_offset, zoom_pixel_offset), {32, 2}}, Color::red());
|
||||
|
|
@ -394,8 +568,9 @@ void GeoMap::paint(Painter& painter) {
|
|||
|
||||
// Draw the other markers
|
||||
draw_markers(painter);
|
||||
draw_scale(painter);
|
||||
if (!use_osm) draw_scale(painter);
|
||||
draw_mypos(painter);
|
||||
if (has_osm) draw_switcher(painter);
|
||||
set_clean();
|
||||
}
|
||||
|
||||
|
|
@ -405,6 +580,12 @@ void GeoMap::paint(Painter& painter) {
|
|||
}
|
||||
}
|
||||
|
||||
void GeoMap::draw_switcher(Painter& painter) {
|
||||
painter.fill_rectangle({screen_rect().left(), screen_rect().top(), 3 * 20, 20}, Theme::getInstance()->bg_darker->background);
|
||||
std::string_view txt = (use_osm) ? "B I N" : "O S M";
|
||||
painter.draw_string({screen_rect().left() + 5, screen_rect().top() + 2}, *Theme::getInstance()->fg_light, txt);
|
||||
}
|
||||
|
||||
bool GeoMap::on_keyboard(KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ') return on_encoder(1);
|
||||
if (key == '-') return on_encoder(-1);
|
||||
|
|
@ -413,11 +594,26 @@ bool GeoMap::on_keyboard(KeyboardEvent key) {
|
|||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
if (has_osm && event.type == TouchEvent::Type::Start && event.point.x() < screen_rect().left() + 3 * 20 && event.point.y() < screen_rect().top() + 20) {
|
||||
use_osm = !use_osm;
|
||||
move(lon_, lat_); // to re calculate the center for each map type
|
||||
if (use_osm) set_osm_max_zoom();
|
||||
redraw_map = true;
|
||||
set_dirty();
|
||||
return false; // false, because with true this hits 2 times
|
||||
}
|
||||
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
Point p;
|
||||
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);
|
||||
if (!use_osm) {
|
||||
p = event.point - screen_rect().center();
|
||||
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio, false);
|
||||
} else {
|
||||
p = event.point - screen_rect().location();
|
||||
on_move(tile_pixel_x_to_lon(p.x() + viewport_top_left_px, map_osm_zoom), tile_pixel_y_to_lat(p.y() + viewport_top_left_py, map_osm_zoom), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -426,25 +622,30 @@ bool GeoMap::on_touch(const TouchEvent event) {
|
|||
|
||||
void GeoMap::move(const float lon, const float lat) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
bool is_changed = (lon_ != lon || lat_ != lat);
|
||||
lon_ = lon;
|
||||
lat_ = lat;
|
||||
if (!use_osm) {
|
||||
// Calculate x_pos/y_pos in map file corresponding to CENTER pixel of screen rect
|
||||
// (Note there is a 1:1 correspondence between map file pixels and screen pixels when map_zoom=1)
|
||||
GeoPoint mapPoint = lat_lon_to_map_pixel(lat_, lon_);
|
||||
x_pos = mapPoint.x;
|
||||
y_pos = mapPoint.y;
|
||||
// Cap position
|
||||
if (x_pos > (map_width - r.width() / 2))
|
||||
x_pos = map_width - r.width() / 2;
|
||||
if (y_pos > (map_height + r.height() / 2))
|
||||
y_pos = map_height - r.height() / 2;
|
||||
|
||||
// Calculate x_pos/y_pos in map file corresponding to CENTER pixel of screen rect
|
||||
// (Note there is a 1:1 correspondence between map file pixels and screen pixels when map_zoom=1)
|
||||
GeoPoint mapPoint = lat_lon_to_map_pixel(lat_, lon_);
|
||||
x_pos = mapPoint.x;
|
||||
y_pos = mapPoint.y;
|
||||
|
||||
// Cap position
|
||||
if (x_pos > (map_width - r.width() / 2))
|
||||
x_pos = map_width - r.width() / 2;
|
||||
if (y_pos > (map_height + r.height() / 2))
|
||||
y_pos = map_height - r.height() / 2;
|
||||
|
||||
// Scale calculation
|
||||
float km_per_deg_lon = cos(lat * pi / 180) * 111.321; // 111.321 km/deg longitude at equator, and 0 km at poles
|
||||
pixels_per_km = (r.width() / 2) / km_per_deg_lon;
|
||||
// Scale calculation
|
||||
float km_per_deg_lon = cos(lat * pi / 180) * 111.321; // 111.321 km/deg longitude at equator, and 0 km at poles
|
||||
pixels_per_km = (r.width() / 2) / km_per_deg_lon;
|
||||
} else {
|
||||
if (is_changed) {
|
||||
set_osm_max_zoom();
|
||||
redraw_map = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoMap::init() {
|
||||
|
|
@ -469,8 +670,7 @@ bool GeoMap::init() {
|
|||
map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
map_world_lon = map_width / (2 * pi);
|
||||
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
|
||||
|
||||
return map_opened;
|
||||
return map_opened || has_osm;
|
||||
}
|
||||
|
||||
void GeoMap::set_mode(GeoMapMode mode) {
|
||||
|
|
@ -595,24 +795,29 @@ MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
|
|||
}
|
||||
|
||||
void GeoMap::update_my_position(float lat, float lon, int32_t altitude) {
|
||||
bool is_changed = (my_pos.lat != lat) || (my_pos.lon != lon);
|
||||
my_pos.lat = lat;
|
||||
my_pos.lon = lon;
|
||||
my_altitude = altitude;
|
||||
redraw_map = true;
|
||||
redraw_map = is_changed;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
|
||||
bool is_changed = (my_pos.angle != angle);
|
||||
my_pos.angle = angle;
|
||||
if (refresh) {
|
||||
if (refresh && is_changed) {
|
||||
redraw_map = true;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
MapType GeoMap::get_map_type() {
|
||||
return use_osm ? MAP_TYPE_OSM : MAP_TYPE_BIN;
|
||||
}
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
if (!geomap.map_file_opened())
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/" + adsb_dir.string() + "/ directory", ABORT);
|
||||
}
|
||||
|
|
@ -629,7 +834,7 @@ void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t a
|
|||
geomap.set_dirty();
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_changed = lat_ != lat || lon_ != lon || altitude_ != altitude || speed_ != speed || angle_ != angle;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
altitude_ = altitude;
|
||||
|
|
@ -644,7 +849,7 @@ void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t a
|
|||
geopos.set_report_change(true);
|
||||
|
||||
geomap.set_angle(angle);
|
||||
geomap.move(lon_, lat_);
|
||||
if (is_changed) geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
||||
|
|
@ -660,19 +865,25 @@ void GeoMapView::setup() {
|
|||
geopos.set_lon(lon_);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon, int32_t speed) {
|
||||
bool is_changed = (altitude_ != altitude) || (lat_ != lat) || (lon_ != lon) || (speed_ != speed);
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
speed_ = speed;
|
||||
geopos.hide_altandspeed();
|
||||
geomap.set_manual_panning(true);
|
||||
geomap.move(lon_, lat_);
|
||||
if (is_changed) geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
};
|
||||
|
||||
geomap.on_move = [this](float move_x, float move_y) {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
geomap.on_move = [this](float move_x, float move_y, bool absolute) {
|
||||
if (absolute) {
|
||||
lon_ = move_x;
|
||||
lat_ = move_y;
|
||||
} else {
|
||||
lon_ += move_x;
|
||||
lat_ += move_y;
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
|
|
@ -769,4 +980,8 @@ MapMarkerStored GeoMapView::store_marker(GeoMarker& marker) {
|
|||
return geomap.store_marker(marker);
|
||||
}
|
||||
|
||||
MapType GeoMapView::get_map_type() {
|
||||
return geomap.get_map_type();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "ui.hpp"
|
||||
#include "file.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "bmpfile.hpp"
|
||||
#include "mathdef.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
|
|
@ -43,6 +45,8 @@ namespace ui {
|
|||
#define GEOMAP_RECT_WIDTH 240
|
||||
#define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT)
|
||||
|
||||
#define TILE_SIZE 256
|
||||
|
||||
enum GeoMapMode {
|
||||
DISPLAY,
|
||||
PROMPT
|
||||
|
|
@ -193,9 +197,14 @@ enum MapMarkerStored {
|
|||
MARKER_LIST_FULL
|
||||
};
|
||||
|
||||
enum MapType {
|
||||
MAP_TYPE_OSM,
|
||||
MAP_TYPE_BIN
|
||||
};
|
||||
|
||||
class GeoMap : public Widget {
|
||||
public:
|
||||
std::function<void(float, float)> on_move{};
|
||||
std::function<void(float, float, bool)> on_move{};
|
||||
|
||||
GeoMap(Rect parent_rect);
|
||||
|
||||
|
|
@ -216,6 +225,7 @@ class GeoMap : public Widget {
|
|||
void set_tag(std::string new_tag) {
|
||||
tag_ = new_tag;
|
||||
}
|
||||
MapType get_map_type();
|
||||
|
||||
void set_angle(uint16_t new_angle) {
|
||||
angle_ = new_angle;
|
||||
|
|
@ -246,8 +256,22 @@ class GeoMap : public Widget {
|
|||
void draw_markers(Painter& painter);
|
||||
void draw_mypos(Painter& painter);
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
void draw_map_grid();
|
||||
void map_read_line(ui::Color* buffer, uint16_t pixels);
|
||||
void draw_map_grid(ui::Rect r);
|
||||
void draw_switcher(Painter& painter);
|
||||
void map_read_line_bin(ui::Color* buffer, uint16_t pixels);
|
||||
// open street map related
|
||||
uint8_t find_osm_file_tile();
|
||||
void set_osm_max_zoom();
|
||||
bool draw_osm_file(int zoom, int tile_x, int tile_y, int relative_x, int relative_y);
|
||||
int lon2tile(double lon, int zoom);
|
||||
int lat2tile(double lat, int zoom);
|
||||
double lon_to_pixel_x_tile(double lon, int zoom);
|
||||
double lat_to_pixel_y_tile(double lat, int zoom);
|
||||
double tile_pixel_x_to_lon(int x, int zoom);
|
||||
double tile_pixel_y_to_lat(int y, int zoom);
|
||||
uint8_t map_osm_zoom{3};
|
||||
double viewport_top_left_px = 0;
|
||||
double viewport_top_left_py = 0;
|
||||
|
||||
bool manual_panning_{false};
|
||||
bool hide_center_marker_{false};
|
||||
|
|
@ -258,11 +282,11 @@ class GeoMap : public Widget {
|
|||
uint16_t map_width{}, map_height{};
|
||||
int32_t map_center_x{}, map_center_y{};
|
||||
int16_t map_zoom{1};
|
||||
|
||||
float lon_ratio{}, lat_ratio{};
|
||||
double map_bottom{};
|
||||
double map_world_lon{};
|
||||
double map_offset{};
|
||||
|
||||
float x_pos{}, y_pos{};
|
||||
float prev_x_pos{32767.0f}, prev_y_pos{32767.0f};
|
||||
float lat_{};
|
||||
|
|
@ -278,7 +302,9 @@ class GeoMap : public Widget {
|
|||
|
||||
int markerListLen{0};
|
||||
GeoMarker markerList[NumMarkerListElements];
|
||||
bool redraw_map{false};
|
||||
bool redraw_map{true};
|
||||
bool use_osm{false};
|
||||
bool has_osm{false};
|
||||
};
|
||||
|
||||
class GeoMapView : public View {
|
||||
|
|
@ -313,6 +339,8 @@ class GeoMapView : public View {
|
|||
void update_my_position(float lat, float lon, int32_t altitude);
|
||||
void update_my_orientation(uint16_t angle, bool refresh = false);
|
||||
|
||||
MapType get_map_type();
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
void clear_markers();
|
||||
|
|
|
|||
|
|
@ -85,7 +85,8 @@ void EPIRBProcessor::payload_handler(const baseband::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
void EPIRBProcessor::on_message(const Message*) {
|
||||
void EPIRBProcessor::on_message(const Message* const message) {
|
||||
(void)message; // Unused in this processor
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#include "mathdef.hpp"
|
||||
|
||||
float FSKRxProcessor::detect_peak_power(const buffer_c8_t& buffer, int N) {
|
||||
int32_t power = 0;
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@
|
|||
#include <random>
|
||||
#include <cmath>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
#include "mathdef.hpp"
|
||||
|
||||
void JammerProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ const float adsb_lat_lut[58] = {
|
|||
83.07199445, 83.99173563, 84.89166191, 85.75541621,
|
||||
86.53536998, 87.00000000};
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
#include "mathdef.hpp"
|
||||
|
||||
const float NZ = 15.0;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ void BMPFile::close() {
|
|||
bmpimage.close();
|
||||
}
|
||||
|
||||
// creates a new bmp file. for now, hardcoded to 3 byte colour depth
|
||||
// creates a new bmp file. hardcoded to 3 byte (24-bit) colour depth
|
||||
bool BMPFile::create(const std::filesystem::path& file, uint32_t x, uint32_t y) {
|
||||
is_opened = false;
|
||||
is_read_ony = true;
|
||||
is_read_only = true;
|
||||
bmpimage.close(); // if already open, close before open a new
|
||||
if (file_exists(file)) {
|
||||
delete_file(file); // overwrite
|
||||
|
|
@ -85,7 +85,7 @@ bool BMPFile::create(const std::filesystem::path& file, uint32_t x, uint32_t y)
|
|||
bmpimage.write(&bmp_header, sizeof(bmp_header_t));
|
||||
file_pos = bmp_header.image_data;
|
||||
is_opened = true;
|
||||
is_read_ony = false;
|
||||
is_read_only = false;
|
||||
if (!expand_y(y)) return false; // will fill with 0, and update header data
|
||||
seek(0, 0);
|
||||
return true;
|
||||
|
|
@ -94,30 +94,36 @@ bool BMPFile::create(const std::filesystem::path& file, uint32_t x, uint32_t y)
|
|||
// opens the file and parses header data. return true on success
|
||||
bool BMPFile::open(const std::filesystem::path& file, bool readonly) {
|
||||
is_opened = false;
|
||||
is_read_ony = true;
|
||||
bmpimage.close(); // if already open, close before open a new
|
||||
is_read_only = true;
|
||||
bmpimage.close();
|
||||
|
||||
auto result = bmpimage.open(file, readonly, false);
|
||||
if (!result.value().ok()) return false;
|
||||
file_pos = 0;
|
||||
bmpimage.seek(file_pos);
|
||||
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header_t));
|
||||
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature
|
||||
(bmp_header.planes == 1) && // Seems always to be 1
|
||||
(bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression
|
||||
bmpimage.read(&bmp_header, sizeof(bmp_header_t));
|
||||
if (!((bmp_header.signature == 0x4D42) &&
|
||||
(bmp_header.planes == 1) &&
|
||||
(bmp_header.compression == 0 || bmp_header.compression == 3))) {
|
||||
return false;
|
||||
}
|
||||
char buffer[257];
|
||||
|
||||
switch (bmp_header.bpp) {
|
||||
case 8:
|
||||
type = 4;
|
||||
byte_per_px = 1;
|
||||
// bmpimage.seek(sizeof(bmp_header_t));
|
||||
// bmpimage.read(color_palette, 1024);
|
||||
return false; // niy
|
||||
break;
|
||||
|
||||
case 16:
|
||||
file_pos = 0x36;
|
||||
memset(buffer, 0, 16);
|
||||
bmpimage.read(buffer, 16);
|
||||
byte_per_px = 2;
|
||||
if (buffer[1] == 0x7C)
|
||||
type = 3; // A1R5G5B5
|
||||
else
|
||||
type = 0; // R5G6B5
|
||||
type = 5;
|
||||
if (bmp_header.compression == 3) {
|
||||
return false;
|
||||
} // niy
|
||||
|
||||
break;
|
||||
case 24:
|
||||
type = 1;
|
||||
|
|
@ -130,12 +136,11 @@ bool BMPFile::open(const std::filesystem::path& file, bool readonly) {
|
|||
default:
|
||||
// not supported
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
byte_per_row = (bmp_header.width * byte_per_px % 4 == 0) ? bmp_header.width * byte_per_px : (bmp_header.width * byte_per_px + (4 - ((bmp_header.width * byte_per_px) % 4)));
|
||||
byte_per_row = (bmp_header.width * byte_per_px + 3) & ~3;
|
||||
file_pos = bmp_header.image_data;
|
||||
is_opened = true;
|
||||
is_read_ony = readonly;
|
||||
is_read_only = readonly;
|
||||
currx = 0;
|
||||
curry = 0;
|
||||
return true;
|
||||
|
|
@ -161,20 +166,24 @@ bool BMPFile::read_next_px(ui::Color& px, bool seek = true) {
|
|||
auto res = bmpimage.read(buffer, byte_per_px);
|
||||
if (res.is_error()) return false;
|
||||
switch (type) {
|
||||
case 0: // R5G6B5
|
||||
case 3: // A1R5G5B5
|
||||
if (!type)
|
||||
px = ui::Color((uint16_t)buffer[0] | ((uint16_t)buffer[1] << 8));
|
||||
else
|
||||
px = ui::Color(((uint16_t)buffer[0] & 0x1F) | ((uint16_t)buffer[0] & 0xE0) << 1 | ((uint16_t)buffer[1] & 0x7F) << 9);
|
||||
case 5: {
|
||||
uint16_t color16 = (uint16_t)buffer[0] | ((uint16_t)buffer[1] << 8);
|
||||
px = ui::Color(color16); // has glitches!
|
||||
break;
|
||||
}
|
||||
case 2: // 32
|
||||
px = ui::Color(buffer[2], buffer[1], buffer[0]);
|
||||
break;
|
||||
|
||||
case 4: { // 8-bit
|
||||
// uint8_t index = buffer[0];
|
||||
// px = ui::Color(color_palette[index][2], color_palette[index][1], color_palette[index][0]); // Palette is BGR
|
||||
// px = ui::Color(buffer[0]); // niy, since needs a lot of ram for the palette
|
||||
break;
|
||||
}
|
||||
case 1: // 24
|
||||
default:
|
||||
px = ui::Color(buffer[2], buffer[1], buffer[0]);
|
||||
|
||||
break;
|
||||
case 2: // 32
|
||||
px = ui::Color(buffer[2], buffer[1], buffer[0]);
|
||||
break;
|
||||
}
|
||||
if (seek) advance_curr_px();
|
||||
|
|
@ -188,23 +197,23 @@ void BMPFile::set_bg_color(ui::Color background) {
|
|||
}
|
||||
|
||||
// delete bg color. default. creation or expansion will be fast, but the file will contain random garbage. no problem if you write all pixels later.
|
||||
void BMPFile::delete_db_color() {
|
||||
void BMPFile::delete_bg_color() {
|
||||
use_bg = false;
|
||||
}
|
||||
|
||||
// writes a color data to the current position, and advances 1 px. true on success, false on error
|
||||
bool BMPFile::write_next_px(ui::Color& px) {
|
||||
if (!is_opened) return false;
|
||||
if (is_read_ony) return false;
|
||||
if (!is_opened || is_read_only) return false;
|
||||
uint8_t buffer[4];
|
||||
switch (type) {
|
||||
case 5:
|
||||
case 0: // R5G6B5
|
||||
case 3: // A1R5G5B5
|
||||
if (!type) {
|
||||
buffer[0] = (px.r() << 3) | (px.g() >> 3); // todo test in future
|
||||
buffer[0] = (px.r() << 3) | (px.g() >> 3);
|
||||
buffer[1] = (px.g() << 5) | px.b();
|
||||
} else {
|
||||
buffer[0] = (1 << 7) | (px.r() << 2) | (px.g() >> 3); // todo test in future
|
||||
buffer[0] = (1 << 7) | (px.r() << 2) | (px.g() >> 3);
|
||||
buffer[1] = (px.g() << 5) | px.b();
|
||||
}
|
||||
break;
|
||||
|
|
@ -220,6 +229,8 @@ bool BMPFile::write_next_px(ui::Color& px) {
|
|||
buffer[0] = px.b();
|
||||
buffer[3] = 255;
|
||||
break;
|
||||
case 4: // 8-bit
|
||||
return false;
|
||||
}
|
||||
auto res = bmpimage.write(buffer, byte_per_px);
|
||||
if (res.is_error()) return false;
|
||||
|
|
@ -260,7 +271,7 @@ bool BMPFile::expand_y(uint32_t new_y) {
|
|||
if (!is_opened) return false; // not yet opened
|
||||
uint32_t old_height = get_real_height();
|
||||
if (new_y < old_height) return true; // already bigger
|
||||
if (is_read_ony) return false; // can't expand
|
||||
if (is_read_only) return false; // can't expand
|
||||
uint32_t delta = (new_y - old_height) * byte_per_row;
|
||||
bmp_header.size += delta;
|
||||
bmp_header.data_size += delta;
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ class BMPFile {
|
|||
uint32_t get_width();
|
||||
bool is_bottomup();
|
||||
void set_bg_color(ui::Color background);
|
||||
void delete_db_color();
|
||||
void delete_bg_color();
|
||||
|
||||
private:
|
||||
bool advance_curr_px(uint32_t num);
|
||||
bool is_opened = false;
|
||||
bool is_read_ony = true;
|
||||
bool is_read_only = true;
|
||||
|
||||
File bmpimage{};
|
||||
size_t file_pos = 0;
|
||||
|
|
@ -64,6 +64,7 @@ class BMPFile {
|
|||
uint32_t currx = 0;
|
||||
uint32_t curry = 0;
|
||||
ui::Color bg{};
|
||||
// uint8_t color_palette[256][4];
|
||||
bool use_bg = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@
|
|||
#include <complex>
|
||||
#include <cmath>
|
||||
|
||||
constexpr float pi{3.141592653589793238462643383279502884f};
|
||||
#include "mathdef.hpp"
|
||||
|
||||
constexpr float pi{M_PI};
|
||||
|
||||
namespace std {
|
||||
|
||||
|
|
|
|||
4
firmware/common/mathdef.hpp
Normal file
4
firmware/common/mathdef.hpp
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define M_PI PI
|
||||
|
|
@ -47,7 +47,7 @@ static uint8_t calfrchk[51]; // so subframes are preserved while populate
|
|||
#define pos_GPSecefY 0x114 // 0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop)
|
||||
#define pos_GPSecefZ 0x118 // 0x11C // 4 byte (same as Y)
|
||||
|
||||
#define PI 3.1415926535897932384626433832795 // 3.1416 //(3.1415926535897932384626433832795)
|
||||
#include "mathdef.hpp"
|
||||
|
||||
Packet::Packet(
|
||||
const baseband::Packet& packet,
|
||||
|
|
|
|||
14
sdcard/OSM/readme.md
Normal file
14
sdcard/OSM/readme.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Download OSM Tile files to the root of the SD card to the folder: /OSM/
|
||||
There must be the standard directory structure of OSM ( /OSM/{zoom}/{x}/{y}.bmp).
|
||||
The tile files must be BMP 24 bit color depth format.
|
||||
|
||||
|
||||
To disable this function, simply delete the subfolders from this OSM folder.
|
||||
|
||||
|
||||
There is a website, that can download tile files in the given format and structure: https://pposm.creativo.hu
|
||||
You select the area you want to download, and the maximum zoom level. There will be a size estimation.
|
||||
Then Click on start download button, and browse the sd cards /OSM folder. Then click select and enable (twice).
|
||||
The download will begin. Depending on the area size and the max zoom level it can take a lot of time.
|
||||
With this app you can download multiple regions in multiple depths, just run it again.
|
||||
For the site to work, you must use up to date Chromium based browser.
|
||||
Loading…
Add table
Add a link
Reference in a new issue