From bfa7f3ca6bb7391788841285a9c8dc80c95fc8b3 Mon Sep 17 00:00:00 2001 From: Totoo Date: Mon, 16 Sep 2024 21:59:28 +0200 Subject: [PATCH] Wardrive map improv (#2251) Added paginator. Added Flipper SUB file support. --- firmware/application/CMakeLists.txt | 1 + .../external/wardrivemap/ui_wardrivemap.cpp | 98 ++++++++++++--- .../external/wardrivemap/ui_wardrivemap.hpp | 22 ++-- firmware/application/flipper_subfile.cpp | 117 ++++++++++++++++++ firmware/application/flipper_subfile.hpp | 53 ++++++++ 5 files changed, 263 insertions(+), 28 deletions(-) create mode 100644 firmware/application/flipper_subfile.cpp create mode 100644 firmware/application/flipper_subfile.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index ad94d010..1d8ac12f 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -206,6 +206,7 @@ set(CPPSRC irq_rtc.cpp log_file.cpp metadata_file.cpp + flipper_subfile.cpp portapack.cpp usb_serial_shell.cpp usb_serial_shell_filesystem.cpp diff --git a/firmware/application/external/wardrivemap/ui_wardrivemap.cpp b/firmware/application/external/wardrivemap/ui_wardrivemap.cpp index 39052b3d..21f5d73a 100644 --- a/firmware/application/external/wardrivemap/ui_wardrivemap.cpp +++ b/firmware/application/external/wardrivemap/ui_wardrivemap.cpp @@ -23,6 +23,7 @@ #include "string_format.hpp" #include "file_path.hpp" #include "metadata_file.hpp" +#include "flipper_subfile.hpp" #include "portapack_persistent_memory.hpp" using namespace portapack; @@ -36,14 +37,14 @@ void WardriveMapView::focus() { // needs to load on every map change, because won't store or draw all while marker is not in the current view. // todo optimize this somehow -bool WardriveMapView::load_markers() { - uint16_t cnt = 0; +void WardriveMapView::load_markers() { uint16_t displayed_cnt = 0; + uint16_t cnt = 0; geomap.clear_markers(); // serach for files with geotag, and add it to geomap as marker with tag. limit to N bc of mem limit. for (const auto& entry : std::filesystem::directory_iterator(captures_dir, u"*.txt")) { if (std::filesystem::is_regular_file(entry.status())) { - if (displayed_cnt > 30) break; + if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements std::filesystem::path pth = captures_dir; pth += u"/" + entry.path(); auto metadata_path = get_metadata_path(pth); @@ -51,23 +52,65 @@ bool WardriveMapView::load_markers() { if (metadata) { if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) { - if (first_init == false) { - // move map there before add, so will display this. - geopos.set_report_change(false); - geopos.set_lat(metadata.value().latitude); - geopos.set_lon(metadata.value().longitude); - geopos.set_report_change(true); - geomap.move(metadata.value().longitude, metadata.value().latitude); - first_init = true; + // skip nth + if (marker_start <= cnt) { + if (first_init == false) { + // move map there before add, so will display this. + geopos.set_report_change(false); + geopos.set_lat(metadata.value().latitude); + geopos.set_lon(metadata.value().longitude); + geopos.set_report_change(true); + geomap.move(metadata.value().longitude, metadata.value().latitude); + first_init = true; + } + GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()}; + if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++; + if (!markers_counted) marker_cntall++; } - GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()}; - if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++; cnt++; } } } } - return (cnt > 0); + // load flipper files too + for (const auto& entry : std::filesystem::directory_iterator(flippersub_dir, u"*.sub")) { + if (std::filesystem::is_regular_file(entry.status())) { + if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements + std::filesystem::path pth = flippersub_dir; + pth += u"/" + entry.path(); + auto metadata = read_flippersub_file(pth); + + if (metadata) { + if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) { + // skip nth + if (marker_start <= cnt) { + if (first_init == false) { + // move map there before add, so will display this. + geopos.set_report_change(false); + geopos.set_lat(metadata.value().latitude); + geopos.set_lon(metadata.value().longitude); + geopos.set_report_change(true); + geomap.move(metadata.value().longitude, metadata.value().latitude); + first_init = true; + } + GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()}; + if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++; + if (!markers_counted) marker_cntall++; + } + cnt++; + } + } + } + } + + markers_counted = true; + // show / hide paginator buttons + btn_back.hidden((marker_start == 0) || (marker_cntall == 0)); + btn_next.hidden(((marker_start + ui::GeoMap::NumMarkerListElements) >= marker_cntall) || (marker_cntall == 0)); + // update text + text_info.set(to_string_dec_uint(marker_start + 1) + " - " + to_string_dec_uint(displayed_cnt + marker_start) + " / " + to_string_dec_uint(marker_cntall)); + set_dirty(); + return; } WardriveMapView::WardriveMapView(NavigationView& nav) @@ -75,7 +118,9 @@ WardriveMapView::WardriveMapView(NavigationView& nav) add_children({&text_info, &geomap, &geopos, - &text_notfound}); + &text_notfound, + &btn_back, + &btn_next}); geomap.set_mode(DISPLAY); geomap.set_manual_panning(false); @@ -100,21 +145,38 @@ WardriveMapView::WardriveMapView(NavigationView& nav) load_markers(); geomap.set_dirty(); }; - text_notfound.hidden(true); geomap.init(); - // load markers - if (load_markers()) { + load_markers(); + if (marker_cntall > 0) { text_notfound.hidden(true); geomap.set_dirty(); } else { geomap.hidden(true); geopos.hidden(true); + text_notfound.hidden(false); + text_info.hidden(true); } + + // never move this before the first load() bc that will mess load up geomap.on_move = [this](float lon, float lat) { (void)lon; (void)lat; load_markers(); }; + btn_back.on_select = [this, &nav](Button&) { + if (marker_start - ui::GeoMap::NumMarkerListElements >= 0) + marker_start = marker_start - ui::GeoMap::NumMarkerListElements; + else + marker_start = 0; + load_markers(); + }; + btn_next.on_select = [this, &nav](Button&) { + if (marker_start + ui::GeoMap::NumMarkerListElements <= marker_cntall) + marker_start = marker_start + ui::GeoMap::NumMarkerListElements; + else + marker_start = marker_cntall - ui::GeoMap::NumMarkerListElements; + load_markers(); + }; } WardriveMapView::~WardriveMapView() { diff --git a/firmware/application/external/wardrivemap/ui_wardrivemap.hpp b/firmware/application/external/wardrivemap/ui_wardrivemap.hpp index 8ec9f570..759cd011 100644 --- a/firmware/application/external/wardrivemap/ui_wardrivemap.hpp +++ b/firmware/application/external/wardrivemap/ui_wardrivemap.hpp @@ -1,6 +1,5 @@ /* - * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. - * Copyright (C) 2017 Furrtek + * Copyright (C) 2024 HTotoo * * This file is part of PortaPack. * @@ -20,10 +19,6 @@ * Boston, MA 02110-1301, USA. */ -// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols -// Thanks for the work of the original creators! -// Saying thanks in the main view! - #ifndef __UI_WARDRIVEMAP_H__ #define __UI_WARDRIVEMAP_H__ @@ -50,22 +45,29 @@ class WardriveMapView : public View { }; private: + const std::filesystem::path flippersub_dir = u"subghz"; NavigationView& nav_; - Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "All GEOTAG from CAPTURES"}; - Text text_notfound{{0 * 8, 3 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"}; + Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"}; + Text text_notfound{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"}; GeoPos geopos{ {0, 20}, GeoPos::alt_unit::METERS, GeoPos::spd_unit::HIDDEN}; GeoMap geomap{{0, 75, 240, 320 - 75}}; + Button btn_back{{22 * 8, 0 * 8, 3 * 8, 16}, "<-"}; + Button btn_next{{26 * 8, 0 * 8, 3 * 8, 16}, "->"}; + void on_gps(const GPSPosDataMessage* msg); void on_orientation(const OrientationDataMessage* msg); - bool load_markers(); // returns true if any exists, false if none. + void load_markers(); // returns true if any exists, false if none. - bool first_init = false; + bool first_init = false; // to center map to first marker, before callback is set + bool markers_counted = false; // to iterate all files on first, but only on first. + uint16_t marker_start = 0; // for paginator, this will be the first displayed + uint16_t marker_cntall = 0; // all geotagged marker count MessageHandlerRegistration message_handler_gps{ Message::ID::GPSPosData, diff --git a/firmware/application/flipper_subfile.cpp b/firmware/application/flipper_subfile.cpp new file mode 100644 index 00000000..7cca20cc --- /dev/null +++ b/firmware/application/flipper_subfile.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 HTotoo + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "flipper_subfile.hpp" + +#include "convert.hpp" +#include "file_reader.hpp" +#include "string_format.hpp" +#include + +namespace fs = std::filesystem; +using namespace std::literals; + +const std::string_view filetype_name = "Filetype"sv; +const std::string_view frequency_name = "Frequency"sv; +const std::string_view latitude_name = "Latitute"sv; +const std::string_view longitude_name = "Longitude"sv; +const std::string_view protocol_name = "Protocol"sv; +const std::string_view preset_name = "Preset"sv; +const std::string_view te_name = "TE"sv; // only in BinRAW + +/* +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Latitute: nan +Longitude: nan +Protocol: BinRAW +Bit: 1730 +TE: 495 +Bit_RAW: 1730 +Data_RAW: 02 10 84 + +te: is the quantization interval, in us. +bit: all bit counts in file. +bit_raw: the bits stored in the next data_raw. this 2 can repeat +data_raw: is an encoded sequence of durations, where each bit in the sequence encodes one TE interval: 1 - high level (there is a carrier), 0 - low (no carrier). For example, TE=100, Bit_RAW=8, Data_RAW=0x37 => 0b00110111, that is, -200 200 -100 300 will be transmitted + + + +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 5832 -12188 130 -162 + +raw_data- positive: carrier for n time, negative: no carrier for n time. (us) +*/ + +Optional read_flippersub_file(const fs::path& path) { + File f; + auto error = f.open(path); + + if (error) + return {}; + + flippersub_metadata metadata{}; + + auto reader = FileLineReader(f); + for (const auto& line : reader) { + auto cols = split_string(line, ':'); + + if (cols.size() != 2) + continue; // Bad line. + if (cols[1].length() <= 1) continue; + std::string fixed = cols[1].data() + 1; + fixed = trim(fixed); + if (cols[0] == filetype_name) { + if (fixed != "Flipper SubGhz Key File" && fixed != "Flipper SubGhz RAW File") return {}; // not supported + } else if (cols[0] == frequency_name) + parse_int(fixed, metadata.center_frequency); + else if (cols[0] == latitude_name) + parse_float_meta(fixed, metadata.latitude); + else if (cols[0] == longitude_name) + parse_float_meta(fixed, metadata.longitude); + else if (cols[0] == protocol_name) { + if (fixed == "RAW") metadata.protocol = FLIPPER_PROTO_RAW; + if (fixed == "BinRAW") metadata.protocol = FLIPPER_PROTO_BINRAW; + } else if (cols[0] == te_name) { + metadata.te = atoi(fixed.c_str()); + } else if (cols[0] == preset_name) { + if (fixed.find("FSK") != std::string::npos) { + metadata.preset = FLIPPER_PRESET_2FSK; + } else if (fixed.find("Ook") != std::string::npos) { + metadata.preset = FLIPPER_PRESET_OOK; + } else if (fixed.find("Custom") != std::string::npos) { + metadata.preset = FLIPPER_PRESET_CUSTOM; + } + + } else + continue; + } + + if (metadata.center_frequency == 0) return {}; // Parse failed. + + return metadata; +} diff --git a/firmware/application/flipper_subfile.hpp b/firmware/application/flipper_subfile.hpp new file mode 100644 index 00000000..0a123edf --- /dev/null +++ b/firmware/application/flipper_subfile.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 HTotoo + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __FLIPPER_SUBFILE_HPP__ +#define __FLIPPER_SUBFILE_HPP__ + +#include "metadata_file.hpp" + +typedef enum : uint8_t { + FLIPPER_PROTO_UNSUPPORTED = 0, + FLIPPER_PROTO_RAW = 1, + FLIPPER_PROTO_BINRAW = 2 +} FlipperProto; + +typedef enum : uint8_t { + FLIPPER_PRESET_UNK = 0, + FLIPPER_PRESET_CUSTOM = 1, + FLIPPER_PRESET_OOK = 2, + FLIPPER_PRESET_2FSK = 3, +} FlipperPreset; + +struct flippersub_metadata { + rf::Frequency center_frequency; + float latitude = 0; + float longitude = 0; + FlipperProto protocol = FLIPPER_PROTO_UNSUPPORTED; + FlipperPreset preset = FLIPPER_PRESET_UNK; + uint16_t te = 0; +}; + +Optional read_flippersub_file(const std::filesystem::path& path); + +// Maybe sometime there will be a data part reader / converter + +#endif // __FLIPPER_SUBFILE_HPP__ \ No newline at end of file