Compare commits

...

52 Commits

Author SHA1 Message Date
gullradriel 110a543ed3 save and restore region and or manual frequency values (#2340) 2024-11-05 14:56:38 +01:00
Benjamin Møller da5322765d Add about_simple updater from github (#2328)
* Generator for updating the about-page in PP from github.

* Fixing typo

* Rename generate_ui-about-simple.ccp.py to generate_ui-about-simple.cpp.py

Rename file, fix typo
2024-11-05 10:41:08 +08:00
gullradriel 8df05a57ea Disable ui_fsk_rx (#2337)
* removing ui_fsk_rx.cpp from targets

* removing unneeded ui_fsk_rx.hpp from includes

* deleting the now unused ui_fsk_rx.cpp and hpp files

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-11-04 08:28:03 +08:00
sommermorgentraum bea9f444c6 fine tune waveform and ook again (#2322) 2024-11-03 20:19:29 +01:00
jLynx abd6177303 H4M CPLD (#2335)
* Added CPLD code for H4M

* Added CPLD code for H4M

* Added CPLD code for H4M

* Added CPLD code for H4M

* Clean up

* Clean up
2024-11-03 11:25:43 +13:00
Totoo d6a9c74665 Auto start again (#2333)
* Extend autostart a bit

* @zxkmm 's sizeof fix
2024-11-02 22:15:33 +08:00
sommermorgentraum 5bb23b636e autostart fix 2 (#2331) 2024-11-02 10:43:42 +01:00
jLynx d5296c6ac3 H4M open source schematic (#2332)
* Create test20241102

* Delete hardware/test20241102

* Create H4 schematic files

H4 schematic files

* Delete hardware/portapack_h4/a

* Create 20241101

20241101

* Delete hardware/portapack_h4 directory

* Create 20241101

* Schematic Files for the H4M

The schematic files for the portapack H4M.

* Rename 20241101 to readme_h4m

* Update readme_h4m

---------

Co-authored-by: OpenSourceSDRLab <opensourcesdr@outlook.com>
2024-11-02 22:30:10 +13:00
Totoo c9863187d6 battery display imp (#2327) 2024-10-30 12:19:17 +01:00
sommermorgentraum 13e4f225ea bump hackrf fw bin (#2321) 2024-10-28 07:02:41 +13:00
Benjamin Møller 0df2c085a1 Fix output overflow at converting to hpp. Function is untouched. (#2320) 2024-10-24 17:43:18 +02:00
Benjamin Møller a4c2e155e5 Add generator and parser for bitmap.hpp (#2313)
* Combined the converter from <ico>.png to bitmap.hpp and reverse in one script: pp_png2hpp.py

* Minor change ficed variables from testing.

* Cleanup output for parser. Add description to readme.md

* Update pp_png2hpp.py

Added the suggested and much cleaner argparse code from zxkmm.
Added a icon-name handling, to convert one or a comma seperated subset of icons by name.

* Update pp_bitmap_parser.py

Updated the handler with dynamic in/outputs, in parallel to the pp_png2hpp.py script ... But I think I'll delete this one, after I decide how to handle the alpha (transparent) code.
2024-10-20 20:19:33 +08:00
Totoo c90f0944b1 I2cDev_PPmod periodic sensor query (#2315)
* Add more data tx from esp

* command enum rework. +1 for JT

* filter 0 query interval

* i2c timeouts and sanity check on ppmod
2024-10-20 00:03:47 +02:00
sommermorgentraum 7a38b04192 fix read data of display (#2312) 2024-10-19 15:31:49 +02:00
sommermorgentraum 9f84ccbe1f add color support for err message when compiling with ninja (#2311) 2024-10-19 15:30:43 +02:00
Totoo cb3774ad81 Increase sensitivity (#2309)
Increase sensitivity of Weather and SubghzD apps.
2024-10-17 10:22:01 +02:00
sommermorgentraum cc7123b28d replace pmem instead of add a new for touchscreen auto detection (#2308)
* replace instead of add

* submodule
2024-10-16 19:28:58 +02:00
sommermorgentraum 8e945024c5 touchscreen threshold (sensitivity) auto detect (#2306)
* gui

* worked but slow

* not do the auto detect

* worked

* remove debug thing

* format

* remove uneeded thing

* fix hackrf submodule bump

* clean up

* format

* format

* format

* remve batt

* add hint text and eta

* code clean up by @HTotoo

* work around to resolve not clear enough

* correct comments
2024-10-17 00:39:59 +08:00
sommermorgentraum 7d28e495c2 workaround for no-touchscreen device (#2301)
* just return un-consumed

* submodule ver

* submodule ver

* merge from upstram next

* revert submodule ver

* revert submodule ver
2024-10-16 17:35:49 +08:00
Totoo 48129cdf8d reorder contributors, to tempfix ui lag (#2307) 2024-10-16 10:08:53 +02:00
Totoo 66aa20161d Remove dead code (#2304)
* remove dead code, playdead

* removed Nuoptix

* remove ui_loadmodule

* removed modules.h

* removed replay_app

* removed handwrite

* removed numbers, script

* remove emu_cc1101

* removed noop, old pocsag

* removed unused abouts

* removed tone_search

* fix format

* forgot to remove

* removed unused py

* removed modules.h too
2024-10-15 22:14:33 +02:00
Totoo 1b3359b050 fix pocsag tx char limit (#2303)
* fix pocsag tx display

* set max to 40, and add 2nd line

* up to 80 chars

* max pocsag to 80 via serial too

* fix amp override
2024-10-15 20:38:18 +02:00
Erwin Ried 8d1fb7cb66 Update ui_about_simple.cpp (#2302)
Update ui_about_simple
2024-10-15 12:52:05 +02:00
Totoo e01a184721 LupusE's patch (#2300)
* LupusE's commit

* fix f obj doesn't exist

---------

Co-authored-by: zxkmm <zxkmm@hotmail.com>
2024-10-15 15:01:19 +08:00
gullradriel 26b830b816 fix out of range warning (#2299) 2024-10-14 18:26:02 +02:00
gullradriel a223bd1f21 Pacman warning fix (#2298)
* fix missing NULL initializers
* fix mispelled OnKeyboad
* set header version to 1
2024-10-14 17:40:29 +02:00
Bernd Herzog 023cee2c3c fixed warning (#2297) 2024-10-14 17:03:24 +02:00
gullradriel fcc52e83de enforce #ifndef __HEADER_H instead of #pragma once, for code consitency and better portability (#2296) 2024-10-14 15:50:04 +02:00
Bernd Herzog dcaa02c1e1 External module api (#2290)
* added external module debug view

* integrated module driver into i2c subsystem

* implemented i2c app loader

* added view mirror api

* fixed build

* added i2c to module api

* implemented standalone app touch event

* implemented focus management

* reverted formating change

* refactoring

* refactoring

* implemented events

* fixed memory usage
2024-10-14 13:07:41 +02:00
sommermorgentraum 5a0066963e rmeove my name cuz there's a better serial dbg tool (#2291) 2024-10-11 03:30:07 +00:00
Totoo 99d8fbdff0 Added the proposed new geotagging from flipper files. kept the old ones for compatibility (#2289) 2024-10-08 12:26:08 +08:00
Totoo 09c2c43be0 acars to ext but disabled (#2288) 2024-10-07 15:58:37 +02:00
Totoo b9771b2350 I2c fix (#2287)
* Resolve rename

* battery has warpers
2024-10-07 11:55:11 +02:00
Totoo 83b65ba6ce I2C device manager (#2282)
* message on dev list change

* dc detect

* added sht3x sensor.

* separete environment data from light

* max17055 moved to i2c dev

* sht fix, goterror detection fix

* fix ext sensor app display for a lot of devices.

* added bh1750 driver

* autoscan on main view

* added devlist mutex

* better timing

* fix h2 sw8 on poweron by usb
2024-10-06 22:14:27 +02:00
sommermorgentraum d4edb5f5f9 setting&autostart&widgets (OptionField and Waveform) imp (#2286)
* test

* test

* format

* format

* tune order
2024-10-06 21:58:03 +02:00
Erwin Ried 536d25db64 Update README.md (#2284)
Previous link was reported as ilegitimate store
2024-10-05 18:38:13 +02:00
Erwin Ried eca1db26e4 New sn0ren video about H4M (#2283) 2024-10-05 11:19:22 +02:00
sommermorgentraum 7d96bc4efe Optional sha512 to spread out the final password for rand_pw app (#2281)
* fix issue from #1444

* add hash sha512

* check point 1, with useless init pw usage

* gui

* should be works

* fix warning from sah512 lib

* delete diff file for debugging
2024-10-05 11:19:11 +02:00
Erwin Ried b0c92d9baf Update README.md (#2280)
aliexpress fixed the links for the alternative portapacks (the non opensourcesdrlabs)
2024-10-03 13:43:00 +02:00
Erwin Ried c1af935022 Update README.md (#2279)
Link to OpenSourceSDRLab store
2024-10-03 07:22:21 +02:00
sommermorgentraum e75fb59169 fix issue from #1444 (#2278) 2024-10-02 16:17:40 +02:00
sommermorgentraum 2d1314a3fa fine tune pwd gen app layout (#2277)
* init

* format
2024-10-01 16:16:59 +08:00
Totoo a398ed1634 Random app imp (#2276) 2024-09-30 17:45:13 +02:00
zxkmm 105742acbc add random ext app (#2273)
* copy paste from afsk

* add generate thing

* todo: remove uneeded code

* todo: remove uneeded code

* todo: asycnmsg detect indicate, check way to not use global password

* removed audio

* add log warn modal

* small tune

* remove drunk code

* password var global

* seed as text instead of console

* remove console

* should be almost done

* naming fix

* bitmap now moved to seperate folder, that header i deleted isn't related to firmware

* get cmake format back

* get cmake format back - try2

* try to revert cmake file

* get cmake format back - try3

* get cmake format back - try4

* move to util

* disable amp when launch

* refactor name

* cmake fix

* try to revert cmake file

* init in methods local var

* user another methods to generate

* change pause to flood

* fix log

* fine tune

* clang format

* fix name
2024-09-30 22:13:55 +08:00
zxkmm b43eaa8786 protect serial asyncmsg with indicate to prevent halt (#2272) 2024-09-29 10:19:29 +02:00
gullradriel b19ed5fec5 Looking glass narrow view marker wrap around (#2271)
* allow 1 MHz wide view (because why not?)
* allow marker to wrap around each corner
* added another define for clarity
2024-09-29 01:33:09 +08:00
zxkmm 419bc75d2f add ccache option and transparent color instead of bool (#2269)
* add ccache option and use language helper for proto view app

* add transparent color

* typo

* fix my typo

* fix my typo 2
2024-09-28 14:50:37 +02:00
Totoo b3a0ad018c Limit 50 items per page in sndboard. Maybe solves oom (#2263) 2024-09-28 14:48:32 +02:00
Totoo a21e9ca1af Nav fix, fixes #1197 (#2270)
* Nav fix, fixes #1197
2024-09-26 14:29:35 +02:00
Totoo 20f45e88f3 fix protoview shift, and add comments to languagehelper (#2268) 2024-09-26 09:22:24 +08:00
zxkmm 967506fb97 add pause feat for protoView app (#2267) 2024-09-25 17:45:45 +02:00
zxkmm 24ab2f29fa touch to pop splash (#2260)
* touch to pop

* debug

* have to add another var

* works but since touch screen is laggy, it's tricky

* tune to single touch

* remove debug things

* safer with sleep

* textual

* @HTotoo resolved the sleep issue

* use pop handler in another place (button)
2024-09-23 20:57:15 +02:00
213 changed files with 51817 additions and 5660 deletions
+24 -7
View File
@@ -23,6 +23,12 @@ cmake_policy(SET CMP0005 NEW)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
set(CMAKE_COLOR_MAKEFILE ON)
add_compile_options(-fdiagnostics-color=always)
option(USE_CCACHE "Enable ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary" OFF)
project(portapack-h1)
set(EXPECTED_GCC_VERSION "9.2.1")
@@ -72,13 +78,24 @@ set(HACKRF_CPLD_XSVF_PATH ${HACKRF_PATH}/firmware/cpld/sgpio_if/${HACKRF_CPLD_XS
set(HACKRF_FIRMWARE_DFU_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_DFU_FILENAME})
set(HACKRF_FIRMWARE_BIN_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_BIN_FILENAME})
# this seems causing some issues (ref. in discord discussions), so temporarily disabled, until figure out the pros and cons and bugs of this tool.
#find_program(CCACHE "ccache")
#if(CCACHE)
# set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
# set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
# set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros)
#endif(CCACHE)
# this seems causing some issues (ref. in discord discussions), but it sometimes do helps, so you have your choice to use. it's default disabled, unless you use such command:
# cmake -DUSE_CCACHE=ON ..
# `make` or `make -j` or `make -j10` etc
# the default behavior `cmake ..` isn't changed (aka don't use ccache)
message(STATUS "-------v ccache info--------")
if(USE_CCACHE)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_FOUND})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_FOUND})
message(STATUS "Using ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary.\n-- use `cmake -DUSE_CCACHE=OFF ..` to turn off ccache")
else()
message(WARNING "ccache is enabled but not found on the system.")
endif()
else()
message(STATUS "Not using ccache, use `cmake -DUSE_CCACHE=ON ..` to turn on ccache")
endif()
message(STATUS "-------^ ccache info--------")
enable_testing()
add_subdirectory(firmware)
+6 -6
View File
@@ -11,15 +11,15 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://grabify.link/7T28JP) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
*[PortaPack H2+HackRF+battery](https://grabify.link/ZYYPLB) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
# What is this?
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="700">](https://grabify.link/X4D5TF)
[<img alt="The new HackRF Portapack H4M" src="https://img.youtube.com/vi/onQRdCITmuk/maxresdefault.jpg" width="701">](https://grabify.link/0JUHZ6)
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="230">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="230">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="230">](https://grabify.link/C0J6ZR)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://grabify.link/X4D5TF) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="172">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="172">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="172">](https://grabify.link/C0J6ZR)
# Frequently Asked Questions
@@ -27,11 +27,11 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/VPMPSL), featuring numerous improvements and accessories. Sold by our friends at OpenSourceSDRLab. Join their giveaways on discord (check the badge on top).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/VPMPSL), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://grabify.link/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/7T28JP), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/ZYYPLB), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/HTDXG5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/XNGTAP), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
+7 -32
View File
@@ -125,12 +125,14 @@ set(CSRC
${CHIBIOS}/os/various/chprintf.c
)
#look for all i2cdev_ files
file(GLOB I2CDEV_SOURCES ${COMMON}/i2cdev_*.cpp)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
main.cpp
shell.cpp
${COMMON}/acars_packet.cpp
${COMMON}/adsb.cpp
${COMMON}/adsb_frame.cpp
${COMMON}/ais_baseband.cpp
@@ -176,8 +178,8 @@ set(CPPSRC
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/ads1110.cpp
${COMMON}/max17055.cpp
${COMMON}/i2cdevmanager.cpp
${I2CDEV_SOURCES}
${COMMON}/battery.cpp
${COMMON}/performance_counter.cpp
${COMMON}/bmpfile.cpp
@@ -189,7 +191,6 @@ set(CPPSRC
core_control.cpp
database.cpp
de_bruijn.cpp
#emu_cc1101.cpp
rfm69.cpp
event_m0.cpp
file_reader.cpp
@@ -244,7 +245,6 @@ set(CPPSRC
hw/touch_adc.cpp
ui_baseband_stats_view.cpp
ui_navigation.cpp
ui_playdead.cpp
ui_record_view.cpp
ui_sd_card_status_view.cpp
ui/ui_alphanum.cpp
@@ -266,50 +266,36 @@ set(CPPSRC
ui/ui_tone_key.cpp
ui/ui_transmitter.cpp
ui/ui_bmpview.cpp
apps/acars_app.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
# apps/analog_tv_app.cpp
apps/ble_comm_app.cpp
apps/ble_rx_app.cpp
apps/ble_tx_app.cpp
apps/capture_app.cpp
apps/ert_app.cpp
# apps/gps_sim_app.cpp
# apps/lge_app.cpp
apps/pocsag_app.cpp
# apps/replay_app.cpp
apps/soundboard_app.cpp
# apps/tpms_app.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
# apps/ui_adsb_tx.cpp #moved to ext
apps/ui_aprs_rx.cpp
apps/ui_aprs_tx.cpp
apps/ui_battinfo.cpp
apps/ui_bht_tx.cpp
apps/ui_bmp_file_viewer.cpp
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_debug_battery.cpp
apps/ui_debug_max17055.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
apps/ui_external_module_view.cpp
apps/ui_fileman.cpp
apps/ui_flash_utility.cpp
apps/ui_freqman.cpp
apps/ui_fsk_rx.cpp
apps/ui_iq_trim.cpp
# apps/ui_jammer.cpp
# apps/ui_keyfob.cpp
# apps/ui_lcr.cpp
apps/ui_level.cpp
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
# apps/ui_morse.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
apps/ui_pocsag_tx.cpp
apps/ui_rds.cpp
@@ -323,16 +309,11 @@ set(CPPSRC
apps/ui_settings.cpp
apps/ui_siggen.cpp
apps/ui_sonde.cpp
# apps/ui_spectrum_painter_image.cpp
# apps/ui_spectrum_painter_text.cpp
# apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
# apps/ui_sstvtx.cpp #moved to ext
apps/ui_standalone_view.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
apps/ui_text_editor.cpp
apps/ui_tone_search.cpp
apps/ui_touch_calibration.cpp
apps/ui_touchtunes.cpp
apps/ui_view_wav.cpp
@@ -343,14 +324,8 @@ set(CPPSRC
protocols/bht.cpp
protocols/dcs.cpp
protocols/encoders.cpp
# protocols/lcr.cpp
protocols/modems.cpp
protocols/rds.cpp
# ui_handwrite.cpp
# ui_loadmodule.cpp
# ui_numbers.cpp
# ui_replay_view.cpp
# ui_script.cpp
ui_sd_card_debug.cpp
config_mode.cpp
${CPLD_20150901_DATA_CPP}
-224
View File
@@ -1,224 +0,0 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyleft (ↄ) 2022 NotPike
*
* 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 "replay_app.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "io_convert.hpp"
#include "baseband_api.hpp"
#include "metadata_file.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "utility.hpp"
using namespace portapack;
namespace fs = std::filesystem;
namespace ui {
void ReplayAppView::set_ready() {
ready_signal = true;
}
void ReplayAppView::on_file_changed(const fs::path& new_file_path) {
file_path = new_file_path;
File::Size file_size{};
{ // Get the size of the data file.
File data_file;
auto error = data_file.open(file_path);
if (error) {
file_error();
return;
}
file_size = data_file.size();
}
// Get original record frequency if available.
auto metadata_path = get_metadata_path(file_path);
auto metadata = read_metadata_file(metadata_path);
if (metadata) {
field_frequency.set_value(metadata->center_frequency);
sample_rate = metadata->sample_rate;
} else {
// TODO: This is interesting because it implies that the
// The capture will just be replayed at the freq set on the
// FrequencyField. Is that an intentional behavior?
sample_rate = 500000;
}
// UI Fixup.
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz");
progressbar.set_max(file_size);
text_filename.set(truncate(file_path.filename().string(), 12));
uint8_t sample_size = capture_file_sample_size(current()->path);
auto duration = ms_duration(file_size, sample_rate, sample_size);
text_duration.set(to_string_time_ms(duration));
button_play.focus();
}
void ReplayAppView::on_tx_progress(const uint32_t progress) {
progressbar.set_value(progress);
}
void ReplayAppView::focus() {
button_open.focus();
}
void ReplayAppView::file_error() {
nav_.display_modal("Error", "File read error.");
}
bool ReplayAppView::is_active() const {
return (bool)replay_thread;
}
void ReplayAppView::toggle() {
if (is_active()) {
stop(false);
} else {
start();
}
}
void ReplayAppView::start() {
stop(false);
std::unique_ptr<stream::Reader> reader;
auto p = std::make_unique<FileConvertReader>();
auto open_error = p->open(file_path);
if (open_error.is_valid()) {
file_error();
return; // Fixes TX bug if there's a file error
} else {
reader = std::move(p);
}
if (reader) {
button_play.set_bitmap(&bitmap_stop);
baseband::set_sample_rate(sample_rate, OversampleRate::x8);
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
read_size, buffer_count,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
transmitter_model.set_sampling_rate(sample_rate * toUType(OversampleRate::x8));
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable();
if (portapack::persistent_memory::stealth_mode()) {
DisplaySleepMessage message;
EventDispatcher::send_message(message);
}
}
void ReplayAppView::stop(const bool do_loop) {
if (is_active())
replay_thread.reset();
if (do_loop && check_loop.value()) {
start();
} else {
transmitter_model.disable();
button_play.set_bitmap(&bitmap_play);
}
ready_signal = false;
}
void ReplayAppView::handle_replay_thread_done(const uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
stop(true);
} else if (return_code == ReplayThread::READ_ERROR) {
stop(false);
file_error();
}
progressbar.set_value(0);
}
ReplayAppView::ReplayAppView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({
&button_open,
&text_filename,
&text_sample_rate,
&text_duration,
&progressbar,
&field_frequency,
&tx_view, // now it handles previous rfgain, rfamp.
&check_loop,
&button_play,
&waterfall,
});
button_play.on_select = [this](ImageButton&) {
this->toggle();
};
button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".C*");
open_view->on_changed = [this](fs::path new_file_path) {
on_file_changed(new_file_path);
};
};
}
ReplayAppView::~ReplayAppView() {
transmitter_model.disable();
baseband::shutdown();
}
void ReplayAppView::on_hide() {
stop(false);
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
waterfall.on_hide();
View::on_hide();
}
void ReplayAppView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect);
}
} /* namespace ui */
-146
View File
@@ -1,146 +0,0 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __REPLAY_APP_HPP__
#define __REPLAY_APP_HPP__
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "replay_thread.hpp"
#include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
#include <string>
#include <memory>
namespace ui {
class ReplayAppView : public View {
public:
ReplayAppView(NavigationView& nav);
~ReplayAppView();
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
std::string title() const override { return "Replay"; };
private:
NavigationView& nav_;
TxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"tx_replay", app_settings::Mode::TX};
static constexpr ui::Dim header_height = 3 * 16;
uint32_t sample_rate = 0;
int32_t tx_gain{47};
bool rf_amp{true}; // aux private var to store temporal, Replay App rf_amp user selection.
static constexpr uint32_t baseband_bandwidth = 2500000;
const size_t read_size{16384};
const size_t buffer_count{3};
void on_file_changed(const std::filesystem::path& new_file_path);
void on_tx_progress(const uint32_t progress);
void toggle();
void start();
void stop(const bool do_loop);
bool is_active() const;
void set_ready();
void handle_replay_thread_done(const uint32_t return_code);
void file_error();
std::filesystem::path file_path{};
std::unique_ptr<ReplayThread> replay_thread{};
bool ready_signal{false};
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
"Open file"};
Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16},
"-"};
Text text_sample_rate{
{24 * 8, 0 * 16, 6 * 8, 16},
"-"};
Text text_duration{
{11 * 8, 1 * 16, 6 * 8, 16},
"-"};
ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}};
TxFrequencyField field_frequency{
{0 * 8, 2 * 16},
nav_};
TransmitterView2 tx_view{
{11 * 8, 2 * 16},
/*short_ui*/ true};
Checkbox check_loop{
{21 * 8, 2 * 16},
4,
"Loop",
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
this->handle_replay_thread_done(message.return_code);
}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
[this](const Message* const p) {
const auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
this->set_ready();
}
}};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress);
}};
};
} /* namespace ui */
#endif /*__REPLAY_APP_HPP__*/
+5 -3
View File
@@ -35,6 +35,8 @@ using namespace portapack;
namespace ui {
#define FILE_PER_PAGE 50
bool SoundBoardView::is_active() const {
return (bool)replay_thread;
}
@@ -178,9 +180,9 @@ void SoundBoardView::refresh_list() {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
// sounds[c].path = u"WAV/" + entry.path().native();
if (count >= (page - 1) * 100 && count < page * 100) {
if (count >= (page - 1) * FILE_PER_PAGE && count < page * FILE_PER_PAGE) {
file_list.push_back(entry.path());
if (file_list.size() == 100) {
if (file_list.size() == FILE_PER_PAGE) {
page++;
break;
}
@@ -225,7 +227,7 @@ void SoundBoardView::refresh_list() {
menu_view.set_highlighted(0); // Refresh
}
if (file_list.size() < 100) {
if (file_list.size() < FILE_PER_PAGE) {
page = 1;
}
}
-138
View File
@@ -1,138 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "cpld_update.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
#include "ui_about.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
#include <math.h>
#include <cstring>
using namespace lpc43xx;
using namespace portapack;
namespace ui {
// This is pretty much WaterfallView but in the opposite direction
CreditsWidget::CreditsWidget(
Rect parent_rect)
: Widget{parent_rect} {
}
void CreditsWidget::paint(Painter&) {
}
void CreditsWidget::on_show() {
clear();
const auto screen_r = screen_rect();
display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void CreditsWidget::on_hide() {
display.scroll_disable();
}
void CreditsWidget::new_row(
const std::array<Color, 240>& pixel_row) {
// Glitch be here (see comment in main.cpp)
const auto draw_y = display.scroll(-1);
display.draw_pixels(
{{0, draw_y - 1}, {240, 1}},
pixel_row);
}
void CreditsWidget::clear() {
display.fill_rectangle(
screen_rect(),
Theme::getInstance()->bg_darkest->background);
}
void AboutView::update() {
size_t i = 0;
std::array<Color, 240> pixel_row;
slow_down++;
if (slow_down % 3 < 2) return;
if (!timer) {
if (loop) {
credits_index = 0;
loop = false;
}
text = credits[credits_index].text;
timer = credits[credits_index].delay;
start_pos = credits[credits_index].start_pos;
if (timer < 0) {
timer = 240;
loop = true;
} else
timer += 16;
render_line = 0;
credits_index++;
} else
timer--;
if (render_line < 16) {
for (const auto c : text) {
const auto glyph = style().font.glyph(c);
const size_t start = (glyph.size().width() / 8) * render_line;
for (Dim c = 0; c < glyph.size().width(); c++) {
const auto pixel = glyph.pixels()[start + (c >> 3)] & (1U << (c & 0x7));
pixel_row[start_pos + i + c] = pixel ? Theme::getInstance()->bg_darkest->foreground : Theme::getInstance()->bg_darkest->background;
}
const auto advance = glyph.advance();
i += advance.x();
}
render_line++;
}
credits_display.new_row(pixel_row);
}
AboutView::AboutView(
NavigationView& nav) {
add_children({&credits_display,
&button_ok});
button_ok.on_select = [&nav](Button&) {
nav.pop();
};
}
void AboutView::focus() {
button_ok.focus();
}
} /* namespace ui */
-119
View File
@@ -1,119 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UI_ABOUT_H__
#define __UI_ABOUT_H__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include <cstdint>
namespace ui {
class CreditsWidget : public Widget {
public:
CreditsWidget(Rect parent_rect);
void on_show() override;
void on_hide() override;
void paint(Painter&) override;
void new_row(const std::array<Color, 240>& pixel_row);
private:
void clear();
};
class AboutView : public View {
public:
AboutView(NavigationView& nav);
void focus() override;
std::string title() const override { return "About"; };
private:
void update();
uint8_t credits_index{0};
uint8_t render_line{0};
Coord start_pos{0};
uint8_t slow_down{0};
int32_t timer{0};
bool loop{false};
std::string text{};
typedef struct credits_t {
size_t start_pos;
std::string text;
int32_t delay;
} credits_t;
// TODO: Make this dinamically centered and parse \n as the delay value so it is easy to maintain
const credits_t credits[26] = {
// 012345678901234567890123456789
{60, "PortaPack Mayhem", 0},
{60, "PortaPack|HAVOC", 0},
{11 * 8, "Gurus J. Boone", 0},
{18 * 8, "M. Ossmann", 16},
{11 * 8, "HAVOC Furrtek", 16},
{7 * 8, "POCSAG rx T. Sailer", 0},
{18 * 8, "E. Oenal", 16},
{0 * 8, "Radiosonde infos F4GMU", 0},
{18 * 8, "RS1729", 16},
{4 * 8, "RDS waveform C. Jacquet", 16},
{7 * 8, "Xy. infos cLx", 16},
{2 * 8, "OOK scan trick Samy Kamkar", 16},
{7 * 8, "World map NASA", 16},
{0 * 8, "TouchTunes infos Notpike", 16},
{4 * 8, "Subaru infos Tom", 0},
{18 * 8, "Wimmenhove", 16},
{1 * 8, "GPS,TV,BTLE,NRF Shao", 24},
{6 * 8, "Thanks & donators", 16},
{1 * 8, "Rainer Matla Keld Norman", 0},
{1 * 8, " Giorgio C. DC1RDB", 0},
{1 * 8, " Sigmounte Waax", 0},
{1 * 8, " Windyoona Channels", 0},
{1 * 8, " F4GEV Pyr3x", 0},
{1 * 8, " HB3YOE", 24},
{11 * 8, "MMXVIII", -1}};
CreditsWidget credits_display{
{0, 16, 240, 240}};
Button button_ok{
{72, 272, 96, 24},
"OK"};
MessageHandlerRegistration message_handler_update{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update();
}};
};
} /* namespace ui */
#endif /*__UI_ABOUT_H__*/
-413
View File
@@ -1,413 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 <ch.h>
#include "demofont.hpp"
#include "ymdata.hpp"
#include "cpld_update.hpp"
#include "portapack.hpp"
#include "audio.hpp"
#include "event_m0.hpp"
#include "ui_about.hpp"
#include "touch.hpp"
#include "sine_table.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
#include <math.h>
#include <cstring>
using namespace lpc43xx;
using namespace portapack;
namespace ui {
void AboutView::on_show() {
transmitter_model.set_target_frequency(1337000000); // TODO: Change
transmitter_model.set_baseband_configuration({
.mode = 0,
.sampling_rate = 1536000,
.decimation_factor = 1,
});
transmitter_model.set_rf_amp(true);
transmitter_model.set_lna(40);
transmitter_model.set_vga(40);
transmitter_model.enable();
baseband::set_audiotx_data(32, 50, false, 0);
// audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
}
void AboutView::render_video() {
uint8_t p, r, luma, chroma, cy;
ui::Color cc;
char ch;
float s;
// Send framebuffer to LCD. Gotta go fast !
display.render_box({30, 112}, {180, 72}, framebuffer);
// Clear framebuffer to black
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Drum hit palette animation
if (drum > 1) drum--;
// Render copper bars from Y buffer
for (p = 0; p < 72; p++) {
luma = copperbuffer[p] & 0x0F; // 0 is transparent
if (luma) {
chroma = copperbuffer[p] >> 4;
cc = ui::Color(std::min((coppercolor[chroma][0] / luma) * drum, 255), std::min((coppercolor[chroma][1] / luma) * drum, 255), std::min((coppercolor[chroma][2] / luma) * drum, 255));
for (r = 0; r < 180; r++)
framebuffer[(p * 180) + r] = cc;
}
}
// Scroll in/out state machine
if (anim_state == 0) {
// Scroll in
if (ofy < 8) {
ofy++;
anim_state = 0;
} else {
anim_state = 1;
}
if (ofx < (int16_t)(180 - (strlen(credits[credits_index].name) * 16) - 8)) {
ofx += 8;
anim_state = 0;
}
} else if (anim_state == 1) {
// Just wait
if (credits_timer == (30 * 3)) {
credits_timer = 0;
anim_state = 2;
} else {
credits_timer++;
}
} else {
// Scroll out
if (credits[credits_index].change == true) {
if (ofy > -24) {
ofy--;
anim_state = 2;
} else {
anim_state = 0;
}
} else {
anim_state = 0;
}
if (ofx < 180) {
ofx += 8;
anim_state = 2;
}
// Switch to next text
if (anim_state == 0) {
if (credits_index == 9)
credits_index = 0;
else
credits_index++;
ofx = -(strlen(credits[credits_index].name) * 16) - 16;
}
}
// Sine text ("role")
p = 0;
while ((ch = credits[credits_index].role[p])) {
draw_demoglyph({(ui::Coord)(8 + (p * 16)), (ui::Coord)(ofy + (sine_table_f32[((p * 16) + (phase >> 5)) & 0xFF] * 8))}, ch, paletteA);
p++;
}
// Scroll text (name)
p = 0;
while ((ch = credits[credits_index].name[p])) {
draw_demoglyph({(ui::Coord)(ofx + (p * 16)), 56}, ch, paletteB);
p++;
}
// Clear bars Y buffer
memset(copperbuffer, 0, 72);
// Render bars to Y buffer
for (p = 0; p < 5; p++) {
cy = copperbars[p];
for (r = 0; r < 16; r++)
copperbuffer[cy + r] = copperluma[r] + (p << 4);
}
// Animate bars positions
for (p = 0; p < 5; p++) {
s = sine_table_f32[((p * 32) + (phase / 24)) & 0xFF];
s += sine_table_f32[((p * 16) + (phase / 35)) & 0xFF];
copperbars[p] = 28 + (uint8_t)(s * 14);
}
phase += 128;
}
void AboutView::draw_demoglyph(ui::Point p, char ch, ui::Color* pal) {
uint8_t x, y, c, cl, cr;
uint16_t che;
int16_t lbx, il;
// Map ASCII to font bitmap
if ((ch >= 32) && (ch < 96))
che = char_map[ch - 32];
else
che = 0xFF;
if (che < 0xFF) {
che = (che * 128) + 48; // Start in bitmap
il = (180 * p.y) + p.x; // Start il framebuffer
for (y = 0; y < 16; y++) {
if (p.y + y >= 72) break; // Over bottom of framebuffer, abort
if (p.y + y >= 0) {
for (x = 0; x < 8; x++) {
c = demofont_bin[x + (y * 8) + che]; // Split byte in 2 4BPP pixels
cl = c >> 4;
cr = c & 0x0F;
lbx = p.x + (x * 2);
if (cl && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cl];
lbx++;
il++;
if (cr && (lbx < 180) && (lbx >= 0)) framebuffer[il] = pal[cr];
il++;
}
il += 180 - 16;
} else {
il += 180;
}
}
}
}
void AboutView::render_audio() {
uint8_t i, ymdata;
uint16_t ym_render_cnt;
// This is heavily inspired by MAME's ay8910.cpp and the YM2149's datasheet
// Render 1024 music samples
for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) {
// Update registers at 48000/960 = 50Hz
if (ym_sample_cnt == 0) {
// "Decompress" on the fly and update YM registers
for (i = 0; i < 14; i++) {
if (!ym_regs[i].cnt) {
// New run
ymdata = ymdata_bin[ym_regs[i].ptr++];
ym_regs[i].cnt = ymdata & 0x7F;
if (ymdata & 0x80) {
ym_regs[i].same = true;
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
} else {
ym_regs[i].same = false;
}
// Detect drum on channel B
if (i == 3)
if (ym_regs[3].value > 2) drum = 4;
}
if (ym_regs[i].same == false) {
ym_regs[i].value = ymdata_bin[ym_regs[i].ptr++];
if (i == 13) {
// Update envelope attributes
ym_env_att = (ym_regs[13].value & 4) ? 0x1F : 0x00;
if (!(ym_regs[13].value & 8)) {
ym_env_hold = 1;
ym_env_alt = ym_env_att;
} else {
ym_env_hold = ym_regs[13].value & 1;
ym_env_alt = ym_regs[13].value & 2;
}
// Reset envelope counter
ym_env_step = 0x1F;
ym_env_holding = 0;
ym_env_vol = (ym_env_step ^ ym_env_att);
}
}
ym_regs[i].cnt--;
}
ym_frame++;
}
// Square wave oscillators
// 2457600/16/48000 = 3.2, but 4 sounds better than 3...
for (i = 0; i < 3; i++) {
ym_osc_cnt[i] += 4;
if (ym_osc_cnt[i] >= (ym_regs[i * 2].value | ((ym_regs[(i * 2) + 1].value & 0x0f) << 8))) {
ym_osc_cnt[i] = 0;
ym_osc_out[i] ^= 1;
}
}
// Noise generator
ym_noise_cnt += 4;
if (ym_noise_cnt >= ((ym_regs[6].value & 0x1F) * 2)) {
ym_noise_cnt = 0;
ym_rng ^= (((ym_rng & 1) ^ ((ym_rng >> 3) & 1)) << 17);
ym_rng >>= 1;
}
// Mix tones and noise
for (i = 0; i < 3; i++)
ym_ch[i] = (ym_osc_out[i] | ((ym_regs[7].value >> i) & 1)) & ((ym_rng & 1) | ((ym_regs[7].value >> (i + 3)) & 1));
// Envelope generator
if (!ym_env_holding) {
ym_env_cnt += 8;
if (ym_env_cnt >= (ym_regs[11].value | (ym_regs[12].value << 8))) {
ym_env_cnt = 0;
ym_env_step--;
if (ym_env_step < 0) {
if (ym_env_hold) {
if (ym_env_alt)
ym_env_att ^= 0x1F;
ym_env_holding = 1;
ym_env_step = 0;
} else {
if (ym_env_alt && (ym_env_step & 0x20))
ym_env_att ^= 0x1F;
ym_env_step &= 0x1F;
}
}
}
}
ym_env_vol = (ym_env_step ^ ym_env_att);
ym_out = 0;
for (i = 0; i < 3; i++) {
if (ym_regs[i + 8].value & 0x10) {
// Envelope mode
ym_out += (ym_ch[i] ? ym_env_vol : 0);
} else {
// Fixed mode
ym_out += (ym_ch[i] ? (ym_regs[i + 8].value & 0x0F) : 0);
}
}
ym_buffer[ym_render_cnt] = (ym_out * 2) - 45;
if (ym_sample_cnt < 960) {
ym_sample_cnt++;
} else {
ym_sample_cnt = 0;
}
// Loop
if (ym_frame == ym_frames) ym_init();
}
}
void AboutView::update() {
if (framebuffer) {
// Update 1 out of 2 frames, 60Hz is very laggy
if (refresh_cnt & 1) render_video();
refresh_cnt++;
}
// Slowly increase volume to avoid jumpscare
if (headphone_vol < (70 << 2)) {
audio::headphone::set_volume(volume_t::decibel((headphone_vol / 4) - 99) + audio::headphone::volume_range().max);
headphone_vol++;
}
}
void AboutView::ym_init() {
uint8_t reg;
for (reg = 0; reg < 14; reg++) {
ym_regs[reg].cnt = 0;
// Pick up start pointers for each YM registers RLE blocks
ym_regs[reg].ptr = ((uint16_t)(ymdata_bin[(reg * 2) + 3]) << 8) + ymdata_bin[(reg * 2) + 2];
ym_regs[reg].same = false; // Useless ?
ym_regs[reg].value = 0; // Useless ?
}
ym_frame = 0;
}
AboutView::AboutView(
NavigationView& nav) {
uint8_t p, c;
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
add_children({{
&text_title,
&text_firmware,
&text_cpld_hackrf,
&text_cpld_hackrf_status,
&button_ok,
}});
if (cpld_hackrf_verify_eeprom()) {
text_cpld_hackrf_status.set(" OK");
} else {
text_cpld_hackrf_status.set("BAD");
}
// Politely ask for about 26kB
framebuffer = (ui::Color*)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
if (framebuffer) {
memset(framebuffer, 0, 180 * 72 * sizeof(ui::Color));
// Copy original font palette
c = 0;
for (p = 0; p < 48; p += 3)
paletteA[c++] = ui::Color(demofont_bin[p], demofont_bin[p + 1], demofont_bin[p + 2]);
// Increase red in another one
c = 0;
for (p = 0; p < 48; p += 3)
paletteB[c++] = ui::Color(std::min(demofont_bin[p] + 64, 255), demofont_bin[p + 1], demofont_bin[p + 2]);
}
// Init YM synth
ym_frames = ((uint16_t)(ymdata_bin[1]) << 8) + ymdata_bin[0];
ym_init();
button_ok.on_select = [this, &nav](Button&) {
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
nav.pop();
};
}
AboutView::~AboutView() {
transmitter_model.disable();
baseband::shutdown();
}
void AboutView::focus() {
button_ok.focus();
}
} /* namespace ui */
-168
View File
@@ -1,168 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UI_ABOUT_H__
#define __UI_ABOUT_H__
#include "ui_widget.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "transmitter_model.hpp"
#include "baseband_api.hpp"
#include <cstdint>
namespace ui {
class AboutView : public View {
public:
AboutView(NavigationView& nav);
~AboutView();
void on_show() override;
void focus() override;
private:
void ym_init();
void update();
void render_video();
void render_audio();
void draw_demoglyph(ui::Point p, char ch, ui::Color* pal);
uint16_t debug_cnt = 0;
typedef struct ymreg_t {
uint8_t value;
uint8_t cnt;
uint16_t ptr;
bool same;
} ymreg_t;
uint16_t headphone_vol = 5 << 2;
ymreg_t ym_regs[14];
uint16_t ym_frames;
uint16_t ym_frame;
uint8_t drum = 0;
uint16_t ym_osc_cnt[3];
uint32_t ym_rng = 1;
uint16_t ym_noise_cnt;
uint8_t ym_env_att, ym_env_hold, ym_env_alt, ym_env_holding, ym_env_vol;
int8_t ym_env_step;
uint16_t ym_env_cnt;
uint8_t ym_osc_out[3];
uint8_t ym_ch[3];
uint8_t ym_out;
uint16_t ym_sample_cnt = 0;
int8_t ym_buffer[1024];
uint8_t refresh_cnt;
ui::Color paletteA[16];
ui::Color paletteB[16];
ui::Color* framebuffer;
uint32_t phase = 0;
uint8_t copperbars[5] = {0};
uint8_t copperbuffer[72] = {0};
uint8_t anim_state = 0;
uint8_t credits_index = 0;
uint16_t credits_timer = 0;
int16_t ofx = -180, ofy = -24;
const uint8_t char_map[64] = {0xFF, 27, 46, 0xFF, 0xFF, 0xFF, 28, 45,
58, 59, 0xFF, 43, 40, 57, 26, 42,
29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 41, 0xFF, 0xFF, 0xFF, 0xFF, 44,
0xFF, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
const uint8_t copperluma[16] = {8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8};
const uint8_t coppercolor[5][3] = {{255, 0, 0},
{0, 255, 0},
{0, 0, 255},
{255, 0, 255},
{255, 255, 0}};
typedef struct credits_t {
char role[12];
char name[12];
bool change;
} credits_t;
// 0123456789A 0123456789A
const credits_t credits[10] = {{"GURUS", "J. BOONE", false},
{"GURUS", "M. OSSMANN", true},
{"BUGS", "FURRTEK", true},
{"RDS WAVE", "C. JACQUET", true},
{"POCSAG RX", "T. SAILER", false},
{"POCSAG RX", "E. OENAL", true},
{"XYLOS DATA", "CLX", true},
{"GREETS TO", "SIGMOUNTE", false},
{"GREETS TO", "WINDYOONA", true},
{"THIS MUSIC", "BIG ALEC", true}};
Text text_title{
{100, 32, 40, 16},
"About",
};
Text text_firmware{
{0, 236, 240, 16},
"Version " VERSION_STRING,
};
Text text_cpld_hackrf{
{0, 252, 11 * 8, 16},
"HackRF CPLD",
};
Text text_cpld_hackrf_status{
{240 - 3 * 8, 252, 3 * 8, 16},
"???"};
Button button_ok{
{72, 272, 96, 24},
"OK"};
MessageHandlerRegistration message_handler_update{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update();
}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::FIFOSignal,
[this](const Message* const p) {
const auto message = static_cast<const FIFOSignalMessage*>(p);
if (message->signaltype == 1) {
this->render_audio();
baseband::set_fifo_data(ym_buffer);
}
}};
};
} /* namespace ui */
#endif /*__UI_ABOUT_H__*/
+17 -9
View File
@@ -12,17 +12,25 @@ const std::string authors_list[] = {
"johnelder,jwetzell,nnemanjan00",
"N0vaPixel,klockee,gullradriel",
"jamesshao8,ITAxReal,rascafr",
"mcules,dqs105,strijar",
"mcules,dqs105,strijar,fossum",
"zhang00963,RedFox-Fr,aldude999",
"East2West,fossum,ArjanOnwezen",
"vXxOinvizioNxX,teixeluis",
"Brumi-2021,texasyojimbo",
"East2West,ArjanOnwezen,jLynx",
"vXxOinvizioNxX,teixeluis,n0xa",
"Brumi-2021,texasyojimbo,zigad",
"heurist1,intoxsick,ckuethe",
"notpike,jLynx,zigad",
"MichalLeonBorsuk,jimilinuxguy",
"kallanreed,bernd-herzog",
"NotherNgineer,zxkmm,u-foka",
"Netro,HTotoo",
"notpike,MichalLeonBorsuk",
"kallanreed,bernd-herzog,zxkmm",
"NotherNgineer,jimilinuxguy",
"iNetro,HTotoo,BehleZebub,dc2dc",
"arneluehrs,u-foka,Maescool",
"nnesetto,formtapez,F33RNI",
"rusty-labs,andrej-mk,taskinen",
"MatiasFernandez,TQMatvey,KimIV",
"RobertoD91,RndmNmbr,OpCode1300",
"alex10791,GitClo,GermanAizek",
"JimiHFord,jstockdale,aSmig",
"KillerTurtleSoftware,Lpd738",
"MattLodge,z3r0l1nk,joyel24",
" ",
"#Havoc:",
"furrtek,mrmookie,NotPike",
+26 -6
View File
@@ -94,21 +94,39 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
record_view.set_sampling_rate(24000);
options_region.on_change = [this](size_t, int32_t i) {
if (i == 0) {
field_frequency.on_change = [this](rf::Frequency f) { (void)f; };
if (i == 0) { // MAN Manual setting
field_frequency.set_value(aprs_rx_freq);
} else if (i == 1) { // NA - North America - is also the default
field_frequency.set_value(144390000);
} else if (i == 1) {
} else if (i == 2) { // EUR
field_frequency.set_value(144800000);
} else if (i == 2) {
} else if (i == 3) { // AUS
field_frequency.set_value(145175000);
} else if (i == 3) {
} else if (i == 4) { // NZ
field_frequency.set_value(144575000);
} else if (i == 4) {
} else if (i == 5) { // ISS
field_frequency.set_value(145825000);
}
options_region_id = i;
receiver_model.set_target_frequency(field_frequency.value()); // Retune
field_frequency.on_change = [this](rf::Frequency f) {
aprs_rx_freq = f;
options_region.set_selected_index(0, true);
};
};
field_frequency.set_step(100);
options_region.set_selected_index(0, true);
field_frequency.on_edit = [this]() {
auto freq_view = nav_.push<FrequencyKeypadView>(field_frequency.value());
freq_view->on_changed = [this](rf::Frequency f) {
aprs_rx_freq = f;
field_frequency.set_value(f);
};
};
// Note: setting the region is also going to sef field_frequency.on_change
options_region.set_selected_index(options_region_id, true);
logger = std::make_unique<APRSLogger>();
if (logger)
@@ -124,6 +142,8 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
void APRSRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
aprs_rx_freq = freq;
options_region.set_selected_index(0, true);
}
void APRSRxView::on_packet(const APRSPacketMessage* message) {
+14 -9
View File
@@ -190,6 +190,8 @@ class APRSRxView : public View {
private:
void on_data(uint32_t value, bool is_data);
bool reset_console = false;
uint8_t options_region_id = 1; // default to North America
rf::Frequency aprs_rx_freq{144390000}; // default to North America frequency
NavigationView& nav_;
RxRadioState radio_state_{
@@ -198,7 +200,10 @@ class APRSRxView : public View {
3072000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_aprs", app_settings::Mode::RX};
"rx_aprs",
app_settings::Mode::RX,
{{"options_region_id"sv, &options_region_id},
{"aprs_rx_freq"sv, &aprs_rx_freq}}};
uint8_t console_color{0};
std::string str_log{""};
@@ -220,15 +225,15 @@ class APRSRxView : public View {
OptionsField options_region{
{0 * 8, 0 * 8},
3,
{{"NA ", 0},
{"EUR", 1},
{"AUS", 2},
{"NZ ", 3},
{"ISS", 4}}};
{{"MAN", 0},
{"NA ", 1},
{"EUR", 2},
{"AUS", 3},
{"NZ ", 4},
{"ISS", 5}}};
RxFrequencyField field_frequency{
{3 * 8, 0 * 16},
nav_};
FrequencyField field_frequency{
{3 * 8, 0 * 16}};
// DEBUG
RecordView record_view{
+17 -4
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -74,7 +75,19 @@ void BattinfoView::update_result() {
text_current.hidden(false);
text_charge.hidden(false);
text_current.set(to_string_dec_int(current) + " mA");
text_charge.set(current >= 0 ? "Charging" : "Discharging");
if (current >= 25) // when >25mA it is charging in any scenario
text_charge.set("Charging");
else {
if (current > -25) { // between -25 and 25
if (voltage > 4060 || percent == 100) { // the voltage is high enough, so it is full, that's why no mA
text_charge.set("Full");
} else {
text_charge.set("Holding"); // not enough voltage, so should charge. maybe the batttery is off, or USB power is not enough
}
} else {
text_charge.set("Discharging"); // less then -25mA, so it is discharging
}
}
labels_opt.hidden(false);
text_ttef.hidden(false);
@@ -102,7 +115,7 @@ void BattinfoView::update_result() {
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
text_ttef.hidden(false);
float ttef = 0;
if (current <= 0) {
if (current <= 0) { // we keep this yet
ttef = battery::BatteryManagement::get_tte();
} else {
ttef = battery::BatteryManagement::get_ttf();
@@ -132,7 +145,7 @@ void BattinfoView::update_result() {
}
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
BatteryStateMessage msg{valid_mask, percent, current >= 0, voltage};
BatteryStateMessage msg{valid_mask, percent, current >= 25, voltage};
EventDispatcher::send_message(msg);
}
@@ -185,4 +198,4 @@ BattinfoView::~BattinfoView() {
thread = nullptr;
}
}
} // namespace ui
} // namespace ui
+3 -1
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -27,6 +28,7 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "i2cdevmanager.hpp"
namespace ui {
class BattinfoView : public View {
@@ -104,4 +106,4 @@ class BattinfoView : public View {
} /* namespace ui */
#endif /*__UI_BATTINFO__*/
#endif /*__UI_BATTINFO__*/
+14 -8
View File
@@ -36,7 +36,8 @@
#include "ui_font_fixed_8x16.hpp"
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_battery.hpp"
#include "ui_debug_max17055.hpp"
#include "ui_external_module_view.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -226,8 +227,10 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
return radio::debug::second_if::register_read(register_number);
case CT_SI5351:
return portapack::clock_generator.read_register(register_number);
case CT_BATTERY:
return battery::BatteryManagement::read_register(register_number);
case CT_MAX17055: {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
return dev->read_register(register_number);
}
case CT_AUDIO:
return audio::debug::reg_read(register_number);
}
@@ -249,9 +252,11 @@ void RegistersWidget::reg_write(const uint32_t register_number, const uint32_t v
case CT_SI5351:
portapack::clock_generator.write_register(register_number, value);
break;
case CT_BATTERY:
battery::BatteryManagement::write_register(register_number, value);
case CT_MAX17055: {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
dev->write_register(register_number, value);
break;
}
case CT_AUDIO:
audio::debug::reg_write(register_number, value);
break;
@@ -465,9 +470,9 @@ void DebugPeripheralsMenuView::on_populate() {
{si5351x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) {
add_item(
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_BATTERY, 256, 16, 16}); }});
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_MAX17055, 256, 16, 16}); }});
}
set_max_rows(2); // allow wider buttons
}
@@ -510,9 +515,10 @@ void DebugMenuView::on_populate() {
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
{"Ext Module", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<ExternalModuleView>(); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) {
add_item(
{"Battery", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_batt_icon, [this]() { nav_.push<BatteryCapacityView>(); }});
}
+1 -1
View File
@@ -139,7 +139,7 @@ typedef enum {
CT_MAX283X,
CT_SI5351,
CT_AUDIO,
CT_BATTERY,
CT_MAX17055,
} chip_type_t;
struct RegistersWidgetConfig {
@@ -1,11 +1,11 @@
#include "ui_debug_battery.hpp"
#include "ui_debug_max17055.hpp"
#include "string_format.hpp"
namespace ui {
BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) {
if (index < battery::max17055::MAX17055::entries_count) {
return battery::max17055::MAX17055::entries[index];
if (index < i2cdev::I2cDev_MAX17055::entries_count) {
return i2cdev::I2cDev_MAX17055::entries[index];
}
return {"", 0, "", 0, false, "", false, 0, false, false, false, 0, false};
}
@@ -27,6 +27,12 @@ BatteryCapacityView::BatteryCapacityView(NavigationView& nav) {
button_done.on_select = [&nav](Button&) { nav.pop(); };
auto dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (!dev) { // dev not found
nav.pop();
return;
}
populate_page(0);
update_page_text();
}
@@ -37,7 +43,7 @@ void BatteryCapacityView::focus() {
bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
int32_t new_page = current_page + delta;
if (new_page >= 0 && new_page < ((int32_t)battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
if (new_page >= 0 && new_page < ((int32_t)i2cdev::I2cDev_MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
current_page = new_page;
populate_page(current_page * ENTRIES_PER_PAGE);
update_page_text();
@@ -46,11 +52,12 @@ bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
}
void BatteryCapacityView::update_values() {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = current_page * ENTRIES_PER_PAGE + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
if (entry_index < i2cdev::I2cDev_MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
uint16_t raw_value = battery::BatteryManagement::read_register(entry.address);
uint16_t raw_value = dev->read_register(entry.address);
hex_texts[i].set("0x" + to_string_hex(raw_value, 4));
@@ -78,7 +85,7 @@ void BatteryCapacityView::update_values() {
void BatteryCapacityView::populate_page(int start_index) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = start_index + i;
if (entry_index < battery::max17055::MAX17055::entries_count) {
if (entry_index < i2cdev::I2cDev_MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
name_texts[i].set(entry.name);
addr_texts[i].set("0x" + to_string_hex(entry.address, 2));
@@ -97,7 +104,7 @@ void BatteryCapacityView::populate_page(int start_index) {
}
void BatteryCapacityView::update_page_text() {
int total_pages = (battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
int total_pages = (i2cdev::I2cDev_MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
page_text.set("Page " + to_string_dec_uint(current_page + 1) + "/" + to_string_dec_uint(total_pages));
}
@@ -5,7 +5,8 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "battery.hpp"
#include "max17055.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
namespace ui {
@@ -17,7 +18,7 @@ class BatteryCapacityView : public View {
bool on_encoder(const EncoderEvent delta) override;
using RegisterEntry = battery::max17055::RegisterEntry;
using RegisterEntry = i2cdev::I2cDev_MAX17055::RegisterEntry;
private:
static RegisterEntry get_entry(size_t index);
+1 -9
View File
@@ -41,11 +41,6 @@ DfuMenu::DfuMenu(NavigationView& nav)
&text_info_line_8,
&text_info_line_9,
&text_info_line_10});
if (battery::BatteryManagement::isDetected()) {
add_child(&voltage_label);
add_child(&text_info_line_11);
}
}
void DfuMenu::paint(Painter& painter) {
@@ -53,7 +48,7 @@ void DfuMenu::paint(Painter& painter) {
size_t m0_fragmented_free_space = 0;
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
auto lines = (battery::BatteryManagement::isDetected() ? 11 : 10) + 2;
auto lines = 10 + 2;
text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6));
text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6));
@@ -65,9 +60,6 @@ void DfuMenu::paint(Painter& painter) {
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
if (battery::BatteryManagement::isDetected()) {
text_info_line_11.set(to_string_decimal_padding((float)battery::BatteryManagement::getVoltage() / 1000.0, 3, 6));
}
constexpr auto margin = 5;
@@ -60,8 +60,6 @@ class DfuMenu : public View {
{{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "Uptime:", Theme::getInstance()->fg_darkcyan->foreground}};
Labels voltage_label{{{6 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "Voltage:", Theme::getInstance()->fg_darkcyan->foreground}};
Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_3{{15 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@@ -72,7 +70,6 @@ class DfuMenu : public View {
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_9{{15 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_10{{15 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_11{{15 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
class DfuMenu2 : public View {
+43 -8
View File
@@ -25,6 +25,9 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#define PADDING_LEFT 1
#define PADDING_RIGHT 1
using namespace portapack;
namespace ui {
@@ -58,6 +61,7 @@ EncodersConfigView::EncodersConfigView(
options_enctype.on_change = [this](size_t index, int32_t) {
on_type_change(index);
set_dirty();
};
options_enctype.set_options(enc_options);
@@ -104,7 +108,18 @@ void EncodersConfigView::on_type_change(size_t index) {
// Add new SymFields.
Point pos{2 * 8, 9 * 8};
std::string format_string;
std::string format_string = encoder_def->word_format;
/*important notes of this format_String
* previously before PR#1444, this code using the for loop below to generate the format_string, which MAYBE to prevent showing the S symble.
* but after PR#1444, the entireh of symfield code not compatible with this design anymore.
* however the encoder_def struct itself has a field called word_format, which exactly be avle to use as the format_string,
* because itself is ONLY a hint text, not impact protocol things.
* When you find OOK protocol is broken and see this commit from git blame, please be aware that it's not from here.
* previously me and @cusspvz already found it broken, this commit is just to fix the broken that from PR#1444.
* @zxkmm
*/
uint8_t word_length = encoder_def->word_length;
auto on_change_handler = [this](SymField&) {
generate_frame();
@@ -119,11 +134,9 @@ void EncodersConfigView::on_type_change(size_t index) {
switch (symbol_type) {
case 'A':
symfield->set_symbol_list(encoder_def->address_symbols);
format_string += 'A';
break;
case 'D':
symfield->set_symbol_list(encoder_def->data_symbols);
format_string += 'D';
break;
}
@@ -131,8 +144,8 @@ void EncodersConfigView::on_type_change(size_t index) {
pos += Point{8, 0};
}
// Ugly :( Pad to erase
format_string.append(24 - format_string.size(), ' ');
// cut the S, cuz sync bit isn't in symfield for user to chage/edit.
format_string.erase(std::remove(format_string.begin(), format_string.end(), 'S'), format_string.end());
text_format.set(format_string);
generate_frame();
@@ -144,12 +157,34 @@ void EncodersConfigView::on_show() {
}
void EncodersConfigView::draw_waveform() {
// padding reason:
// in real world the signal would always start with low level and became low level again after yout turn off the radio;
// the waveform_buffer only controls drawing, the real send logic that been sent is controlled by frame_fragments
// so just for out of looking things
size_t length = frame_fragments.length();
for (size_t n = 0; n < length; n++)
waveform_buffer[n] = (frame_fragments[n] == '0') ? 0 : 1;
// currently not needed since all the supported OOK protocol wont exceed 550 yet
if (length + (PADDING_LEFT + PADDING_RIGHT) >= WAVEFORM_BUFFER_SIZE) {
length = WAVEFORM_BUFFER_SIZE - (PADDING_LEFT + PADDING_RIGHT);
}
waveform.set_length(length);
// padding l
for (size_t i = 0; i < PADDING_LEFT; i++) {
waveform_buffer[i] = 0;
}
// real wf
for (size_t n = 0; n < length; n++) {
waveform_buffer[n + PADDING_LEFT] = (frame_fragments[n] == '0') ? 0 : 1;
}
// padding r
for (size_t i = length + PADDING_LEFT; i < WAVEFORM_BUFFER_SIZE; i++) {
waveform_buffer[i] = 0;
}
waveform.set_length(length + PADDING_LEFT + PADDING_RIGHT);
waveform.set_dirty();
}
+3 -1
View File
@@ -32,6 +32,8 @@
#include <memory>
#include <vector>
#define WAVEFORM_BUFFER_SIZE 550
using namespace encoders;
namespace ui {
@@ -56,7 +58,7 @@ class EncodersConfigView : public View {
std::string frame_fragments = "0";
private:
int16_t waveform_buffer[550];
int16_t waveform_buffer[WAVEFORM_BUFFER_SIZE];
const encoder_def_t* encoder_def{};
void draw_waveform();
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* 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 "ui_external_module_view.hpp"
#include "portapack.hpp"
#include "ui_standalone_view.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
#include <optional>
namespace ui {
void ExternalModuleView::focus() {
dummy.focus();
}
void ExternalModuleView::on_tick_second() {
i2cdev::I2CDevManager::manual_scan();
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (!dev) {
text_header.set("No module connected");
text_name.set("");
text_version.set("");
text_number_apps.set("");
text_app1_name.set("");
text_app2_name.set("");
text_app3_name.set("");
text_app4_name.set("");
text_app5_name.set("");
return;
}
auto device_info = dev->readDeviceInfo();
if (device_info.has_value() == false) {
text_header.set("No module connected");
text_name.set("");
text_version.set("");
text_number_apps.set("");
text_app1_name.set("");
text_app2_name.set("");
text_app3_name.set("");
text_app4_name.set("");
text_app5_name.set("");
return;
}
text_header.set("Module found");
std::string btnText = (std::string) "Module: " + device_info->module_name;
text_name.set(btnText);
text_version.set("Version: " + std::to_string(device_info->module_version));
text_number_apps.set("No# Apps: " + std::to_string(device_info->application_count));
for (uint32_t i = 0; i < device_info->application_count && i < 5; i++) {
auto appInfo = dev->getStandaloneAppInfo(i);
if (appInfo.has_value() == false) {
continue;
}
std::string btnText = (std::string) "App " + std::to_string(device_info->application_count) + ": " + (const char*)appInfo->app_name;
switch (appInfo->menu_location) {
case app_location_t::UTILITIES:
btnText += " (Utilities)";
break;
case app_location_t::RX:
btnText += " (RX)";
break;
case app_location_t::TX:
btnText += " (TX)";
break;
case app_location_t::DEBUG:
btnText += " (Debug)";
break;
case app_location_t::HOME:
btnText += " (Home)";
break;
}
switch (i) {
case 0:
text_app1_name.set(btnText);
break;
case 1:
text_app2_name.set(btnText);
break;
case 2:
text_app3_name.set(btnText);
break;
case 3:
text_app4_name.set(btnText);
break;
case 4:
text_app5_name.set(btnText);
break;
}
}
}
} // namespace ui
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* 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 __UI_EXTERNAL_MODULE_VIEW_H
#define __UI_EXTERNAL_MODULE_VIEW_H
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
#include "portapack.hpp"
#include "memory_map.hpp"
#include "irq_controls.hpp"
#include <functional>
#include <utility>
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
namespace ui {
class ExternalModuleView : public View {
public:
ExternalModuleView(NavigationView& nav)
: nav_(nav) {
add_children({&text_header,
&text_name,
&text_version,
&text_number_apps,
&text_app1_name,
&text_app2_name,
&text_app3_name,
&text_app4_name,
&text_app5_name,
&dummy});
text_header.set("No module connected");
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
~ExternalModuleView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
}
std::string title() const override { return "Ext Module"; };
void focus() override;
private:
NavigationView& nav_;
Text text_header{{16, 16, 208, 16}};
Text text_name{{24, 32, 200, 16}};
Text text_version{{24, 48, 200, 16}};
Text text_number_apps{{24, 64, 200, 16}};
Text text_app1_name{{24, 96, 200, 16}};
Text text_app2_name{{24, 112, 200, 16}};
Text text_app3_name{{24, 128, 200, 16}};
Text text_app4_name{{24, 144, 200, 16}};
Text text_app5_name{{24, 160, 200, 16}};
Button dummy{
{240, 0, 0, 0},
""};
SignalToken signal_token_tick_second{};
void on_tick_second();
};
} // namespace ui
#endif
-198
View File
@@ -1,198 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_fsk_rx.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include "ui_freqman.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
void FskRxLogger::log_raw_data(const std::string& data, const uint32_t frequency) {
std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz";
// // Raw hex dump of all the codewords
// for (size_t c = 0; c < 16; c++)
// entry += to_string_hex(packet[c], 8) + " ";
log_file.write_entry(data + entry);
}
void FskRxLogger::log_decoded(Timestamp timestamp, const std::string& text) {
log_file.write_entry(timestamp, text);
}
namespace ui {
//---------------------------------------------------------------------------------------------------------------
// Console View
//---------------------------------------------------------------------------------------------------------------
FskRxAppConsoleView::FskRxAppConsoleView(NavigationView& nav, Rect parent_rect)
: View(parent_rect), nav_{nav} {
add_child(&console);
};
void FskRxAppConsoleView::on_packet(uint32_t value, bool is_data) {
if (is_data) {
console.write(to_string_dec_uint(value) + " ");
}
}
void FskRxAppConsoleView::on_show() {
hidden(false);
}
void FskRxAppConsoleView::on_hide() {
hidden(true);
}
FskRxAppConsoleView::~FskRxAppConsoleView() {
}
//---------------------------------------------------------------------------------------------------------------
// Spectrum View
//---------------------------------------------------------------------------------------------------------------
FskRxAppView::FskRxAppView(NavigationView& nav, Rect parent_rect)
: View(parent_rect), nav_{nav} {
add_child(&waterfall);
hidden(true);
}
FskRxAppView::~FskRxAppView() {
}
void FskRxAppView::focus() {
}
void FskRxAppView::on_show() {
hidden(false);
waterfall.start();
}
void FskRxAppView::on_hide() {
hidden(true);
waterfall.stop();
}
//---------------------------------------------------------------------------------------------------------------
// Base View
//---------------------------------------------------------------------------------------------------------------
FskxRxMainView::FskxRxMainView(NavigationView& nav)
: nav_{nav} {
add_children({&tab_view,
&view_data,
&view_stream,
&labels,
&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&deviation_frequency,
&record_view});
baseband::run_image(portapack::spi_flash::image_tag_fskrx);
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
deviation_frequency.on_change = [this](rf::Frequency f) {
refresh_ui(f);
};
// Set initial sampling rate
/* Bandwidth of 2FSK is 2 * Deviation */
record_view.set_sampling_rate(initial_deviation * 2);
field_frequency.set_value(initial_target_frequency);
deviation_frequency.set_value(initial_deviation);
logger.append(logs_dir / u"FSKRX.TXT");
baseband::set_fsk(initial_deviation);
audio::output::start();
receiver_model.enable();
}
void FskxRxMainView::handle_decoded(Timestamp timestamp, const std::string& prefix) {
if (logging()) {
logger.log_decoded(timestamp, prefix);
}
}
void FskxRxMainView::refresh_ui(rf::Frequency deviationHz) {
/* Nyquist would imply a sample rate of 2x bandwidth, but because the ADC
* provides 2 values (I,Q), the sample_rate is equal to bandwidth here. */
/* Bandwidth of 2FSK is 2 * Deviation */
auto sample_rate = deviationHz * 2;
/* base_rate (bandwidth) is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card. */
if (!view_stream.hidden()) {
view_stream.waterfall.stop();
}
// record_view determines the correct oversampling to apply and returns the actual sample rate.
// NB: record_view is what actually updates proc_capture baseband settings.
auto actual_sample_rate = record_view.set_sampling_rate(sample_rate);
// Update the radio model with the actual sampling rate.
receiver_model.set_sampling_rate(actual_sample_rate);
// Get suitable anti-aliasing BPF bandwidth for MAX2837 given the actual sample rate.
auto anti_alias_filter_bandwidth = filter_bandwidth_for_sampling_rate(actual_sample_rate);
receiver_model.set_baseband_bandwidth(anti_alias_filter_bandwidth);
if (!view_stream.hidden()) {
view_stream.waterfall.start();
}
}
void FskxRxMainView::focus() {
field_frequency.focus();
}
void FskxRxMainView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
ui::Rect waterfall_rect{0, 0, new_parent_rect.width(), new_parent_rect.height() - header_height};
view_stream.waterfall.set_parent_rect(waterfall_rect);
}
FskxRxMainView::~FskxRxMainView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
} /* namespace ui */
-177
View File
@@ -1,177 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
*
* 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 __UI_FSK_RX_H__
#define __UI_FSK_RX_H__
#include "ui_widget.hpp"
#include "ui_freq_field.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp"
#include "ui_rssi.hpp"
#include "ui_spectrum.hpp"
#include "ui_tabview.hpp"
#include "app_settings.hpp"
#include "log_file.hpp"
#include "radio_state.hpp"
#include "pocsag_app.hpp"
#include <functional>
class FskRxLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_raw_data(const std::string& data, const uint32_t frequency);
void log_decoded(Timestamp timestamp, const std::string& text);
private:
LogFile log_file{};
};
namespace ui {
class FskRxAppConsoleView : public View {
public:
FskRxAppConsoleView(NavigationView& nav, Rect parent_rec);
~FskRxAppConsoleView();
std::string title() const override { return "FSK RX Data"; };
void on_packet(uint32_t value, bool is_data);
void on_show() override;
void on_hide() override;
private:
NavigationView& nav_;
Console console{
{0, 0, 240, 224}};
};
class FskRxAppView : public View {
public:
FskRxAppView(NavigationView& nav, Rect parent_rect);
~FskRxAppView();
void focus() override;
void on_show() override;
void on_hide() override;
spectrum::WaterfallView waterfall{};
std::string title() const override { return "FSK RX Stream"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{};
};
class FskxRxMainView : public View {
public:
FskxRxMainView(NavigationView& nav);
~FskxRxMainView();
void focus() override;
void set_parent_rect(const Rect new_parent_rect) override;
std::string title() const override { return "FSK RX"; };
private:
static constexpr uint32_t initial_target_frequency = 902'075'000;
static constexpr ui::Dim header_height = (5 * 16);
uint32_t initial_deviation{3750};
bool logging() const { return false; };
bool logging_raw() const { return false; };
NavigationView& nav_;
Rect view_rect = {0, header_height, 240, 224};
FskRxAppView view_stream{nav_, view_rect};
FskRxAppConsoleView view_data{nav_, view_rect};
TabView tab_view{
{"Data", Theme::getInstance()->fg_yellow->foreground, &view_data},
{"Stream", Theme::getInstance()->fg_cyan->foreground, &view_stream}};
void refresh_ui(rf::Frequency f);
void on_packet(uint32_t value, bool is_data);
void handle_decoded(Timestamp timestamp, const std::string& prefix);
uint32_t last_address = 0;
FskRxLogger logger{};
uint16_t packet_count = 0;
RxFrequencyField field_frequency{
{0 * 8, 4 * 8},
nav_};
RFAmpField field_rf_amp{
{11 * 8, 2 * 16}};
LNAGainField field_lna{
{13 * 8, 2 * 16}};
VGAGainField field_vga{
{16 * 8, 2 * 16}};
RSSI rssi{
{19 * 8 - 4, 35, 6 * 8, 4}};
Channel channel{
{19 * 8 - 4, 40, 6 * 8, 4}};
Labels labels{
{{0 * 8, 3 * 16}, "Deviation:", Theme::getInstance()->fg_light->foreground},
};
FrequencyField deviation_frequency{
{10 * 8, 3 * 16},
{3750, 500000},
};
// DEBUG
RecordView record_view{
{0 * 8, 4 * 16, 30 * 8, 1 * 16},
u"FSKRX_????.C16",
u"FSKRX",
RecordView::FileType::RawS16,
16384,
3};
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->view_data.on_packet(message->value, message->is_data);
}};
};
} /* namespace ui */
#endif /*__POCSAG_APP_H__*/
@@ -98,7 +98,6 @@ rf::Frequency GlassView::get_freq_from_bin_pos(uint8_t pos) {
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2);
} else
freq_at_pos = f_min + (2 * offset * each_bin_size) + (pos * looking_glass_range) / SCREEN_W;
return freq_at_pos;
}
@@ -257,7 +256,7 @@ void GlassView::on_range_changed() {
bin_length = SCREEN_W;
ignore_dc = 0;
looking_glass_bandwidth = looking_glass_range;
looking_glass_sampling_rate = looking_glass_bandwidth;
looking_glass_sampling_rate = looking_glass_range;
each_bin_size = looking_glass_bandwidth / SCREEN_W;
looking_glass_step = looking_glass_bandwidth;
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
@@ -265,7 +264,7 @@ void GlassView::on_range_changed() {
// view is made in multiple pass, use original bin picking
mode = scan_type.selected_index_value();
looking_glass_bandwidth = LOOKING_GLASS_SLICE_WIDTH_MAX;
looking_glass_sampling_rate = LOOKING_GLASS_SLICE_WIDTH_MAX;
looking_glass_sampling_rate = LOOKING_GLASS_MAX_SAMPLERATE;
each_bin_size = LOOKING_GLASS_SLICE_WIDTH_MAX / SPEC_NB_BINS;
if (mode == LOOKING_GLASS_FASTSCAN) {
offset = 2;
@@ -315,8 +314,8 @@ void GlassView::update_min(int32_t v) {
int32_t min_size = steps;
if (locked_range)
min_size = search_span;
if (min_size < 2)
min_size = 2;
if (min_size < 1)
min_size = 1;
if (v > 7200 - min_size) {
v = 7200 - min_size;
}
@@ -332,8 +331,8 @@ void GlassView::update_max(int32_t v) {
int32_t min_size = steps;
if (locked_range)
min_size = search_span;
if (min_size < 2)
min_size = 2;
if (min_size < 1)
min_size = 1;
if (v < min_size) {
v = min_size;
}
@@ -487,7 +486,12 @@ GlassView::GlassView(
range_presets.set_selected_index(preset_index);
field_marker.on_encoder_change = [this](TextField&, EncoderEvent delta) {
marker_pixel_index = clip<uint8_t>(marker_pixel_index + delta, 0, SCREEN_W);
if ((marker_pixel_index + delta) < 0)
marker_pixel_index = marker_pixel_index + delta + SCREEN_W;
else if ((marker_pixel_index + delta) > SCREEN_W)
marker_pixel_index = marker_pixel_index + delta - SCREEN_W;
else
marker_pixel_index = marker_pixel_index + delta;
on_marker_change();
};
@@ -40,6 +40,7 @@
namespace ui {
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define LOOKING_GLASS_MAX_SAMPLERATE 20000000
#define MHZ_DIV 1000000
// blanked DC (16 centered bins ignored ) and top left and right (2 bins ignored on each side )
-288
View File
@@ -1,288 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_numbers.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace ui {
// TODO: This app takes way too much space, find a way to shrink/simplify or make it an SD card module (loadable)
void NumbersStationView::focus() {
if (file_error)
nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT);
else
button_exit.focus();
}
NumbersStationView::~NumbersStationView() {
transmitter_model.disable();
baseband::shutdown();
}
NumbersStationView::wav_file_t* NumbersStationView::get_wav(uint32_t index) {
return &current_voice->available_wavs[index];
}
void NumbersStationView::prepare_audio() {
uint8_t code;
wav_file_t* wav_file;
if (sample_counter >= sample_length) {
if (segment == ANNOUNCE) {
if (!announce_loop) {
code_index = 0;
segment = MESSAGE;
} else {
wav_file = get_wav(11);
reader->open(current_voice->dir + file_names[wav_file->index].name + ".wav");
sample_length = wav_file->length;
announce_loop--;
}
}
if (segment == MESSAGE) {
if (code_index == 25) {
transmitter_model.disable();
return;
}
code = symfield_code.get_offset(code_index);
if (code >= 10) {
memset(audio_buffer, 0, 1024);
if (code == 10) {
pause = 11025; // p: 0.25s @ 44100Hz
} else if (code == 11) {
pause = 33075; // P: 0.75s @ 44100Hz
} else if (code == 12) {
transmitter_model.disable();
return;
}
} else {
wav_file = get_wav(code);
reader->open(current_voice->dir + file_names[code].name + ".wav");
sample_length = wav_file->length;
}
code_index++;
}
sample_counter = 0;
}
if (!pause) {
auto bytes_read = reader->read(audio_buffer, 1024).value();
// Unsigned to signed, pretty stupid :/
for (size_t n = 0; n < bytes_read; n++)
audio_buffer[n] -= 0x80;
for (size_t n = bytes_read; n < 1024; n++)
audio_buffer[n] = 0;
sample_counter += 1024;
} else {
if (pause >= 1024) {
pause -= 1024;
} else {
sample_counter = sample_length;
pause = 0;
}
}
baseband::set_fifo_data(audio_buffer);
}
void NumbersStationView::start_tx() {
// sample_length = sound_sizes[10]; // Announce
sample_counter = sample_length;
code_index = 0;
announce_loop = 2;
segment = ANNOUNCE;
prepare_audio();
transmitter_model.set_rf_amp(true);
transmitter_model.enable();
baseband::set_audiotx_data(
(1536000 / 44100) - 1, // TODO: Read wav file's samplerate
12000,
1,
false,
0);
}
void NumbersStationView::on_tick_second() {
armed_blink = not armed_blink;
if (armed_blink)
check_armed.set_style(Theme::getInstance()->fg_red);
else
check_armed.set_style(&style());
check_armed.set_dirty();
}
void NumbersStationView::on_voice_changed(size_t index) {
std::string code_list;
for (const auto& wavs : voices[index].available_wavs)
code_list += wavs.code;
symfield_code.set_symbol_list(code_list);
current_voice = &voices[index];
}
bool NumbersStationView::check_wav_validity(const std::string dir, const std::string file) {
if (reader->open("/numbers/" + dir + "/" + file)) {
// Check format (mono, 8 bits)
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8))
return true;
else
return false;
} else
return false;
}
NumbersStationView::NumbersStationView(
NavigationView& nav)
: nav_(nav) {
std::vector<std::filesystem::path> directory_list;
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t voice_options;
voice_t temp_voice{};
bool valid;
uint32_t c;
// uint8_t y, m, d, dayofweek;
reader = std::make_unique<WAVFileReader>();
// Search for valid voice directories
directory_list = scan_root_directories("/numbers");
if (!directory_list.size()) {
file_error = true;
return;
}
for (const auto& dir : directory_list) {
c = 0;
for (const auto& file_name : file_names) {
valid = check_wav_validity(dir.string(), file_name.name + ".wav");
if ((!valid) && (file_name.required)) {
temp_voice.available_wavs.clear();
break; // Invalid required file means invalid voice
} else if (valid) {
temp_voice.available_wavs.push_back({file_name.code, c++, 0, 0}); // TODO: Needs length and samplerate
}
}
if (!temp_voice.available_wavs.empty()) {
// Voice can be used, are there accent files ?
c = 0;
for (const auto& file_name : file_names) {
valid = check_wav_validity(dir.string(), file_name.name + "a.wav");
if ((!valid) && (file_name.required)) {
c = 0;
break; // Invalid required file means accents can't be used
} else if (valid) {
c++;
}
}
temp_voice.accent = c ? true : false;
temp_voice.dir = dir.string();
voices.push_back(temp_voice);
}
}
if (voices.empty()) {
file_error = true;
return;
}
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
add_children({&labels,
&symfield_code,
&check_armed,
&options_voices,
&text_voice_flags,
//&button_tx_now,
&button_exit});
for (const auto& voice : voices)
voice_options.emplace_back(voice.dir.substr(0, 4), 0);
options_voices.set_options(voice_options);
options_voices.on_change = [this](size_t i, int32_t) {
this->on_voice_changed(i);
};
options_voices.set_selected_index(0);
check_armed.set_value(false);
check_armed.on_select = [this](Checkbox&, bool v) {
if (v) {
armed_blink = false;
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};
} else {
check_armed.set_style(&style());
rtc_time::signal_tick_second -= signal_token_tick_second;
}
};
// DEBUG
symfield_code.set_offset(0, 10);
symfield_code.set_offset(1, 3);
symfield_code.set_offset(2, 4);
symfield_code.set_offset(3, 11);
symfield_code.set_offset(4, 6);
symfield_code.set_offset(5, 1);
symfield_code.set_offset(6, 9);
symfield_code.set_offset(7, 7);
symfield_code.set_offset(8, 8);
symfield_code.set_offset(9, 0);
symfield_code.set_offset(10, 12); // End
/*
dayofweek = rtc_time::current_day_of_week();
text_title.set(day_of_week[dayofweek]);
*/
button_exit.on_select = [&nav](Button&) {
nav.pop();
};
}
} /* namespace ui */
-175
View File
@@ -1,175 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UI_NUMBERS_H__
#define __UI_NUMBERS_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_receiver.hpp"
#include "ui_navigation.hpp"
#include "rtc_time.hpp"
#include "clock_manager.hpp"
#include "baseband_api.hpp"
#include "utility.hpp"
#include "message.hpp"
#include "file.hpp"
#include "io_wave.hpp"
#include "radio_state.hpp"
namespace ui {
class NumbersStationView : public View {
public:
NumbersStationView(NavigationView& nav);
~NumbersStationView();
NumbersStationView(const NumbersStationView&) = delete;
NumbersStationView(NumbersStationView&&) = delete;
NumbersStationView& operator=(const NumbersStationView&) = delete;
NumbersStationView& operator=(NumbersStationView&&) = delete;
void focus() override;
std::string title() const override { return "Station"; };
private:
NavigationView& nav_;
TxRadioState radio_state_{
0 /* frequency */,
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
// Sequencing state machine
enum segments {
IDLE = 0,
ANNOUNCE,
MESSAGE,
SIGNOFF
};
typedef struct {
char code;
uint32_t index;
uint32_t length;
uint32_t samplerate;
} wav_file_t;
struct voice_t {
std::string dir;
std::vector<wav_file_t> available_wavs;
bool accent;
};
std::vector<voice_t> voices{};
voice_t* current_voice{};
struct wav_file_list_t {
std::string name;
bool required;
char code;
};
const std::vector<wav_file_list_t> file_names = {
{"0", true, '0'},
{"1", true, '1'},
{"2", true, '2'},
{"3", true, '3'},
{"4", true, '4'},
{"5", true, '5'},
{"6", true, '6'},
{"7", true, '7'},
{"8", true, '8'},
{"9", true, '9'},
{"announce", false, 'A'}};
segments segment{IDLE};
bool armed{false};
bool file_error{false};
// const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
// const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
std::unique_ptr<WAVFileReader> reader{};
uint8_t code_index{0}, announce_loop{0};
uint32_t sample_counter{0};
uint32_t sample_length{0};
int8_t audio_buffer[1024]{};
uint32_t pause{0};
bool armed_blink{false};
SignalToken signal_token_tick_second{};
wav_file_t* get_wav(uint32_t index);
bool check_wav_validity(const std::string dir, const std::string file);
void on_voice_changed(size_t index);
void on_tick_second();
void prepare_audio();
void start_tx();
Labels labels{
{{2 * 8, 5 * 8}, "Voice: Flags:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 8 * 8}, "Code:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_voices{
{8 * 8, 1 * 8},
4,
{}};
Text text_voice_flags{
{19 * 8, 1 * 8, 2 * 8, 16},
""};
SymField symfield_code{
{1 * 8, 10 * 8},
25,
SymField::Type::Custom};
Checkbox check_armed{
{2 * 8, 13 * 16},
5,
"Armed"};
/*
Button button_tx_now {
{ 18 * 8, 13 * 16, 10 * 8, 32 },
"TX now"};
*/
Button button_exit{
{21 * 8, 16 * 16, 64, 32},
"Exit"};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
[this](const Message* const p) {
const auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
this->prepare_audio();
}
}};
};
} /* namespace ui */
#endif /*__UI_NUMBERS_H__*/
-185
View File
@@ -1,185 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_nuoptix.hpp"
#include "ch.h"
#include "portapack.hpp"
#include "lfsr_random.hpp"
#include "string_format.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
#include <vector>
using namespace portapack;
namespace ui {
void NuoptixView::focus() {
number_timecode.focus();
}
NuoptixView::~NuoptixView() {
transmitter_model.disable();
baseband::shutdown();
}
void NuoptixView::on_tx_progress(const uint32_t progress, const bool done) {
if (done)
transmit(false);
else
progressbar.set_value(progress);
}
void NuoptixView::transmit(bool setup) {
uint8_t mod, tone_code;
uint8_t c;
uint8_t dtmf_message[6];
rtc::RTC datetime;
if (!tx_mode) {
transmitter_model.disable();
return;
}
if (tx_mode == IMPROVISE)
timecode = lfsr_iterate(timecode) % 1999; // Could be 9999 but that would be one long audio track !
if (setup) {
progressbar.set_max(6 * 2);
if (tx_mode == IMPROVISE) {
// Seed from RTC
rtcGetTime(&RTCD1, &datetime);
timecode = datetime.day() + datetime.second();
} else {
timecode = number_timecode.value();
}
transmitter_model.set_rf_amp(true);
transmitter_model.enable();
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
dtmf_message[1] = 'A'; // "Restart" method #1
} else {
dtmf_message[0] = '#';
dtmf_message[1] = (timecode / 1000) % 10;
chThdSleepMilliseconds(92); // 141-49ms
number_timecode.set_value(timecode);
}
progressbar.set_value(0);
dtmf_message[2] = (timecode / 100) % 10;
dtmf_message[3] = (timecode / 10) % 10;
dtmf_message[4] = timecode % 10;
mod = 0;
for (c = 1; c < 5; c++)
if (dtmf_message[c] <= 9)
mod += dtmf_message[c];
mod = 10 - (mod % 10);
if (mod == 10) mod = 0; // Is this right ?
text_mod.set("Mod: " + to_string_dec_uint(mod));
dtmf_message[5] = mod;
for (c = 0; c < 6; c++) {
tone_code = dtmf_message[c];
if (tone_code == 'A')
tone_code = 10;
else if (tone_code == 'B')
tone_code = 11;
else if (tone_code == 'C')
tone_code = 12;
else if (tone_code == 'D')
tone_code = 13;
else if (tone_code == '#')
tone_code = 14;
else if (tone_code == '*')
tone_code = 15;
shared_memory.bb_data.tones_data.message[c * 2] = tone_code;
shared_memory.bb_data.tones_data.message[c * 2 + 1] = 0xFF; // Silence
}
for (c = 0; c < 16; c++) {
baseband::set_tone(c * 2, dtmf_deltas[c][0], NUOPTIX_TONE_LENGTH);
baseband::set_tone(c * 2 + 1, dtmf_deltas[c][1], NUOPTIX_TONE_LENGTH);
}
shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space
audio::set_rate(audio::Rate::Hz_24000);
baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, 6 * 2, true, true);
timecode++;
}
NuoptixView::NuoptixView(
NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_tones);
add_children({&number_timecode,
&text_timecode,
&text_mod,
&progressbar,
&tx_view});
number_timecode.set_value(1);
tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
};
tx_view.on_start = [this]() {
tx_view.set_transmitting(true);
tx_mode = NORMAL;
transmit(true);
};
tx_view.on_stop = [this]() {
tx_view.set_transmitting(false);
tx_mode = IDLE;
};
/*button_impro.on_select = [this](Button&){
if (tx_mode == IMPROVISE) {
tx_mode = IDLE;
button_impro.set_text("IMPROVISE");
} else if (tx_mode == IDLE) {
tx_mode = IMPROVISE;
button_impro.set_text("STOP");
transmit(true);
}
};*/
}
} // namespace ui
-109
View File
@@ -1,109 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UI_NUOPTIX_H__
#define __UI_NUOPTIX_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "baseband_api.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
#include "rtc_time.hpp"
#include "tonesets.hpp"
#include "message.hpp"
#include "volume.hpp"
#include "audio.hpp"
#include "radio_state.hpp"
#define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms
namespace ui {
class NuoptixView : public View {
public:
NuoptixView(NavigationView& nav);
~NuoptixView();
void focus() override;
std::string title() const override { return "Nuoptix sync"; };
private:
enum tx_modes {
IDLE = 0,
NORMAL,
IMPROVISE
};
TxRadioState radio_state_{
0 /* frequency */,
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
tx_modes tx_mode{IDLE};
void transmit(bool setup);
void on_tx_progress(const uint32_t progress, const bool done);
uint32_t timecode{0};
Text text_timecode{
{10 * 8, 2 * 16, 9 * 8, 16},
"Timecode:"};
NumberField number_timecode{
{13 * 8, 3 * 16},
4,
{1, 9999},
1,
'0'};
Text text_mod{
{10 * 8, 5 * 16, 6 * 8, 16},
"Mod: "};
ProgressBar progressbar{
{16, 14 * 16, 208, 16}};
/*Button button_impro {
{ 64, 184, 112, 40 },
"IMPROVISE"
};*/
TransmitterView tx_view{
16 * 16,
10000,
15};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress, message.done);
}};
};
} /* namespace ui */
#endif /*__UI_NUOPTIX_H__*/
+16 -5
View File
@@ -33,6 +33,8 @@ using namespace pocsag;
namespace ui {
#define MAX_POCSAG_LENGTH 80
void POCSAGTXView::focus() {
field_address.focus();
}
@@ -57,6 +59,7 @@ void POCSAGTXView::on_remote(const PocsagTosendMessage data) {
options_phase.set_selected_index(data.phase == 'P' ? 0 : 1);
field_address.set_value(data.addr);
message = (char*)data.msg;
buffer = message;
text_message.set(message);
options_bitrate.dirty();
options_type.dirty();
@@ -105,9 +108,9 @@ bool POCSAGTXView::start_tx() {
progressbar.set_max(total_frames);
transmitter_model.set_rf_amp(true);
transmitter_model.set_tx_gain(40);
// We left exactly same TX LPF settings as previous fw 1.7.4, TX LPF= 1M75 (min). It is fine even in max FM dev. 150khz and 2400 bauds.
// transmitter_model.set_rf_amp(true);
// transmitter_model.set_tx_gain(40);
// We left exactly same TX LPF settings as previous fw 1.7.4, TX LPF= 1M75 (min). It is fine even in max FM dev. 150khz and 2400 bauds.
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF . Pocsag is NBFM , using BW channel 25khz or 12khz
transmitter_model.enable();
@@ -139,11 +142,18 @@ bool POCSAGTXView::start_tx() {
void POCSAGTXView::paint(Painter&) {
message = buffer;
text_message.set(message);
text_message.set(message); // the whole message, but it may not fit.
if (message.length() > 30 && message.length() <= 60) {
text_message_l2.set(message.substr(29)); // remaining to 2nd line
} else if (message.length() > 60) {
text_message_l2.set(message.substr(29, 27) + "...");
} else {
text_message_l2.set("");
}
}
void POCSAGTXView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 30);
text_prompt(nav, buffer, MAX_POCSAG_LENGTH);
}
POCSAGTXView::POCSAGTXView(
@@ -158,6 +168,7 @@ POCSAGTXView::POCSAGTXView(
&options_function,
&options_phase,
&text_message,
&text_message_l2,
&button_message,
&progressbar,
&tx_view});
+6 -3
View File
@@ -122,15 +122,18 @@ class POCSAGTXView : public View {
}};
Text text_message{
{0 * 8, 16 * 8, 16 * 8, 16},
{0 * 8, 16 * 8, 30 * 8, 16},
""};
Text text_message_l2{
{0 * 8, 18 * 8, 30 * 8, 16},
""};
Button button_message{
{0 * 8, 18 * 8, 14 * 8, 32},
{0 * 8, 20 * 8, 14 * 8, 32},
"Set message"};
ProgressBar progressbar{
{16, 200, 208, 16}};
{16, 210, 208, 16}};
TransmitterView tx_view{
16 * 16,
-1
View File
@@ -28,7 +28,6 @@
#include "file.hpp"
#include "file_reader.hpp"
#include "tone_key.hpp"
#include "replay_app.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
-106
View File
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_script.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
#include <cstring>
using namespace portapack;
namespace ui {
void ScriptView::on_frequency_select() {
// button_edit_freq.focus();
}
void ScriptView::on_edit_freq(rf::Frequency f) {
(void)f;
// frequencies[menu_view.highlighted()].value = f;
setup_list();
}
void ScriptView::on_edit_desc(NavigationView& nav) {
(void)nav;
}
void ScriptView::on_delete() {
// frequencies.erase(frequencies.begin() + menu_view.highlighted());
setup_list();
}
void ScriptView::setup_list() {
// size_t n;
menu_view.clear();
/*for (n = 0; n < frequencies.size(); n++) {
menu_view.add_item({ freqman_item_string(frequencies[n]), Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ on_frequency_select(); } });
}*/
menu_view.set_parent_rect({0, 0, 240, 168});
menu_view.set_highlighted(menu_view.highlighted()); // Refresh
}
void ScriptView::focus() {
menu_view.focus();
}
ScriptView::ScriptView(
NavigationView& nav) {
add_children({&menu_view,
&text_edit,
&button_edit_freq,
&button_edit_desc,
&button_del,
&button_exit});
setup_list();
button_edit_freq.on_select = [this, &nav](Button&) {
/*auto new_view = nav.push<FrequencyKeypadView>(frequencies[menu_view.highlighted()].value);
new_view->on_changed = [this](rf::Frequency f) {
on_edit_freq(f);
};*/
};
button_edit_desc.on_select = [this, &nav](Button&) {
on_edit_desc(nav);
};
button_del.on_select = [this, &nav](Button&) {
nav.push<ModalMessageView>("Confirm", "Are you sure?", YESNO,
[this](bool choice) {
if (choice) {
on_delete();
}
});
};
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
}
} // namespace ui
-88
View File
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_textentry.hpp"
#include "rtc_time.hpp"
namespace ui {
enum script_keyword {
STOP = 0,
WAIT_N,
WAIT_RTC,
IF,
LOOP,
END,
TX,
RX
};
struct script_line {
script_keyword keyword;
};
class ScriptView : public View {
public:
ScriptView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Script editor"; };
private:
void on_frequency_select();
void on_edit_freq(rf::Frequency f);
void on_edit_desc(NavigationView& nav);
void on_delete();
void setup_list();
std::vector<script_line> script{};
MenuView menu_view{
{0, 0, 240, 168},
true};
Text text_edit{
{16, 194, 5 * 8, 16},
"Edit:"};
Button button_edit_freq{
{16, 194 + 16, 88, 32},
"Frequency"};
Button button_edit_desc{
{16, 194 + 16 + 34, 88, 32},
"Description"};
Button button_del{
{160, 192, 72, 64},
"Delete"};
Button button_exit{
{160, 264, 72, 32},
"Exit"};
};
} /* namespace ui */
+115 -2
View File
@@ -5,6 +5,7 @@
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyright (C) 2024 HTotoo
* Copyleft (ɔ) 2024 zxkmm under GPL license
*
* This file is part of PortaPack.
@@ -49,6 +50,8 @@ namespace fs = std::filesystem;
#include "ui_font_fixed_8x16.hpp"
#include "cpld_update.hpp"
#include "config_mode.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
extern ui::SystemView* system_view_ptr;
@@ -815,6 +818,107 @@ void SetDisplayView::focus() {
button_save.focus();
}
/* SetTouchscreenSensitivityView ************************************/
/* sample max: 1023 sample_t AKA uint16_t
* touch_sensitivity: range: 1 to 128
* threshold = 1023 / sensitive
* threshold range: 1023/1 to 1023/128 = 1023 to 8
*/
SetTouchscreenThresholdView::SetTouchscreenThresholdView(NavigationView& nav) {
add_children({&labels,
&field_threshold,
&button_autodetect,
&button_reset,
&button_save,
&button_cancel,
&text_hint,
&text_wait_timer});
set_dirty();
org_threshold = portapack::touch_threshold;
field_threshold.set_value(pmem::touchscreen_threshold());
text_hint.set_style(Theme::getInstance()->error_dark);
text_hint.hidden(true);
text_wait_timer.set_style(Theme::getInstance()->error_dark);
text_wait_timer.hidden(true);
// clang-format off
button_autodetect.on_select = [this, &nav](Button&) {
nav.display_modal("NOTICE",
"Now on don't touch screen;\n"
"Use arrow keys to operate.\n"
"Follow instructions.\n"
"Press YES to continue",
YESNO, [this, &nav](bool choice) {
if (choice){
time_start_auto_detect = chTimeNow();
text_hint.hidden(false);
text_wait_timer.hidden(false);
text_wait_timer.set("ETA " + to_string_dec_uint(10) + "s");
in_auto_detect = true;
field_threshold.set_value(1);
portapack::touch_threshold = 1;
set_dirty(); } }, TRUE);
};
// clang-format on
button_reset.on_select = [this](Button&) {
field_threshold.set_value(32);
portapack::touch_threshold = 32;
};
button_save.on_select = [&nav, this](Button&) {
pmem::set_touchscreen_threshold(field_threshold.value());
portapack::touch_threshold = field_threshold.value();
send_system_refresh();
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
portapack::touch_threshold = org_threshold;
nav.pop();
};
}
void SetTouchscreenThresholdView::focus() {
button_autodetect.focus();
set_dirty();
}
void SetTouchscreenThresholdView::on_frame_sync() {
if (!in_auto_detect) return;
uint32_t time_now = chTimeNow();
int32_t time_diff = time_now - time_start_auto_detect;
text_wait_timer.set("ETA " + to_string_dec_uint((10 - time_diff / 1000) <= 0 ? 0 : 10 - time_diff / 1000) + "s");
if (time_diff >= 10001 && !auto_detect_succeed_consumed) { // 10s
in_auto_detect = false;
text_wait_timer.hidden(true);
text_hint.set("OK, press save and reboot");
portapack::touch_threshold = org_threshold;
pmem::set_touchscreen_threshold(org_threshold);
set_dirty();
auto_detect_succeed_consumed = true;
button_save.focus();
return;
}
if (get_touch_frame().touch) {
if (in_auto_detect) {
uint16_t sen = field_threshold.value();
sen++;
portapack::touch_threshold = sen;
field_threshold.set_value(sen);
}
}
}
SetTouchscreenThresholdView::~SetTouchscreenThresholdView() {
// it seems that sometimes in the msg handler func it would enter the condi that not possible to entered,
// so added this workaround.
// TODO: find out why
in_auto_detect = false;
auto_detect_succeed_consumed = false;
time_start_auto_detect = 0;
}
/* SetMenuColorView ************************************/
void SetMenuColorView::paint_sample() {
@@ -876,6 +980,7 @@ SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&button_reset,
&options});
button_save.on_select = [&nav, this](Button&) {
@@ -892,6 +997,12 @@ SetAutostartView::SetAutostartView(NavigationView& nav) {
nav.pop();
};
button_reset.on_select = [this](Button&) {
selected = 0;
options.set_selected_index(0);
autostart_app = "";
};
// options
i = 0;
OptionsField::option_t o{"-none-", i};
@@ -969,7 +1080,7 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
&button_cancel,
&checkbox_overridebatt});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BATT_MAX17055) add_children({&button_reset, &labels2});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
@@ -979,7 +1090,8 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
};
button_reset.on_select = [&nav, this](Button&) {
if (battery::BatteryManagement::reset_learned())
auto dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (dev->reset_learned())
nav.display_modal("Reset", "Battery parameters reset");
else
nav.display_modal("Error", "Error parameter reset");
@@ -1011,6 +1123,7 @@ void SettingsMenuView::on_populate() {
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [this]() { nav_.push<AppSettingsView>(); }},
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [this]() { nav_.push<SetAudioView>(); }},
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [this]() { nav_.push<TouchCalibrationView>(); }},
{"TouchThreshold", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [this]() { nav_.push<SetTouchscreenThresholdView>(); }},
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [this]() { nav_.push<SetConfigModeView>(); }},
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetConverterSettingsView>(); }},
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [this]() { nav_.push<SetDateTimeView>(); }},
+84 -6
View File
@@ -35,6 +35,7 @@
#include "bitmap.hpp"
#include "ff.h"
#include "portapack_persistent_memory.hpp"
#include "irq_controls.hpp"
#include <cstdint>
@@ -754,6 +755,77 @@ class SetDisplayView : public View {
};
};
using portapack::persistent_memory::touchscreen_threshold;
class SetTouchscreenThresholdView : public View {
public:
SetTouchscreenThresholdView(NavigationView& nav);
~SetTouchscreenThresholdView();
void focus() override;
std::string title() const override { return "Touch S"; };
private:
bool in_auto_detect = false;
uint16_t org_threshold = 0;
uint8_t auto_detect_succeed_consumed = false; // prevent screen flash but can still change text content
uint32_t time_start_auto_detect = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Set touchscreen sensitivity", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "Or press auto detect button", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "FOLLOW INSTRUCTIONS", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 4 * 16}, "REBOOT TO APPLY", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 11 * 16}, "Threshold:", Theme::getInstance()->fg_light->foreground},
};
Text text_hint{
{1 * 8, 7 * 16, screen_width - 2 * 8, 1 * 16},
"DON'T TOUCH SCREEN"};
Text text_wait_timer{
{1 * 8, 8 * 16, screen_width - 2 * 8, 1 * 16},
"ETA 00:00"};
void on_frame_sync();
/* sample max: 1023 sample_t AKA uint16_t
* touch_sensitivity: range: 1 to 128
* threshold range: 1023/1 to 1023/128 = 1023 to 8
*/
NumberField field_threshold{
{1 * 8 + 11 * 8 + 8, 11 * 16},
4,
{1, 1023},
1,
' ',
};
Button button_autodetect{
{2 * 8, 13 * 16, 12 * 8, 32},
"Auto Detect"};
Button button_reset{
{16 * 8, 13 * 16, 12 * 8, 32},
"Reset",
};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_frame_sync();
}};
};
class SetMenuColorView : public View {
public:
SetMenuColorView(NavigationView& nav);
@@ -843,14 +915,19 @@ class SetAutostartView : public View {
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{}};
{0 * 8, 4 * 16},
screen_width / 8,
{},
true};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 6 * 16, screen_width - 4 * 8, 32},
"Reset"};
};
class SetThemeView : public View {
@@ -872,15 +949,16 @@ class SetThemeView : public View {
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{0 * 8, 4 * 16},
screen_width / 8,
{
{"Default - Grey", 0},
{"Yellow", 1},
{"Aqua", 2},
{"Green", 3},
{"Red", 4},
}};
},
true};
Checkbox checkbox_menuset{
{2 * 8, 6 * 16},
@@ -22,6 +22,12 @@
#include "ui_standalone_view.hpp"
#include "irq_controls.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
#include "ui_font_fixed_5x8.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {
void create_thread(int32_t (*fn)(void*), void* arg, size_t stack_size, int priority) {
@@ -34,6 +40,16 @@ void fill_rectangle(int x, int y, int width, int height, uint16_t color) {
painter.fill_rectangle({x, y, width, height}, ui::Color(color));
}
void fill_rectangle_unrolled8(int x, int y, int width, int height, uint16_t color) {
ui::Painter painter;
painter.fill_rectangle_unrolled8({x, y, width, height}, ui::Color(color));
}
void draw_bitmap(int x, int y, int width, int height, const uint8_t* pixels, uint16_t foreground, uint16_t background) {
ui::Painter painter;
painter.draw_bitmap({x, y}, {{width, height}, pixels}, ui::Color(foreground), ui::Color(background));
}
void* alloc(size_t size) {
void* p = chHeapAlloc(0x0, size);
if (p == nullptr)
@@ -45,6 +61,45 @@ uint64_t get_switches_state_ulong() {
return get_switches_state().to_ulong();
}
ui::Coord scroll_area_y(const ui::Coord y) {
return portapack::display.scroll_area_y(y);
}
void scroll_set_area(const ui::Coord top_y, const ui::Coord bottom_y) {
portapack::display.scroll_set_area(top_y, bottom_y);
}
void scroll_disable() {
portapack::display.scroll_disable();
}
ui::Coord scroll_set_position(const ui::Coord position) {
return portapack::display.scroll_set_position(position);
}
ui::Coord scroll(const int32_t delta) {
return portapack::display.scroll(delta);
}
bool i2c_read(uint8_t* cmd, size_t cmd_len, uint8_t* data, size_t data_len) {
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (!dev) {
return false;
}
if (data_len == 0 || data == nullptr)
return dev->i2c_write(nullptr, 0, cmd, cmd_len);
return dev->i2c_read(cmd, cmd_len, data, data_len);
}
StandaloneView* standaloneView = nullptr;
void set_dirty() {
if (standaloneView != nullptr)
standaloneView->set_dirty();
}
standalone_application_api_t api = {
/* .malloc = */ &alloc,
/* .calloc = */ &calloc,
@@ -54,20 +109,78 @@ standalone_application_api_t api = {
/* .fill_rectangle = */ &fill_rectangle,
/* .swizzled_switches = */ &swizzled_switches,
/* .get_switches_state = */ &get_switches_state_ulong,
/* .fixed_5x8_glyph_data = */ ui::font::fixed_5x8.get_data(),
/* .fixed_8x16_glyph_data = */ ui::font::fixed_8x16.get_data(),
/* .fill_rectangle_unrolled8 = */ &fill_rectangle_unrolled8,
/* .draw_bitmap = */ &draw_bitmap,
/* .scroll_area_y = */ &scroll_area_y,
/* .scroll_set_area = */ &scroll_set_area,
/* .scroll_disable = */ &scroll_disable,
/* .scroll_set_position = */ &scroll_set_position,
/* .scroll = */ &scroll,
/* .i2c_read = */ &i2c_read,
/* .panic = */ &chDbgPanic,
/* .set_dirty = */ &set_dirty,
};
StandaloneView::StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image)
: nav_(nav), _app_image(std::move(app_image)) {
get_application_information()->initialize(api);
add_children({&dummy});
StandaloneView::StandaloneView(NavigationView& nav, uint8_t* app_image)
: nav_(nav),
_app_image(*app_image) {
if (app_image == nullptr) {
chDbgPanic("Invalid application image");
}
set_focusable(true);
standaloneView = this;
}
void StandaloneView::focus() {
dummy.focus();
View::focus();
}
void StandaloneView::paint(Painter& painter) {
(void)painter;
if (initialized &&
get_application_information()->header_version > 1) {
get_application_information()->PaintViewMirror();
}
}
void StandaloneView::on_focus() {
if (get_application_information()->header_version > 1) {
get_application_information()->OnFocus();
}
}
bool StandaloneView::on_key(const KeyEvent key) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnKeyEvent((uint8_t)key);
}
return false;
}
bool StandaloneView::on_encoder(const EncoderEvent event) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnEncoder((int32_t)event);
}
return false;
}
bool StandaloneView::on_touch(const TouchEvent event) {
if (get_application_information()->header_version > 1) {
get_application_information()->OnTouchEvent(event.point.x(), event.point.y(), (uint32_t)event.type);
}
return true;
}
bool StandaloneView::on_keyboard(const KeyboardEvent event) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnKeyboard((uint8_t)event);
}
return false;
}
void StandaloneView::frame_sync() {
@@ -79,4 +192,14 @@ void StandaloneView::frame_sync() {
}
}
void StandaloneView::on_after_attach() {
context().focus_manager().setMirror(this);
get_application_information()->initialize(api);
}
void StandaloneView::on_before_detach() {
get_application_information()->shutdown();
context().focus_manager().clearMirror();
}
} // namespace ui
@@ -31,28 +31,34 @@ namespace ui {
class StandaloneView : public View {
public:
StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image);
virtual ~StandaloneView() override { get_application_information()->shutdown(); }
void focus() override;
StandaloneView(NavigationView& nav, uint8_t* app_image);
virtual ~StandaloneView() override {
}
std::string title() const override { return (const char*)get_application_information()->app_name; };
void focus() override;
void paint(Painter& painter) override;
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent event) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void on_after_attach() override;
void on_before_detach() override;
void frame_sync();
private:
bool initialized = false;
NavigationView& nav_;
std::unique_ptr<uint8_t[]> _app_image;
uint8_t& _app_image;
standalone_application_information_t* get_application_information() const {
return reinterpret_cast<standalone_application_information_t*>(_app_image.get());
return reinterpret_cast<standalone_application_information_t*>(&_app_image);
}
Button dummy{
{240, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_sample{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
@@ -1,54 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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 "ui_tone_search.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
using namespace portapack;
namespace ui {
void ToneSearchView::focus() {
// field_frequency_min.focus();
}
ToneSearchView::~ToneSearchView() {
receiver_model.disable();
baseband::shutdown();
}
ToneSearchView::ToneSearchView(
NavigationView& nav)
: nav_(nav) {
// baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({
&labels,
&field_lna,
&field_vga,
&field_rf_amp,
});
}
} /* namespace ui */
@@ -1,68 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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 "receiver_model.hpp"
#include "ui_receiver.hpp"
namespace ui {
class ToneSearchView : public View {
public:
ToneSearchView(NavigationView& nav);
~ToneSearchView();
void focus() override;
std::string title() const override { return "Tone search"; };
private:
NavigationView& nav_;
Labels labels{
{{0 * 8, 0 * 8}, "LNA: VGA: AMP:", Theme::getInstance()->fg_light->foreground}};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
VGAGainField field_vga{
{11 * 8, 0 * 16}};
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
/*
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
this->do_timers();
}
};*/
};
} /* namespace ui */
@@ -213,6 +213,11 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
return "EmosE601x";
case FPW_SolightTE44:
return "SolightTE44";
case FPW_Bresser3CH:
case FPW_Bresser3CH_V1:
return "Bresser3CH";
case FPW_Vauno_EN8822:
return "Vauno EN8822";
case FPW_Invalid:
default:
return "Unknown";
@@ -532,6 +537,42 @@ WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
break;
case FPW_Bresser3CH:
ret.id = (data->decode_data >> 28) & 0xff;
ret.channel = ((data->decode_data >> 27) & 0x01) | (((data->decode_data >> 26) & 0x01) << 1);
// ret.btn = ((data->decode_data >> 25) & 0x1);
ret.battery_low = ((data->decode_data >> 24) & 0x1);
i16 = (data->decode_data >> 12) & 0x0fff;
/* Handle signed data */
if (i16 & 0x0800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
ret.humidity = data->decode_data & 0xff;
break;
case FPW_Bresser3CH_V1:
ret.id = (data->decode_data >> 32) & 0xff;
ret.battery_low = ((data->decode_data >> 31) & 0x1);
// ret.btn = (data->decode_data >> 30) & 0x1;
ret.channel = (data->decode_data >> 28) & 0x3;
ret.temp = (data->decode_data >> 16) & 0xfff;
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)(ret.temp - 900) / 10.0);
ret.humidity = (data->decode_data >> 8) & 0xff;
break;
case FPW_Vauno_EN8822:
ret.id = (data->decode_data >> 34) & 0xff;
ret.battery_low = (data->decode_data >> 33) & 0x01;
ret.channel = ((data->decode_data >> 30) & 0x03);
i16 = (data->decode_data >> 18) & 0x0fff;
/* Handle signed data */
if (i16 & 0x0800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
ret.humidity = (data->decode_data >> 11) & 0x7f;
break;
case FPW_Invalid:
default:
-46
View File
@@ -1,46 +0,0 @@
/*
* Copyright (C) 2017 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* 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 "emu_cc1101.hpp"
namespace cc1101 {
void CC1101Emu::whitening_init() {
whitening_pn = 0x1FF;
}
// See TI app note DN509
uint8_t CC1101Emu::whiten_byte(uint8_t byte) {
uint_fast8_t new_bit;
byte ^= (whitening_pn & 0xFF);
for (size_t step = 0; step < 8; step++) {
new_bit = (whitening_pn & 1) ^ ((whitening_pn >> 5) & 1);
whitening_pn >>= 1;
whitening_pn |= (new_bit << 8);
}
return byte;
}
} /* namespace cc1101 */
@@ -20,45 +20,26 @@
* Boston, MA 02110-1301, USA.
*/
#include "acars_app.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
#include "audio.hpp"
#include "acars_app.hpp"
using namespace portapack;
using namespace acars;
#include "string_format.hpp"
#include "utility.hpp"
void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequency) {
(void)frequency;
std::string entry{}; //= "Raw: F:" + to_string_dec_uint(frequency) + "Hz ";
entry.reserve(256);
namespace ui::external_app::acars_rx {
// Raw hex dump of all the bytes
// for (size_t c = 0; c < packet.length(); c += 32)
// entry += to_string_hex(packet.read(c, 32), 8) + " ";
for (size_t c = 0; c < 256; c += 32)
entry += to_string_bin(packet.read(c, 32), 32);
log_file.write_entry(packet.received_at(), entry);
void ACARSLogger::log_str(std::string msg) {
log_file.write_entry(msg);
}
/*void ACARSLogger::log_decoded(
const acars::Packet& packet,
const std::string text) {
log_file.write_entry(packet.timestamp(), text);
}*/
namespace ui {
ACARSAppView::ACARSAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_acars);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
@@ -66,6 +47,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
&field_lna,
&field_vga,
&field_frequency,
&field_volume,
&check_log,
&console});
@@ -79,6 +61,9 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
logger = std::make_unique<ACARSLogger>();
if (logger)
logger->append(logs_dir / u"ACARS.TXT");
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
ACARSAppView::~ACARSAppView() {
@@ -90,29 +75,31 @@ void ACARSAppView::focus() {
field_frequency.focus();
}
void ACARSAppView::on_packet(const acars::Packet& packet) {
void ACARSAppView::on_packet(const ACARSPacketMessage* packet) {
std::string console_info;
/*if (!packet.is_valid()) {
console_info = to_string_datetime(packet.received_at(), HMS);
console_info += " INVALID";
console.writeln(console_info);
} else {
console_info = to_string_datetime(packet.received_at(), HMS);
console_info += ":" + to_string_bin(packet.read(0, 32), 32);
//console_info += " REG:" + packet.registration_number();
console.writeln(console_info);
}*/
packet_counter++;
if (packet_counter % 10 == 0)
console.writeln("Block #" + to_string_dec_uint(packet_counter));
// Log raw data whatever it contains
if (logger && logging)
logger->log_raw_data(packet, receiver_model.target_frequency());
if (packet->state == 255) {
// got a packet, parse it, and display
rtc::RTC datetime;
rtc_time::now(datetime);
// todo parity error recovery
console_info = to_string_datetime(datetime, HMS);
console_info += ": ";
console_info += packet->message;
console.writeln(console_info);
// Log raw data whatever it contains
if (logger && logging)
logger->log_str(console_info);
} else {
// debug message arrived
console_info = "State: ";
console_info += to_string_dec_int(packet->state);
console_info += " lastbyte: ";
console_info += to_string_dec_uint(packet->message[0]);
console.writeln(console_info);
if (logger && logging)
logger->log_str(console_info);
}
}
} /* namespace ui */
} // namespace ui::external_app::acars_rx
@@ -31,23 +31,19 @@
#include "ui_rssi.hpp"
#include "log_file.hpp"
#include "acars_packet.hpp"
namespace ui::external_app::acars_rx {
class ACARSLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_raw_data(const acars::Packet& packet, const uint32_t frequency);
// void log_decoded(const acars::Packet& packet, const std::string text);
void log_str(std::string msg);
private:
LogFile log_file{};
};
namespace ui {
class ACARSAppView : public View {
public:
ACARSAppView(NavigationView& nav);
@@ -55,12 +51,12 @@ class ACARSAppView : public View {
void focus() override;
std::string title() const override { return "ACARS (WIP)"; };
std::string title() const override { return "ACARS"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
131550000 /* frequency */,
131825000 /* frequency */,
1750000 /* bandwidth */,
2457600 /* sampling rate */
};
@@ -85,7 +81,7 @@ class ACARSAppView : public View {
{0 * 8, 0 * 8},
nav_};
Checkbox check_log{
{22 * 8, 21},
{16 * 8, 1 * 16},
3,
"LOG",
true};
@@ -93,19 +89,21 @@ class ACARSAppView : public View {
Console console{
{0, 3 * 16, 240, 256}};
AudioVolumeField field_volume{
{28 * 8, 1 * 16}};
std::unique_ptr<ACARSLogger> logger{};
void on_packet(const acars::Packet& packet);
void on_packet(const ACARSPacketMessage* packet);
MessageHandlerRegistration message_handler_packet{
Message::ID::ACARSPacket,
[this](Message* const p) {
const auto message = static_cast<const ACARSPacketMessage*>(p);
const acars::Packet packet{message->packet};
this->on_packet(packet);
this->on_packet(message);
}};
};
} /* namespace ui */
} // namespace ui::external_app::acars_rx
#endif /*__ACARS_APP_H__*/
#endif /*__ACARS_APP_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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 "ui.hpp"
#include "acars_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::acars_rx {
void initialize_app(ui::NavigationView& nav) {
nav.push<ACARSAppView>();
}
} // namespace ui::external_app::acars_rx
extern "C" {
__attribute__((section(".external_app.app_acars_rx.application_information"), used)) application_information_t _application_information_acars_rx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::acars_rx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "ACARS",
/*.bitmap_data = */ {
0x80,
0x01,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF8,
0x1F,
0xFE,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF0,
0x0F,
0xF8,
0x1F,
},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_acars */ {'P', 'A', 'C', 'A'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+13
View File
@@ -96,6 +96,17 @@ set(EXTCPPSRC
#sstvtx
external/sstvtx/main.cpp
external/sstvtx/ui_sstvtx.cpp
#random
external/random_password/main.cpp
external/random_password/ui_random_password.cpp
external/random_password/sha512.cpp
external/random_password/sha512.h
#acars
external/acars_rx/main.cpp
external/acars_rx/acars_app.cpp
)
set(EXTAPPLIST
@@ -122,4 +133,6 @@ set(EXTAPPLIST
adsbtx
morse_tx
sstvtx
random_password
#acars_rx
)
+15
View File
@@ -46,6 +46,8 @@ MEMORY
ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k
ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k
ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
}
SECTIONS
@@ -190,6 +192,19 @@ SECTIONS
*(*ui*external_app*sstvtx*);
} > ram_external_app_sstvtx
.external_app_random_password : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_random_password.application_information));
*(*ui*external_app*random_password*);
} > ram_external_app_random_password
.external_app_acars_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_acars_rx.application_information));
*(*ui*external_app*acars_rx*);
} > ram_external_app_acars_rx
}
+33 -4
View File
@@ -22,6 +22,7 @@
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "i2cdevmanager.hpp"
using namespace portapack;
using namespace ui;
@@ -29,7 +30,7 @@ using namespace ui;
namespace ui::external_app::extsensors {
void ExtSensorsView::focus() {
text_info.focus();
console.focus();
}
ExtSensorsView::ExtSensorsView(NavigationView& nav)
@@ -39,10 +40,33 @@ ExtSensorsView::ExtSensorsView(NavigationView& nav)
&text_gps,
&text_orientation,
&text_envl1,
&text_envl2});
&text_envl2,
&text_envl3,
&console});
prev_scan_int = i2cdev::I2CDevManager::get_autoscan_interval();
refreshi2c();
i2cdev::I2CDevManager::set_autoscan_interval(3); // scan each 3 sec for new i2c devices
}
void ExtSensorsView::on_new_dev() {
refreshi2c();
}
void ExtSensorsView::refreshi2c() {
console.clear(true);
console.writeln("Found I2C devices:");
auto addrlist = i2cdev::I2CDevManager::get_gev_list_by_addr();
for (size_t i = 0; i < addrlist.size(); ++i) {
console.write("0x");
console.write(to_string_hex(addrlist[i]));
console.write(", ");
}
if (addrlist.size() == 0) console.writeln("No I2C devs found.");
}
ExtSensorsView::~ExtSensorsView() {
i2cdev::I2CDevManager::set_autoscan_interval(prev_scan_int);
}
void ExtSensorsView::on_any() {
@@ -78,9 +102,14 @@ void ExtSensorsView::on_environment(const EnvironmentDataMessage* msg) {
tmp += "C";
tmp += "; H: " + to_string_decimal(msg->humidity, 1) + "%"; // humidity
text_envl1.set(tmp);
tmp = "P: " + to_string_decimal(msg->pressure, 1) + " hPa; L:"; // pressure
tmp += to_string_dec_int(msg->light) + " LUX"; // light
tmp = "P: " + to_string_decimal(msg->pressure, 1) + " hPa"; // pressure
text_envl2.set(tmp);
}
void ExtSensorsView::on_light(const LightDataMessage* msg) {
on_any();
std::string tmp = "L: " + to_string_dec_int(msg->light) + " LUX";
text_envl3.set(tmp);
}
} // namespace ui::external_app::extsensors
@@ -53,6 +53,7 @@ class ExtSensorsView : public View {
NavigationView& nav_;
bool has_data = false;
uint16_t prev_scan_int = 0;
Labels labels{
{{0 * 8, 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground},
@@ -64,9 +65,15 @@ class ExtSensorsView : public View {
Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"};
Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"};
Text text_envl2{{1 * 8, 9 * 16, 24 * 8, 16}, "-"};
Text text_envl3{{1 * 8, 11 * 16, 24 * 8, 16}, "-"};
Console console{
{1, 13 * 16, screen_width - 1, screen_height - 13 * 16}};
void refreshi2c();
void on_new_dev();
void on_any();
void on_light(const LightDataMessage* msg);
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
void on_environment(const EnvironmentDataMessage* msg);
@@ -90,6 +97,20 @@ class ExtSensorsView : public View {
const auto message = static_cast<const EnvironmentDataMessage*>(p);
this->on_environment(message);
}};
MessageHandlerRegistration message_handler_light{
Message::ID::LightData,
[this](Message* const p) {
const auto message = static_cast<const LightDataMessage*>(p);
this->on_light(message);
}};
MessageHandlerRegistration message_handler_dev{
Message::ID::I2CDevListChanged,
[this](Message* const p) {
(void)p; // make compiler happy
this->on_new_dev();
}};
};
}; // namespace ui::external_app::extsensors
+41 -15
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo, zxkmm
*
* This file is part of PortaPack.
*
@@ -49,10 +48,12 @@ ProtoView::ProtoView(NavigationView& nav)
&field_vga,
&field_volume,
&field_frequency,
&labels,
&label_zoom,
&label_shift,
&options_zoom,
&number_shift,
&button_reset,
&button_pause,
&waveform,
&waveform2,
&waveform3,
@@ -72,6 +73,10 @@ ProtoView::ProtoView(NavigationView& nav)
button_reset.on_select = [this](Button&) {
reset();
};
button_pause.on_select = [this](Button&) {
set_pause(!paused);
};
set_pause(false); // need to use this to default hide shift functionality
baseband::set_subghzd_config(0, receiver_model.sampling_rate());
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
@@ -80,6 +85,7 @@ ProtoView::ProtoView(NavigationView& nav)
void ProtoView::reset() {
cnt = 0;
set_pause(false);
number_shift.set_value(0);
waveform_shift = 0;
for (uint16_t i = 0; i < MAXSIGNALBUFFER; i++) time_buffer[i] = 0;
@@ -134,19 +140,20 @@ void ProtoView::draw() {
drawcnt = 0;
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
// add empty data for padding (so you can shift left/nagetive)
for (int32_t i = 0;
i < ((waveform_shift > 0) ? 0 : -waveform_shift) && drawcnt < MAXDRAWCNT; // this is for shift nagetive (left side)
++i) {
waveform_buffer[drawcnt++] = 0;
// add empty data for padding (so you can shift left/negative)
if (waveform_shift < 0) {
for (int32_t i = 0; (i < -1 * waveform_shift) && drawcnt < MAXDRAWCNT; // this is for shift negative (move to right)
++i) {
waveform_buffer[drawcnt++] = 0;
}
}
for (uint16_t i = ((waveform_shift < 0) ? -waveform_shift : 0); // this is for shift positive aka right side
i < MAXSIGNALBUFFER && drawcnt < MAXDRAWCNT; // prevent out of ranging
uint16_t skipped = 0;
uint16_t to_skip = ((waveform_shift > 0) ? waveform_shift : 0); // when >0 it'll skip some to move left
for (uint16_t i = 0;
i < MAXSIGNALBUFFER && drawcnt < MAXDRAWCNT; // prevent out of ranging
++i) {
uint16_t buffer_index = (i + waveform_shift + MAXSIGNALBUFFER) % MAXSIGNALBUFFER;
state = time_buffer[buffer_index] >= 0;
int32_t timeabs = state ? time_buffer[buffer_index] : -1 * time_buffer[buffer_index];
state = time_buffer[i] >= 0;
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
int32_t timesize = timeabs / zoom;
if (timesize == 0) {
remain += timeabs;
@@ -164,7 +171,11 @@ void ProtoView::draw() {
remain = 0;
lmax = 0;
for (int32_t ii = 0; ii < timesize && drawcnt < MAXDRAWCNT; ++ii) {
waveform_buffer[drawcnt++] = state;
if (skipped < to_skip) {
skipped++;
} else {
waveform_buffer[drawcnt++] = state;
}
}
}
}
@@ -175,6 +186,7 @@ void ProtoView::add_time(int32_t time) {
}
void ProtoView::on_data(const ProtoViewDataMessage* message) {
if (paused) return;
// filter out invalid ones.
uint16_t start = 0;
uint16_t stop = 0;
@@ -207,6 +219,20 @@ void ProtoView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void ProtoView::set_pause(bool pause) {
paused = pause;
if (pause) {
label_shift.hidden(false);
number_shift.hidden(false);
button_pause.set_text(LanguageHelper::currentMessages[LANG_RESUME]);
} else {
label_shift.hidden(true);
number_shift.hidden(true);
button_pause.set_text(LanguageHelper::currentMessages[LANG_PAUSE]);
}
set_dirty();
}
ProtoView::~ProtoView() {
audio::output::stop();
receiver_model.disable();
+12 -3
View File
@@ -74,8 +74,11 @@ class ProtoView : public View {
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
Labels labels{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground},
// need to seperate because label shift need to hide independently
Labels label_zoom{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
Labels label_shift{
{{0 * 8, 2 * 16}, "Shift: ", Theme::getInstance()->fg_light->foreground}};
OptionsField options_zoom{
@@ -103,6 +106,10 @@ class ProtoView : public View {
{screen_width - 12 * 8, 1 * 16, 96, 24},
LanguageHelper::currentMessages[LANG_RESET]};
Button button_pause{
{screen_width - 12 * 8, 1 * 16 + 24, 96, 24},
LanguageHelper::currentMessages[LANG_PAUSE]};
Waveform waveform{
{0, 8 * 8, 240, 50},
waveform_buffer,
@@ -136,6 +143,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
bool needCntReset = false;
bool paused = false;
int16_t zoom = 1; // one value in ms
int16_t waveform_shift = 0;
@@ -151,6 +159,7 @@ class ProtoView : public View {
void on_data(const ProtoViewDataMessage* message);
void draw();
void draw2();
void set_pause(bool pause);
void reset();
MessageHandlerRegistration message_handler_packet{
@@ -178,4 +187,4 @@ class ProtoView : public View {
} // namespace ui::external_app::protoview
#endif /*__UI_PROTOVIEW_H__*/
#endif /*__UI_PROTOVIEW_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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 "ui.hpp"
#include "ui_random_password.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::random_password {
void initialize_app(ui::NavigationView& nav) {
nav.push<RandomPasswordView>();
}
} // namespace ui::external_app::random_password
extern "C" {
__attribute__((section(".external_app.app_random_password.application_information"), used)) application_information_t _application_information_random_password = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::random_password::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Random passwd",
/*.bitmap_data = */ {
0xC0,
0x03,
0xE0,
0x07,
0x30,
0x0C,
0x30,
0x0C,
0x30,
0x0C,
0x30,
0x0C,
0xE0,
0x07,
0xC0,
0x03,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x07,
0x80,
0x03,
0x80,
0x07,
0x80,
0x01,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+156
View File
@@ -0,0 +1,156 @@
/* modified from https://github.com/ulwanski/sha512
* copyright @ulwanski
* */
#include "sha512.h"
namespace ui::external_app::random_password {
const unsigned long long SHA512::sha512_k[80] = // ULL = uint64
{0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL};
void SHA512::transform(const unsigned char* message, unsigned int block_nb) {
uint64 w[80];
uint64 wv[8];
uint64 t1, t2;
const unsigned char* sub_block;
int i, j;
for (i = 0; i < (int)block_nb; i++) {
sub_block = message + (i << 7);
for (j = 0; j < 16; j++) {
SHA2_PACK64(&sub_block[j << 3], &w[j]);
}
for (j = 16; j < 80; j++) {
w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16];
}
for (j = 0; j < 8; j++) {
wv[j] = m_h[j];
}
for (j = 0; j < 80; j++) {
t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j];
t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
m_h[j] += wv[j];
}
}
}
void SHA512::init() {
m_h[0] = 0x6a09e667f3bcc908ULL;
m_h[1] = 0xbb67ae8584caa73bULL;
m_h[2] = 0x3c6ef372fe94f82bULL;
m_h[3] = 0xa54ff53a5f1d36f1ULL;
m_h[4] = 0x510e527fade682d1ULL;
m_h[5] = 0x9b05688c2b3e6c1fULL;
m_h[6] = 0x1f83d9abfb41bd6bULL;
m_h[7] = 0x5be0cd19137e2179ULL;
m_len = 0;
m_tot_len = 0;
}
void SHA512::update(const unsigned char* message, unsigned int len) {
unsigned int block_nb;
unsigned int new_len, rem_len, tmp_len;
const unsigned char* shifted_message;
tmp_len = SHA384_512_BLOCK_SIZE - m_len;
rem_len = len < tmp_len ? len : tmp_len;
memcpy(&m_block[m_len], message, rem_len);
if (m_len + len < SHA384_512_BLOCK_SIZE) {
m_len += len;
return;
}
new_len = len - rem_len;
block_nb = new_len / SHA384_512_BLOCK_SIZE;
shifted_message = message + rem_len;
transform(m_block, 1);
transform(shifted_message, block_nb);
rem_len = new_len % SHA384_512_BLOCK_SIZE;
memcpy(m_block, &shifted_message[block_nb << 7], rem_len);
m_len = rem_len;
m_tot_len += (block_nb + 1) << 7;
}
void SHA512::final(unsigned char* digest) {
unsigned int block_nb;
unsigned int pm_len;
unsigned int len_b;
int i;
block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) < (m_len % SHA384_512_BLOCK_SIZE));
len_b = (m_tot_len + m_len) << 3;
pm_len = block_nb << 7;
memset(m_block + m_len, 0, pm_len - m_len);
m_block[m_len] = 0x80;
SHA2_UNPACK32(len_b, m_block + pm_len - 4);
transform(m_block, block_nb);
for (i = 0; i < 8; i++) {
SHA2_UNPACK64(m_h[i], &digest[i << 3]);
}
}
std::string sha512(const void* dat, size_t len) {
unsigned char digest[SHA512::DIGEST_SIZE];
memset(digest, 0, SHA512::DIGEST_SIZE);
SHA512 ctx = SHA512();
ctx.init();
ctx.update((const unsigned char*)dat, len);
ctx.final(digest);
char buf[2 * SHA512::DIGEST_SIZE + 1];
buf[2 * SHA512::DIGEST_SIZE] = 0;
for (unsigned int i = 0; i < SHA512::DIGEST_SIZE; i++)
sprintf(buf + i * 2, "%02x", digest[i]);
return std::string(buf);
}
std::string sha512(std::string input) {
return sha512((const unsigned char*)input.c_str(), (size_t)input.length());
}
} // namespace ui::external_app::random_password
+76
View File
@@ -0,0 +1,76 @@
/* modified from https://github.com/ulwanski/sha512
* copyright @ulwanski
* */
#ifndef SHA512_H
#define SHA512_H
#include <string>
#include <cstring>
#include <fstream>
#define _CRT_SECURE_NO_WARNINGS
std::string sha512(const void* dat, size_t len);
std::string sha512(std::string input);
namespace ui::external_app::random_password {
class SHA512 {
protected:
typedef unsigned char uint8;
typedef unsigned long uint32;
typedef unsigned long long uint64;
const static uint64 sha512_k[];
static const unsigned int SHA384_512_BLOCK_SIZE = (1024 / 8);
public:
void init();
void update(const unsigned char* message, unsigned int len);
void final(unsigned char* digest);
static const unsigned int DIGEST_SIZE = (512 / 8);
protected:
void transform(const unsigned char* message, unsigned int block_nb);
unsigned int m_tot_len;
unsigned int m_len;
unsigned char m_block[2 * SHA384_512_BLOCK_SIZE];
uint64 m_h[8];
};
std::string sha512(std::string input);
#define SHA2_SHFR(x, n) (x >> n)
#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39))
#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41))
#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7))
#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6))
#define SHA2_UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8)((x)); \
*((str) + 2) = (uint8)((x) >> 8); \
*((str) + 1) = (uint8)((x) >> 16); \
*((str) + 0) = (uint8)((x) >> 24); \
}
#define SHA2_UNPACK64(x, str) \
{ \
*((str) + 7) = (uint8)((x)); \
*((str) + 6) = (uint8)((x) >> 8); \
*((str) + 5) = (uint8)((x) >> 16); \
*((str) + 4) = (uint8)((x) >> 24); \
*((str) + 3) = (uint8)((x) >> 32); \
*((str) + 2) = (uint8)((x) >> 40); \
*((str) + 1) = (uint8)((x) >> 48); \
*((str) + 0) = (uint8)((x) >> 56); \
}
#define SHA2_PACK64(str, x) \
{ \
*(x) = ((uint64) * ((str) + 7)) | ((uint64) * ((str) + 6) << 8) | ((uint64) * ((str) + 5) << 16) | ((uint64) * ((str) + 4) << 24) | ((uint64) * ((str) + 3) << 32) | ((uint64) * ((str) + 2) << 40) | ((uint64) * ((str) + 1) << 48) | ((uint64) * ((str) + 0) << 56); \
}
} // namespace ui::external_app::random_password
#endif
@@ -0,0 +1,397 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 zxkmm
* 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.
*/
/* Notes about safety/randomness:
* - Radio data is from nature, so if it's evenly tiled in space, it's random enough, but usually it's not
* - A unsafe point is the freq that fetch data is from LCG (from Cpp STL) using time as seed, but should be spread out by later random calculation
* - A unsafe point is the init data is still using LCG to generate, but used following methods to spread out:
* - - Radio data
* - - save a buffer of seeds and generate char by char, this cover all the possible chars combination
* - - Rollout with teo seeds and get a final char, this spread out again
* - - Hash out the generated password and spread out again if you choose. SHA-512 is proven very spreading out in random space by math
* - But still, this isn't meant to become a TRNG generator, don't use at critical/high safety requited/important system, this FOSS has absolutely no warranty
*/
#include "ui_random_password.hpp"
#include "ui_modemsetup.hpp"
#include "modems.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
#include "sha512.h"
using namespace portapack;
using namespace modems;
using namespace ui;
namespace ui::external_app::random_password {
void RandomPasswordLogger::log_raw_data(const std::string& data) {
log_file.write_entry(data);
}
void RandomPasswordView::focus() {
button_refresh.focus();
}
RandomPasswordView::RandomPasswordView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&check_log,
&button_modem_setup,
&labels,
&text_generated_passwd,
&text_char_type_hints,
&check_digits,
&check_latin_lower,
&check_latin_upper,
&check_punctuation,
&check_show_seeds,
&check_auto_send,
&button_refresh,
&button_show_qr,
&button_flood,
&button_send,
&field_digits,
&field_method,
&check_allow_confusable_chars,
&text_seed,
&progressbar});
// no idea what's these, i copied from afsk rx app and they seems needed'
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
serial_format.data_bits = 7;
serial_format.parity = EVEN;
serial_format.stop_bits = 1;
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
progressbar.set_max(MAX_DIGITS * 2);
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
if (v) {
nav_.display_modal(
"Warning",
"Sure?\n"
"this will save all generated\n"
"password to sdcard\n"
"in plain text\n"
"those which generated before\n"
"you check me, will lost",
YESNO,
[this, v](bool c) {
if (c) {
logging = v;
} else {
check_log.set_value(false);
// this is needed to check back to false cuz when trigger by human, the check to true already happened
// this blocked interface so won't accidently saved even if user checked but selected no later here,
// but take care of here if in the future implemented ticking/auto/batch save etc
}
});
} else {
logging = v;
}
};
button_modem_setup.on_select = [&nav](Button&) { // copied from afsk rx app
nav.push<ModemSetupView>();
};
check_digits.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_latin_lower.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_latin_upper.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_punctuation.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_allow_confusable_chars.on_select = [this](Checkbox&, bool) {
this->new_password();
};
button_refresh.on_select = [this](Button&) {
this->set_random_freq();
this->new_password();
};
button_show_qr.on_select = [this, &nav](Button&) {
nav.push<QRCodeView>(password.data());
};
button_flood.on_select = [this](Button&) {
if (flooding) {
flooding = false;
button_flood.set_text(LanguageHelper::currentMessages[LANG_FLOOD]);
} else {
flooding = true;
button_flood.set_text(LanguageHelper::currentMessages[LANG_STOP]);
}
};
button_send.on_select = [this, &nav](Button&) {
async_prev_val = portapack::async_tx_enabled;
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg(password);
portapack::async_tx_enabled = async_prev_val;
};
field_digits.on_change = [this](int32_t) {
clean_buffer();
this->new_password();
};
/// v check defauly val init
check_digits.set_value(true);
check_latin_lower.set_value(true);
check_latin_upper.set_value(true);
check_punctuation.set_value(true);
check_show_seeds.set_value(true);
field_digits.set_value(16);
field_method.set_by_value(Method::RADIO_LCG_ROLL_HASH);
///^ check defauly val init
logger = std::make_unique<RandomPasswordLogger>();
if (logger)
logger->append(logs_dir / u"random.TXT");
// Auto-configure modem for LCR RX (will be removed later), copied from afsk rx app
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false);
receiver_model.enable();
receiver_model.set_rf_amp(false);
set_random_freq();
new_password();
}
void RandomPasswordView::on_data(uint32_t value, bool is_data) {
if (is_data) {
seed = static_cast<unsigned int>(value);
text_seed.set(to_string_dec_uint(check_show_seeds.value() ? seed : 0));
/// v feed deque
seeds_deque.push_back(value);
if (seeds_deque.size() > MAX_DIGITS * 2) {
seeds_deque.pop_front();
}
///^ feed deque
progressbar.set_value(seeds_deque.size());
if (flooding && seeds_deque.size() >= MAX_DIGITS * 2) {
new_password();
}
} else {
text_generated_passwd.set("Baudrate estimation: ~");
text_char_type_hints.set(to_string_dec_uint(value));
}
}
void RandomPasswordView::clean_buffer() {
seeds_deque = {};
}
void RandomPasswordView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void RandomPasswordView::set_random_freq() {
std::srand(LPC_RTC->CTIME0);
// this is only for seed to visit random freq, the radio is still real random
auto random_freq = 100000000 + (std::rand() % 900000000); // 100mhz to 1ghz
receiver_model.set_target_frequency(random_freq);
field_frequency.set_value(random_freq);
}
void RandomPasswordView::new_password() {
if (seeds_deque.size() < MAX_DIGITS * 2) {
seeds_buffer_not_full = true;
text_generated_passwd.set("wait seeds buffer full");
text_char_type_hints.set("then press generate");
return;
}
password = "";
std::string charset = "";
std::string char_type_hints = "";
std::string initial_password = "";
int password_length = field_digits.value();
/// charset worker
if (check_digits.value())
charset += "0123456789";
if (check_latin_lower.value())
charset += "abcdefghijklmnopqrstuvwxyz";
if (check_latin_upper.value())
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (check_punctuation.value())
charset += ".,-!?";
if (!check_allow_confusable_chars.value()) {
charset.erase(std::remove_if(charset.begin(), charset.end(),
[](char c) { return c == '0' || c == 'O' || c == 'o' || c == '1' || c == 'l'; }),
charset.end());
}
if (charset.empty()) {
text_generated_passwd.set("generate failed,");
text_char_type_hints.set("select at least 1 type");
return;
}
/// roll worker
for (int i = 0; i < password_length * 2; i += 2) {
unsigned int seed = seeds_deque[i];
std::srand(seed);
uint8_t rollnum = (uint8_t)(seeds_deque[i + 1] % 128);
uint8_t nu = 0;
for (uint8_t o = 0; o < rollnum; ++o) nu = std::rand();
nu++;
char c = charset[std::rand() % charset.length()];
initial_password += c;
}
// hash worker
std::string hashed_password = sha512(initial_password);
std::vector<char> password_chars(password_length);
for (int i = 0; i < password_length; i++) {
unsigned int index = std::stoul(hashed_password.substr(i * 2, 2), nullptr, 16) % charset.length(); // result from 0 to charset.length()-1,should be very evenly tiled in the charset space
password_chars[i] = charset[index];
}
/// hint text worker
for (char c : password_chars) {
password += c;
if (std::isdigit(c)) {
char_type_hints += "1";
} else if (std::islower(c)) {
char_type_hints += "a";
} else if (std::isupper(c)) {
char_type_hints += "A";
} else {
char_type_hints += ",";
}
}
/// decision worker
switch (field_method.selected_index_value()) {
case Method::RADIO_LCG_ROLL:
password = initial_password;
break;
case Method::RADIO_LCG_ROLL_HASH:
break;
default:
break;
}
/// give out result worker
text_generated_passwd.set(password);
text_char_type_hints.set(char_type_hints);
paint_password_hints();
if (logger && logging) {
str_log += generate_log_line();
logger->log_raw_data(str_log);
str_log = "";
}
if (check_auto_send.value() || flooding) {
async_prev_val = portapack::async_tx_enabled;
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg(password);
portapack::async_tx_enabled = async_prev_val;
}
clean_buffer();
}
void RandomPasswordView::paint_password_hints() {
Painter painter;
const int char_width = 8;
const int char_height = 16;
const int start_y = 6 * char_height;
const int rect_height = 4;
for (size_t i = 0; i < password.length(); i++) {
char c = password[i];
Color color;
if (std::isdigit(c)) {
color = Color::red();
} else if (std::islower(c)) {
color = Color::green();
} else if (std::isupper(c)) {
color = Color::blue();
} else {
color = Color::white();
}
painter.fill_rectangle(
{{static_cast<int>(i) * char_width, start_y},
{char_width, rect_height}},
color);
}
}
std::string RandomPasswordView::generate_log_line() {
std::string seeds_set = "";
for (auto seed : seeds_deque) {
seeds_set += std::to_string(seed);
seeds_set += " ";
}
std::string line = "\npassword=" + password +
"\nseeds=" + seeds_set +
"\n";
return line;
}
RandomPasswordView::~RandomPasswordView() {
receiver_model.disable();
baseband::shutdown();
}
} // namespace ui::external_app::random_password
@@ -0,0 +1,235 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 zxkmm
* 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 __UI_RANDOM_PASSWORD_H__
#define __UI_RANDOM_PASSWORD_H__
#define MAX_DIGITS 30
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "ui_qrcode.hpp"
#include "usb_serial_asyncmsg.hpp"
#include "sha512.h"
#include <deque>
#include <string>
using namespace ui;
namespace ui::external_app::random_password {
enum Method {
RADIO_LCG_ROLL,
RADIO_LCG_ROLL_HASH,
};
class RandomPasswordLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_raw_data(const std::string& data);
private:
LogFile log_file{};
};
class RandomPasswordView : public View {
public:
RandomPasswordView(NavigationView& nav);
~RandomPasswordView();
void focus() override;
std::string title() const override { return "R.passwd"; };
private:
unsigned int seed = 0; // extern void srand (unsigned int __seed) __THROW;
std::string password = "";
std::deque<unsigned int> seeds_deque = {0};
bool seeds_buffer_not_full = true;
bool in_benchmark = false;
bool flooding = false;
bool logging = false;
bool async_prev_val = false;
std::string str_log{""};
void on_data(uint32_t value, bool is_data);
void clean_buffer();
void new_password();
std::string generate_log_line();
void paint_password_hints();
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_passgen", app_settings::Mode::RX};
Labels labels{
{{0 * 8, 0 * 16}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground},
{{screen_width / 2, 7 * 16 - 2}, "method:", Theme::getInstance()->fg_light->foreground},
};
RFAmpField field_rf_amp{
{13 * 8, 1 * 16}};
LNAGainField field_lna{
{15 * 8, 1 * 16}};
VGAGainField field_vga{
{18 * 8, 1 * 16}};
RSSI rssi{
{21 * 8, 1 * 16 + 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 1 * 16 + 5, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 1 * 16},
nav_};
Button button_modem_setup{
{screen_width - 12 * 8, 2 * 16 - 1, 96, 16 + 2},
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Text text_seed{
{0, 2 * 16, 10 * 8, 16},
"0000000000"};
ProgressBar progressbar{
{10 * 8 + 2, 2 * 16, screen_width - 96 - (10 * 8 + 4) - 1, 16}};
Text text_generated_passwd{
{0, 4 * 16, screen_width, 16},
"000000000000000000000000000000"};
Text text_char_type_hints{
{0, 5 * 16 + 4, screen_width, 16},
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"};
Checkbox check_show_seeds{
{17 * 8, 8 * 16},
6,
"Show seed"};
Checkbox check_auto_send{
{1 * 8, 8 * 16},
20,
"Auto send"};
Checkbox check_punctuation{
{17 * 8, 12 * 16},
6,
".,-!?"};
Checkbox check_allow_confusable_chars{
{1 * 8, 10 * 16},
20,
"0 O o 1 l"};
Checkbox check_digits{
{1 * 8, 12 * 16},
3,
"123"};
Checkbox check_latin_lower{
{1 * 8, 14 * 16},
3,
"abc"};
Checkbox check_latin_upper{
{17 * 8, 14 * 16},
3,
"ABC"};
Checkbox check_log{
{17 * 8, 10 * 16},
3,
LanguageHelper::currentMessages[LANG_SAVE]};
Button button_flood{
{0 * 8, 15 * 16 + 18, screen_width / 2, 22},
LanguageHelper::currentMessages[LANG_FLOOD]};
Button button_send{
{screen_width / 2 + 2, 15 * 16 + 18, screen_width / 2 - 2, 22},
"Send pwd"};
Button button_refresh{
{0 * 8, 17 * 16 + 10, screen_width / 2, 22},
"Generate"};
Button button_show_qr{
{screen_width / 2 + 2, 17 * 16 + 10, screen_width / 2 - 2, 22},
LanguageHelper::currentMessages[LANG_SHOWQR]};
NumberField field_digits{
{0 + (sizeof("digits:") - 1) * 8, 7 * 16 - 2},
2,
{1, 30},
1,
' '};
OptionsField field_method{
{(screen_width / 2) + (sizeof("method:") - 1) * 8, 7 * 16 - 2},
sizeof("R+L+R+H"),
{{"R+L+R", Method::RADIO_LCG_ROLL},
{"R+L+R+H", Method::RADIO_LCG_ROLL_HASH}}};
void on_data_afsk(const AFSKDataMessage& message);
std::unique_ptr<RandomPasswordLogger> logger{};
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void set_random_freq();
};
} // namespace ui::external_app::random_password
#endif /*__UI_RANDOM_PASSWORD_H__*/
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __UI_SPECTRUM_PAINTER_H
#define __UI_SPECTRUM_PAINTER_H
#include "ui.hpp"
#include "ui_language.hpp"
@@ -165,3 +166,5 @@ class SpectrumPainterView : public View {
};
} // namespace ui::external_app::spainter
#endif
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __UI_SPECTRUM_PAINTER_IMAGE_H
#define __UI_SPECTRUM_PAINTER_IMAGE_H
#include "ui.hpp"
#include "ui_widget.hpp"
@@ -64,3 +65,5 @@ class SpectrumInputImageView : public View {
};
} // namespace ui::external_app::spainter
#endif
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __UI_SPECTRUM_PAINTER_TEXT_H
#define __UI_SPECTRUM_PAINTER_TEXT_H
#include "ui.hpp"
#include "ui_widget.hpp"
@@ -110,3 +111,5 @@ class SpectrumInputTextView : public View {
};
} // namespace ui::external_app::spainter
#endif
+8 -2
View File
@@ -31,8 +31,10 @@ 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 latitude_name_old = "Latitute"sv;
const std::string_view longitude_name_old = "Longitude"sv;
const std::string_view latitude_name = "Lat"sv;
const std::string_view longitude_name = "Lon"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
@@ -93,6 +95,10 @@ Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
parse_float_meta(fixed, metadata.latitude);
else if (cols[0] == longitude_name)
parse_float_meta(fixed, metadata.longitude);
else if (cols[0] == latitude_name_old)
parse_float_meta(fixed, metadata.latitude);
else if (cols[0] == longitude_name_old)
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;
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __IO_H
#define __IO_H
#include "file.hpp"
@@ -38,3 +39,5 @@ class Writer {
};
} /* namespace stream */
#endif
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __IO_CONVERT_H
#define __IO_CONVERT_H
#include "io_file.hpp"
@@ -77,3 +78,5 @@ class FileConvertWriter : public stream::Writer {
File file_{};
uint64_t bytes_written_{0};
};
#endif
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __IO_FILE_H
#define __IO_FILE_H
#include "io.hpp"
@@ -71,3 +72,5 @@ class FileWriter : public stream::Writer {
};
using RawFileWriter = FileWriter;
#endif
+4 -1
View File
@@ -20,7 +20,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __IO_WAVE_H
#define __IO_WAVE_H
#include "io_file.hpp"
@@ -181,3 +182,5 @@ class WAVFileWriter : public FileWriter {
Optional<File::Error> update_header();
Optional<File::Error> write_tags();
};
#endif
+1 -1
View File
@@ -85,7 +85,7 @@ static bool touch_update() {
case IO::TouchPinsConfig::SensePressure: {
const auto z1 = samples.xp - samples.xn;
const auto z2 = samples.yp - samples.yn;
const auto touch_raw = (z1 > touch::touch_threshold) || (z2 > touch::touch_threshold);
const auto touch_raw = (z1 > portapack::touch_threshold) || (z2 > portapack::touch_threshold);
touch_debounce = (touch_debounce << 1) | (touch_raw ? 1U : 0U);
touch_detected = ((touch_debounce & touch_debounce_mask) == touch_debounce_mask);
if (!touch_detected && !touch_cycle) {
-2
View File
@@ -96,7 +96,6 @@ Continuous (Fox-oring)
// Multimon-style stuff:
// TODO: DMR detector
// TODO: GSM channel detector
// TODO: Playdead amnesia and login
// TODO: Setup: Play dead by default ? Enable/disable ?
// Old or low-priority stuff:
@@ -106,7 +105,6 @@ Continuous (Fox-oring)
// TODO: Check more OOK encoders
// BUG (fixed ?): No audio in about when shown second time
// TODO: Show MD5 mismatches for modules not found, etc...
// TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found ui_loadmodule
// BUG: Description doesn't show up first time going to system>module info (UI drawn on top)
// TODO: Two players tic-tac-toe
// TODO: Analog TV pong game
+10 -1
View File
@@ -53,6 +53,8 @@ using asahi_kasei::ak4951::AK4951;
#include "string_format.hpp"
#include "bitmap.hpp"
#include "ui_widget.hpp"
#include "i2cdevmanager.hpp"
#include "battery.hpp"
namespace portapack {
@@ -93,6 +95,7 @@ TemperatureLogger temperature_logger;
bool antenna_bias{false};
uint32_t bl_tick_counter{0};
uint16_t touch_threshold{32};
void set_antenna_bias(const bool v) {
antenna_bias = v;
@@ -536,6 +539,11 @@ init_status_t init() {
set_cpu_clock_speed();
if (persistent_memory::config_lcd_inverted_mode()) display.set_inverted(true);
/* sample max: 1023 sample_t AKA uint16_t
* touch_sensitivity: range: 1 to 128
* threshold range: 1023/1 to 1023/128 = 1023 to 8
*/
touch_threshold = portapack::persistent_memory::touchscreen_threshold();
if (lcd_fast_setup)
draw_splash_screen_icon(0, ui::bitmap_icon_memory);
@@ -588,7 +596,8 @@ init_status_t init() {
chThdSleepMilliseconds(10);
audio::init(portapack_audio_codec());
battery::BatteryManagement::init(persistent_memory::ui_override_batt_calc());
battery::BatteryManagement::set_calc_override(persistent_memory::ui_override_batt_calc());
i2cdev::I2CDevManager::init();
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
+5 -5
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __PORTAPACK_H
#define __PORTAPACK_H
#include "portapack_io.hpp"
@@ -33,13 +34,9 @@
#include "backlight.hpp"
#include "usb_serial.hpp"
#include "ads1110.hpp"
#include "max17055.hpp"
#include "radio.hpp"
#include "clock_manager.hpp"
#include "temperature_logger.hpp"
#include "battery.hpp"
#include "theme.hpp"
/* TODO: This would be better as a class to add
@@ -71,6 +68,7 @@ extern TransmitterModel transmitter_model;
extern uint32_t bl_tick_counter;
extern bool antenna_bias;
extern uint16_t touch_threshold;
extern TemperatureLogger temperature_logger;
@@ -89,3 +87,5 @@ Backlight* backlight();
extern bool async_tx_enabled; // this is for serial tx things, globally
} /* namespace portapack */
#endif
-8
View File
@@ -36,14 +36,6 @@ namespace touch {
using sample_t = uint16_t;
constexpr sample_t sample_max = 1023;
// If you have a dead bottom-left corner try to increase the sensitivity,
// but look for flickering touch indicator in the Buttons test screen
// in which case decrease sensitivity to avoid killing backlight timeout
constexpr sample_t touch_sensitivity = 32;
constexpr sample_t touch_threshold = sample_max / touch_sensitivity;
struct Samples {
sample_t xp;
sample_t xn;
@@ -4,14 +4,39 @@
#include "file_path.hpp"
#include "ui_standalone_view.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
namespace ui {
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
// iterates over all possible ext apps-s, and if it is runnable on the current system, it'll call the callback, and pass minimal info. used to print to console, and for autostart setting's app list. where the minimal info is enough
// please keep in sync with load_external_items
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback, bool module_included) {
if (!callback) return;
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (dev && module_included) {
auto device_info = dev->readDeviceInfo();
if (device_info.has_value()) {
for (uint32_t i = 0; i < device_info->application_count; i++) {
auto appInfo = dev->getStandaloneAppInfo(i);
if (appInfo.has_value() == false) {
continue;
}
if (appInfo->header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
continue;
AppInfoConsole appInfoConsole = {reinterpret_cast<char*>(&appInfo->app_name[0]), reinterpret_cast<char*>(&appInfo->app_name[0]), appInfo->menu_location};
callback(appInfoConsole);
}
}
}
if (sd_card::status() != sd_card::Status::Mounted)
return;
@@ -33,18 +58,16 @@ namespace ui {
continue;
bool versionMatches = VERSION_MD5 == application_information.app_version;
if (!versionMatches) continue;
// here the app is startable and good.
std::string appshortname = filePath.filename().string();
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
// Remove the ".ppma" suffix
appshortname = appshortname.substr(0, appshortname.size() - 5);
if (versionMatches) {
std::string appshortname = filePath.filename().string();
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
// Remove the ".ppma" suffix
appshortname = appshortname.substr(0, appshortname.size() - 5);
}
AppInfoConsole appInfoConsole = {appshortname.c_str(), reinterpret_cast<char*>(&application_information.app_name[0]), application_information.menu_location};
callback(appInfoConsole);
}
AppInfoConsole info{
.appCallName = appshortname.c_str(),
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
.appLocation = application_information.menu_location};
callback(info);
}
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppmp")) {
@@ -61,21 +84,16 @@ namespace ui {
if (!readResult)
continue;
if (application_information.header_version < CURRENT_STANDALONE_APPLICATION_API_VERSION)
if (application_information.header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
continue;
// here the app is startable and good.
std::string appshortname = filePath.filename().string();
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppmp") {
// Remove the ".ppmp" suffix
appshortname = appshortname.substr(0, appshortname.size() - 5);
}
AppInfoConsole info{
.appCallName = appshortname.c_str(),
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
.appLocation = application_information.menu_location};
callback(info);
AppInfoConsole appInfoConsole = {appshortname.c_str(), reinterpret_cast<char*>(&application_information.app_name[0]), application_information.menu_location};
callback(appInfoConsole);
}
}
@@ -84,6 +102,59 @@ namespace ui {
std::vector<GridItem> external_apps;
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (dev) {
auto device_info = dev->readDeviceInfo();
if (device_info.has_value()) {
for (uint32_t i = 0; i < device_info->application_count; i++) {
auto appInfo = dev->getStandaloneAppInfo(i);
if (appInfo.has_value() == false) {
continue;
}
if (appInfo->menu_location != app_location) {
continue;
}
if (appInfo->header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
continue;
GridItem gridItem = {};
gridItem.text = reinterpret_cast<char*>(&appInfo->app_name[0]);
gridItem.color = Color((uint16_t)appInfo->icon_color);
auto dyn_bmp = DynamicBitmap<16, 16>{appInfo->bitmap_data};
gridItem.bitmap = dyn_bmp.bitmap();
bitmaps.push_back(std::move(dyn_bmp));
gridItem.on_select = [&nav, appInfo, i]() {
auto dev2 = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (dev2) {
auto app_image = reinterpret_cast<uint8_t*>(portapack::memory::map::m4_code.end() - appInfo->binary_size);
for (size_t j = 0; j < appInfo->binary_size; j += 128) {
auto segment = dev2->downloadStandaloneApp(i, j);
if (segment.size() != 128) {
continue;
}
std::copy(segment.begin(), segment.end(), app_image + j);
}
if (!run_module_app(nav, app_image, appInfo->binary_size)) {
nav.display_modal("Error", "Unable to run downloaded app.");
}
} else
nav.display_modal("Error", "Unable to download app.");
};
external_apps.push_back(gridItem);
}
}
}
if (sd_card::status() != sd_card::Status::Mounted)
return external_apps;
@@ -259,6 +330,7 @@ namespace ui {
return true;
}
// TODO: implement baseband image support
/* static */ bool ExternalItemsMenuLoader::run_standalone_app(ui::NavigationView& nav, std::filesystem::path filePath) {
File app;
@@ -266,7 +338,8 @@ namespace ui {
if (openError)
return false;
auto app_image = std::make_unique<uint8_t[]>(app.size());
// TODO: move this to m4 memory space
auto app_image = reinterpret_cast<uint8_t*>(portapack::memory::map::m4_code.end() - app.size());
// read file in 512 byte chunks
for (size_t file_read_index = 0; file_read_index < app.size(); file_read_index += std::filesystem::max_file_block_size) {
@@ -286,11 +359,25 @@ namespace ui {
uint32_t* ptr = reinterpret_cast<uint32_t*>(&app_image[file_read_index * 4]);
if (*ptr >= 0xADB10000 && *ptr < (0xADB10000 + 64 * 1024)) {
*ptr = *ptr - 0xADB10000 + (uint32_t)app_image.get();
*ptr = *ptr - 0xADB10000 + (uint32_t)app_image;
}
}
nav.push<StandaloneView>(std::move(app_image));
nav.push<StandaloneView>(app_image);
return true;
}
// TODO: implement baseband image support
/* static */ bool ExternalItemsMenuLoader::run_module_app(ui::NavigationView& nav, uint8_t* app_image, size_t app_size) {
for (size_t file_read_index = 0; file_read_index < app_size / 4; file_read_index++) {
uint32_t* ptr = reinterpret_cast<uint32_t*>(&app_image[file_read_index * 4]);
if (*ptr >= 0xADB10000 && *ptr < (0xADB10000 + 64 * 1024)) {
*ptr = *ptr - 0xADB10000 + (uint32_t)app_image;
}
}
nav.push<StandaloneView>(app_image);
return true;
}
@@ -59,7 +59,8 @@ class ExternalItemsMenuLoader {
ExternalItemsMenuLoader() = delete;
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
static bool run_standalone_app(ui::NavigationView&, std::filesystem::path);
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback);
static bool run_module_app(ui::NavigationView&, uint8_t*, size_t);
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback, bool module_included = false);
private:
static std::vector<DynamicBitmap<16, 16>> bitmaps;
-344
View File
@@ -1,344 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_handwrite.hpp"
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
#include <algorithm>
using namespace portapack;
namespace ui {
void HandWriteView::paint(Painter& painter) {
_painter = &painter;
}
HandWriteView::HandWriteView(
NavigationView& nav,
std::string* str,
size_t max_length)
: TextEntryView(nav, str, max_length) {
size_t n;
// Handwriting alphabet definition here
handwriting = &handwriting_unistroke;
add_children({&button_case});
const auto button_fn = [this](Button& button) {
this->on_button(button);
};
n = 0;
for (auto& button : num_buttons) {
add_child(&button);
button.on_select = button_fn;
button.set_parent_rect({static_cast<Coord>(n * 24),
static_cast<Coord>(236),
24, 28});
const std::string label{
(char)(n + '0')};
button.set_text(label);
button.id = n + '0';
n++;
}
n = 0;
for (auto& button : special_buttons) {
add_child(&button);
button.on_select = button_fn;
button.set_parent_rect({static_cast<Coord>(50 + n * 24),
static_cast<Coord>(270),
24, 28});
const std::string label{
(char)(special_chars[n])};
button.set_text(label);
button.id = special_chars[n];
n++;
}
button_case.on_select = [this, &nav](Button&) {
if (_lowercase == true) {
_lowercase = false;
button_case.set_text("LC");
} else {
_lowercase = true;
button_case.set_text("UC");
}
};
button_ok.on_select = [this, &nav](Button&) {
if (on_changed)
on_changed(_str);
nav.pop();
};
update_text();
}
bool HandWriteView::on_touch(const TouchEvent event) {
if (event.type == ui::TouchEvent::Type::Start) {
stroke_index = 0;
move_wait = 3;
tracing = true;
}
if (event.type == ui::TouchEvent::Type::End) {
tracing = false;
guess_letter();
}
if (event.type == ui::TouchEvent::Type::Move) {
if (tracing)
current_pos = event.point;
}
return true;
}
void HandWriteView::clear_zone(const Color color, const bool flash) {
display.fill_rectangle(
{{0, 32}, {240, 216}},
color);
if (flash) {
flash_timer = 8;
} else {
// Draw grid
_painter->draw_rectangle(
{{0, 32}, {80, 216}},
Color::grey());
_painter->draw_rectangle(
{{80, 32}, {80, 216}},
Color::grey());
_painter->draw_rectangle(
{{160, 32}, {80, 216}},
Color::grey());
_painter->draw_rectangle(
{{0, 104}, {240, 72}},
Color::grey());
}
}
void HandWriteView::guess_letter() {
uint32_t symbol, match, count, stroke_idx, stroke_data;
Condition cond;
Direction dir;
bool matched;
// Letter guessing
if (stroke_index) {
for (symbol = 0; symbol < handwriting->letter_count; symbol++) {
count = handwriting->letter[symbol].count;
matched = false;
if (count) {
// We have a count match to do
if ((count == 1) && (stroke_index == 1)) matched = true;
if ((count == 2) && (stroke_index == 2)) matched = true;
if ((count == 3) && (stroke_index > 2)) matched = true;
} else {
matched = true;
}
if (matched) {
for (match = 0; match < 3; match++) {
cond = handwriting->letter[symbol].match[match].cond;
dir = handwriting->letter[symbol].match[match].dir;
if ((cond != cond_empty) && (dir != dir_empty)) {
if (cond == last) {
if (stroke_index)
stroke_idx = stroke_index - 1;
else
stroke_idx = 0;
} else if (cond == stroke_a)
stroke_idx = 0;
else if (cond == stroke_b)
stroke_idx = 1;
else if (cond == stroke_c)
stroke_idx = 2;
else
stroke_idx = 3;
if (stroke_idx >= stroke_index) break;
stroke_data = stroke_list[stroke_idx];
if ((dir & 0xF0) == 0xF0) {
if ((dir & 0x0F) != (stroke_data & 0x0F)) break;
} else if ((dir & 0x0F) == 0x0F) {
if ((dir & 0xF0) != (stroke_data & 0xF0)) break;
} else {
if (dir != (int32_t)stroke_data) break;
}
}
}
if (match == 3)
break;
else
matched = false;
}
}
if (matched) {
if (symbol) {
if (_lowercase)
char_add('a' + symbol - 1);
else
char_add('A' + symbol - 1);
clear_zone(Color::green(), true); // Green flash
} else {
if (_cursor_pos) {
char_delete();
clear_zone(Color::yellow(), true); // Yellow flash
} else {
clear_zone(Color::red(), true); // Red flash
}
}
} else {
clear_zone(Color::red(), true); // Red flash
}
} else {
// Short tap is space
char_add(' ');
clear_zone(Color::green(), true); // Green flash
}
update_text();
stroke_index = 0;
}
void HandWriteView::add_stroke(uint8_t dir) {
if (stroke_index < 8) {
stroke_list[stroke_index] = dir;
stroke_index++;
} else {
guess_letter();
}
}
void HandWriteView::sample_pen() {
int16_t diff_x, diff_y;
uint8_t dir, dir_ud, dir_lr, stroke_prev;
draw_cursor();
if (flash_timer) {
if (flash_timer == 1) clear_zone(Color::black(), false);
flash_timer--;
}
if (!(sample_skip & 1)) {
if (tracing) {
if (move_wait) {
move_wait--; // ~100ms delay to get rid of jitter from touch start
} else {
diff_x = current_pos.x() - last_pos.x();
diff_y = current_pos.y() - last_pos.y();
if (current_pos.y() <= 240) {
display.fill_rectangle(
{{current_pos.x(), current_pos.y()}, {4, 4}},
Color::grey());
}
dir = 0; // UL by default
if (abs(diff_x) > 7) {
if (diff_x > 0)
dir |= 0x01; // R
} else {
dir |= 0x02; // ?
}
if (abs(diff_y) > 7) {
if (diff_y > 0)
dir |= 0x10; // D
} else {
dir |= 0x20; // ?
}
// Need at least two identical directions to validate stroke
if ((dir & 0x11) == (dir_prev & 0x11))
dir_cnt++;
else
dir_cnt = 0;
dir_prev = dir;
if (dir_cnt > 1) {
dir_cnt = 0;
if (stroke_index) {
if ((stroke_list[stroke_index - 1] != dir) && (dir != 0x22)) {
// Register stroke if different from last one
dir_ud = (dir & 0xF0);
dir_lr = (dir & 0x0F);
stroke_prev = stroke_list[stroke_index - 1];
if (dir_ud == 0x20) {
// LR changed
if ((stroke_prev & 0x0F) != dir_lr) add_stroke(dir);
} else if (dir_lr == 0x02) {
// UD changed
if ((stroke_prev & 0xF0) != dir_ud) add_stroke(dir);
} else {
// Add direction
if (((stroke_prev & 0xF0) == 0x20) && (dir_ud != 0x20)) {
// Add UD
if ((stroke_prev & 0x0F) == dir_lr) {
// Replace totally
stroke_list[stroke_index - 1] = dir;
} else if (dir_lr == 0x02) {
// Merge UD
stroke_list[stroke_index - 1] = dir_ud | (stroke_prev & 0x0F);
} else {
add_stroke(dir);
}
} else if (((stroke_prev & 0x0F) == 0x02) && (dir_lr != 0x02)) {
// Add LR
if ((stroke_prev & 0xF0) == dir_ud) {
// Replace totally
stroke_list[stroke_index - 1] = dir;
} else if (dir_ud == 0x20) {
// Merge LR
stroke_list[stroke_index - 1] = dir_lr | (stroke_prev & 0xF0);
} else {
add_stroke(dir);
}
} else {
add_stroke(dir);
}
}
}
} else {
// Register first stroke
if (dir != 0x22) add_stroke(dir);
}
}
}
last_pos = current_pos;
}
}
sample_skip++;
}
void HandWriteView::on_show() {
clear_zone(Color::black(), false);
}
void HandWriteView::on_button(Button& button) {
char_add(button.id);
update_text();
}
} // namespace ui
-88
View File
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UNISTROKE_H__
#define __UNISTROKE_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_textentry.hpp"
#include "unistroke.hpp"
namespace ui {
class HandWriteView : public TextEntryView {
public:
HandWriteView(NavigationView& nav, std::string* str, size_t max_length);
HandWriteView(const HandWriteView&) = delete;
HandWriteView(HandWriteView&&) = delete;
HandWriteView& operator=(const HandWriteView&) = delete;
HandWriteView& operator=(HandWriteView&&) = delete;
void paint(Painter& painter) override;
void on_show() override;
bool on_touch(const TouchEvent event) override;
private:
const char special_chars[5] = {'\'', '.', '?', '!', '='};
const HandWriting* handwriting{};
Painter* _painter{};
uint8_t dir_cnt{0};
uint8_t dir_prev{0};
uint8_t flash_timer{0};
bool tracing{false};
uint8_t stroke_index{0};
uint8_t sample_skip{0}, move_wait{0};
uint8_t stroke_list[8];
Point start_pos{}, current_pos{}, last_pos{};
bool _lowercase = false;
void sample_pen();
void add_stroke(uint8_t dir);
void guess_letter();
void clear_zone(const Color color, const bool flash);
void on_button(Button& button);
std::array<Button, 10> num_buttons{};
std::array<Button, 5> special_buttons{};
Button button_case{
{8, 270, 32, 28},
"UC"};
Button button_ok{
{190, 270, 40, 28},
"OK"};
MessageHandlerRegistration message_handler_sample{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->sample_pen();
}};
};
} /* namespace ui */
#endif /*__UNISTROKE_H__*/
-170
View File
@@ -1,170 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_loadmodule.hpp"
#include "ch.h"
#include "ff.h"
#include "event_m0.hpp"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "portapack_shared_memory.hpp"
#include "hackrf_hal.hpp"
#include "string_format.hpp"
#include "ui_rds.hpp"
#include "ui_xylos.hpp"
#include "ui_epar.hpp"
#include "ui_lcr.hpp"
#include "analog_audio_app.hpp"
#include "ui_soundboard.hpp"
#include "ui_debug.hpp"
#include "ui_closecall.hpp"
#include "ui_audiotx.hpp"
#include "ui_jammer.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace ui {
void LoadModuleView::focus() {
button_ok.focus();
}
void LoadModuleView::on_show() {
char md5_signature[16];
uint8_t c;
memcpy(md5_signature, (const void*)(0x10087FF0), 16);
for (c = 0; c < 16; c++) {
if (md5_signature[c] != _hash[c]) break;
}
// text_info.set(to_string_hex(*((unsigned int*)0x10087FF0), 8));
if (c == 16) {
text_info.set("Module already loaded :)");
_mod_loaded = true;
} else {
text_info.set("Loading module");
loadmodule();
}
}
int LoadModuleView::load_image() {
const char magic[6] = {'P', 'P', 'M', ' ', 0x02, 0x00};
UINT bw;
uint8_t i;
uint32_t cnt;
char md5sum[16];
FILINFO modinfo;
FIL modfile;
DIR rootdir;
FRESULT res;
// Scan SD card root directory for files with the right MD5 fingerprint at the right location
if (f_opendir(&rootdir, "/") == FR_OK) {
for (;;) {
res = f_readdir(&rootdir, &modinfo);
if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort
// Only care about files with .bin extension
if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) {
res = f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ);
if (res != FR_OK) return 0;
// Magic bytes and version check
f_read(&modfile, &md5sum, 6, &bw);
for (i = 0; i < 6; i++)
if (md5sum[i] != magic[i]) break;
if (i == 6) {
f_lseek(&modfile, 26);
f_read(&modfile, &md5sum, 16, &bw);
for (i = 0; i < 16; i++)
if (md5sum[i] != _hash[i]) break;
// f_read can't read more than 512 bytes at a time ?
if (i == 16) {
f_lseek(&modfile, 512);
for (cnt = 0; cnt < 64; cnt++) {
if (f_read(&modfile, reinterpret_cast<void*>(portapack::memory::map::m4_code.base() + (cnt * 512)), 512, &bw)) return 0;
}
f_close(&modfile);
f_closedir(&rootdir);
LPC_RGU->RESET_CTRL[0] = (1 << 13);
return 1;
}
}
f_close(&modfile);
}
}
f_closedir(&rootdir);
}
return 0;
}
void LoadModuleView::loadmodule() {
// baseband::shutdown();
/*EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch,
[this](Message* const p) {
(void)p;*/
if (load_image()) {
text_infob.set("Module loaded :)");
_mod_loaded = true;
} else {
text_info.set("Module not found :(");
_mod_loaded = false;
}
// }
//);
}
LoadModuleView::LoadModuleView(
NavigationView& nav,
const char* hash,
ViewID viewid) {
add_children({&text_info,
&text_infob,
&button_ok});
_hash = hash;
button_ok.on_select = [this, &nav, viewid](Button&) {
nav.pop();
if (_mod_loaded == true) {
if (viewid == AudioTX) nav.push<AudioTXView>();
if (viewid == Xylos) nav.push<XylosView>();
if (viewid == EPAR) nav.push<EPARView>();
if (viewid == LCR) nav.push<LCRView>();
if (viewid == SoundBoard) nav.push<SoundBoardView>();
if (viewid == AnalogAudio) nav.push<AnalogAudioView>();
if (viewid == RDS) nav.push<RDSView>();
if (viewid == CloseCall) nav.push<CloseCallView>();
if (viewid == Receiver) nav.push<ReceiverMenuView>();
if (viewid == Jammer) nav.push<JammerView>();
}
};
}
} /* namespace ui */
-70
View File
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "core_control.hpp"
namespace ui {
enum ViewID {
Receiver,
AudioTX,
CloseCall,
Xylos,
EPAR,
LCR,
SoundBoard,
AnalogAudio,
RDS,
Jammer
};
class LoadModuleView : public View {
public:
LoadModuleView(NavigationView& nav, const char* hash, ViewID viewid);
void loadmodule();
void on_show() override;
void focus() override;
private:
int load_image(void);
const char* _hash;
bool _mod_loaded = false;
Text text_info{
{8, 64, 224, 16},
"-"};
Text text_infob{
{8, 64 + 16, 224, 16},
"-"};
Button button_ok{
{88, 128, 64, 32},
"OK"};
};
} /* namespace ui */
+77 -56
View File
@@ -24,8 +24,6 @@
#include "ui_navigation.hpp"
// #include "modules.h"
#include "bmp_modal_warning.hpp"
#include "bmp_splash.hpp"
#include "event_m0.hpp"
@@ -35,31 +33,21 @@
#include "ui_about_simple.hpp"
#include "ui_adsb_rx.hpp"
// #include "ui_adsb_tx.hpp" //moved to ext
#include "ui_aprs_rx.hpp"
#include "ui_aprs_tx.hpp"
#include "ui_bht_tx.hpp"
#include "ui_btle_rx.hpp"
// #include "ui_coasterp.hpp" //moved to ext
#include "ui_debug.hpp"
#include "ui_encoders.hpp"
#include "ui_fileman.hpp"
#include "ui_flash_utility.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "ui_freqman.hpp"
#include "ui_fsk_rx.hpp"
#include "ui_iq_trim.hpp"
// #include "ui_jammer.hpp" //moved to ext
// #include "ui_keyfob.hpp" //moved to ext
// #include "ui_lcr.hpp"
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "ui_mictx.hpp"
// #include "ui_morse.hpp" //moved to ext
// #include "ui_nrf_rx.hpp" //moved to ext
// #include "ui_numbers.hpp"
// #include "ui_nuoptix.hpp"
// #include "ui_playdead.hpp"
#include "ui_playlist.hpp"
#include "ui_pocsag_tx.hpp"
#include "ui_rds.hpp"
@@ -72,12 +60,9 @@
#include "ui_settings.hpp"
#include "ui_siggen.hpp"
#include "ui_sonde.hpp"
// #include "ui_spectrum_painter.hpp" //moved to ext app
#include "ui_ss_viewer.hpp"
// #include "ui_sstvtx.hpp" //moved to ext
// #include "ui_test.hpp"
#include "ui_text_editor.hpp"
#include "ui_tone_search.hpp"
#include "ui_touchtunes.hpp"
#include "ui_view_wav.hpp"
#include "ui_weatherstation.hpp"
@@ -86,21 +71,15 @@
#include "ui_battinfo.hpp"
#include "ui_external_items_menu_loader.hpp"
// #include "acars_app.hpp"
#include "ais_app.hpp"
#include "analog_audio_app.hpp"
// #include "analog_tv_app.hpp" //moved to ext
// #include "ble_comm_app.hpp"
#include "ble_rx_app.hpp"
#include "ble_tx_app.hpp"
#include "capture_app.hpp"
#include "ert_app.hpp"
// #include "gps_sim_app.hpp" //moved to ext
// #include "lge_app.hpp" //moved to ext
#include "pocsag_app.hpp"
#include "replay_app.hpp"
#include "soundboard_app.hpp"
// #include "tpms_app.hpp" //moved to ext
#include "core_control.hpp"
#include "file.hpp"
@@ -143,7 +122,6 @@ static NavigationView::AppMap generate_app_map(const NavigationView::AppList& ap
// TODO(u-foka): Check consistency of command names (where we add rx/tx postfix)
const NavigationView::AppList NavigationView::appList = {
/* HOME ******************************************************************/
//{"playdead", "Play dead", HOME, Color::red(), &bitmap_icon_playdead, new ViewFactory<PlayDeadView>()},
{nullptr, "Receive", HOME, Color::cyan(), &bitmap_icon_receivers, new ViewFactory<ReceiversMenuView>()},
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
@@ -155,14 +133,11 @@ const NavigationView::AppList NavigationView::appList = {
{nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory<UtilitiesMenuView>()},
{nullptr, "Settings", HOME, Color::cyan(), &bitmap_icon_setup, new ViewFactory<SettingsMenuView>()},
{nullptr, "Debug", HOME, Color::light_grey(), &bitmap_icon_debug, new ViewFactory<DebugMenuView>()},
//{"about", "About", HOME, Color::cyan(), nullptr, new ViewFactory<AboutView>()},
/* RX ********************************************************************/
//{"acars", "ACARS", RX, Color::yellow(), &bitmap_icon_adsb, new ViewFactory<ACARSAppView>()},
{"adsbrx", "ADS-B", RX, Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBRxView>()},
{"ais", "AIS Boats", RX, Color::green(), &bitmap_icon_ais, new ViewFactory<AISAppView>()},
{"aprsrx", "APRS", RX, Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSRXView>()},
{"audio", "Audio", RX, Color::green(), &bitmap_icon_speaker, new ViewFactory<AnalogAudioView>()},
//{"btle", "BTLE", RX, Color::yellow(), &bitmap_icon_btle, new ViewFactory<BTLERxView>()},
//{"blecomm", "BLE Comm", RX, ui::Color::orange(), &bitmap_icon_btle, new ViewFactory<BLECommView>()},
{"blerx", "BLE Rx", RX, Color::green(), &bitmap_icon_btle, new ViewFactory<BLERxView>()},
{"ert", "ERT Meter", RX, Color::green(), &bitmap_icon_ert, new ViewFactory<ERTAppView>()},
@@ -171,10 +146,9 @@ const NavigationView::AppList NavigationView::appList = {
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
{"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
//{"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory<TPMSAppView>()},
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
//{"fskrx", "FSK RX", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<FskxRxMainView>()},
//{"fskrx", "FSK RX", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<FskxRxMainView>()}, //for JT
//{"dmr", "DMR", RX, Color::dark_grey(), &bitmap_icon_dmr, new ViewFactory<NotImplementedView>()},
//{"sigfox", "SIGFOX", RX, Color::dark_grey(), &bitmap_icon_fox, new ViewFactory<NotImplementedView>()},
//{"lora", "LoRa", RX, Color::dark_grey(), &bitmap_icon_lora, new ViewFactory<NotImplementedView>()},
@@ -185,13 +159,10 @@ const NavigationView::AppList NavigationView::appList = {
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},
{"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory<BHTView>()},
{"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory<BLETxView>()},
//{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory<MorseView>()}, //moved to ext
//{"nuoptixdtmf", "Nuoptix DTMF", TX, ui::Color::green(), &bitmap_icon_nuoptix, new ViewFactory<NuoptixView>()},
{"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory<EncodersView>()},
{"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGTXView>()},
{"rdstx", "RDS", TX, ui::Color::green(), &bitmap_icon_rds, new ViewFactory<RDSView>()},
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
//{"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory<SSTVTXView>()}, //moved to ext
{"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory<TouchTunesView>()},
/* UTILITIES *************************************************************/
{"antennalength", "Antenna Length", UTILITIES, Color::green(), &bitmap_icon_tools_antenna, new ViewFactory<WhipCalcView>()},
@@ -202,7 +173,7 @@ const NavigationView::AppList NavigationView::appList = {
{nullptr, "SD Over USB", UTILITIES, Color::yellow(), &bitmap_icon_hackrf, new ViewFactory<SdOverUsbView>()},
{"signalgen", "Signal Gen", UTILITIES, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
//{"testapp", "Test App", UTILITIES, Color::dark_grey(), nullptr, new ViewFactory<TestView>()},
//{"tonesearch", "Tone Search", UTILITIES, Color::dark_grey(), nullptr, new ViewFactory<ToneSearchView>()},
{"wavview", "Wav View", UTILITIES, Color::yellow(), &bitmap_icon_soundboard, new ViewFactory<ViewWavView>()},
// Dangerous apps.
{nullptr, "Flash Utility", UTILITIES, Color::red(), &bitmap_icon_temperature, new ViewFactory<FlashUtilityView>()},
@@ -388,7 +359,6 @@ void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
if (!batt_was_inited) {
batt_was_inited = true;
refresh();
return;
}
if (!pmem::ui_hide_numeric_battery()) {
battery_text.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
@@ -416,14 +386,11 @@ void SystemStatusView::refresh() {
if (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
uint8_t percent = battery::BatteryManagement::getPercent();
if (!pmem::ui_hide_battery_icon()) {
status_icons.add(&battery_icon);
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false); // got an on select, that may pop up the details of the battery.
};
if (!pmem::ui_hide_numeric_battery()) {
status_icons.add(&battery_text);
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false);
}
}
@@ -658,12 +625,10 @@ bool NavigationView::is_valid() const {
View* NavigationView::push_view(std::unique_ptr<View> new_view) {
free_view();
const auto p = new_view.get();
view_stack.emplace_back(ViewState{std::move(new_view), {}});
update_view();
return p;
}
@@ -722,8 +687,8 @@ void NavigationView::update_view() {
auto top_view = top.view.get();
add_child(top_view);
top_view->set_parent_rect({{0, 0}, size()});
auto newSize = (is_top()) ? Size{size().width(), size().height() - 16} : size(); // if top(), then there is the info bar at the bottom, so leave space for it
top_view->set_parent_rect({{0, 0}, newSize});
focus();
set_dirty();
@@ -758,15 +723,40 @@ void NavigationView::handle_autostart() {
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
if (!autostart_app.empty()) {
if (StartAppByName(autostart_app.c_str())) return;
// if returned false, check for external apps by that name, and try to start it
std::string appwithpath = "/" + apps_dir.string() + "/";
appwithpath += autostart_app;
appwithpath += ".ppma";
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
std::filesystem::path pth = conv.from_bytes(appwithpath.c_str());
ui::ExternalItemsMenuLoader::run_external_app(*this, pth);
}
bool started = false;
// inner app
if (StartAppByName(autostart_app.c_str())) {
started = true;
}
if (!started) {
// ppma
std::string appwithpath = "/" + apps_dir.string() + "/" + autostart_app + ".ppma";
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
std::filesystem::path pth = conv.from_bytes(appwithpath.c_str());
if (ui::ExternalItemsMenuLoader::run_external_app(*this, pth)) {
started = true;
}
if (!started) {
// ppmp / standalone
appwithpath = "/" + apps_dir.string() + "/" + autostart_app + ".ppmp";
pth = conv.from_bytes(appwithpath.c_str());
if (ui::ExternalItemsMenuLoader::run_standalone_app(*this, pth)) {
started = true;
}
}
}
if (!started) {
display_modal(
"Notice", "Autostart failed:\n" +
autostart_app +
"\nupdate sdcard content\n" +
"and check if .ppma exists");
}
} // autostart end
return;
}
/* Helpers **************************************************************/
@@ -775,7 +765,9 @@ static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc)
for (auto& app : NavigationView::appList) {
if (app.menuLocation == loc) {
grid.add_item({app.displayName, app.iconColor, app.icon,
[&nav, &app]() { nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }});
[&nav, &app]() {
i2cdev::I2CDevManager::set_autoscan_interval(0); //if i navigate away from any menu, turn off autoscan
nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }});
}
};
}
@@ -872,7 +864,6 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
void SystemMenuView::on_populate() {
add_apps(nav_, *this, HOME);
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
}
@@ -912,6 +903,7 @@ SystemView::SystemView(
} else {
add_child(&info_view);
info_view.refresh();
i2cdev::I2CDevManager::set_autoscan_interval(3); // turn on autoscan in sysmainv
}
this->status_view.set_back_enabled(!this->navigation_view.is_top());
@@ -996,17 +988,46 @@ void BMPView::focus() {
button_done.focus();
}
BMPView::BMPView(NavigationView& nav) {
BMPView::BMPView(NavigationView& nav)
: nav_(nav) {
add_children({&button_done});
button_done.on_select = [this, &nav](Button&) {
nav.pop();
button_done.on_select = [this](Button&) {
handle_pop();
};
}
void BMPView::paint(Painter&) {
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, true);
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
}
bool BMPView::on_touch(const TouchEvent event) {
/* the event thing were resolved by HTotoo, talked here https://discord.com/channels/719669764804444213/956561375155589192/1287756910950486027
* the touch screen policy can be better, talked here https://discord.com/channels/719669764804444213/956561375155589192/1198926225897443328
* this workaround discussed here: https://discord.com/channels/719669764804444213/1170738202924044338/1295630640158478418
*/
if (!nav_.is_valid()) {
return false;
}
switch (event.type) {
case TouchEvent::Type::Start:
handle_pop();
return false;
default:
break;
}
return false;
}
void BMPView::handle_pop() {
if (nav_.is_valid()) {
nav_.pop();
}
}
/* NotImplementedView ****************************************************/
@@ -1072,7 +1093,7 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter& painter) {
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, true);
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
// Break lines.
auto lines = split_string(message_, '\n');
+5
View File
@@ -50,6 +50,7 @@
#include "sd_card.hpp"
#include "external_app.hpp"
#include "view_factory.hpp"
#include "battery.hpp"
// for incrementing fake date when RTC battery is dead
#define DATE_FILEFLAG u"/SETTINGS/DATE_FILEFLAG"
@@ -343,7 +344,11 @@ class BMPView : public View {
void paint(Painter&) override;
void focus() override;
bool on_touch(const TouchEvent event) override;
void handle_pop();
private:
NavigationView& nav_;
Button button_done{
{240, 0, 1, 1},
""};
-82
View File
@@ -1,82 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 "ui_playdead.hpp"
#include "portapack_persistent_memory.hpp"
#include "string_format.hpp"
using namespace portapack;
namespace ui {
void PlayDeadView::focus() {
button_seq_entry.focus();
}
void PlayDeadView::paint(Painter& painter) {
if (persistent_memory::config_login()) {
// Blank the whole display
painter.fill_rectangle(
display.screen_rect(),
style().background);
}
}
PlayDeadView::PlayDeadView(NavigationView& nav) {
rtc::RTC datetime;
persistent_memory::set_playing_dead(0x5920C1DF); // Enable
add_children({
&text_playdead1,
&text_playdead2,
&text_playdead3,
&button_seq_entry,
});
// Seed from RTC
rtcGetTime(&RTCD1, &datetime);
text_playdead2.set("0x" + to_string_hex(lfsr_iterate(datetime.second()), 6) + "00");
text_playdead3.hidden(true);
button_seq_entry.on_dir = [this](Button&, KeyEvent key) {
sequence = (sequence << 3) | (static_cast<std::underlying_type<KeyEvent>::type>(key) + 1);
return true;
};
button_seq_entry.on_select = [this, &nav](Button&) {
if (sequence == persistent_memory::playdead_sequence()) {
persistent_memory::set_playing_dead(0x82175E23); // Disable
if (persistent_memory::config_login()) {
text_playdead3.hidden(false);
} else {
nav.pop();
nav.push<SystemMenuView>();
}
} else {
sequence = 0;
}
};
}
} /* namespace ui */
-60
View File
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* 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 __UI_PLAYDEAD_H__
#define __UI_PLAYDEAD_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
namespace ui {
class PlayDeadView : public View {
public:
PlayDeadView(NavigationView& nav);
void focus() override;
void paint(Painter& painter) override;
private:
uint32_t sequence = 0;
Text text_playdead1{
{6 * 8, 7 * 16, 14 * 8, 16},
"\x46irmwa"
"re "
"er\x72o\x72"};
Text text_playdead2{
{6 * 8, 9 * 16, 16 * 8, 16},
""};
Text text_playdead3{
{6 * 8, 12 * 16, 16 * 8, 16},
"Please reset"};
Button button_seq_entry{
{240, 0, 1, 1},
""};
};
} /* namespace ui */
#endif /*__UI_PLAYDEAD_H__*/
+7 -2
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_H
#define __USB_SERIAL_H
#include "ch.h"
#include "hal.h"
@@ -37,6 +38,8 @@ class USBSerial {
void on_channel_closed();
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
bool serial_connected() { return connected; }
private:
void enable_xtal();
void disable_pll0();
@@ -51,4 +54,6 @@ class USBSerial {
EventDispatcher* _eventDispatcher = NULL;
};
} // namespace portapack
} // namespace portapack
#endif
+16 -15
View File
@@ -23,6 +23,7 @@
*/
#include "usb_serial_asyncmsg.hpp"
#include "usb_serial.hpp"
/// value
// to_string_bin/ to_string_decimal/ to_string_hex/ to_string_hex_array/ to_string_dec_uint/ to_string_dec_int etc seems usellss so i didn't add them here
@@ -30,7 +31,7 @@
template <>
void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -38,7 +39,7 @@ void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -46,7 +47,7 @@ void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -54,7 +55,7 @@ void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -62,7 +63,7 @@ void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -70,7 +71,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -78,7 +79,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -86,7 +87,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -94,7 +95,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<float>(const float& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_decimal(data, 7).c_str());
@@ -105,7 +106,7 @@ void UsbSerialAsyncmsg::asyncmsg<float>(const float& data) {
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(path);
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::path& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
std::string path_str = data.string();
@@ -114,7 +115,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::p
template <>
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path::string_type>(const std::filesystem::path::string_type& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
std::string str_data(data.begin(), data.end());
@@ -127,7 +128,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path::string_type>(const std::
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(str);
void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data.c_str());
@@ -136,7 +137,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
// string literal AKA char[]
// usage: UsbSerialAsyncmsg::asyncmsg("abc");
void UsbSerialAsyncmsg::asyncmsg(const char* data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data);
@@ -146,8 +147,8 @@ void UsbSerialAsyncmsg::asyncmsg(const char* data) {
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(true);
void UsbSerialAsyncmsg::asyncmsg<bool>(const bool& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data ? "true" : "false");
}
}
+3 -5
View File
@@ -47,10 +47,8 @@ class UsbSerialAsyncmsg {
};
/*Notes:
* - Don't use MayhemHub since it currently not support real time serial output
* - If you don't use this class linker will drop it so it won't use any space
* - so delete all debug things before you push your code to production
* - use this client to filter only PP devices: https://github.com/zxkmm/Pyserial-Demo-portapack
* - Don't use MayhemHub since it's currently not supporting real time serial output (it's hiding some instructions to support some features like screenshot, and it's filtering out answers by response type)
* - use this client to debug with serial: https://github.com/portapack-mayhem/mayhem-cli
* - usage:
* portapack::async_tx_enabled = true; // note that use this when debugging, unless the msg would be forbidden. but don't use this in production, since it's not real async and multiple serial transmittions will broken each other. if this class is used in other scene in the future, just use command to cover (protect your serial tramsnitton) in your extern thing: asyncmsg enable --- your cmd --- asyncmsg disable
* #include "usb_serial_asyncmsg.hpp"
@@ -61,7 +59,7 @@ class UsbSerialAsyncmsg {
// ussgae: UsbSerialAsyncmsg::asyncmsg(vec);
template <typename VECTORCOVER>
void UsbSerialAsyncmsg::asyncmsg(const std::vector<VECTORCOVER>& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
for (const auto& item : data) {
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_CDC_H
#define __USB_SERIAL_CDC_H
#include "ch.h"
#include "hal.h"
@@ -44,3 +45,5 @@ usb_request_status_t usb_set_line_coding_request(usb_endpoint_t* const endpoint,
#endif
void setup_usb_serial_controller(void);
#endif
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_DESCRIPTOR_H
#define __USB_SERIAL_DESCRIPTOR_H
#include "ch.h"
#include "hal.h"
@@ -42,3 +43,5 @@ extern uint8_t* usb_descriptor_strings[];
#define USB_WCID_VENDOR_REQ 0x19
extern uint8_t wcid_string_descriptor[];
extern uint8_t wcid_feature_descriptor[];
#endif
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_DEVICE_TO_HOST_H
#define __USB_SERIAL_DEVICE_TO_HOST_H
#include "ch.h"
#include "hal.h"
@@ -56,3 +57,5 @@ size_t fillOBuffer(OutputQueue* oqp, const uint8_t* bp, size_t n);
#ifdef __cplusplus
}
#endif
#endif
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_ENDPOINTS_H
#define __USB_SERIAL_ENDPOINTS_H
#include "ch.h"
#include "hal.h"
@@ -49,3 +50,5 @@ extern USB_DECLARE_QUEUE(usb_endpoint_bulk_in);
extern usb_endpoint_t usb_endpoint_bulk_out;
extern USB_DECLARE_QUEUE(usb_endpoint_bulk_out);
#endif
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_EVENT_H
#define __USB_SERIAL_EVENT_H
#include "ch.h"
#include "hal.h"
@@ -32,3 +33,5 @@ void on_channel_closed(void);
#ifdef __cplusplus
}
#endif
#endif
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_HOST_TO_DEVICE_H
#define __USB_SERIAL_HOST_TO_DEVICE_H
#include "ch.h"
#include "hal.h"
@@ -31,3 +32,5 @@ void reset_transfer_queues();
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
void schedule_host_to_device_transfer();
void complete_host_to_device_transfer();
#endif
+24 -6
View File
@@ -939,7 +939,7 @@ static void cmd_gotorientation(BaseSequentialStream* chp, int argc, char* argv[]
}
static void cmd_gotenv(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: gotenv <temperature> [humidity] [pressure] [light]\r\n";
const char* usage = "usage: gotenv <temperature> [humidity] [pressure] [light]\r\n"; // keeping light here too for compatibility
if (argc < 1 || argc > 4) {
chprintf(chp, usage);
return;
@@ -950,8 +950,25 @@ static void cmd_gotenv(BaseSequentialStream* chp, int argc, char* argv[]) {
uint16_t light = 0;
if (argc > 1) humi = atof(argv[1]);
if (argc > 2) pressure = atof(argv[2]);
if (argc > 3) light = strtol(argv[3], NULL, 10);
EnvironmentDataMessage msg{temp, humi, pressure, light};
if (argc > 3) light = strtol(argv[0], NULL, 10);
EnvironmentDataMessage msg{temp, humi, pressure};
EventDispatcher::send_message(msg);
// compatibility:
if (argc > 3) {
LightDataMessage msg{light};
EventDispatcher::send_message(msg);
}
chprintf(chp, "ok\r\n");
}
static void cmd_gotlight(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: gotlight <light_lux>\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
uint16_t light = strtol(argv[0], NULL, 10);
LightDataMessage msg{light};
EventDispatcher::send_message(msg);
chprintf(chp, "ok\r\n");
}
@@ -1062,8 +1079,8 @@ static void cmd_sendpocsag(BaseSequentialStream* chp, int argc, char* argv[]) {
}
uint64_t addr = atol(argv[0]);
int msglen = atoi(argv[1]); // without minimum limit, since addr only don't send anything
if (msglen > 30 || msglen < 0) {
chprintf(chp, "error, msglen max is 30\r\n");
if (msglen > 80 || msglen < 0) {
chprintf(chp, "error, msglen max is 80\r\n");
return;
}
@@ -1103,7 +1120,7 @@ static void cmd_sendpocsag(BaseSequentialStream* chp, int argc, char* argv[]) {
}
}
uint8_t msg[31] = {0};
uint8_t msg[81] = {0};
if (msglen > 0) {
chprintf(chp, "send %d bytes\r\n", msglen);
do {
@@ -1191,6 +1208,7 @@ static const ShellCommand commands[] = {
{"gotgps", cmd_gotgps},
{"gotorientation", cmd_gotorientation},
{"gotenv", cmd_gotenv},
{"gotlight", cmd_gotlight},
{"sysinfo", cmd_sysinfo},
{"radioinfo", cmd_radioinfo},
{"pmemreset", cmd_pmemreset},
+4 -1
View File
@@ -19,7 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifndef __USB_SERIAL_SHELL_H
#define __USB_SERIAL_SHELL_H
#include "ch.h"
#include "hal.h"
@@ -29,3 +30,5 @@
class EventDispatcher;
void create_shell(EventDispatcher* evtd);
#endif
+9 -19
View File
@@ -314,12 +314,6 @@ endmacro()
set(add_to_firmware TRUE)
### ACARS RX
set(MODE_CPPSRC
proc_acars.cpp
)
DeclareTargets(PACA acars)
### ADS-B RX
@@ -430,13 +424,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PNFM nfm_audio)
#### No op
#
#set(MODE_CPPSRC
# proc_noop.cpp
#)
#DeclareTargets(PNOP no_operation)
### OOK
set(MODE_CPPSRC
@@ -444,12 +431,6 @@ set(MODE_CPPSRC
)
DeclareTargets(POOK ook)
#### POCSAG RX
#
#set(MODE_CPPSRC
# proc_pocsag.cpp
#)
#DeclareTargets(PPOC pocsag)
### POCSAG2 RX
@@ -579,6 +560,15 @@ DeclareTargets(PUSB sd_over_usb)
### Place external app and disabled images below so they don't get added to the firmware
set(add_to_firmware FALSE)
### ACARS RX
set(MODE_CPPSRC
proc_acars.cpp
)
DeclareTargets(PACA acars)
### AFSK RX
set(MODE_CPPSRC
+197
View File
@@ -0,0 +1,197 @@
#ifndef __FPROTO_Bresser3ch_H__
#define __FPROTO_Bresser3ch_H__
#include "weatherbase.hpp"
#define BRESSER_V0_DATA 36
#define BRESSER_V0_DATA_AND_TAIL 52
#define BRESSER_V1_DATA 40
typedef enum {
Bresser3chDecoderStepReset = 0,
Bresser3chDecoderStepV0SaveDuration,
Bresser3chDecoderStepV0CheckDuration,
Bresser3chDecoderStepV0TailCheckDuration,
Bresser3chDecoderStepV1PreambleDn,
Bresser3chDecoderStepV1PreambleUp,
Bresser3chDecoderStepV1SaveDuration,
Bresser3chDecoderStepV1CheckDuration,
} Bresser3chDecoderStepV1;
class FProtoWeatheBresser3CH : public FProtoWeatherBase {
public:
FProtoWeatheBresser3CH() {
sensorType = FPW_Bresser3CH;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Bresser3chDecoderStepReset:
if (level && DURATION_DIFF(duration, te_short * 3) < te_delta) {
te_last = duration;
parser_step = Bresser3chDecoderStepV1PreambleDn;
decode_data = 0;
decode_count_bit = 0;
} else if ((!level) && duration >= te_long) {
parser_step = Bresser3chDecoderStepV0SaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case Bresser3chDecoderStepV0SaveDuration:
if (level) {
te_last = duration;
if (decode_count_bit < BRESSER_V0_DATA) {
parser_step = Bresser3chDecoderStepV0CheckDuration;
} else {
parser_step = Bresser3chDecoderStepV0TailCheckDuration;
}
} else {
parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepV0CheckDuration:
if (!level) {
if (DURATION_DIFF(te_last, te_short) < te_delta) {
if (DURATION_DIFF(duration, te_short * 2) < te_delta) {
subghz_protocol_blocks_add_bit(0);
parser_step = Bresser3chDecoderStepV0SaveDuration;
} else if (
DURATION_DIFF(duration, te_short * 4) < te_delta) {
subghz_protocol_blocks_add_bit(1);
parser_step = Bresser3chDecoderStepV0SaveDuration;
} else
parser_step = Bresser3chDecoderStepReset;
} else
parser_step = Bresser3chDecoderStepReset;
} else
parser_step = Bresser3chDecoderStepReset;
break;
case Bresser3chDecoderStepV0TailCheckDuration:
if (!level) {
if (duration >= te_long) {
if (decode_count_bit == BRESSER_V0_DATA_AND_TAIL &&
ws_protocol_bresser_3ch_check_v0()) {
decode_count_bit = BRESSER_V0_DATA;
sensorType = FPW_Bresser3CH;
// ws_protocol_bresser_3ch_extract_data_v0();
if (callback) {
callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Bresser3chDecoderStepReset;
} else if (
decode_count_bit < BRESSER_V0_DATA_AND_TAIL &&
DURATION_DIFF(te_last, te_short) < te_delta &&
DURATION_DIFF(duration, te_short * 2) < te_delta) {
decode_count_bit++;
parser_step = Bresser3chDecoderStepV0SaveDuration;
} else
parser_step = Bresser3chDecoderStepReset;
} else
parser_step = Bresser3chDecoderStepReset;
break;
case Bresser3chDecoderStepV1PreambleDn:
if ((!level) && DURATION_DIFF(duration, te_short_v1 * 3) < te_delta) {
if (DURATION_DIFF(te_last, te_short_v1 * 12) < te_delta * 2) {
// End of sync after 4*750 (12*250) high values, start reading the message
parser_step = Bresser3chDecoderStepV1SaveDuration;
} else {
parser_step = Bresser3chDecoderStepV1PreambleUp;
}
} else {
parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepV1PreambleUp:
if (level && DURATION_DIFF(duration, te_short_v1 * 3) < te_delta) {
te_last = te_last + duration;
parser_step = Bresser3chDecoderStepV1PreambleDn;
} else {
parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepV1SaveDuration:
if (decode_count_bit == BRESSER_V1_DATA) {
if (ws_protocol_bresser_3ch_check_v1()) {
// ws_protocol_bresser_3ch_extract_data_v1(&instance->generic);
sensorType = FPW_Bresser3CH_V1;
if (callback) {
callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Bresser3chDecoderStepReset;
} else if (level) {
te_last = duration;
parser_step = Bresser3chDecoderStepV1CheckDuration;
} else {
parser_step = Bresser3chDecoderStepReset;
}
break;
case Bresser3chDecoderStepV1CheckDuration:
if (!level) {
if (DURATION_DIFF(te_last, te_short_v1) < te_delta && DURATION_DIFF(duration, te_long_v1) < te_delta) {
subghz_protocol_blocks_add_bit(0);
parser_step = Bresser3chDecoderStepV1SaveDuration;
} else if (
DURATION_DIFF(te_last, te_long_v1) < te_delta && DURATION_DIFF(duration, te_short_v1) < te_delta) {
subghz_protocol_blocks_add_bit(1);
parser_step = Bresser3chDecoderStepV1SaveDuration;
} else
parser_step = Bresser3chDecoderStepReset;
} else
parser_step = Bresser3chDecoderStepReset;
break;
}
}
protected:
uint32_t te_short = 475;
uint32_t te_long = 3900;
uint32_t te_delta = 150;
uint32_t te_short_v1 = 250;
uint32_t te_long_v1 = 500;
bool ws_protocol_bresser_3ch_check_v0() {
if (!decode_data) return false;
// No CRC, so better sanity checks here
if (((decode_data >> 8) & 0x0f) != 0x0f) return false; // separator not 0xf
if (((decode_data >> 28) & 0xff) == 0xff) return false; // ID only ones?
if (((decode_data >> 28) & 0xff) == 0x00) return false; // ID only zeroes?
if (((decode_data >> 25) & 0x0f) == 0x0f) return false; // flags only ones?
if (((decode_data >> 25) & 0x0f) == 0x00) return false; // flags only zeroes?
if (((decode_data >> 12) & 0x0fff) == 0x0fff)
return false; // temperature maxed out?
if ((decode_data & 0xff) < 20)
return false; // humidity percentage less than 20?
if ((decode_data & 0xff) > 95)
return false; // humidity percentage more than 95?
return true;
}
bool ws_protocol_bresser_3ch_check_v1() {
if (!decode_data) return false;
uint8_t sum = (((decode_data >> 32) & 0xff) +
((decode_data >> 24) & 0xff) +
((decode_data >> 16) & 0xff) +
((decode_data >> 8) & 0xff)) &
0xff;
return (decode_data & 0xff) == sum;
}
};
#endif
@@ -0,0 +1,88 @@
#ifndef __FPROTO_VAUNO_EN8822_H__
#define __FPROTO_VAUNO_EN8822_H__
#include "weatherbase.hpp"
typedef enum {
VaunoEN8822CDecoderStepReset = 0,
VaunoEN8822CDecoderStepSaveDuration,
VaunoEN8822CDecoderStepCheckDuration,
} VaunoEN8822CDecoderStep;
class FProtoWeatherVaunoEN8822 : public FProtoWeatherBase {
public:
FProtoWeatherVaunoEN8822() {
sensorType = FPW_Vauno_EN8822;
}
void feed(bool level, uint32_t duration) override {
switch (parser_step) {
case VaunoEN8822CDecoderStepReset:
if ((!level) && DURATION_DIFF(duration, te_long * 4) < te_delta) {
parser_step = VaunoEN8822CDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case VaunoEN8822CDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = VaunoEN8822CDecoderStepCheckDuration;
} else {
parser_step = VaunoEN8822CDecoderStepReset;
}
break;
case VaunoEN8822CDecoderStepCheckDuration:
if (!level) {
if (DURATION_DIFF(te_last, te_short) < te_delta) {
if (DURATION_DIFF(duration, te_long * 2) < te_delta) {
subghz_protocol_blocks_add_bit(1);
parser_step = VaunoEN8822CDecoderStepSaveDuration;
} else if (DURATION_DIFF(duration, te_long) < te_delta) {
subghz_protocol_blocks_add_bit(0);
parser_step = VaunoEN8822CDecoderStepSaveDuration;
} else if (DURATION_DIFF(duration, te_long * 4) < te_delta) {
parser_step = VaunoEN8822CDecoderStepReset;
if (decode_count_bit == min_count_bit_for_found && ws_protocol_vauno_en8822c_check()) {
// ws_protocol_vauno_en8822c_extract_data(&instance->generic);
if (callback) {
callback(this);
}
} else if (decode_count_bit == 1) {
parser_step = VaunoEN8822CDecoderStepSaveDuration;
}
decode_data = 0;
decode_count_bit = 0;
} else
parser_step = VaunoEN8822CDecoderStepReset;
} else
parser_step = VaunoEN8822CDecoderStepReset;
} else
parser_step = VaunoEN8822CDecoderStepReset;
break;
}
}
protected:
// timing values
uint32_t te_short = 500;
uint32_t te_long = 1940;
uint32_t te_delta = 150;
uint32_t min_count_bit_for_found = 42;
bool ws_protocol_vauno_en8822c_check() {
if (!decode_data) return false;
// The sum of all nibbles should match the last 6 bits
uint8_t sum = 0;
for (uint8_t i = 6; i <= 38; i += 4) {
sum += ((decode_data >> i) & 0x0f);
}
return sum != 0 && (sum & 0x3f) == (decode_data & 0x3f);
}
};
#endif
+6 -1
View File
@@ -29,6 +29,8 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include "w-acurite5in1.hpp"
#include "w-emose601x.hpp"
#include "w-solight_te44.hpp"
#include "w-bresser_3ch.hpp"
#include "w-vauno_en8822.hpp"
#include <vector>
#include <memory>
@@ -66,10 +68,13 @@ class WeatherProtos : public FProtoListGeneral {
protos[FPW_Acurite5in1] = new FProtoWeatherAcurite5in1();
protos[FPW_EmosE601x] = new FProtoWeatherEmosE601x();
protos[FPW_SolightTE44] = new FProtoWeatherSolightTE44();
protos[FPW_Bresser3CH] = new FProtoWeatheBresser3CH();
protos[FPW_Bresser3CH_V1] = nullptr; // done by FProtoWeatheBresser3CH
protos[FPW_Vauno_EN8822] = new FProtoWeatherVaunoEN8822();
// set callback for them
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
protos[i]->setCallback(callbackTarget);
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
}
}
@@ -39,6 +39,9 @@ enum FPROTO_WEATHER_SENSOR {
FPW_Acurite5in1 = 21,
FPW_EmosE601x = 22,
FPW_SolightTE44 = 23,
FPW_Bresser3CH = 24,
FPW_Bresser3CH_V1 = 25,
FPW_Vauno_EN8822 = 26,
FPW_COUNT // this must be the last
};
+143 -21
View File
@@ -20,18 +20,27 @@
* Boston, MA 02110-1301, USA.
*/
#include "proc_acars.hpp"
#include "portapack_shared_memory.hpp"
#include "proc_acars.hpp"
#include "dsp_fir_taps.hpp"
#include "audio_dma.hpp"
#include "event_m4.hpp"
#define SYN 0x16
#define SOH 0x01
#define STX 0x02
#define ETX 0x83
#define ETB 0x97
#define DLE 0x7f
ACARSProcessor::ACARSProcessor() {
audio::dma::init_audio_out();
decim_0.configure(taps_11k0_decim_0.taps);
decim_1.configure(taps_11k0_decim_1.taps);
packet.clear();
decode_data = 0;
decode_count_bit = 0;
audio_output.configure(false);
baseband_thread.start();
}
@@ -45,6 +54,9 @@ void ACARSProcessor::execute(const buffer_c8_t& buffer) {
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
auto audio = demod.execute(decimator_out, audio_buffer);
audio_output.write(audio);
for (size_t i = 0; i < decimator_out.count; i++) {
if (mf.execute_once(decimator_out.p[i])) {
clock_recovery(mf.get_output());
@@ -52,24 +64,134 @@ void ACARSProcessor::execute(const buffer_c8_t& buffer) {
}
}
void ACARSProcessor::consume_symbol(
const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
// const auto decoded_symbol = acars_decode(sliced_symbol);
// DEBUG
packet.add(sliced_symbol);
if (packet.size() == 256) {
payload_handler(packet);
packet.clear();
}
// packet_builder.execute(decoded_symbol);
void ACARSProcessor::add_bit(uint8_t bit) {
decode_data = decode_data << 1 | bit;
decode_count_bit++;
}
void ACARSProcessor::payload_handler(
const baseband::Packet& packet) {
const ACARSPacketMessage message{packet};
uint16_t ACARSProcessor::update_crc(uint8_t dataByte) {
(void)dataByte;
return 0;
}
void ACARSProcessor::sendDebug() {
// if (curr_state <= 1) return;
message.state = curr_state;
shared_memory.application_queue.push(message);
}
void ACARSProcessor::reset() {
decode_data = 0;
decode_count_bit = 0;
curr_state = WSYN;
message.msg_len = 0;
memset(message.message, 0, 250);
message.crc[0] = 0;
message.crc[1] = 0;
parity_errors = 0;
}
void ACARSProcessor::consume_symbol(const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
add_bit(sliced_symbol);
if (curr_state == WSYN && decode_count_bit == 8) {
if ((decode_data & 0xff) == SYN) {
curr_state = SYN2;
decode_data = 0;
decode_count_bit = 0;
} else {
decode_count_bit -= 1; // just drop the first bit
}
return;
}
if (curr_state == SYN2 && decode_count_bit == 8) {
if ((decode_data & 0xff) == SYN) {
curr_state = SOH1;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
return;
}
// here i don't have the right packets. so reset
reset();
}
if (curr_state == SOH1 && decode_count_bit == 8) {
if ((decode_data & 0xff) == SOH) {
reset();
curr_state = TXT;
sendDebug();
return;
}
message.message[0] = (decode_data & 0xff); // debug
reset();
sendDebug();
}
if (curr_state == TXT && decode_count_bit == 8) {
uint8_t ch = (decode_data & 0xff);
message.message[message.msg_len++] = ch;
if (!ParityCheck::parity_check(ch)) {
// parity error
parity_errors++;
if (parity_errors > 4) {
reset(); // too many parity errors, skip packet
sendDebug();
return;
}
}
if (ch == ETX || ch == ETB) {
curr_state = CRC1;
sendDebug();
decode_data = 0;
decode_count_bit = 0;
return;
}
if (message.msg_len > 240) {
reset();
sendDebug();
}
if (message.msg_len > 20 && ch == DLE) {
message.msg_len -= 3;
message.crc[0] = message.message[message.msg_len];
message.crc[1] = message.message[message.msg_len + 1];
curr_state = CRC2;
sendDebug();
// to hack the path:
decode_data = message.crc[1];
} else {
decode_count_bit = 0;
decode_data = 0;
return;
}
}
if (curr_state == CRC1 && decode_count_bit == 8) {
message.crc[0] = (decode_data & 0xff);
curr_state = CRC2;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
}
if (curr_state == CRC2 && decode_count_bit == 8) {
message.crc[1] = (decode_data & 0xff);
// send it to app cpu, and it'll take care of the rest
payload_handler();
reset();
curr_state = END;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
}
if (curr_state == END && decode_count_bit == 8) {
reset();
sendDebug();
}
}
void ACARSProcessor::payload_handler() {
message.state = 255; // to indicate this is an actual payload, not a debug packet
shared_memory.application_queue.push(message);
}
@@ -77,4 +199,4 @@ int main() {
EventDispatcher event_dispatcher{std::make_unique<ACARSProcessor>()};
event_dispatcher.run();
return 0;
}
}

Some files were not shown because too many files have changed in this diff Show More