Openstreetmap support (#2765)

This commit is contained in:
Totoo 2025-08-27 08:37:57 +02:00 committed by GitHub
parent e6d4081c06
commit 4dda7cfaa5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 447 additions and 162 deletions

View file

@ -456,7 +456,10 @@ void AISAppView::on_tick_second() {
++timer_seconds; ++timer_seconds;
if (timer_seconds % 10 == 0) { if (timer_seconds % 10 == 0) {
if (recent_entry_detail_view.hidden()) return; 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) { if (logger) {
logger->on_packet(packet); logger->on_packet(packet);
} }
got_new_packet = true;
auto& entry = ::on_packet(recent, packet.source_id()); auto& entry = ::on_packet(recent, packet.source_id());
entry.update(packet); entry.update(packet);
recent_entries_view.set_dirty(); recent_entries_view.set_dirty();

View file

@ -221,6 +221,7 @@ class AISAppView : public View {
}; };
SignalToken signal_token_tick_second{}; SignalToken signal_token_tick_second{};
uint8_t timer_seconds = 0; uint8_t timer_seconds = 0;
bool got_new_packet{false}; // got any new packet since latest screen update?
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::AISPacket, Message::ID::AISPacket,

View file

@ -635,14 +635,14 @@ void ADSBRxView::refresh_ui() {
if (details_view->map_active()) { if (details_view->map_active()) {
// Is it time to clear and refresh the map's markers? // 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; map_needs_update = true;
ticks_since_marker_refresh = 0; ticks_since_marker_refresh = 0;
details_view->clear_map_markers(); details_view->clear_map_markers();
} }
} else { } else {
// Refresh map immediately once active. // 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. // Process the entries list.

View file

@ -64,8 +64,9 @@ namespace ui {
#define VEL_AIR_SUBSONIC 3 #define VEL_AIR_SUBSONIC 3
#define VEL_AIR_SUPERSONIC 4 #define VEL_AIR_SUPERSONIC 4
#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames #define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames
#define MARKER_UPDATE_SECONDS 5 // "other" map marker redraw interval #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. */ /* Thresholds (in seconds) that define the transition between ages. */
struct ADSBAgeLimit { struct ADSBAgeLimit {
@ -277,6 +278,13 @@ class ADSBRxDetailsView : public View {
std::string title() const override { return "Details"; } 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: private:
void refresh_ui(); void refresh_ui();
void on_gps(const GPSPosDataMessage* msg); void on_gps(const GPSPosDataMessage* msg);
@ -388,7 +396,7 @@ class ADSBRxView : public View {
SignalToken signal_token_tick_second{}; SignalToken signal_token_tick_second{};
uint8_t tick_count = 0; 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. /* Max number of entries that can be updated in a single pass.
* 16 is one screen of recent entries. */ * 16 is one screen of recent entries. */

View file

@ -440,7 +440,7 @@ static void _sumlr(void);
#undef __O #undef __O
#undef __P #undef __P
#define PI 3.141592653589793 #include "mathdef.hpp"
static void _numinput(byte k); static void _numinput(byte k);
static double dpush(double d); static double dpush(double d);

View file

@ -31,7 +31,7 @@ int HALF_HEIGHT = 0;
#define JOGGING_SPEED 1.2 #define JOGGING_SPEED 1.2
#define MAX_ENTITY_DISTANCE 200 #define MAX_ENTITY_DISTANCE 200
#define ITEM_COLLIDER_DIST 6 #define ITEM_COLLIDER_DIST 6
#define PI 3.14159265358979323846 #include "mathdef.hpp"
#define GUN_WIDTH 30 #define GUN_WIDTH 30
#define GUN_HEIGHT 40 #define GUN_HEIGHT 40
#define GUN_TARGET_POS 24 #define GUN_TARGET_POS 24

View file

@ -158,9 +158,10 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
} }
// never move this before the first load() bc that will mess load up // 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)lon;
(void)lat; (void)lat;
(void)absolute;
load_markers(); load_markers();
}; };
btn_back.on_select = [this, &nav](Button&) { btn_back.on_select = [this, &nav](Button&) {

View file

@ -113,7 +113,7 @@ File::Result<bool> File::eof() {
File::Result<File::Offset> File::seek(Offset new_position) { File::Result<File::Offset> File::seek(Offset new_position) {
/* NOTE: Returns *old* position, not new position */ /* NOTE: Returns *old* position, not new position */
const auto old_position = tell(); 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) { if (result != FR_OK) {
return {static_cast<Error>(result)}; return {static_cast<Error>(result)};
} }

View file

@ -22,21 +22,17 @@
*/ */
#include "ui_geomap.hpp" #include "ui_geomap.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include <cstring> #include <cstring>
#include <stdio.h> #include <stdio.h>
#include <string_view>
using namespace portapack; using namespace portapack;
#include "string_format.hpp" #include "string_format.hpp"
#include "complex.hpp" #include "complex.hpp"
#include "ui_font_fixed_5x8.hpp" #include "ui_font_fixed_5x8.hpp"
#include "file_path.hpp" #include "file_path.hpp"
namespace ui { namespace ui {
GeoPos::GeoPos( GeoPos::GeoPos(
const Point pos, const Point pos,
const alt_unit altitude_unit, const alt_unit altitude_unit,
@ -188,6 +184,7 @@ int32_t GeoPos::speed() {
GeoMap::GeoMap( GeoMap::GeoMap(
Rect parent_rect) Rect parent_rect)
: Widget{parent_rect}, markerListLen(0) { : Widget{parent_rect}, markerListLen(0) {
has_osm = use_osm = find_osm_file_tile();
} }
bool GeoMap::on_encoder(const EncoderEvent delta) { 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_zoom += (map_zoom >= MAP_ZOOM_RESOLUTION_LIMIT) ? map_zoom : 1;
} }
} }
map_osm_zoom++;
if (has_osm) set_osm_max_zoom();
} else if (delta < 0) { } else if (delta < 0) {
if (map_zoom > -MAX_MAP_ZOOM_OUT) { if (map_zoom > -MAX_MAP_ZOOM_OUT) {
if (map_zoom == 1) { if (map_zoom == 1) {
@ -212,12 +211,18 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
map_zoom--; map_zoom--;
} }
} }
if (map_osm_zoom > 0) map_osm_zoom--;
} else { } else {
return false; return false;
} }
map_visible = map_opened && (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT); 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 // Trigger map redraw
redraw_map = true; redraw_map = true;
@ -225,7 +230,7 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
return true; 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) { if (map_zoom == 1) {
map_file.read(buffer, pixels << 1); map_file.read(buffer, pixels << 1);
} else if (map_zoom > 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) { void GeoMap::draw_marker_item(Painter& painter, GeoMarker& item, const Color color, const Color fontColor, const Color backColor) {
const auto r = screen_rect(); const auto r = screen_rect();
const ui::Point itemPoint = item_rect_pixel(item); const ui::Point itemPoint = item_rect_pixel(item);
if ((itemPoint.x() >= 0) && (itemPoint.x() < r.width()) && if ((itemPoint.x() >= 0) && (itemPoint.x() < r.width()) &&
(itemPoint.y() > 10) && (itemPoint.y() < r.height())) // Dont draw within symbol size of top (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. // Calculate screen position of item, adjusted for zoom factor.
ui::Point GeoMap::item_rect_pixel(GeoMarker& item) { ui::Point GeoMap::item_rect_pixel(GeoMarker& item) {
const auto r = screen_rect(); if (!use_osm) {
const auto geomap_rect_half_width = r.width() / 2; const auto r = screen_rect();
const auto geomap_rect_half_height = r.height() / 2; 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); GeoPoint mapPoint = lat_lon_to_map_pixel(item.lat, item.lon);
float x = mapPoint.x - x_pos; float x = mapPoint.x - x_pos;
float y = mapPoint.y - y_pos; float y = mapPoint.y - y_pos;
if (map_zoom > 1) {
if (map_zoom > 1) { x = x * map_zoom + zoom_pixel_offset;
x = x * map_zoom + zoom_pixel_offset; y = y * map_zoom + zoom_pixel_offset;
y = y * map_zoom + zoom_pixel_offset; } else if (map_zoom < 0) {
} else if (map_zoom < 0) { x = x / (-map_zoom);
x = x / (-map_zoom); y = y / (-map_zoom);
y = y / (-map_zoom); }
x += geomap_rect_half_width;
y += geomap_rect_half_height;
return {(int16_t)x, (int16_t)y};
} }
// osm calculation
x += geomap_rect_half_width; double y = lat_to_pixel_y_tile(item.lat, map_osm_zoom) - viewport_top_left_py;
y += geomap_rect_half_height; double x = lon_to_pixel_x_tile(item.lon, map_osm_zoom) - viewport_top_left_px;
return {(int16_t)x, (int16_t)y}; 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) // (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) { GeoPoint GeoMap::lat_lon_to_map_pixel(float lat, float lon) {
// Using WGS 84/Pseudo-Mercator projection // 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). // Draw grid in place of map (when zoom-in level is too high).
void GeoMap::draw_map_grid() { void GeoMap::draw_map_grid(ui::Rect r) {
const auto r = screen_rect();
// Grid spacing is just based on zoom at the moment, and centered on screen. // Grid spacing is just based on zoom at the moment, and centered on screen.
// TODO: Maybe align with latitude/longitude seconds instead? // TODO: Maybe align with latitude/longitude seconds instead?
int grid_spacing = map_zoom * 2; 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) { void GeoMap::paint(Painter& painter) {
const auto r = screen_rect(); const auto r = screen_rect();
std::array<ui::Color, geomap_rect_width> map_line_buffer; std::array<ui::Color, geomap_rect_width> map_line_buffer;
int16_t zoom_seek_x, zoom_seek_y; 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 (!use_osm) {
if (map_zoom <= 1) { // Ony redraw map if it moved by at least 1 pixel or the markers list was updated
// Zooming out, or no zoom if (map_zoom <= 1) {
const int min_diff = abs(map_zoom); // Zooming out, or no zoom
if ((int)abs(x_pos - prev_x_pos) >= min_diff) const int min_diff = abs(map_zoom);
redraw_map = true; if ((int)abs(x_pos - prev_x_pos) >= min_diff)
else if ((int)abs(y_pos - prev_y_pos) >= min_diff) redraw_map = true;
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 { } else {
// Zooming in; magnify position differences (utilizing zoom_pixel_offset) // using osm; needs to be stricter with the redraws, it'll be checked on move
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;
} }
if (redraw_map) { 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; 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) { if (map_visible) {
// Read from map file and display to zoomed scale if (!use_osm) {
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom; prev_x_pos = x_pos; // Note x_pos/y_pos pixel position in map file now correspond to screen rect CENTER pixel
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) { prev_y_pos = y_pos;
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom)); // Adjust starting corner position of map per zoom setting;
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1)); // When zooming in the map should technically by shifted left & up by another map_zoom/2 pixels but
map_read_line(map_line_buffer.data(), r.width()); // 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++) { } else {
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer); // 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 { } else {
// No map data or excessive zoom; just draw a grid // 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 // Draw crosshairs in center in manual panning mode
if (manual_panning_) { if (manual_panning_) {
display.fill_rectangle({r.center() - Point(16, 1) + Point(zoom_pixel_offset, zoom_pixel_offset), {32, 2}}, Color::red()); 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 the other markers
draw_markers(painter); draw_markers(painter);
draw_scale(painter); if (!use_osm) draw_scale(painter);
draw_mypos(painter); draw_mypos(painter);
if (has_osm) draw_switcher(painter);
set_clean(); 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) { bool GeoMap::on_keyboard(KeyboardEvent key) {
if (key == '+' || key == ' ') return on_encoder(1); if (key == '+' || key == ' ') return on_encoder(1);
if (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) { 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)) { if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
Point p;
set_highlighted(true); set_highlighted(true);
if (on_move) { if (on_move) {
Point p = event.point - screen_rect().center(); if (!use_osm) {
on_move(p.x() / 2.0 * lon_ratio, p.y() / 2.0 * lat_ratio); 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; return true;
} }
} }
@ -426,25 +622,30 @@ bool GeoMap::on_touch(const TouchEvent event) {
void GeoMap::move(const float lon, const float lat) { void GeoMap::move(const float lon, const float lat) {
const auto r = screen_rect(); const auto r = screen_rect();
bool is_changed = (lon_ != lon || lat_ != lat);
lon_ = lon; lon_ = lon;
lat_ = lat; 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 // Scale calculation
// (Note there is a 1:1 correspondence between map file pixels and screen pixels when map_zoom=1) float km_per_deg_lon = cos(lat * pi / 180) * 111.321; // 111.321 km/deg longitude at equator, and 0 km at poles
GeoPoint mapPoint = lat_lon_to_map_pixel(lat_, lon_); pixels_per_km = (r.width() / 2) / km_per_deg_lon;
x_pos = mapPoint.x; } else {
y_pos = mapPoint.y; if (is_changed) {
set_osm_max_zoom();
// Cap position redraw_map = true;
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;
} }
bool GeoMap::init() { 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_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
map_world_lon = map_width / (2 * pi); map_world_lon = map_width / (2 * pi);
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom))); map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
return map_opened || has_osm;
return map_opened;
} }
void GeoMap::set_mode(GeoMapMode mode) { 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) { 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.lat = lat;
my_pos.lon = lon; my_pos.lon = lon;
my_altitude = altitude; my_altitude = altitude;
redraw_map = true; redraw_map = is_changed;
set_dirty(); set_dirty();
} }
void GeoMap::update_my_orientation(uint16_t angle, bool refresh) { void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
bool is_changed = (my_pos.angle != angle);
my_pos.angle = angle; my_pos.angle = angle;
if (refresh) { if (refresh && is_changed) {
redraw_map = true; redraw_map = true;
set_dirty(); set_dirty();
} }
} }
MapType GeoMap::get_map_type() {
return use_osm ? MAP_TYPE_OSM : MAP_TYPE_BIN;
}
void GeoMapView::focus() { void GeoMapView::focus() {
geopos.focus(); geopos.focus();
if (!geomap.map_file_opened()) if (!geomap.map_file_opened())
nav_.display_modal("No map", "No world_map.bin file in\n/" + adsb_dir.string() + "/ directory", ABORT); 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(); geomap.set_dirty();
return; return;
} }
bool is_changed = lat_ != lat || lon_ != lon || altitude_ != altitude || speed_ != speed || angle_ != angle;
lat_ = lat; lat_ = lat;
lon_ = lon; lon_ = lon;
altitude_ = altitude; 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); geopos.set_report_change(true);
geomap.set_angle(angle); geomap.set_angle(angle);
geomap.move(lon_, lat_); if (is_changed) geomap.move(lon_, lat_);
geomap.set_dirty(); geomap.set_dirty();
} }
@ -660,19 +865,25 @@ void GeoMapView::setup() {
geopos.set_lon(lon_); geopos.set_lon(lon_);
geopos.on_change = [this](int32_t altitude, float lat, float lon, int32_t speed) { 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; altitude_ = altitude;
lat_ = lat; lat_ = lat;
lon_ = lon; lon_ = lon;
speed_ = speed; speed_ = speed;
geopos.hide_altandspeed(); geopos.hide_altandspeed();
geomap.set_manual_panning(true); geomap.set_manual_panning(true);
geomap.move(lon_, lat_); if (is_changed) geomap.move(lon_, lat_);
geomap.set_dirty(); geomap.set_dirty();
}; };
geomap.on_move = [this](float move_x, float move_y) { geomap.on_move = [this](float move_x, float move_y, bool absolute) {
lon_ += move_x; if (absolute) {
lat_ += move_y; lon_ = move_x;
lat_ = move_y;
} else {
lon_ += move_x;
lat_ += move_y;
}
// Stupid hack to avoid an event loop // Stupid hack to avoid an event loop
geopos.set_report_change(false); geopos.set_report_change(false);
@ -769,4 +980,8 @@ MapMarkerStored GeoMapView::store_marker(GeoMarker& marker) {
return geomap.store_marker(marker); return geomap.store_marker(marker);
} }
MapType GeoMapView::get_map_type() {
return geomap.get_map_type();
}
} /* namespace ui */ } /* namespace ui */

View file

@ -27,6 +27,8 @@
#include "ui.hpp" #include "ui.hpp"
#include "file.hpp" #include "file.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "bmpfile.hpp"
#include "mathdef.hpp"
#include "portapack.hpp" #include "portapack.hpp"
@ -43,6 +45,8 @@ namespace ui {
#define GEOMAP_RECT_WIDTH 240 #define GEOMAP_RECT_WIDTH 240
#define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT) #define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT)
#define TILE_SIZE 256
enum GeoMapMode { enum GeoMapMode {
DISPLAY, DISPLAY,
PROMPT PROMPT
@ -193,9 +197,14 @@ enum MapMarkerStored {
MARKER_LIST_FULL MARKER_LIST_FULL
}; };
enum MapType {
MAP_TYPE_OSM,
MAP_TYPE_BIN
};
class GeoMap : public Widget { class GeoMap : public Widget {
public: public:
std::function<void(float, float)> on_move{}; std::function<void(float, float, bool)> on_move{};
GeoMap(Rect parent_rect); GeoMap(Rect parent_rect);
@ -216,6 +225,7 @@ class GeoMap : public Widget {
void set_tag(std::string new_tag) { void set_tag(std::string new_tag) {
tag_ = new_tag; tag_ = new_tag;
} }
MapType get_map_type();
void set_angle(uint16_t new_angle) { void set_angle(uint16_t new_angle) {
angle_ = new_angle; angle_ = new_angle;
@ -246,8 +256,22 @@ class GeoMap : public Widget {
void draw_markers(Painter& painter); void draw_markers(Painter& painter);
void draw_mypos(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_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
void draw_map_grid(); void draw_map_grid(ui::Rect r);
void map_read_line(ui::Color* buffer, uint16_t pixels); 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 manual_panning_{false};
bool hide_center_marker_{false}; bool hide_center_marker_{false};
@ -258,11 +282,11 @@ class GeoMap : public Widget {
uint16_t map_width{}, map_height{}; uint16_t map_width{}, map_height{};
int32_t map_center_x{}, map_center_y{}; int32_t map_center_x{}, map_center_y{};
int16_t map_zoom{1}; int16_t map_zoom{1};
float lon_ratio{}, lat_ratio{}; float lon_ratio{}, lat_ratio{};
double map_bottom{}; double map_bottom{};
double map_world_lon{}; double map_world_lon{};
double map_offset{}; double map_offset{};
float x_pos{}, y_pos{}; float x_pos{}, y_pos{};
float prev_x_pos{32767.0f}, prev_y_pos{32767.0f}; float prev_x_pos{32767.0f}, prev_y_pos{32767.0f};
float lat_{}; float lat_{};
@ -278,7 +302,9 @@ class GeoMap : public Widget {
int markerListLen{0}; int markerListLen{0};
GeoMarker markerList[NumMarkerListElements]; GeoMarker markerList[NumMarkerListElements];
bool redraw_map{false}; bool redraw_map{true};
bool use_osm{false};
bool has_osm{false};
}; };
class GeoMapView : public View { 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_position(float lat, float lon, int32_t altitude);
void update_my_orientation(uint16_t angle, bool refresh = false); void update_my_orientation(uint16_t angle, bool refresh = false);
MapType get_map_type();
std::string title() const override { return "Map view"; }; std::string title() const override { return "Map view"; };
void clear_markers(); void clear_markers();

View file

@ -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() { int main() {

View file

@ -33,9 +33,7 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#ifndef M_PI #include "mathdef.hpp"
#define M_PI 3.14159265358979323846
#endif
float FSKRxProcessor::detect_peak_power(const buffer_c8_t& buffer, int N) { float FSKRxProcessor::detect_peak_power(const buffer_c8_t& buffer, int N) {
int32_t power = 0; int32_t power = 0;

View file

@ -30,9 +30,7 @@
#include <random> #include <random>
#include <cmath> #include <cmath>
#ifndef M_PI #include "mathdef.hpp"
#define M_PI 3.14159265358979323846f
#endif
void JammerProcessor::execute(const buffer_c8_t& buffer) { void JammerProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return; if (!configured) return;

View file

@ -91,7 +91,7 @@ const float adsb_lat_lut[58] = {
83.07199445, 83.99173563, 84.89166191, 85.75541621, 83.07199445, 83.99173563, 84.89166191, 85.75541621,
86.53536998, 87.00000000}; 86.53536998, 87.00000000};
const float PI = 3.14159265358979323846; #include "mathdef.hpp"
const float NZ = 15.0; const float NZ = 15.0;

View file

@ -52,10 +52,10 @@ void BMPFile::close() {
bmpimage.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) { bool BMPFile::create(const std::filesystem::path& file, uint32_t x, uint32_t y) {
is_opened = false; is_opened = false;
is_read_ony = true; is_read_only = true;
bmpimage.close(); // if already open, close before open a new bmpimage.close(); // if already open, close before open a new
if (file_exists(file)) { if (file_exists(file)) {
delete_file(file); // overwrite 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)); bmpimage.write(&bmp_header, sizeof(bmp_header_t));
file_pos = bmp_header.image_data; file_pos = bmp_header.image_data;
is_opened = true; 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 if (!expand_y(y)) return false; // will fill with 0, and update header data
seek(0, 0); seek(0, 0);
return true; 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 // opens the file and parses header data. return true on success
bool BMPFile::open(const std::filesystem::path& file, bool readonly) { bool BMPFile::open(const std::filesystem::path& file, bool readonly) {
is_opened = false; is_opened = false;
is_read_ony = true; is_read_only = true;
bmpimage.close(); // if already open, close before open a new bmpimage.close();
auto result = bmpimage.open(file, readonly, false); auto result = bmpimage.open(file, readonly, false);
if (!result.value().ok()) return false; if (!result.value().ok()) return false;
file_pos = 0; file_pos = 0;
bmpimage.seek(file_pos); bmpimage.seek(file_pos);
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header_t)); bmpimage.read(&bmp_header, sizeof(bmp_header_t));
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature if (!((bmp_header.signature == 0x4D42) &&
(bmp_header.planes == 1) && // Seems always to be 1 (bmp_header.planes == 1) &&
(bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression (bmp_header.compression == 0 || bmp_header.compression == 3))) {
return false; return false;
} }
char buffer[257];
switch (bmp_header.bpp) { 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: case 16:
file_pos = 0x36;
memset(buffer, 0, 16);
bmpimage.read(buffer, 16);
byte_per_px = 2; byte_per_px = 2;
if (buffer[1] == 0x7C) type = 5;
type = 3; // A1R5G5B5 if (bmp_header.compression == 3) {
else return false;
type = 0; // R5G6B5 } // niy
break; break;
case 24: case 24:
type = 1; type = 1;
@ -130,12 +136,11 @@ bool BMPFile::open(const std::filesystem::path& file, bool readonly) {
default: default:
// not supported // not supported
return false; 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; file_pos = bmp_header.image_data;
is_opened = true; is_opened = true;
is_read_ony = readonly; is_read_only = readonly;
currx = 0; currx = 0;
curry = 0; curry = 0;
return true; 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); auto res = bmpimage.read(buffer, byte_per_px);
if (res.is_error()) return false; if (res.is_error()) return false;
switch (type) { switch (type) {
case 0: // R5G6B5 case 5: {
case 3: // A1R5G5B5 uint16_t color16 = (uint16_t)buffer[0] | ((uint16_t)buffer[1] << 8);
if (!type) px = ui::Color(color16); // has glitches!
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);
break; 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 case 1: // 24
default: default:
px = ui::Color(buffer[2], buffer[1], buffer[0]); px = ui::Color(buffer[2], buffer[1], buffer[0]);
break;
case 2: // 32
px = ui::Color(buffer[2], buffer[1], buffer[0]);
break; break;
} }
if (seek) advance_curr_px(); 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. // 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; use_bg = false;
} }
// writes a color data to the current position, and advances 1 px. true on success, false on error // 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) { bool BMPFile::write_next_px(ui::Color& px) {
if (!is_opened) return false; if (!is_opened || is_read_only) return false;
if (is_read_ony) return false;
uint8_t buffer[4]; uint8_t buffer[4];
switch (type) { switch (type) {
case 5:
case 0: // R5G6B5 case 0: // R5G6B5
case 3: // A1R5G5B5 case 3: // A1R5G5B5
if (!type) { 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(); buffer[1] = (px.g() << 5) | px.b();
} else { } 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(); buffer[1] = (px.g() << 5) | px.b();
} }
break; break;
@ -220,6 +229,8 @@ bool BMPFile::write_next_px(ui::Color& px) {
buffer[0] = px.b(); buffer[0] = px.b();
buffer[3] = 255; buffer[3] = 255;
break; break;
case 4: // 8-bit
return false;
} }
auto res = bmpimage.write(buffer, byte_per_px); auto res = bmpimage.write(buffer, byte_per_px);
if (res.is_error()) return false; 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 if (!is_opened) return false; // not yet opened
uint32_t old_height = get_real_height(); uint32_t old_height = get_real_height();
if (new_y < old_height) return true; // already bigger 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; uint32_t delta = (new_y - old_height) * byte_per_row;
bmp_header.size += delta; bmp_header.size += delta;
bmp_header.data_size += delta; bmp_header.data_size += delta;

View file

@ -47,12 +47,12 @@ class BMPFile {
uint32_t get_width(); uint32_t get_width();
bool is_bottomup(); bool is_bottomup();
void set_bg_color(ui::Color background); void set_bg_color(ui::Color background);
void delete_db_color(); void delete_bg_color();
private: private:
bool advance_curr_px(uint32_t num); bool advance_curr_px(uint32_t num);
bool is_opened = false; bool is_opened = false;
bool is_read_ony = true; bool is_read_only = true;
File bmpimage{}; File bmpimage{};
size_t file_pos = 0; size_t file_pos = 0;
@ -64,6 +64,7 @@ class BMPFile {
uint32_t currx = 0; uint32_t currx = 0;
uint32_t curry = 0; uint32_t curry = 0;
ui::Color bg{}; ui::Color bg{};
// uint8_t color_palette[256][4];
bool use_bg = false; bool use_bg = false;
}; };

View file

@ -26,7 +26,9 @@
#include <complex> #include <complex>
#include <cmath> #include <cmath>
constexpr float pi{3.141592653589793238462643383279502884f}; #include "mathdef.hpp"
constexpr float pi{M_PI};
namespace std { namespace std {

View file

@ -0,0 +1,4 @@
#pragma once
#define PI 3.1415926535897932384626433832795
#define M_PI PI

View file

@ -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_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 pos_GPSecefZ 0x118 // 0x11C // 4 byte (same as Y)
#define PI 3.1415926535897932384626433832795 // 3.1416 //(3.1415926535897932384626433832795) #include "mathdef.hpp"
Packet::Packet( Packet::Packet(
const baseband::Packet& packet, const baseband::Packet& packet,

14
sdcard/OSM/readme.md Normal file
View 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.