Compare commits

...

40 Commits

Author SHA1 Message Date
sommermorgentraum fcdccdea85 add cursor to font viewer app (#2528) 2025-02-22 13:37:06 +01:00
sommermorgentraum 5e55444f19 Support Bug Key AKA Auto Key for OOK Editor app (#2523)
* _

* some final tune

* textual

* rename following gull's suggestion
2025-02-19 13:51:09 +08:00
sommermorgentraum 73f7f84718 Playlist editor (#2506)
* make both exist
* format
* fix focusing issue
* add example hopper payload
* fix compiler err
* clean up
* correct linker script addr
* lint
* PoC
* unknown: write_line issue
* clean up
* merge
* fix read line
* remove debug code
* fix english
* support new file
* support enter delay
* fix crash
* remove debug code
* some final tune
2025-02-18 22:05:40 +01:00
sommermorgentraum 7ad4ad99dd Add widget preview tool (#2520)
* PoC

* opt

* opt
2025-02-16 23:42:06 +08:00
sommermorgentraum 200f10397b Add new app "hopper" app. (#2482)
* make both jammer and hopper exist
* add example hopper payload
* example files
* swap scanner and recon app location
2025-02-14 20:10:01 +01:00
E.T. 9b352f45a2 fix docker build warnings in dockerfile-nogit (#2518) 2025-02-13 07:29:20 +01:00
Lerold 51c445054e Moved country-specific FREQMAN files to the separate repository set up for it. (#2517) 2025-02-10 11:02:55 +01:00
RocketGod 02586101e6 Add modal to turn off screen when charging is detected (#2514) 2025-02-08 22:09:40 +01:00
Gaurav Chaturvedi b66d8b1aa1 Removing vim swap files (#2503)
* Removing vim swap files
* Added vim swap file to .gitignore
2025-01-28 16:31:11 +01:00
gullradriel 8dc3851b55 externalize wipe sdcard (#2502) 2025-01-27 22:10:33 +01:00
gullradriel aef7c2be93 fix baseband (#2501) 2025-01-26 22:23:29 +01:00
gullradriel 4e38dc4d3e put back app in 'Utilities' (#2500)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-01-26 22:15:02 +01:00
Davide Rovelli 520ad97f78 Added different modulations in signal generator (#2492)
* Added DSB, AM 100% mod index and AM 50% mod index. Changed UI.
2025-01-26 21:41:11 +01:00
Lucas C. Villa Real 5bd208bca1 APRS: add frequency settings for Brazil (#2494)
* Add frequency settings for Brazil, named 'BR' that tunes to 145.570 MHz.
* Also added: Japan: 144.640 MHz (JAP), Thailand: 144.900 MHz (THA), Philippines: 144.740 MHz (PHI)
* Reordered list by increasing frequency
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2025-01-26 21:39:38 +01:00
gullradriel a65ef3ce2e Externalize antenna calc and wav view (#2498)
* externalize antenna calc and wav view
* Added a tool to check if all the pictures in graphics are used in internal apps
2025-01-26 21:36:21 +01:00
gullradriel b2bb37af74 Flipper tx: use file_path, example file (#2496)
* added subghz_dir
* use subghz_dir from file_path
* example file
2025-01-26 17:46:43 +00:00
gullradriel 54ad82a423 fix fallthrough warning (#2497) 2025-01-26 16:27:29 +01:00
gullradriel e463405c77 fix cmake_minimum_required to 3.16 (#2499) 2025-01-26 16:26:02 +01:00
sommermorgentraum c413f925a9 Add fast flash script for sdcard switch hardware (#2480) 2025-01-20 12:43:57 +01:00
Erwin Ried 9e1eb31150 Update README.md
Discord badge was broken, switching to shields.io
2025-01-20 09:22:40 +01:00
sommermorgentraum 7eb08f34cb Fix encoder setting p.mem issue (#2475) 2025-01-09 15:56:14 +01:00
Erwin Ried 2de1f8e6bf Update README.md
Lab401 link with our redirection
2025-01-09 10:05:27 +01:00
sommermorgentraum 55db0e8c87 Add the feature to decide rotate direction of encoder (#2472) 2025-01-08 21:02:05 +01:00
gullradriel dfa35b4290 added discord server information (#2471) 2025-01-07 14:56:08 +01:00
gullradriel 0124051c4f fix for empty text prompt crash (#2468) 2025-01-07 13:46:51 +01:00
gullradriel 65f301399f disabling button on main menu, change labels and add 'icons' (#2466)
* disabling button on main menu, change labels and add 'icons'

* fix reverse order of buttons

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-01-04 10:50:53 +08:00
sommermorgentraum 97889494cf rename bitmaps into bmp 2025-01-03 14:33:30 +01:00
Erwin Ried 32b35b1507 Update README.md 2025-01-02 15:27:13 +01:00
quantum-x fccff0484b Update README.md (#2456)
Added a link to Lab401.com as a purchase option for EU customers.
(Lab401 was added as the EU exclusive distributor for the H4M - https://opensourcesdrlab.com/pages/distributors)
2025-01-02 15:22:58 +01:00
OpenSourceSDRLab d94afe364b The gerber files of the portapack H4 (#2463)
* Create README.txt

* Update README.txt

* Add files via upload

The gerber files of the portapack h4.

* delete

* Upload the gerber files for H4
2025-01-02 09:31:28 +13:00
Erwin Ried 2e4db3dd27 Update README.md
New metal case link
2024-12-31 22:39:35 +01:00
E.T. 97d3edbde7 docker improvements (#2455) 2024-12-31 11:27:38 +01:00
gullradriel 4c18b80e42 Navigation buttons (#2458)
* regenerate bitmap data
* pagination in submenu
* using little font so we are not eating menu buttons
2024-12-31 10:01:03 +08:00
Mark Thompson 01034af077 Renamed parameters in rx_capture.ini file (#2452)
* Rename settings in file to match screen
* Renamed variables for hopefully better clarity
2024-12-29 18:29:31 +01:00
Mark Thompson 57ce978bab Remember previous capture settings (#2450) 2024-12-29 09:21:08 +01:00
Totoo 854ac514cb M10 additional parser (#2448) 2024-12-28 12:47:58 +01:00
sommermorgentraum b47a5fe0c8 Add + - buttons in Encoder dial settings (#2447) 2024-12-27 22:04:11 +01:00
sommermorgentraum af362600ef app manager (#2442)
* fix unset autostart
* clean up - add comments to prevent misleading
* move the app to external and with necessary changes
* replace autostart app
2024-12-27 12:01:33 +01:00
hackrfstuff bad57d1391 Fake brightness reimplementation (#2444)
* Fake brightness reimplementation
* indentation
* added call to the function which is caching the display settings values
* use cached values instead of pmem
2024-12-26 23:23:02 +01:00
Erwin Ried ef28e63a8c Update README.md
Actually valid complete ali h4m link
2024-12-21 01:36:49 +01:00
103 changed files with 53795 additions and 931 deletions
+2 -1
View File
@@ -1 +1,2 @@
build
*
!firmware/tools/docker-entrypoint.sh
+1
View File
@@ -61,6 +61,7 @@ cmake-build-debug/
*.sublime-workspace
.vscode
.idea
*.swp
# VSCodium extensions
.history
+1 -1
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0005 NEW)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
+3 -3
View File
@@ -5,7 +5,7 @@
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.--->
# PortaPack Mayhem
[![Nightly Release](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases) [![GitHub Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/latest/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://dcbadge.vercel.app/api/server/tuwVMv3?style=flat)](https://discord.gg/tuwVMv3)
[![Nightly Release](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases) [![GitHub Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/latest/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://img.shields.io/discord/719669764804444213?label=Discord)](https://discord.gg/tuwVMv3)
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
@@ -26,11 +26,11 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
<!---not direct to h4m but to opensourcesdrlab https://share.hackrf.app/TUOLYI--->
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous H4M [complete](https://share.hackrf.app/TUOLYI) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous H4M [complete](https://share.hackrf.app/HR60Q4) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top). _EU customers_ can purchase via [Lab401](https://share.hackrf.app/0CI2CR).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://www.ebay.com/itm/116382397447), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://share.hackrf.app/LZPBH9), 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://share.hackrf.app/9FJFJE), 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:
+3 -3
View File
@@ -3,7 +3,7 @@ FROM ubuntu:noble
# Set location to download ARM toolkit from.
# This will need to be changed over time or replaced with a static link to the latest release.
ENV ARMBINURL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2?revision=108bd959-44bd-4619-9c19-26187abf5225&la=en&hash=E788CE92E5DFD64B2A8C246BBA91A249CB8E2D2D \
PATH=$HOME/bin:$PATH:/opt/build/armbin/bin
PATH=$PATH:/opt/build/armbin/bin
#Create volume /havoc/bin for compiled firmware binaries
VOLUME /havoc
@@ -15,8 +15,8 @@ RUN apt-get update \
&& apt-get -qy autoremove \
&& rm -rf /var/lib/apt/lists/*
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
# Grab the GNU ARM toolchain from arm.com
# Then extract contents to /opt/build/armbin/
Executable
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
#################################################
# This script aids building mayhem inside docker
#
# Basic usage:
# - Build dev container: ./dockerize.sh build
# - Build mayhem: ./dockerize.sh
#
# The image will be automatically build if it
# does not exist, but if the dockerfile changes
# it need to be rebuilt manually.
#
# Advanced parameters:
# - Get a shell inside the build image to
# inspect problems: ./dockerize.sh shell
# - Give additional parameters to the container:
# ./dockerize.sh -j10
# ./dockerize.sh ninja -j10
# - Use a different dockerfile:
# ./dockerize.sh build dockerfile-other
# - Use a different cpu architecture:
# ./dockerize.sh build dockerfile-nogit-arm arm64
#
# Environment variables:
# - It is possible to override the default image
# name that is being used to build and run the
# build container using an environment variable.
# The default is 'portapack-dev'
# Override by setting the following environment
# variable: MAYHEM_DEV_DOCKER_IMAGE
#
#
# Copyright (C) 2024 u-foka
#
# 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.
#
set -e # exit immediatelly on any failure
DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
IMAGE="${MAYHEM_DEV_DOCKER_IMAGE:-portapack-dev}"
build_image() {
DOCKERFILE=${1:-dockerfile-nogit}
PLATFORM=${2:-amd64}
docker build --platform "linux/${PLATFORM}" -t "${IMAGE}" -f "${DOCKERFILE}" .
}
start_docker() {
if [ -z "$(docker images -q ${IMAGE} 2> /dev/null)" ]; then
build_image
fi
exec docker run -v "${DIR}:/havoc" -u "$(id -u):$(id -g)" -ti --rm "${IMAGE}" "$@"
}
if [ "$1" = 'shell' ]; then # open a shell into the container
start_docker "bash -li"
elif [ "$1" = 'build' ]; then # build the default (or specified) target with ninja
shift # remove the first item from $@ as we consumed it, we can then pass the rest on to make
build_image "$@"
exit $?
fi
start_docker "$@"
+1 -1
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
project(firmware)
+1 -4
View File
@@ -305,7 +305,6 @@ set(CPPSRC
apps/ui_recon.cpp
apps/ui_scanner.cpp
apps/ui_sd_over_usb.cpp
apps/ui_sd_wipe.cpp
apps/ui_search.cpp
apps/ui_settings.cpp
apps/ui_siggen.cpp
@@ -317,9 +316,7 @@ set(CPPSRC
apps/ui_text_editor.cpp
apps/ui_touch_calibration.cpp
apps/ui_touchtunes.cpp
apps/ui_view_wav.cpp
apps/ui_weatherstation.cpp
apps/ui_whipcalc.cpp
protocols/aprs.cpp
protocols/ax25.cpp
protocols/bht.cpp
@@ -377,7 +374,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
hw
apps
protocols
bitmaps
bmps
)
#
+43 -16
View File
@@ -39,6 +39,14 @@ using namespace portapack;
using namespace modems;
namespace fs = std::filesystem;
#define BLE_RX_NO_ERROR 0
#define BLE_RX_LIST_FILENAME_EMPTY_ERROR 1
#define BLE_RX_ENTRY_FILENAME_EMPTY_ERROR 2
#define BLE_RX_LIST_SAVE_ERROR 3
#define BLE_RX_ENTRY_SAVE_ERROR 4
static uint8_t ble_rx_error = BLE_RX_NO_ERROR;
void BLELogger::log_raw_data(const std::string& data) {
log_file.write_entry(data);
}
@@ -202,19 +210,23 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
}
void BleRecentEntryDetailView::on_save_file(const std::string value, BLETxPacket packetToSave) {
ensure_directory(packet_save_path);
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
saveFile(new_path, packetToSave);
if (value.length() > 0) {
ensure_directory(packet_save_path);
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
ble_rx_error = saveFile(new_path, packetToSave);
} else {
nav_.pop();
ble_rx_error = BLE_RX_ENTRY_FILENAME_EMPTY_ERROR;
}
}
bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETxPacket packetToSave) {
File f;
auto error = f.create(path);
if (error)
return false;
return BLE_RX_ENTRY_SAVE_ERROR;
std::string macAddressStr = packetToSave.macAddress;
std::string advertisementDataStr = packetToSave.advertisementData;
@@ -224,7 +236,7 @@ bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETx
f.write(packetString.c_str(), packetString.length());
return true;
return BLE_RX_NO_ERROR;
}
void BleRecentEntryDetailView::update_data() {
@@ -568,11 +580,14 @@ std::string BLERxView::build_line_str(BleRecentEntry entry) {
}
void BLERxView::on_save_file(const std::string value) {
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
saveFile(new_path);
if (value.length() > 0) {
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
ble_rx_error = saveFile(new_path);
} else {
ble_rx_error = BLE_RX_LIST_FILENAME_EMPTY_ERROR;
}
}
bool BLERxView::saveFile(const std::filesystem::path& path) {
@@ -584,7 +599,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
auto error = src->open(path, false, true);
if (error) {
return false;
return BLE_RX_LIST_SAVE_ERROR;
}
for (const auto& entry : recent) {
@@ -615,7 +630,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
auto error = dst->open(tempFilePath, false, true);
if (error) {
return false;
return BLE_RX_LIST_SAVE_ERROR;
}
dst->write_line(headerStr.c_str());
@@ -695,7 +710,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
tempList.clear();
return true;
return BLE_RX_NO_ERROR;
}
void BLERxView::on_data(BlePacketData* packet) {
@@ -831,6 +846,18 @@ void BLERxView::on_timer() {
baseband::set_btlerx(randomChannel);
}
}
if (ble_rx_error != BLE_RX_NO_ERROR) {
if (ble_rx_error == BLE_RX_LIST_FILENAME_EMPTY_ERROR) {
nav_.display_modal("Error", "List filename is empty !");
} else if (ble_rx_error == BLE_RX_ENTRY_FILENAME_EMPTY_ERROR) {
nav_.display_modal("Error", "Entry filename is empty !");
} else if (ble_rx_error == BLE_RX_LIST_SAVE_ERROR) {
nav_.display_modal("Error", "Couldn't save list !");
} else if (ble_rx_error == BLE_RX_ENTRY_SAVE_ERROR) {
nav_.display_modal("Error", "Couldn't save entry !");
}
ble_rx_error = BLE_RX_NO_ERROR;
}
}
void BLERxView::handle_entries_sort(uint8_t index) {
+11 -9
View File
@@ -55,22 +55,24 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
this->field_frequency.set_step(v);
};
option_format.set_selected_index(0); // Default to C16
option_format.set_selected_index(file_format);
option_format.on_change = [this](size_t, uint32_t file_type) {
file_format = file_type;
record_view.set_file_type((RecordView::FileType)file_type);
};
check_trim.set_value(trim);
check_trim.on_select = [this](Checkbox&, bool v) {
trim = v;
record_view.set_auto_trim(v);
};
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
option_bandwidth.on_change = [this](size_t, uint32_t bandwidth) {
option_bandwidth.on_change = [this](size_t, uint32_t new_capture_rate) {
/* 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. */
auto sample_rate = bandwidth;
/* base_rate (bandwidth) is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* capture_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. */
@@ -78,7 +80,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
// 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);
auto actual_sample_rate = record_view.set_sampling_rate(new_capture_rate);
// Update the radio model with the actual sampling rate.
receiver_model.set_sampling_rate(actual_sample_rate);
@@ -88,19 +90,19 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
receiver_model.set_baseband_bandwidth(anti_alias_filter_bandwidth);
// Automatically switch default capture format to C8 when bandwidth setting is increased to >=1.5MHz anb back to C16 for <=1,25Mhz
if ((bandwidth >= 1500000) && (previous_bandwidth < 1500000)) {
if ((new_capture_rate >= 1500000) && (capture_rate < 1500000)) {
option_format.set_selected_index(1); // Default C8 format for REC, 1500K ... 5500k
}
if ((bandwidth <= 1250000) && (previous_bandwidth > 1250000)) {
if ((new_capture_rate <= 1250000) && (capture_rate > 1250000)) {
option_format.set_selected_index(0); // Default C16 format for REC , 12k5 ... 1250K
}
previous_bandwidth = bandwidth;
capture_rate = new_capture_rate;
waterfall.start();
};
receiver_model.enable();
option_bandwidth.set_by_value(500000);
option_bandwidth.set_by_value(capture_rate);
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
+11 -2
View File
@@ -48,12 +48,21 @@ class CaptureAppView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
uint32_t previous_bandwidth{500000};
uint32_t capture_rate{500000};
uint32_t file_format{0};
bool trim{false};
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::Capture};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX};
"rx_capture",
app_settings::Mode::RX,
{
{"capture_rate"sv, &capture_rate},
{"file_format"sv, &file_format},
{"trim"sv, &trim},
}};
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
+14 -13
View File
@@ -7,30 +7,33 @@
namespace ui {
// Information: a line starting with a '#' will be yellow coloured
constexpr std::string_view authors_list[] = {
"# * List of contributors * ",
constexpr std::string_view mayhem_information_list[] = {
"#****** Mayhem Community ******",
" ",
" https://discord.mayhem.app",
" ",
"#**** List of contributors ****",
" ",
"#Mayhem-Firmware:",
"jboone,eried,furrtek,",
"NotherNgineer,gullradriel,",
"jLynx,kallanreed,Brumi-2021,",
"htotoo,bernd-herzog,zxkmm,",
"htotoo,zxkmm,bernd-herzog,",
"ArjanOnwezen,euquiq,u-foka,",
"iNetro,heurist1,dqs105,",
"teixeluis,jwetzell,",
"jimilinuxguy,gregoryfenton,",
"notpike,strijar,BehleZebub,",
"arneluehrs,rascafr,joyel24,",
"ImDroided,zigad,johnelder,",
"klockee,nnesetto,LupusE,",
"argilo,dc2dc,formtapez,",
"arneluehrs,mcules,rascafr,",
"joyel24,ImDroided,zigad,",
"johnelder,klockee,nnesetto,",
"LupusE,argilo,dc2dc,formtapez,",
"RocketGod-git,mrmookie,",
"ITAxReal,F33RNI,F4GEV,",
"rusty-labs,mjwaxios,andrej-mk,",
"RedFox-Fr,nemanjan00,",
"MichalLeonBorsuk,",
"MatiasFernandez,Giorgiofox,",
"ckuethe",
"MatiasFernandez,Giorgiofox",
" ",
"#Havoc:",
"jboone,furrtek,eried,argilo,",
@@ -72,7 +75,7 @@ AboutView::AboutView(NavigationView& nav) {
button_ok.focus();
};
for (auto& authors_line : authors_list) {
for (auto& authors_line : mayhem_information_list) {
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
if (authors_line.size() > 0) {
if (authors_line[0] == '#') {
@@ -103,15 +106,13 @@ void AboutView::on_frame_sync() {
menu_view.set_highlighted(current + 1);
} else {
menu_view.set_highlighted(0);
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
menu_view.set_highlighted(2); // the first line that has human name
}
}
}
void AboutView::focus() {
button_ok.focus();
menu_view.set_highlighted(2); // the first line that has human name
menu_view.set_highlighted(3); // contributors block starting line
}
bool AboutView::on_touch(const TouchEvent) {
+14 -6
View File
@@ -99,13 +99,21 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
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 == 2) { // EUR
field_frequency.set_value(144800000);
} else if (i == 3) { // AUS
field_frequency.set_value(145175000);
} else if (i == 4) { // NZ
} else if (i == 2) { // NZ
field_frequency.set_value(144575000);
} else if (i == 5) { // ISS
} else if (i == 3) { // JAP
field_frequency.set_value(144640000);
} else if (i == 4) { // PHI
field_frequency.set_value(144740000);
} else if (i == 5) { // EUR
field_frequency.set_value(144800000);
} else if (i == 6) { // THA
field_frequency.set_value(144900000);
} else if (i == 7) { // AUS
field_frequency.set_value(145175000);
} else if (i == 8) { // BR
field_frequency.set_value(145570000);
} else if (i == 9) { // ISS
field_frequency.set_value(145825000);
}
options_region_id = i;
+8 -4
View File
@@ -227,10 +227,14 @@ class APRSRxView : public View {
3,
{{"MAN", 0},
{"NA ", 1},
{"EUR", 2},
{"AUS", 3},
{"NZ ", 4},
{"ISS", 5}}};
{"NZ ", 2},
{"JAP", 3},
{"PHI", 4},
{"EUR", 5},
{"THA", 6},
{"AUS", 7},
{"BR ", 8},
{"ISS", 9}}};
FrequencyField field_frequency{
{3 * 8, 0 * 16}};
@@ -92,6 +92,9 @@ void ExternalModuleView::on_tick_second() {
case app_location_t::TX:
btnText += " (TX)";
break;
case app_location_t::SETTINGS:
btnText += " (Settings)";
break;
case app_location_t::DEBUG:
btnText += " (Debug)";
break;
@@ -177,4 +177,4 @@ void FlashUtilityView::focus() {
menu_view.focus();
}
} /* namespace ui */
} /* namespace ui */
+50 -66
View File
@@ -53,6 +53,8 @@ namespace fs = std::filesystem;
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
#include "file_reader.hpp"
extern ui::SystemView* system_view_ptr;
namespace pmem = portapack::persistent_memory;
@@ -324,6 +326,7 @@ SetUIView::SetUIView(NavigationView& nav) {
&toggle_bias_tee,
&toggle_clock,
&toggle_mute,
&toggle_fake_brightness,
&toggle_sd_card,
&button_save,
&button_cancel});
@@ -361,6 +364,7 @@ SetUIView::SetUIView(NavigationView& nav) {
toggle_clock.set_value(!pmem::ui_hide_clock());
toggle_speaker.set_value(!pmem::ui_hide_speaker());
toggle_mute.set_value(!pmem::ui_hide_mute());
toggle_fake_brightness.set_value(!pmem::ui_hide_fake_brightness());
toggle_battery_icon.set_value(!pmem::ui_hide_battery_icon());
toggle_battery_text.set_value(!pmem::ui_hide_numeric_battery());
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
@@ -389,6 +393,7 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_ui_hide_clock(!toggle_clock.value());
pmem::set_ui_hide_speaker(!toggle_speaker.value());
pmem::set_ui_hide_mute(!toggle_mute.value());
pmem::set_ui_hide_fake_brightness(!toggle_fake_brightness.value());
pmem::set_ui_hide_battery_icon(!toggle_battery_icon.value());
pmem::set_ui_hide_numeric_battery(!toggle_battery_text.value());
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
@@ -702,14 +707,34 @@ SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
&field_encoder_dial_sensitivity,
&field_encoder_rate_multiplier,
&button_save,
&button_cancel});
&button_cancel,
&button_dial_sensitivity_plus,
&button_dial_sensitivity_minus,
&button_rate_multiplier_plus,
&button_rate_multiplier_minus,
&field_encoder_dial_direction});
field_encoder_dial_sensitivity.set_by_value(pmem::encoder_dial_sensitivity());
field_encoder_rate_multiplier.set_value(pmem::encoder_rate_multiplier());
field_encoder_dial_direction.set_by_value(pmem::encoder_dial_direction());
button_dial_sensitivity_plus.on_select = [this](Button&) {
field_encoder_dial_sensitivity.on_encoder(1);
};
button_dial_sensitivity_minus.on_select = [this](Button&) {
field_encoder_dial_sensitivity.on_encoder(-1);
};
button_rate_multiplier_plus.on_select = [this](Button&) {
field_encoder_rate_multiplier.on_encoder(1);
};
button_rate_multiplier_minus.on_select = [this](Button&) {
field_encoder_rate_multiplier.on_encoder(-1);
};
button_save.on_select = [&nav, this](Button&) {
pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value());
pmem::set_encoder_rate_multiplier(field_encoder_rate_multiplier.value());
pmem::set_encoder_dial_direction(field_encoder_dial_direction.selected_index_value());
nav.pop();
};
@@ -777,13 +802,20 @@ void SetConfigModeView::focus() {
/* SetDisplayView ************************************/
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&button_save,
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch});
&checkbox_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
@@ -792,6 +824,14 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
@@ -957,67 +997,6 @@ void SetMenuColorView::focus() {
button_save.focus();
}
/* SetAutoStartView ************************************/
SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&button_reset,
&options});
button_save.on_select = [&nav, this](Button&) {
autostart_app = "";
if (selected != 0) {
auto it = full_app_list.find(selected);
if (it != full_app_list.end())
autostart_app = it->second;
}
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
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};
opts.emplace_back(o);
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
i++;
o = {app.displayName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.id);
if (autostart_app == app.id) selected = i;
}
ExternalItemsMenuLoader::load_all_external_items_callback([this](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
i++;
OptionsField::option_t o = {app.appFriendlyName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.appCallName);
if (autostart_app == app.appCallName) selected = i;
});
options.set_options(opts);
options.on_change = [this](size_t, OptionsField::value_t v) {
selected = v;
};
options.set_selected_index(selected);
}
void SetAutostartView::focus() {
options.focus();
}
/* SetThemeView ************************************/
SetThemeView::SetThemeView(NavigationView& nav) {
@@ -1099,9 +1078,12 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav)
}
void SettingsMenuView::on_populate() {
if (pmem::show_gui_return_icon()) {
const bool return_icon = pmem::show_gui_return_icon();
if (return_icon) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_items({
{"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>(); }},
@@ -1120,9 +1102,11 @@ void SettingsMenuView::on_populate() {
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
add_external_items(nav_, app_location_t::SETTINGS, *this, return_icon ? 1 : 0);
}
} /* namespace ui */
+60 -53
View File
@@ -363,8 +363,12 @@ class SetUIView : public View {
{19 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_sd_card{
ImageToggle toggle_fake_brightness{
{21 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
ImageToggle toggle_sd_card{
{23 * 8, 14 * 16 + 2, 16, 16},
&bitmap_sd_card_ok};
Button button_save{
@@ -570,6 +574,7 @@ class SetQRCodeView : public View {
};
};
using portapack::persistent_memory::encoder_dial_direction;
using portapack::persistent_memory::encoder_dial_sensitivity;
using portapack::persistent_memory::encoder_rate_multiplier;
@@ -583,30 +588,52 @@ class SetEncoderDialView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "rotation position (number of", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "steps per full rotation):", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Dial sensitivity:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 8 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 9 * 16}, "rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 10 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 12 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 0 * 16}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "Sensitivity:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 14 * 16}, "Direction:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_encoder_dial_sensitivity{
{20 * 8, 5 * 16},
{20 * 8, 3 * 16},
6,
{{"LOW", encoder_dial_sensitivity::DIAL_SENSITIVITY_LOW},
{"NORMAL", encoder_dial_sensitivity::DIAL_SENSITIVITY_NORMAL},
{"HIGH", encoder_dial_sensitivity::DIAL_SENSITIVITY_HIGH}}};
NumberField field_encoder_rate_multiplier{
{20 * 8, 12 * 16},
{20 * 8, 10 * 16},
2,
{1, 15},
1,
' '};
OptionsField field_encoder_dial_direction{
{18 * 8, 14 * 16},
7,
{{"NORMAL", false},
{"REVERSE", true}}};
Button button_dial_sensitivity_plus{
{20 * 8, 2 * 16, 16, 16},
"+"};
Button button_dial_sensitivity_minus{
{20 * 8, 4 * 16, 16, 16},
"-"};
Button button_rate_multiplier_plus{
{20 * 8, 9 * 16, 16, 16},
"+"};
Button button_rate_multiplier_minus{
{20 * 8, 11 * 16, 16, 16},
"-"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -705,6 +732,7 @@ class SetConfigModeView : public View {
"Cancel",
};
};
using portapack::persistent_memory::fake_brightness_level_options;
class SetDisplayView : public View {
public:
@@ -715,8 +743,27 @@ class SetDisplayView : public View {
std::string title() const override { return "Display"; };
private:
Labels labels{
{{1 * 8, 1 * 16}, "Limits screen brightness", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
{20 * 8, 8 * 16},
6,
{{"12.5%", fake_brightness_level_options::BRIGHTNESS_12p5},
{"25%", fake_brightness_level_options::BRIGHTNESS_25},
{"50%", fake_brightness_level_options::BRIGHTNESS_50}}};
Checkbox checkbox_brightness_switch{
{1 * 8, 5 * 16},
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 2 * 16},
{1 * 8, 10 * 16},
23,
"Invert colors (For IPS)"};
@@ -864,47 +911,6 @@ class SetMenuColorView : public View {
};
};
class SetAutostartView : public View {
public:
SetAutostartView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Autostart"; };
private:
int32_t i = 0;
std::string autostart_app{""};
OptionsField::options_t opts{};
std::map<int32_t, std::string> full_app_list{}; // looking table
int32_t selected = 0;
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
Labels labels{
{{1 * 8, 1 * 16}, "Select app to start on boot", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "(an SD Card is required)", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
OptionsField options{
{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 {
public:
SetThemeView(NavigationView& nav);
@@ -988,6 +994,7 @@ class SettingsMenuView : public BtnGridView {
private:
NavigationView& nav_;
void on_populate() override;
};
+33 -6
View File
@@ -44,9 +44,9 @@ SigGenView::~SigGenView() {
void SigGenView::update_config() {
if (checkbox_stop.value())
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), field_stop.value());
else
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0);
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), 0);
}
void SigGenView::update_tone() {
@@ -78,6 +78,7 @@ SigGenView::SigGenView(
baseband::run_image(portapack::spi_flash::image_tag_siggen);
add_children({&labels,
&options_mod,
&options_shape,
&text_shape,
&symfield_tone,
@@ -87,22 +88,48 @@ SigGenView::SigGenView(
&field_stop,
&tx_view});
symfield_tone.hidden(1); // At first launch , by default we are in CW Shape has NO MOD , we are not using Tone modulation.
symfield_tone.hidden(true); // At first launch , by default we are in CW: Shape ignored, we are not using Tone modulation.
options_shape.hidden(true);
text_shape.hidden(true);
symfield_tone.set_value(1000); // Default: 1000 Hz
options_shape.on_change = [this](size_t, OptionsField::value_t v) {
text_shape.set(shape_strings[v]);
if (auto_update)
update_config();
if ((v == 0) || (v == 6)) { // In Shapes Options (CW & Pseudo Random Noise) we are not using Tone modulation freq.
symfield_tone.hidden(1);
if (v == 5) { // In Shape Pseudo Random Noise we are not using Tone modulation freq.
symfield_tone.hidden(true);
} else {
symfield_tone.hidden(0);
symfield_tone.hidden(false);
}
set_dirty();
};
options_shape.set_selected_index(0);
text_shape.set(shape_strings[0]);
options_mod.on_change = [this](size_t, OptionsField::value_t v) {
if (auto_update)
update_config();
if (v == 0) { // In Modulation Options CW we are not using Tone modulation freq.
symfield_tone.hidden(true);
} else {
symfield_tone.hidden(false);
}
if ((v == 0) || (v == 2) || (v == 3)) { // In Modulation Options CW, QPSK, BPSK we are not using Shapes.
options_shape.hidden(true);
text_shape.hidden(true);
} else {
options_shape.hidden(false);
text_shape.hidden(false);
}
set_dirty();
};
options_mod.set_selected_index(0);
field_stop.set_value(1);
symfield_tone.set_value(1000); // Default: 1000 Hz
+35 -30
View File
@@ -58,68 +58,73 @@ class SigGenView : public View {
app_settings::SettingsManager settings_{
"tx_siggen", app_settings::Mode::TX};
const std::string shape_strings[9] = {
"CW (No mod.) ",
"Sine mod. FM",
"Triangle mod.FM", // max 15 character text space.
"Saw up mod. FM",
"Saw down mod.FM",
"Square mod. FM",
"Pseudo Noise FM", // using 16 bits LFSR register, 16 order polynomial feedback.
"BPSK 0,1,0,1...",
"QPSK 00-01-10.."};
const std::string shape_strings[6] = {// max 15 character text space.
"Sine",
"Triangle",
"Saw up",
"Saw down",
"Square",
"Pseudo Noise"};
bool auto_update{false};
Labels labels{
{{3 * 8, 4 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
{{22 * 8, 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 20 * 8}, "Modulation: FM", Theme::getInstance()->fg_light->foreground}};
{{3 * 8, 2 * 8}, "Modulation:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 3 * 8 + 8 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 2 * 8 + 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
{{22 * 8, 2 * 8 + 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground}};
ImageOptionsField options_shape{
{10 * 8, 4, 32, 32},
{10 * 8, 3 * 8 + 8, 32, 32},
Theme::getInstance()->bg_darkest->foreground,
Theme::getInstance()->bg_darkest->background,
{{&bitmap_sig_cw, 0},
{&bitmap_sig_sine, 1},
{&bitmap_sig_tri, 2},
{&bitmap_sig_saw_up, 3},
{&bitmap_sig_saw_down, 4},
{&bitmap_sig_square, 5},
{&bitmap_sig_noise, 6},
{&bitmap_sig_noise, 7}, // Pending to add a correct BPSK icon.
{&bitmap_sig_noise, 8}}}; // Pending to add a correct QPSK icon.
{{&bitmap_sig_sine, 0},
{&bitmap_sig_tri, 1},
{&bitmap_sig_saw_up, 2},
{&bitmap_sig_saw_down, 3},
{&bitmap_sig_square, 4},
{&bitmap_sig_noise, 5}}};
Text text_shape{
{15 * 8, 4 + 10, 15 * 8, 16},
{15 * 8, 3 * 8 + 8 + 10, 15 * 8, 16},
""};
SymField symfield_tone{
{12 * 8, 7 * 8},
{12 * 8, 2 * 8 + 7 * 8},
5};
Button button_update{
{5 * 8, 10 * 8, 8 * 8, 3 * 8},
{5 * 8, 2 * 8 + 10 * 8, 8 * 8, 3 * 8},
"Update"};
Checkbox checkbox_auto{
{15 * 8, 10 * 8},
{15 * 8, 2 * 8 + 10 * 8},
4,
"Auto"};
Checkbox checkbox_stop{
{5 * 8, 15 * 8},
{5 * 8, 2 * 8 + 15 * 8},
10,
"Stop after"};
NumberField field_stop{
{20 * 8, 15 * 8 + 4},
{20 * 8, 2 * 8 + 15 * 8 + 4},
2,
{1, 99},
1,
' '};
OptionsField options_mod{
{15 * 8, 2 * 8},
12,
{{"CW (No mod.)", 0},
{"FM", 1},
{"BPSK", 2},
{"QPSK", 3},
{"DSB", 4},
{"AM 100% dep.", 5},
{"AM 50% depth", 6}}};
TransmitterView tx_view{
16 * 16,
10000,
+153 -76
View File
@@ -987,44 +987,6 @@ static constexpr Bitmap bitmap_icon_previous{
{16, 16},
bitmap_icon_previous_data};
static constexpr uint8_t bitmap_icon_tools_antenna_data[] = {
0x38,
0x3E,
0x10,
0x22,
0x10,
0x26,
0x10,
0x22,
0x10,
0x2E,
0x10,
0x22,
0x10,
0x26,
0x10,
0x22,
0x38,
0x2E,
0x38,
0x22,
0x38,
0x26,
0x38,
0x22,
0x38,
0x2E,
0x38,
0x22,
0x38,
0x3E,
0x00,
0x00,
};
static constexpr Bitmap bitmap_icon_tools_antenna{
{16, 16},
bitmap_icon_tools_antenna_data};
static constexpr uint8_t bitmap_icon_batt_text_data[] = {
0x00,
0x00,
@@ -1799,6 +1761,45 @@ static constexpr uint8_t bitmap_icon_lge_data[] = {
0x00,
0x00,
};
static constexpr uint8_t bitmap_icon_file_iq_data[] = {
0xFC,
0x03,
0x04,
0x06,
0x04,
0x0E,
0x04,
0x1E,
0x04,
0x3E,
0x04,
0x20,
0x04,
0x20,
0x04,
0x21,
0x44,
0x25,
0x54,
0x25,
0xF4,
0x2F,
0xA4,
0x2A,
0x84,
0x22,
0x04,
0x22,
0x04,
0x20,
0xFC,
0x3F,
};
static constexpr Bitmap bitmap_icon_file_iq{
{16, 16},
bitmap_icon_file_iq_data};
static constexpr Bitmap bitmap_icon_lge{
{16, 16},
bitmap_icon_lge_data};
@@ -2531,6 +2532,44 @@ static constexpr Bitmap bitmap_icon_batt_icon{
{16, 16},
bitmap_icon_batt_icon_data};
static constexpr uint8_t bitmap_icon_temperature_data[] = {
0x00,
0x01,
0x80,
0x01,
0x80,
0x05,
0xC0,
0x0D,
0x40,
0x0D,
0xD0,
0x1F,
0x70,
0x15,
0xB0,
0x1A,
0x58,
0x35,
0xB8,
0x3A,
0x58,
0x34,
0x28,
0x28,
0x18,
0x30,
0x30,
0x18,
0x60,
0x0C,
0xC0,
0x07,
};
static constexpr Bitmap bitmap_icon_temperature{
{16, 16},
bitmap_icon_temperature_data};
static constexpr uint8_t bitmap_tab_edge_data[] = {
0x00,
0x01,
@@ -2675,44 +2714,6 @@ static constexpr Bitmap bitmap_icon_touchtunes{
{16, 16},
bitmap_icon_touchtunes_data};
static constexpr uint8_t bitmap_icon_file_iq_data[] = {
0xFC,
0x03,
0x04,
0x06,
0x04,
0x0E,
0x04,
0x1E,
0x04,
0x3E,
0x04,
0x20,
0x04,
0x20,
0x04,
0x21,
0x44,
0x25,
0x54,
0x25,
0xF4,
0x2F,
0xA4,
0x2A,
0x84,
0x22,
0x04,
0x22,
0x04,
0x20,
0xFC,
0x3F,
};
static constexpr Bitmap bitmap_icon_file_iq{
{16, 16},
bitmap_icon_file_iq_data};
static constexpr uint8_t bitmap_icon_sd_data[] = {
0x00,
0x00,
@@ -4789,6 +4790,44 @@ static constexpr Bitmap bitmap_icon_ais{
{16, 16},
bitmap_icon_ais_data};
static constexpr uint8_t bitmap_temperature_data[] = {
0x00,
0x00,
0x20,
0x00,
0x70,
0x3E,
0x88,
0x00,
0x88,
0x00,
0x88,
0x3E,
0x88,
0x00,
0x88,
0x00,
0x88,
0x3E,
0x88,
0x00,
0x04,
0x01,
0x74,
0x01,
0x04,
0x01,
0x88,
0x00,
0x70,
0x00,
0x00,
0x00,
};
static constexpr Bitmap bitmap_temperature{
{16, 16},
bitmap_temperature_data};
static constexpr uint8_t bitmap_titlebar_image_data[] = {
0x00,
0x00,
@@ -5701,6 +5740,44 @@ static constexpr Bitmap bitmap_icon_options_datetime{
{16, 16},
bitmap_icon_options_datetime_data};
static constexpr uint8_t bitmap_icon_tune_fork_data[] = {
0x00,
0x00,
0x00,
0x00,
0x22,
0x44,
0x21,
0x84,
0x2D,
0xB4,
0x25,
0xA4,
0x25,
0xA4,
0x2D,
0xB4,
0x61,
0x86,
0xC2,
0x43,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x00,
0x00,
0x00,
0x00,
};
static constexpr Bitmap bitmap_icon_tune_fork{
{16, 16},
bitmap_icon_tune_fork_data};
static constexpr uint8_t bitmap_target_verify_data[] = {
0x00,
0xE0,
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.hpp"
#include "ui_whipcalc.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::antenna_length {
void initialize_app(ui::NavigationView& nav) {
nav.push<WhipCalcView>();
}
} // namespace ui::external_app::antenna_length
extern "C" {
__attribute__((section(".external_app.app_antenna_length.application_information"), used)) application_information_t _application_information_antenna_length = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::antenna_length::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Antenna Length",
/*.bitmap_data = */ {0x38, 0x3E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x10, 0x2E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x26, 0x38, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x3E, 0x00, 0x00},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -32,8 +32,9 @@
#include <cstring>
using namespace portapack;
using namespace ui;
namespace ui {
namespace ui::external_app::antenna_length {
void WhipCalcView::focus() {
field_frequency.focus();
@@ -176,4 +177,4 @@ void WhipCalcView::load_antenna_db() {
void WhipCalcView::add_default_antenna() {
antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); // store a default ant500
}
} // namespace ui
} // namespace ui::external_app::antenna_length
@@ -31,7 +31,10 @@
#include "string_format.hpp"
#include <vector>
namespace ui {
using namespace ui;
namespace ui::external_app::antenna_length {
class WhipCalcView : public View {
public:
WhipCalcView(NavigationView& nav);
@@ -90,6 +93,6 @@ class WhipCalcView : public View {
"Back"};
};
} /* namespace ui */
} // namespace ui::external_app::antenna_length
#endif /*__UI_WHIPCALC__*/
+84
View File
@@ -0,0 +1,84 @@
/*
* 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.hpp"
#include "ui_app_manager.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::app_manager {
void initialize_app(ui::NavigationView& nav) {
nav.push<AppManagerView>();
}
} // namespace ui::external_app::app_manager
extern "C" {
__attribute__((section(".external_app.app_app_manager.application_information"), used)) application_information_t _application_information_app_manager = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::app_manager::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "AppManager",
/*.bitmap_data = */ {
0xE0,
0x00,
0x10,
0x01,
0x10,
0x01,
0x1F,
0x1F,
0x01,
0x10,
0x01,
0x10,
0x01,
0x10,
0x01,
0x70,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x70,
0xE1,
0x10,
0x11,
0x11,
0x11,
0x11,
0x1F,
0x1F,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::SETTINGS,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,275 @@
/*
* copyleft spammingdramaqueen
*
* 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_app_manager.hpp"
#include "ui_navigation.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "file.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
#include "file_reader.hpp"
using namespace portapack;
namespace ui::external_app::app_manager {
/* AppManagerView **************************************/
/* | inner apps | external apps |
* ------------------------------
* | id | appCallName |
* | displayName|appFriendlyName|
*/
AppManagerView::AppManagerView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
&menu_view,
&text_app_info,
&button_hide_unhide,
&button_clean_hide,
&button_set_cancel_autostart,
&button_clean_autostart});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= app_list_index) {
text_app_info.set("");
return;
}
std::string info;
auto app_name = get_app_info(menu_view.highlighted_index(), true);
auto app_id = get_app_info(menu_view.highlighted_index(), false);
info += "Hidden:";
if (is_blacklisted(app_name)) {
info += "Yes ";
} else {
info += "No ";
}
info += "Autostart:";
if (is_autostart_app(app_id)) {
info += "Yes";
} else {
info += "No";
}
if (info.empty()) {
info = "Highlight an app";
}
text_app_info.set(info);
};
button_hide_unhide.on_select = [this](Button&) {
hide_unhide_app();
refresh_list();
};
button_clean_hide.on_select = [this](Button&) {
clean_blacklist();
refresh_list();
};
button_set_cancel_autostart.on_select = [this](Button&) {
set_unset_autostart_app();
refresh_list();
};
button_clean_autostart.on_select = [this](Button&) {
unset_auto_start();
refresh_list();
};
refresh_list();
}
void AppManagerView::refresh_list() {
auto previous_cursor_index = menu_view.highlighted_index();
app_list_index = 0;
menu_view.clear();
size_t index = 0;
auto padding = [](const std::string& str) {
return str.length() < 25 ? std::string(25 - str.length(), ' ') + '' : "";
// that weird char is a icon in portapack's font base that looks like a switch, which i use to indicate the auto start
};
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
app_list_index++;
menu_view.add_item({app.displayName + std::string(is_autostart_app(app.id) ? padding(app.displayName) : ""),
app.iconColor,
app.icon,
[this, app_id = std::string(app.id)](KeyEvent) {
button_hide_unhide.focus();
}});
}
ExternalItemsMenuLoader::load_all_external_items_callback([this, &index, &padding](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
app_list_index++;
menu_view.add_item({app.appFriendlyName + std::string(is_autostart_app(app.appCallName) ? padding(app.appFriendlyName) : ""),
ui::Color::light_grey(),
&bitmap_icon_sdcard,
[this, app_id = std::string(app.appCallName)](KeyEvent) {
button_hide_unhide.focus();
}});
});
menu_view.set_highlighted(previous_cursor_index);
}
void AppManagerView::focus() {
menu_view.focus();
}
void AppManagerView::hide_app() {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
blacklist.push_back(get_app_info(menu_view.highlighted_index(), true));
write_blacklist(blacklist);
}
void AppManagerView::unhide_app() {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
blacklist.erase(std::remove(blacklist.begin(), blacklist.end(), get_app_info(menu_view.highlighted_index(), true)), blacklist.end());
write_blacklist(blacklist);
}
void AppManagerView::hide_unhide_app() {
if (is_blacklisted(get_app_info(menu_view.highlighted_index(), true)))
unhide_app();
else
hide_app();
}
void AppManagerView::get_blacklist(std::vector<std::string>& blacklist) {
File f;
auto error = f.open(u"SETTINGS/blacklist");
if (error)
return;
auto reader = FileLineReader(f);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
blacklist.push_back(trim(line));
}
}
void AppManagerView::write_blacklist(const std::vector<std::string>& blacklist) {
File f;
auto error = f.create(u"SETTINGS/blacklist");
if (error)
return;
for (const auto& app_name : blacklist) {
auto line = app_name + "\n";
f.write(line.c_str(), line.length());
}
}
void AppManagerView::clean_blacklist() {
std::vector<std::string> blacklist = {};
write_blacklist(blacklist);
}
bool AppManagerView::is_blacklisted(const std::string& app_name) {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
return std::find(blacklist.begin(), blacklist.end(), app_name) != blacklist.end();
}
void AppManagerView::set_auto_start() {
auto app_index = menu_view.highlighted_index();
if (app_index >= app_list_index) return;
auto id_aka_app_call_name = get_app_info(app_index, false);
autostart_app = id_aka_app_call_name;
refresh_list();
}
void AppManagerView::unset_auto_start() {
autostart_app = "";
refresh_list();
}
void AppManagerView::set_unset_autostart_app() {
auto app_index = menu_view.highlighted_index();
if (app_index >= app_list_index) return;
auto id_aka_friendly_name = get_app_info(app_index, false);
if (is_autostart_app(id_aka_friendly_name))
unset_auto_start();
else
set_auto_start();
}
bool AppManagerView::is_autostart_app(const char* id_aka_friendly_name) {
return autostart_app == std::string(id_aka_friendly_name);
}
bool AppManagerView::is_autostart_app(const std::string& id_aka_friendly_name) {
return autostart_app == id_aka_friendly_name;
}
std::string AppManagerView::get_app_info(uint16_t index, bool is_display_name) {
size_t current_index = 0;
std::string result;
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
if (current_index == index) {
return is_display_name ? std::string(app.displayName) : std::string(app.id);
}
current_index++;
}
ExternalItemsMenuLoader::load_all_external_items_callback([&current_index, &index, &result, &is_display_name](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
if (current_index == index) {
result = is_display_name ? app.appFriendlyName : app.appCallName;
}
current_index++;
});
return result;
}
} // namespace ui::external_app::app_manager
@@ -0,0 +1,87 @@
/*
* copyleft spammingdramaqueen
*
* 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_APP_MANAGER_H__
#define __UI_APP_MANAGER_H__
#include "ui_navigation.hpp"
namespace ui::external_app::app_manager {
class AppManagerView : public View {
public:
AppManagerView(NavigationView& nav);
std::string title() const override { return "AppMan"; };
private:
NavigationView& nav_;
std::string autostart_app{""};
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
void focus() override;
void refresh_list();
uint16_t app_list_index{0};
Labels labels{
{{0 * 8, 0 * 16}, "App list:", Theme::getInstance()->fg_light->foreground}};
MenuView menu_view{};
Text text_app_info{
{0, 27 * 8, screen_width, 16},
"Highlight an app"};
Button button_hide_unhide{
{0, 29 * 8, screen_width / 2 - 1, 32},
"Hide/Show"};
Button button_clean_hide{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
"Clean Hidden"};
Button button_set_cancel_autostart{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
"Set Autostart"};
Button button_clean_autostart{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
"Del Autostart"};
std::string get_app_info(uint16_t index, bool is_display_name);
void get_blacklist(std::vector<std::string>& blacklist);
void write_blacklist(const std::vector<std::string>& blacklist);
bool is_blacklisted(const std::string& app_display_name);
void hide_app();
void unhide_app();
void hide_unhide_app();
void clean_blacklist();
bool is_autostart_app(const std::string& display_name);
void set_auto_start();
void unset_auto_start();
void set_unset_autostart_app();
bool is_autostart_app(const char* id_aka_app_call_name);
};
} // namespace ui::external_app::app_manager
#endif // __UI_APP_MANAGER_H__
+30
View File
@@ -146,6 +146,30 @@ set(EXTCPPSRC
#metronome
external/metronome/main.cpp
external/metronome/ui_metronome.cpp
#app_manager
external/app_manager/main.cpp
external/app_manager/ui_app_manager.cpp
#hopper
external/hopper/main.cpp
external/hopper/ui_hopper.cpp
# whip calculator
external/antenna_length/main.cpp
external/antenna_length/ui_whipcalc.cpp
# wav viewer
external/wav_view/main.cpp
external/wav_view/ui_view_wav.cpp
# wipe sdcard
external/sd_wipe/main.cpp
external/sd_wipe/ui_sd_wipe.cpp
# playlist editor
external/playlist_editor/main.cpp
external/playlist_editor/ui_playlist_editor.cpp
)
set(EXTAPPLIST
@@ -184,4 +208,10 @@ set(EXTAPPLIST
fmradio
tuner
metronome
app_manager
hopper
antenna_length
view_wav
sd_wipe
playlist_editor
)
+42
View File
@@ -58,6 +58,12 @@ MEMORY
ram_external_app_fmradio(rwx) : org = 0xADD10000, len = 32k
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k
ram_external_app_hopper(rwx) : org = 0xADD50000, len = 32k
ram_external_app_antenna_length(rwx) : org = 0xADD60000, len = 32k
ram_external_app_view_wav(rwx) : org = 0xADD70000, len = 32k
ram_external_app_sd_wipe(rwx) : org = 0xADD80000, len = 32k
ram_external_app_playlist_editor(rwx) : org = 0xADD90000, len = 32k
}
SECTIONS
@@ -272,4 +278,40 @@ SECTIONS
KEEP(*(.external_app.app_metronome.application_information));
*(*ui*external_app*metronome*);
} > ram_external_app_metronome
.external_app_app_manager : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_app_manager.application_information));
*(*ui*external_app*app_manager*);
} > ram_external_app_app_manager
.external_app_hopper : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_hopper.application_information));
*(*ui*external_app*hopper*);
} > ram_external_app_hopper
.external_app_antenna_length : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_antenna_length.application_information));
*(*ui*external_app*antenna_length*);
} > ram_external_app_antenna_length
.external_app_view_wav : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_view_wav.application_information));
*(*ui*external_app*view_wav*);
} > ram_external_app_view_wav
.external_app_sd_wipe : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_sd_wipe.application_information));
*(*ui*external_app*sd_wipe*);
} > ram_external_app_sd_wipe
.external_app_playlist_editor : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_playlist_editor.application_information));
*(*ui*external_app*playlist_editor*);
} > ram_external_app_playlist_editor
}
@@ -89,8 +89,6 @@ class FlipperTxView : public View {
std::unique_ptr<FlipperPlayThread> replay_thread{};
Optional<flippersub_metadata> submeta{};
const std::filesystem::path subghz_dir = u"subghz";
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 Mark Thompson
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -32,7 +33,7 @@ namespace ui::external_app::font_viewer {
/* DebugFontsView *******************************************************/
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name) {
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name, bool is_big_font) {
auto char_width{font_style->font.char_width()};
auto char_height{font_style->font.line_height()};
auto cpl{((screen_width / char_width) - 6) & 0xF8}; // Display a multiple of 8 characters per line
@@ -47,7 +48,15 @@ uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const
if ((c % cpl) == 0)
painter.draw_string({0, line_pos}, *font_style, "Ox" + to_string_hex(c + 0x20, 2));
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
Style highlight_style_big = *Theme::getInstance()->fg_red;
Style highlight_style_small = *Theme::getInstance()->bg_important_small;
if (c == field_cursor.value()) {
painter.draw_char({((c % cpl) + 5) * char_width, line_pos},
is_big_font ? highlight_style_big : highlight_style_small,
(char)(c + 0x20));
} else {
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
}
}
return line_pos + char_height;
@@ -56,13 +65,29 @@ uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const
void DebugFontsView::paint(Painter& painter) {
int16_t line_pos;
line_pos = display_font(painter, 32, Theme::getInstance()->bg_darkest, "Fixed 8x16");
display_font(painter, line_pos + 16, Theme::getInstance()->bg_darkest_small, "Fixed 5x8");
line_pos = display_font(painter, 32, Theme::getInstance()->bg_darkest, "Fixed 8x16", true);
display_font(painter, line_pos + 16, Theme::getInstance()->bg_darkest_small, "Fixed 5x8", false);
}
void DebugFontsView::update_address_text() {
uint8_t ascii_value = field_cursor.value() + 0x20;
text_address.set("0x" + to_string_hex(ascii_value, 2));
}
DebugFontsView::DebugFontsView(NavigationView& nav)
: nav_{nav} {
add_children({&field_cursor,
&text_address});
set_focusable(true);
field_cursor.on_change = [&](int32_t) {
update_address_text();
set_dirty();
};
}
void DebugFontsView::focus() {
field_cursor.focus();
}
} /* namespace ui::external_app::font_viewer */
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 Mark Thompson
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -39,10 +40,23 @@ class DebugFontsView : public View {
public:
DebugFontsView(NavigationView& nav);
void paint(Painter& painter) override;
void focus() override;
std::string title() const override { return "Fonts"; };
private:
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name);
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name, bool is_big_font);
void update_address_text();
NumberField field_cursor{
{0 * 8, 0 * 8},
1,
{0, 1000},
1,
' '};
Text text_address{
{screen_width / 2, 0 * 16, screen_width / 2, 16},
"0x20",
};
NavigationView& nav_;
};
+83
View File
@@ -0,0 +1,83 @@
/*
* 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_hopper.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::hopper {
void initialize_app(ui::NavigationView& nav) {
nav.push<HopperView>();
}
} // namespace ui::external_app::hopper
extern "C" {
__attribute__((section(".external_app.app_hopper.application_information"), used)) application_information_t _application_information_hopper = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::hopper::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Hopper",
/*.bitmap_data = */ {
0xE0,
0x07,
0xF8,
0x1F,
0x1C,
0x38,
0x0E,
0x78,
0x06,
0x7C,
0x03,
0xCE,
0x03,
0xC7,
0x83,
0xC3,
0xC3,
0xC1,
0xE3,
0xC0,
0x73,
0xC0,
0x3E,
0x60,
0x1E,
0x70,
0x1C,
0x38,
0xF8,
0x1F,
0xE0,
0x07,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+346
View File
@@ -0,0 +1,346 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 euquiq
* copyleft mr.r0b0t from the F society
*
* 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_hopper.hpp"
#include "ui_receiver.hpp"
#include "ui_freqman.hpp"
#include "ui_fileman.hpp"
#include "file_path.hpp"
#include "file_reader.hpp"
#include "convert.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
using namespace portapack;
namespace ui::external_app::hopper {
void HopperView::focus() {
button_load_list.focus();
}
HopperView::~HopperView() {
transmitter_model.disable();
baseband::shutdown();
}
void HopperView::update_freq_list_menu_view() {
menu_freq_list.clear();
uint8_t list_count = freq_list.size();
if (list_count == 0) {
menu_freq_list.add_item({"Add freq or load list",
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
for (uint8_t i = 0; i < list_count; i++) {
menu_freq_list.add_item({to_string_rounded_freq(freq_list[i], INT8_MAX),
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
set_dirty();
}
void HopperView::on_retune(const rf::Frequency freq, const uint32_t range) {
if (freq) {
transmitter_model.set_target_frequency(freq);
text_range_number.set(to_string_dec_uint(range, 2));
}
}
void HopperView::set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration) {
hopper_channels[i].enabled = true;
hopper_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
hopper_channels[i].center = center;
hopper_channels[i].duration = 30720 * duration;
}
void HopperView::start_tx() {
uint32_t c, i = 0;
bool out_of_ranges = false;
size_t hop_value = options_hop.selected_index_value();
// Disable all channels by default
for (c = 0; c < JAMMER_MAX_CH; c++)
hopper_channels[c].enabled = false;
uint8_t channel_count = freq_list.size();
if (channel_count <= JAMMER_MAX_CH) {
for (c = 0; c < channel_count; c++) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
break;
}
set_hopper_channel(i, JAMMER_CH_WIDTH, freq_list[c], hop_value);
i++;
}
} else {
out_of_ranges = true;
}
if (!out_of_ranges && i) {
text_range_total.set("/" + to_string_dec_uint(i, 2));
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.set_rf_amp(field_amp.value());
transmitter_model.set_tx_gain(field_gain.value());
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
mscounter = 0; // euquiq: Reset internal ms counter for do_timer()
} else {
if (out_of_ranges)
nav_.display_modal("Error", "Jam freq too much.");
}
}
void HopperView::stop_tx() {
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
transmitter_model.disable();
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
jamming = false;
cooling = false;
}
// called each 1/60th of second
void HopperView::on_timer() {
if (++mscounter == 60) {
mscounter = 0;
if (jamming) {
if (cooling) {
if (++seconds >= field_timepause.value()) { // Re-start TX
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
transmitter_model.enable();
button_transmit.set_text("STOP");
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
int32_t jitter_amount = field_jitter.value();
if (jitter_amount) {
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = false;
seconds = 0;
}
} else {
if (++seconds >= field_timetx.value()) // Start cooling period:
{
transmitter_model.disable();
button_transmit.set_text("PAUSED");
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
int32_t jitter_amount = field_jitter.value();
if (jitter_amount) {
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = true;
seconds = 0;
}
}
}
}
}
void HopperView::load_list() {
freq_list.clear();
auto open_view = nav_.push<FileLoadView>(".PHOP");
open_view->push_dir(hopper_dir);
open_view->on_changed = [this](std::filesystem::path path) {
File f;
auto error = f.open(path);
if (error) {
nav_.display_modal("Err", "Can't open.");
update_freq_list_menu_view();
return;
}
freq_list.clear();
auto reader = FileLineReader(f);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
rf::Frequency freq;
if (parse_int(line, freq)) {
if (freq_list.size() >= JAMMER_MAX_CH) {
break;
}
freq_list.push_back(freq);
}
}
update_freq_list_menu_view();
};
update_freq_list_menu_view();
}
void HopperView::save_list() {
if (freq_list.empty()) {
nav_.display_modal("Err", "Nothing to save");
return;
}
ensure_directory(hopper_dir);
filename_buffer = "";
text_prompt(
nav_,
filename_buffer,
64,
[this](std::string& value) {
auto path = hopper_dir / (value + ".PHOP");
File f;
auto error = f.create(path);
if (error) {
nav_.display_modal("Err", "Create fail.");
return;
}
for (const auto& freq : freq_list) {
auto freq_str = to_string_dec_uint(freq) + "\n";
f.write(freq_str.c_str(), freq_str.length());
}
text_range_number.set("Saved: " + path.filename().string());
});
}
HopperView::HopperView(
NavigationView& nav)
: nav_{nav} {
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&menu_freq_list,
&button_load_list,
&button_add_freq,
&button_delete_freq,
&button_save_list,
&button_clear,
&labels,
&options_type,
&text_range_number,
&text_range_total,
&options_speed,
&options_hop,
&field_timetx,
&field_timepause,
&field_jitter,
&field_gain,
&field_amp,
&button_transmit});
options_type.set_selected_index(3); // Rand CW
options_speed.set_selected_index(3); // 10kHz
options_hop.set_selected_index(1); // 50ms
button_transmit.set_style(&style_val);
field_timetx.set_value(30);
field_timepause.set_value(1);
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp());
button_load_list.on_select = [this]() {
load_list();
update_freq_list_menu_view();
};
button_add_freq.on_select = [this]() {
if (freq_list.size() < JAMMER_MAX_CH) {
auto kb_view = nav_.push<FrequencyKeypadView>(0);
kb_view->on_changed = [this](rf::Frequency freq) {
freq_list.push_back(freq);
update_freq_list_menu_view();
};
} else {
nav_.display_modal("Err", "No more.");
}
update_freq_list_menu_view();
};
button_delete_freq.on_select = [this]() {
auto i = menu_freq_list.highlighted_index();
if (i < freq_list.size()) {
freq_list.erase(freq_list.begin() + i);
}
update_freq_list_menu_view();
};
button_save_list.on_select = [this]() {
save_list();
update_freq_list_menu_view();
};
button_clear.on_select = [this]() {
// clang-format off
nav_.display_modal("Del:", "Clean all?\n", YESNO, [this](bool choice) {
if (choice){
freq_list.clear();
update_freq_list_menu_view();
} }, TRUE);
// clang-format on
update_freq_list_menu_view();
};
button_transmit.on_select = [this](Button&) {
if (jamming || cooling)
stop_tx();
else
start_tx();
};
menu_freq_list.on_left = [this]() {
button_load_list.focus();
};
menu_freq_list.on_right = [this]() {
button_load_list.focus();
};
update_freq_list_menu_view();
}
} // namespace ui::external_app::hopper
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 euquiq
* copyleft mr.r0b0t from the F society
*
* 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_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "transmitter_model.hpp"
#include "message.hpp"
#include "jammer.hpp"
#include "lfsr_random.hpp"
#include "radio_state.hpp"
using namespace jammer;
namespace ui::external_app::hopper {
class HopperView : public View {
public:
HopperView(NavigationView& nav);
~HopperView();
HopperView(const HopperView&) = delete;
HopperView(HopperView&&) = delete;
HopperView& operator=(const HopperView&) = delete;
HopperView& operator=(HopperView&&) = delete;
void focus() override;
void update_freq_list_menu_view();
std::string title() const override { return "Hopper"; };
std::vector<rf::Frequency> freq_list{315000000, 433920000};
private:
NavigationView& nav_;
TxRadioState radio_state_{
0 /* frequency */,
3500000 /* bandwidth */,
3072000 /* sampling rate */
};
void start_tx();
void on_timer();
void stop_tx();
void set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
void on_retune(const rf::Frequency freq, const uint32_t range);
void load_list();
void save_list();
void on_file_save_write(const std::string& value);
HopperChannel* hopper_channels = (HopperChannel*)shared_memory.bb_data.data;
bool jamming{false};
bool cooling{false}; // euquiq: Indicates jammer in cooldown
uint16_t seconds = 0; // euquiq: seconds counter for toggling tx / cooldown
int16_t mscounter = 0; // euquiq: Internal ms counter for do_timer()
lfsr_word_t lfsr_v = 1; // euquiq: Used to generate "random" Jitter
std::string filename_buffer = "";
const Style& style_val = *Theme::getInstance()->fg_green;
const Style& style_cancel = *Theme::getInstance()->fg_red;
MenuView menu_freq_list{
{0, 0, screen_width, 8 * 16},
false};
NewButton button_load_list{
{0 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_load,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_save_list{
{4 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_save,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_add_freq{
{8 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_add,
Color::dark_green(),
/*vcenter*/ true};
NewButton button_delete_freq{
{12 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_trash,
Color::dark_red(),
/*vcenter*/ true};
NewButton button_clear{
{screen_width - 4 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_tools_wipesd,
Color::red(),
/*vcenter*/ true};
Labels labels{
{{2 * 8, 23 * 8}, "Type:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 25 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 31 * 8}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions.
{{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 35 * 8}, "Gain:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 35 * 8}, "A:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_type{
{7 * 8, 23 * 8},
8,
{
{"Rand FSK", 0},
{"FM tone", 1},
{"CW sweep", 2},
{"Rand CW", 3},
}};
Text text_range_number{
{16 * 8, 23 * 8, 2 * 8, 16},
"--"};
Text text_range_total{
{18 * 8, 23 * 8, 3 * 8, 16},
"/--"};
OptionsField options_speed{
{7 * 8, 25 * 8},
6,
{{"10Hz ", 10},
{"100Hz ", 100},
{"1kHz ", 1000},
{"10kHz ", 10000},
{"100kHz", 100000}}};
OptionsField options_hop{
{7 * 8, 27 * 8},
5,
{{"10ms ", 1},
{"50ms ", 5},
{"100ms", 10},
{"1s ", 100},
{"2s ", 200},
{"5s ", 500},
{"10s ", 1000}}};
NumberField field_timetx{
{7 * 8, 29 * 8},
3,
{1, 180},
1,
' ',
};
NumberField field_timepause{
{8 * 8, 31 * 8},
2,
{1, 60},
1,
' ',
};
NumberField field_jitter{
{8 * 8, 33 * 8},
2,
{1, 60},
1,
' ',
};
NumberField field_gain{
{8 * 8, 35 * 8},
2,
{0, 47},
1,
' ',
};
NumberField field_amp{
{13 * 8, 35 * 8},
1,
{0, 1},
1,
' ',
};
Button button_transmit{
{148, 216, 80, 80},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_retune{
Message::ID::Retune,
[this](Message* const p) {
const auto message = static_cast<const RetuneMessage*>(p);
this->on_retune(message->freq, message->range);
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
};
} // namespace ui::external_app::hopper
+155 -1
View File
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* This file is part of PortaPack.
*
@@ -178,7 +180,8 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
&label_waveform,
&waveform,
&button_open,
&button_save});
&button_save,
&button_bug_key});
// Initialize default values for controls
field_symbol_rate.set_value(100);
@@ -267,6 +270,16 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
});
};
button_bug_key.on_select = [&](Button&) {
auto bug_key_input_view = nav_.push<OOKEditorBugKeyView>(ook_data.payload);
bug_key_input_view->on_save = [this](std::string p) {
ook_data.payload = p;
text_payload.set(ook_data.payload);
draw_waveform();
};
};
// Configure button to start or stop the transmission
button_send_stop.on_select = [this](Button&) {
if (!is_transmitting) {
@@ -280,4 +293,145 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
// initial waveform drawing (should be a single line)
draw_waveform();
}
/*************** bug key view ****************/
OOKEditorBugKeyView::OOKEditorBugKeyView(NavigationView& nav, std::string payload)
: nav_{nav},
payload_{payload} {
add_children({&labels,
&field_primary_step,
&field_secondary_step,
&console,
&button_insert_high_level_long,
&button_insert_high_level_short,
&button_insert_low_level_long,
&button_insert_low_level_short,
&button_delete,
&button_save});
button_insert_low_level_short.on_select = [this](Button&) {
on_insert(InsertType::LOW_LEVEL_SHORT);
};
button_insert_low_level_long.on_select = [this](Button&) {
on_insert(InsertType::LOW_LEVEL_LONG);
};
button_insert_high_level_short.on_select = [this](Button&) {
on_insert(InsertType::HIGH_LEVEL_SHORT);
};
button_insert_high_level_long.on_select = [this](Button&) {
on_insert(InsertType::HIGH_LEVEL_LONG);
};
button_delete.on_select = [this](Button&) {
on_delete();
};
button_save.on_select = [this](Button&) {
if (on_save) on_save(build_payload());
nav_.pop();
};
auto update_step_buttons = [this](int32_t value, Button& btnLow, Button& btnHigh) {
std::string low_level_btn_str;
std::string high_level_btn_str;
if (value <= 14) { // the button width allow max 14 chars
for (int i = 0; i < value; i++) {
low_level_btn_str.push_back('0');
high_level_btn_str.push_back('1');
}
} else {
low_level_btn_str = to_string_dec_int(value) + " * \"0\"";
high_level_btn_str = to_string_dec_int(value) + " * \"1\"";
}
btnLow.set_text(" "); // set_dirty broken console. this is work around
btnHigh.set_text(" ");
btnLow.set_text(low_level_btn_str);
btnHigh.set_text(high_level_btn_str);
};
field_primary_step.on_change = [&](int32_t) {
update_step_buttons(field_primary_step.value(),
button_insert_low_level_short,
button_insert_high_level_short);
update_console();
};
field_secondary_step.on_change = [&](int32_t) {
update_step_buttons(field_secondary_step.value(),
button_insert_low_level_long,
button_insert_high_level_long);
update_console();
};
field_primary_step.set_value(1);
field_secondary_step.set_value(2);
update_step_buttons(field_primary_step.value(),
button_insert_low_level_short,
button_insert_high_level_short);
update_console();
}
void OOKEditorBugKeyView::on_insert(InsertType type) {
auto promise_length = 0;
std::string promose_level = "0";
switch (type) {
case InsertType::LOW_LEVEL_SHORT:
promise_length = field_primary_step.value();
promose_level = "0";
break;
case InsertType::LOW_LEVEL_LONG:
promise_length = field_secondary_step.value();
promose_level = "0";
break;
case InsertType::HIGH_LEVEL_SHORT:
promise_length = field_primary_step.value();
promose_level = "1";
break;
case InsertType::HIGH_LEVEL_LONG:
promise_length = field_secondary_step.value();
promose_level = "1";
break;
}
for (auto i = 0; i < promise_length; i++) {
payload_ += promose_level;
}
update_console();
}
void OOKEditorBugKeyView::on_delete() {
// I'm aware that if user inputted like: [long high][long high][short high], this will delete a pile of them
// but this doesnt matter because:
// 1. they should not do it, high or low shoudl cross each other, don't repeat
// 2. don't have too much RAM to trach the input trace
if (payload_.length() > 0) {
size_t len = payload_.length();
char last_char = payload_[len - 1];
size_t pos = len - 1;
while (pos > 0 && payload_[pos - 1] == last_char) {
pos--;
}
payload_.erase(pos);
update_console();
}
}
void OOKEditorBugKeyView::update_console() {
console.clear(true);
console.write(payload_);
}
std::string OOKEditorBugKeyView::build_payload() {
return payload_;
}
void OOKEditorBugKeyView::focus() {
button_save.focus();
}
} // namespace ui::external_app::ook_editor
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* This file is part of PortaPack.
*
@@ -29,6 +31,8 @@ namespace ui::external_app::ook_editor {
#define TRANSMISSION_FREQUENCY_DEFAULT 433920000U // Sets the default transmission frequency (27 MHz).
#define WAVEFORM_BUFFER_SIZE 550
/*****************Editor View ******************/
class OOKEditorAppView : public View {
public:
void focus() override;
@@ -123,6 +127,7 @@ class OOKEditorAppView : public View {
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]};
Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]};
Button button_bug_key{{0, 125 + 28 + 3, screen_width, 28}, "Bug Key"};
Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]};
// Progress bar to display transmission progress.
@@ -132,6 +137,82 @@ class OOKEditorAppView : public View {
Waveform waveform{{0, 208, 240, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
};
/******** bug key input view **********/
enum InsertType {
LOW_LEVEL_SHORT,
LOW_LEVEL_LONG,
HIGH_LEVEL_SHORT,
HIGH_LEVEL_LONG
};
class OOKEditorBugKeyView : public View {
public:
std::function<void(std::string)> on_save{};
OOKEditorBugKeyView(NavigationView& nav, std::string payload);
std::string title() const override { return "Bug.K"; };
void focus() override;
private:
NavigationView& nav_;
std::string payload_ = "";
std::string path_ = "";
uint32_t delay_{0};
std::string delay_str{""}; // needed by text_prompt
void on_insert(InsertType type);
void on_delete();
void update_console();
std::string build_payload();
Labels labels{
{{0 * 8, 0 * 16}, "Primary Step", Theme::getInstance()->fg_light->foreground},
{{(screen_width / 2), 0 * 16}, "Secondary Step", Theme::getInstance()->fg_light->foreground}};
NumberField field_primary_step{
{0 * 8, 1 * 16},
3,
{0, 550},
1,
' '};
NumberField field_secondary_step{
{(screen_width / 2), 1 * 16},
3,
{0, 550},
1,
' '};
Console console{
{0, 3 * 16, screen_width, screen_height - 10 * 16}};
Button button_insert_low_level_long{
{0 * 8, 13 * 16, screen_width / 2, 2 * 16},
"00"};
Button button_insert_low_level_short{
{0 * 8, 15 * 16, screen_width / 2, 2 * 16},
"0"};
Button button_insert_high_level_long{
{(screen_width / 2), 13 * 16, screen_width / 2, 2 * 16},
"11"};
Button button_insert_high_level_short{
{(screen_width / 2), 15 * 16, screen_width / 2, 2 * 16},
"1"};
Button button_delete{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"<Backspace"};
Button button_save{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Save"};
};
}; // namespace ui::external_app::ook_editor
#endif /*__UI_OOK_EDITOR_H__*/
+84
View File
@@ -0,0 +1,84 @@
/*
* 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.hpp"
#include "ui_playlist_editor.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::playlist_editor {
void initialize_app(ui::NavigationView& nav) {
nav.push<PlaylistEditorView>();
}
} // namespace ui::external_app::playlist_editor
extern "C" {
__attribute__((section(".external_app.app_playlist_editor.application_information"), used)) application_information_t _application_information_playlist_editor = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::playlist_editor::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "PlaylistEdit",
/*.bitmap_data = */ {
0x03,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x0F,
0x00,
0x00,
0x00,
0x03,
0x01,
0x80,
0x01,
0xC3,
0x00,
0xE0,
0xFF,
0xEF,
0xFF,
0xC0,
0x00,
0x83,
0x01,
0x00,
0x01,
0x03,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,367 @@
/*
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* 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_playlist_editor.hpp"
#include "ui_navigation.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "file.hpp"
#include "ui_fileman.hpp"
#include "file_path.hpp"
#include "string_format.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
#include "file_reader.hpp"
using namespace portapack;
namespace ui::external_app::playlist_editor {
/*********menu**********/
PlaylistEditorView::PlaylistEditorView(NavigationView& nav)
: nav_{nav} {
portapack::async_tx_enabled = true;
add_children({&labels,
&button_new,
&text_current_ppl_file,
&menu_view,
&text_hint,
&button_open_playlist,
&button_edit,
&button_insert,
&button_save_playlist});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.on_highlight = [this]() {
text_hint.set("Edit:" +
playlist[menu_view.highlighted_index()].substr(playlist[menu_view.highlighted_index()].find_last_of('/') + 1,
playlist[menu_view.highlighted_index()].find(',') -
playlist[menu_view.highlighted_index()].find_last_of('/') - 1));
};
button_new.on_select = [this](Button&) {
if (on_create_ppl()) {
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_FILENAME);
refresh_interface();
}
};
button_open_playlist.on_select = [this](Button&) {
open_file();
};
button_edit.on_select = [this](Button&) {
on_edit_item();
};
button_insert.on_select = [this](Button&) {
on_insert_item();
};
button_save_playlist.on_select = [this](Button&) {
save_ppl();
};
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_NEW_BUTTON);
}
void PlaylistEditorView::focus() {
menu_view.focus();
}
void PlaylistEditorView::open_file() {
auto open_view = nav_.push<FileLoadView>(".PPL");
open_view->push_dir(playlist_dir);
open_view->on_changed = [this](fs::path new_file_path) {
current_ppl_path = new_file_path;
on_file_changed(new_file_path);
};
}
void PlaylistEditorView::swap_opened_file_or_new_button(DisplayFilenameOrNewButton d) {
if (d == DisplayFilenameOrNewButton::DISPLAY_NEW_BUTTON) {
button_new.hidden(false);
text_current_ppl_file.hidden(true);
} else {
button_new.hidden(true);
text_current_ppl_file.hidden(false);
text_current_ppl_file.set(current_ppl_name_buffer);
}
refresh_interface();
}
/*
NB: same name would became as "open file"
*/
bool PlaylistEditorView::on_create_ppl() {
bool success = false;
text_prompt(
nav_,
current_ppl_name_buffer,
100,
[&](std::string& s) {
current_ppl_name_buffer = s;
success = true;
current_ppl_name_buffer += ".PPL";
current_ppl_path = playlist_dir / std::filesystem::path(current_ppl_name_buffer);
File f;
f.open(current_ppl_path, true, true); // prob safer here as standalone obj as read only and then open again in process func
f.close();
on_file_changed(current_ppl_path);
});
return success;
}
void PlaylistEditorView::on_file_changed(const fs::path& new_file_path) {
File playlist_file;
auto error = playlist_file.open(new_file_path.string());
if (error) return;
menu_view.clear();
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
playlist.push_back(line);
}
for (auto& line : playlist) {
// remove empty lines
if (line == "\n" || line == "\r\n" || line == "\r") {
playlist.erase(std::remove(playlist.begin(), playlist.end(), line), playlist.end());
}
// remove line end \n etc
if (line.length() > 0 && (line[line.length() - 1] == '\n' || line[line.length() - 1] == '\r')) {
line = line.substr(0, line.length() - 1);
}
}
text_hint.set("Highlight an entry");
text_current_ppl_file.set(new_file_path.string());
ever_opened = true;
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_FILENAME);
refresh_menu_view();
}
void PlaylistEditorView::refresh_menu_view() {
menu_view.clear();
for (const auto& line : playlist) {
if (line.length() == 0 || line[0] == '#') {
menu_view.add_item({line,
ui::Color::grey(),
&bitmap_icon_notepad,
[this](KeyEvent) {
button_insert.focus();
}});
} else {
const auto filename = line.substr(line.find_last_of('/') + 1, line.find(',') - line.find_last_of('/') - 1);
menu_view.add_item({filename,
ui::Color::white(),
&bitmap_icon_cwgen,
[this](KeyEvent) {
button_edit.focus();
}});
}
}
}
void PlaylistEditorView::on_edit_item() {
if (!ever_opened || playlist.empty()) {
nav_.display_modal("Err", "No entry");
return;
}
auto edit_view = nav_.push<PlaylistItemEditView>(
playlist[menu_view.highlighted_index()]);
edit_view->set_on_delete([this]() {
playlist.erase(playlist.begin() + menu_view.highlighted_index());
refresh_interface();
});
edit_view->on_save = [this](std::string new_item) {
playlist[menu_view.highlighted_index()] = new_item;
refresh_interface();
};
}
void PlaylistEditorView::on_insert_item() {
// if (current_ppl_path.empty() || current_ppl_path.string().find_first_not_of(" \t\n\r") == std::string::npos) {
if (!ever_opened) { // TODO: this is a workaround because the above line is not working and I took one hour and didn't find the issue
nav_.display_modal("Err", "No playlist file loaded");
return;
}
auto edit_view = nav_.push<PlaylistItemEditView>(
"");
edit_view->on_save = [&](std::string new_item) {
if (playlist.empty()) {
playlist.push_back(new_item);
} else {
playlist.insert(playlist.begin() + menu_view.highlighted_index() + 1, new_item);
}
refresh_interface();
};
}
void PlaylistEditorView::refresh_interface() {
const auto previous_index = menu_view.highlighted_index();
refresh_menu_view();
set_dirty();
menu_view.set_highlighted(previous_index);
}
void PlaylistEditorView::save_ppl() {
if (current_ppl_path.empty()) {
nav_.display_modal("Err", "No playlist file loaded");
return;
} else if (playlist.empty()) {
nav_.display_modal("Err", "List is empty");
return;
}
File playlist_file;
auto error = playlist_file.open(current_ppl_path.string(), false, false);
if (error) {
nav_.display_modal("Err", "open err");
return;
}
// clear file
playlist_file.seek(0);
playlist_file.truncate();
// write new data
for (const auto& entry : playlist) {
playlist_file.write_line(entry);
}
nav_.display_modal("Save", "Saved playlist\n" + current_ppl_path.string());
}
/*********edit**********/
PlaylistItemEditView::PlaylistItemEditView(
NavigationView& nav,
std::string item)
: nav_{nav},
original_item_{item} {
add_children({&labels,
&field_path,
&field_delay,
&button_browse,
&button_input_delay,
&button_delete,
&button_save});
button_browse.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".C16");
open_view->push_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
field_path.set_text(path.string());
path_ = path.string();
};
field_delay.on_change = [&](auto) {
delay_ = field_delay.value();
};
};
button_input_delay.on_select = [this](Button&) {
delay_str = to_string_dec_uint(delay_);
if (delay_str == "0") {
delay_str = "";
}
text_prompt(
nav_,
delay_str,
100,
[&](std::string& s) {
delay_ = atoi(s.c_str());
field_delay.set_value(delay_);
refresh_ui();
});
};
button_delete.on_select = [this](Button&) {
if (on_delete) on_delete();
nav_.pop();
};
button_save.on_select = [&](Button&) {
if (path_.empty()) {
nav_.display_modal("Err", "Select a file\n or press back to cancel");
return;
}
if (on_save) on_save(build_item());
nav_.pop();
};
if (!on_delete) {
button_delete.hidden(true);
}
parse_item(item);
refresh_ui();
}
void PlaylistItemEditView::focus() {
button_save.focus();
}
void PlaylistItemEditView::refresh_ui() {
field_path.set_text(path_);
field_delay.set_value(delay_);
}
void PlaylistItemEditView::parse_item(std::string item) {
// Parse format: path,delay
if (item.empty()) {
return;
}
auto parts = split_string(item, ',');
if (parts.size() >= 1) {
path_ = std::string{parts[0]};
}
if (parts.size() >= 2) {
delay_ = atoi(std::string{parts[1]}.c_str());
}
}
std::string PlaylistItemEditView::build_item() const {
const auto v = path_ + "," + to_string_dec_uint(field_delay.value());
return path_ + "," + to_string_dec_uint(field_delay.value());
}
} // namespace ui::external_app::playlist_editor
@@ -0,0 +1,159 @@
/*
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* 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_PLAYLIST_EDITOR_H__
#define __UI_PLAYLIST_EDITOR_H__
#include "ui_navigation.hpp"
namespace fs = std::filesystem;
namespace ui::external_app::playlist_editor {
enum DisplayFilenameOrNewButton {
DISPLAY_FILENAME,
DISPLAY_NEW_BUTTON
};
class PlaylistEditorView : public View {
public:
PlaylistEditorView(NavigationView& nav);
std::string title() const override { return "PPL Edit"; };
private:
NavigationView& nav_;
void focus() override;
std::vector<std::string> playlist = {};
fs::path current_ppl_path = "";
std::string current_ppl_name_buffer = ""; // this is because text_prompt needs it. TODO: this is so annoying, shoudl refactor that func
bool ever_opened = false;
Labels labels{
{{0 * 8, 0 * 16}, "PPL file:", Theme::getInstance()->fg_light->foreground}};
Button button_new{
{(sizeof("PPL file:") + 1) * 8, 0 * 16, 8 * 5, 16},
"New"};
Text text_current_ppl_file{
{sizeof("PPL file:") * 8, 0 * 16, screen_width - sizeof("PPL file:") * 8, 16},
""};
MenuView menu_view{};
Text text_hint{
{0, 27 * 8, screen_width, 16},
"Open a PPL file"};
Text text_ppl_name{
{0, 27 * 8, screen_width, 16},
"Highlight an app"};
Button button_open_playlist{
{0, 29 * 8, screen_width / 2 - 1, 32},
"Open PPL"};
Button button_edit{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
"Edit Item"};
Button button_insert{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
"Ins. After"};
Button button_save_playlist{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
"Save PPL"};
void open_file();
void swap_opened_file_or_new_button(DisplayFilenameOrNewButton d);
void on_file_changed(const fs::path& path);
bool on_create_ppl();
void refresh_interface();
void on_edit_item();
void on_insert_item();
void refresh_menu_view();
void save_ppl();
};
class PlaylistItemEditView : public View {
public:
std::function<void(std::string)> on_save{};
std::function<void()> on_delete{};
PlaylistItemEditView(NavigationView& nav, std::string item);
std::string title() const override { return "Edit Item"; };
void focus() override;
void set_on_delete(std::function<void()> callback) {
on_delete = callback;
button_delete.hidden(false);
}
private:
NavigationView& nav_;
std::string original_item_ = "";
std::string path_ = "";
uint32_t delay_{0};
std::string delay_str{""}; // needed by text_prompt
void refresh_ui();
void parse_item(std::string item);
std::string build_item() const;
Labels labels{
{{0 * 8, 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Delay(ms):", Theme::getInstance()->fg_light->foreground}};
TextField field_path{
{0, 2 * 16, screen_width, 16},
"empty"};
NumberField field_delay{
{11 * 8, 5 * 16},
5,
{0, 99999},
10,
' '};
Button button_browse{
{2 * 8, 8 * 16, 8 * 8, 3 * 16},
"Browse"};
Button button_input_delay{
{12 * 8, 8 * 16, sizeof("Input Delay") * 8, 3 * 16},
"Input Delay"};
Button button_delete{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Delete"};
Button button_save{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Save"};
};
} // namespace ui::external_app::playlist_editor
#endif // __UI_PLAYLIST_EDITOR_H__
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.hpp"
#include "ui_sd_wipe.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::sd_wipe {
void initialize_app(ui::NavigationView& nav) {
nav.push<WipeSDView>();
}
} // namespace ui::external_app::sd_wipe
extern "C" {
__attribute__((section(".external_app.app_sd_wipe.application_information"), used)) application_information_t _application_information_sd_wipe = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::sd_wipe::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Wipe SD card",
/*.bitmap_data = */ {0xF0, 0x3F, 0x58, 0x35, 0x5C, 0x35, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x3C, 0x1C, 0xBC, 0xC9, 0xBC, 0xE3, 0x2C, 0x77, 0x5C, 0x3E, 0xAC, 0x1C, 0x5C, 0x3E, 0x2C, 0x77, 0x9C, 0xE3, 0xAC, 0xC1},
/*.icon_color = */ ui::Color::red().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -22,7 +22,9 @@
#include "ui_sd_wipe.hpp"
namespace ui {
using namespace ui;
namespace ui::external_app::sd_wipe {
Thread* WipeSDView::thread{nullptr};
@@ -63,4 +65,4 @@ void WipeSDView::focus() {
}
}
} /* namespace ui */
} // namespace ui::external_app::sd_wipe
@@ -30,7 +30,9 @@
#include <cstdint>
namespace ui {
using namespace ui;
namespace ui::external_app::sd_wipe {
class WipeSDView : public View {
public:
@@ -87,6 +89,6 @@ class WipeSDView : public View {
""};
};
} /* namespace ui */
} // namespace ui::external_app::sd_wipe
#endif /*__UI_SD_WIPE_H__*/
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.hpp"
#include "ui_view_wav.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::view_wav {
void initialize_app(ui::NavigationView& nav) {
nav.push<ViewWavView>();
}
} // namespace ui::external_app::view_wav
extern "C" {
__attribute__((section(".external_app.app_view_wav.application_information"), used)) application_information_t _application_information_view_wav = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::view_wav::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "WAV Viewer",
/*.bitmap_data = */ {0xF0, 0x0F, 0x1C, 0x18, 0x17, 0x38, 0x15, 0x78, 0x15, 0xF8, 0x15, 0x82, 0x15, 0x8B, 0xD5, 0x83, 0xD5, 0xBB, 0xD5, 0x83, 0x15, 0x8B, 0x15, 0x92, 0x15, 0xA0, 0x17, 0x80, 0x1C, 0x80, 0xF0, 0xFF},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'A', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -28,8 +28,9 @@
#include "string_format.hpp"
using namespace portapack;
using namespace ui;
namespace ui {
namespace ui::external_app::view_wav {
void ViewWavView::update_scale(int32_t new_scale) {
scale = new_scale;
@@ -345,4 +346,4 @@ ViewWavView::~ViewWavView() {
baseband::shutdown();
}
} /* namespace ui */
} /* namespace ui::external_app::view_wav */
@@ -28,7 +28,9 @@
#include "ui_receiver.hpp"
#include "replay_thread.hpp"
namespace ui {
using namespace ui;
namespace ui::external_app::view_wav {
class ViewWavView : public View {
public:
@@ -202,4 +204,4 @@ class ViewWavView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::view_wav */
+4
View File
@@ -50,6 +50,10 @@ Optional<File::Error> File::open_fatfs(const std::filesystem::path& filename, BY
}
}
/*
* @param read_only: open in readonly mode
* @param create: create if it doesnt exist
*/
Optional<File::Error> File::open(const std::filesystem::path& filename, bool read_only, bool create) {
BYTE mode = read_only ? FA_READ : FA_READ | FA_WRITE;
if (create)
+2
View File
@@ -49,3 +49,5 @@ const std::filesystem::path sstv_dir = u"SSTV";
const std::filesystem::path wav_dir = u"WAV";
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
const std::filesystem::path ook_editor_dir = u"OOKFILES";
const std::filesystem::path hopper_dir = u"HOPPER";
const std::filesystem::path subghz_dir = u"SUBGHZ";
+2
View File
@@ -51,5 +51,7 @@ extern const std::filesystem::path sstv_dir;
extern const std::filesystem::path wav_dir;
extern const std::filesystem::path whipcalc_dir;
extern const std::filesystem::path ook_editor_dir;
extern const std::filesystem::path hopper_dir;
extern const std::filesystem::path subghz_dir;
#endif /* __FILE_PATH_H__ */
+5
View File
@@ -66,6 +66,11 @@ int_fast8_t Encoder::update(const uint_fast8_t phase_bits) {
if (direction == prev_direction) {
if ((sensitivity_map[portapack::persistent_memory::encoder_dial_sensitivity()] & (1 << state)) == 0)
return 0;
// false: normal, true: reverse
if (portapack::persistent_memory::encoder_dial_direction())
direction = -direction;
return direction;
}
+82 -33
View File
@@ -38,17 +38,31 @@ BtnGridView::BtnGridView(
set_parent_rect(new_parent_rect);
set_focusable(true);
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
button_pgup.set_focusable(false);
button_pgup.on_select = [this](Button&) {
if (arrow_up_enabled) {
if (((int64_t)highlighted_item - displayed_max) > 0)
set_highlighted(highlighted_item - displayed_max);
else
set_highlighted(0);
}
};
add_child(&arrow_more);
arrow_more.set_focusable(false);
arrow_more.set_foreground(Theme::getInstance()->bg_darkest->background);
button_pgdown.set_focusable(false);
button_pgdown.on_select = [this](Button&) {
if (arrow_down_enabled) {
set_highlighted(highlighted_item + displayed_max);
}
};
button_pgup.set_style(Theme::getInstance()->bg_darkest_small);
button_pgdown.set_style(Theme::getInstance()->bg_darkest_small);
add_child(&button_pgup);
add_child(&button_pgdown);
}
BtnGridView::~BtnGridView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
}
void BtnGridView::set_max_rows(int rows) {
@@ -63,7 +77,10 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
displayed_max = (parent_rect().size().height() / button_h);
arrow_more.set_parent_rect({228, (Coord)(displayed_max * button_h), 8, 8});
button_pgup.set_parent_rect({0, (Coord)(displayed_max * button_h), 120, 16});
button_pgdown.set_parent_rect({120, (Coord)(displayed_max * button_h), 120, 16});
displayed_max *= rows_;
// Delete any existing buttons.
@@ -84,28 +101,40 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
menu_item_views.push_back(std::move(item));
}
update_items();
}
void BtnGridView::set_arrow_enabled(bool enabled) {
void BtnGridView::set_arrow_up_enabled(bool enabled) {
if (!show_arrows)
return;
if (enabled) {
add_child(&arrow_more);
} else {
remove_child(&arrow_more);
if (!arrow_up_enabled) {
arrow_up_enabled = true;
button_pgup.set_text("< PREV");
}
} else if (!enabled) {
if (arrow_up_enabled) {
arrow_up_enabled = false;
button_pgup.set_text(" ");
}
}
};
void BtnGridView::on_tick_second() {
if (more && blink)
arrow_more.set_foreground(Theme::getInstance()->bg_darkest->foreground);
else
arrow_more.set_foreground(Theme::getInstance()->bg_darkest->background);
blink = !blink;
arrow_more.set_dirty();
}
void BtnGridView::set_arrow_down_enabled(bool enabled) {
if (!show_arrows)
return;
if (enabled) {
if (!arrow_down_enabled) {
arrow_down_enabled = true;
button_pgdown.set_text("NEXT >");
}
} else if (!enabled) {
if (arrow_down_enabled) {
arrow_down_enabled = false;
button_pgdown.set_text(" ");
}
}
};
void BtnGridView::clear() {
// clear vector and release memory, not using swap since it's causing capture to glitch/fault
@@ -153,15 +182,23 @@ void BtnGridView::insert_item(const GridItem& new_item, size_t position, bool in
}
}
void BtnGridView::show_hide_arrows() {
if (highlighted_item == 0) {
set_arrow_up_enabled(false);
} else {
set_arrow_up_enabled(true);
}
if (highlighted_item == (menu_items.size() - 1)) {
set_arrow_down_enabled(false);
} else {
set_arrow_down_enabled(true);
}
}
void BtnGridView::update_items() {
size_t i = 0;
Color bg_color = portapack::persistent_memory::menu_color();
if ((menu_items.size()) > (displayed_max + offset)) {
more = true;
blink = true;
} else
more = false;
Color bg_color = portapack::persistent_memory::menu_color();
for (auto& item : menu_item_views) {
if ((i + offset) >= menu_items.size()) {
@@ -189,14 +226,23 @@ NewButton* BtnGridView::item_view(size_t index) const {
return menu_item_views[index].get();
}
void BtnGridView::show_arrows_enabled(bool enabled) {
show_arrows = enabled;
if (!enabled) {
remove_child(&button_pgup);
remove_child(&button_pgdown);
}
}
bool BtnGridView::set_highlighted(int32_t new_value) {
int32_t item_count = (int32_t)menu_items.size();
if (new_value < 0)
return false;
if (new_value >= item_count)
if (new_value >= item_count) {
new_value = item_count - 1;
}
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
// Shift BtnGridView up
@@ -222,6 +268,8 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
if (visible())
item_view(highlighted_item - offset)->focus();
show_hide_arrows();
return true;
}
@@ -235,21 +283,22 @@ void BtnGridView::on_focus() {
void BtnGridView::on_blur() {
#if 0
if (!keep_highlight)
item_view(highlighted_item - offset)->unhighlight();
if (!keep_highlight)
item_view(highlighted_item - offset)->unhighlight();
#endif
}
void BtnGridView::on_show() {
on_populate();
View::on_show();
set_highlighted(highlighted_item);
}
void BtnGridView::on_hide() {
View::on_hide();
clear();
set_arrow_up_enabled(false);
set_arrow_down_enabled(false);
}
bool BtnGridView::on_key(const KeyEvent key) {
+15 -11
View File
@@ -68,11 +68,18 @@ class BtnGridView : public View {
NewButton* item_view(size_t index) const;
bool show_arrows{true}; // flag used to hide arrows in main menu
void show_arrows_enabled(bool enabled);
bool set_highlighted(int32_t new_value);
uint32_t highlighted_index();
void set_parent_rect(const Rect new_parent_rect) override;
void set_arrow_enabled(bool enabled);
bool arrow_up_enabled{false};
bool arrow_down_enabled{false};
void set_arrow_up_enabled(bool enabled);
void set_arrow_down_enabled(bool enabled);
void show_hide_arrows();
void on_focus() override;
void on_blur() override;
void on_show() override;
@@ -88,24 +95,21 @@ class BtnGridView : public View {
private:
int rows_{3};
void on_tick_second();
bool keep_highlight{false};
SignalToken signal_token_tick_second{};
std::vector<GridItem> menu_items{};
std::vector<std::unique_ptr<NewButton>> menu_item_views{};
Image arrow_more{
{228, 320 - 8, 8, 8},
&bitmap_more,
Theme::getInstance()->bg_darkest->foreground,
Theme::getInstance()->bg_darkest->background};
Button button_pgup{
{0, 324, 120, 16},
" "};
Button button_pgdown{
{121, 324, 119, 16},
" "};
int button_w = 240 / rows_;
static constexpr int button_h = 48;
bool blink = false;
bool more = false;
size_t displayed_max{0};
size_t highlighted_item{0};
size_t offset{0};
+36 -20
View File
@@ -54,7 +54,6 @@
#include "ui_recon.hpp"
#include "ui_scanner.hpp"
#include "ui_sd_over_usb.hpp"
#include "ui_sd_wipe.hpp"
#include "ui_search.hpp"
#include "ui_settings.hpp"
#include "ui_siggen.hpp"
@@ -63,10 +62,8 @@
// #include "ui_test.hpp"
#include "ui_text_editor.hpp"
#include "ui_touchtunes.hpp"
#include "ui_view_wav.hpp"
#include "ui_weatherstation.hpp"
#include "ui_subghzd.hpp"
#include "ui_whipcalc.hpp"
#include "ui_battinfo.hpp"
#include "ui_external_items_menu_loader.hpp"
@@ -125,7 +122,7 @@ const NavigationView::AppList NavigationView::appList = {
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
{"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
{"recon", "Recon", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
{nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory<UtilitiesMenuView>()},
@@ -142,7 +139,7 @@ const NavigationView::AppList NavigationView::appList = {
{"level", "Level", RX, Color::green(), &bitmap_icon_options_radio, new ViewFactory<LevelView>()},
{"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGAppView>()},
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
{"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"scanner", "Scanner", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
@@ -153,7 +150,6 @@ const NavigationView::AppList NavigationView::appList = {
//{"sstv", "SSTV", RX, Color::dark_grey(), &bitmap_icon_sstv, new ViewFactory<NotImplementedView>()},
//{"tetra", "TETRA", RX, Color::dark_grey(), &bitmap_icon_tetra, new ViewFactory<NotImplementedView>()},
/* TX ********************************************************************/
//{"adsbtx", "ADS-B TX", TX, ui::Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBTxView>()},
{"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>()},
@@ -163,19 +159,15 @@ const NavigationView::AppList NavigationView::appList = {
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
{"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>()},
{"filemanager", "File Manager", UTILITIES, Color::green(), &bitmap_icon_dir, new ViewFactory<FileManagerView>()},
{"freqman", "Freq. Manager", UTILITIES, Color::green(), &bitmap_icon_freqman, new ViewFactory<FrequencyManagerView>()},
{"notepad", "Notepad", UTILITIES, Color::dark_cyan(), &bitmap_icon_notepad, new ViewFactory<TextEditorView>()},
{"iqtrim", "IQ Trim", UTILITIES, Color::orange(), &bitmap_icon_trim, new ViewFactory<IQTrimView>()},
{"notepad", "Notepad", UTILITIES, Color::dark_cyan(), &bitmap_icon_notepad, new ViewFactory<TextEditorView>()},
{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>()},
{"wavview", "Wav View", UTILITIES, Color::yellow(), &bitmap_icon_soundboard, new ViewFactory<ViewWavView>()},
// Dangerous apps.
{nullptr, "Flash Utility", UTILITIES, Color::red(), &bitmap_icon_peripherals_details, new ViewFactory<FlashUtilityView>()},
{nullptr, "Wipe SD card", UTILITIES, Color::red(), &bitmap_icon_tools_wipesd, new ViewFactory<WipeSDView>()},
};
const NavigationView::AppMap NavigationView::appMap = generate_app_map(NavigationView::appList);
@@ -310,6 +302,15 @@ SystemStatusView::SystemStatusView(
this->on_bias_tee();
};
button_fake_brightness.on_select = [this](ImageButton&) {
set_dirty();
pmem::toggle_fake_brightness_level();
refresh();
if (nullptr != parent()) {
parent()->set_dirty(); // The parent of NavigationView shal be the SystemView
}
};
button_camera.on_select = [this](ImageButton&) {
this->on_camera();
};
@@ -350,12 +351,29 @@ void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
batt_was_inited = true;
refresh();
}
// Check if charging state changed to charging
static bool was_charging = false;
if (msg->on_charger && !was_charging) {
// Only show charging modal when transitioning to charging state
nav_.display_modal(
"CHARGING",
"Screen on while charging?",
YESNO,
[this](bool keep_screen_on) {
if (!keep_screen_on) {
EventDispatcher::set_display_sleep(true);
}
});
}
was_charging = msg->on_charger;
if (!pmem::ui_hide_numeric_battery()) {
battery_text.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
}
if (!pmem::ui_hide_battery_icon()) {
battery_icon.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
};
}
}
void SystemStatusView::refresh() {
@@ -373,6 +391,7 @@ void SystemStatusView::refresh() {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
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;
if (!pmem::ui_hide_battery_icon()) {
@@ -405,6 +424,9 @@ void SystemStatusView::refresh() {
button_converter.set_bitmap(pmem::config_updown_converter() ? &bitmap_icon_downconvert : &bitmap_icon_upconvert);
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@@ -747,7 +769,7 @@ void NavigationView::handle_autostart() {
/* Helpers **************************************************************/
static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) {
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,
@@ -812,9 +834,7 @@ void ReceiversMenuView::on_populate() {
if (return_icon) {
add_item({"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }});
}
add_apps(nav_, *this, RX);
add_external_items(nav_, app_location_t::RX, *this, return_icon ? 1 : 0);
}
@@ -828,9 +848,7 @@ void TransmittersMenuView::on_populate() {
if (return_icon) {
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_apps(nav_, *this, TX);
add_external_items(nav_, app_location_t::TX, *this, return_icon ? 1 : 0);
}
@@ -846,9 +864,7 @@ void UtilitiesMenuView::on_populate() {
if (return_icon) {
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_apps(nav_, *this, UTILITIES);
add_external_items(nav_, app_location_t::UTILITIES, *this, return_icon ? 1 : 0);
}
@@ -869,7 +885,7 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
SystemMenuView::SystemMenuView(NavigationView& nav)
: nav_(nav) {
set_max_rows(2); // allow wider buttons
set_arrow_enabled(false);
show_arrows_enabled(false);
}
void SystemMenuView::on_populate() {
+10
View File
@@ -59,6 +59,10 @@ using namespace sd_card;
namespace ui {
void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc);
void add_external_items(NavigationView& nav, app_location_t location, BtnGridView& grid, uint8_t error_tile_pos);
bool verify_sdcard_format();
enum modal_t {
INFO = 0,
YESNO,
@@ -280,6 +284,12 @@ class SystemStatusView : public View {
Theme::getInstance()->fg_light->foreground,
Theme::getInstance()->bg_dark->background};
ImageButton button_fake_brightness{
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_brightness,
*Theme::getInstance()->status_active,
Theme::getInstance()->bg_dark->background};
SDCardStatusView sd_card_status_view{
{0, 0 * 16, 2 * 8, 1 * 16}};
+67 -48
View File
@@ -38,28 +38,67 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
} else
sample_count--;
if (tone_shape == 0) {
if (modulation == 0) {
// CW
re = 127; // max. signed 8 bits value . (-128 ...+127), max. amplitude , static phasor at 0º
im = 0;
} else {
if (tone_shape == 1) {
} else if (modulation == 2) {
// Digital BPSK consecutive 0,1,0,...continuous cycle, 1 bit/symbol, at rate of 2 symbols / Freq Tone Periode... without any Pulse shape at the moment .
re = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; // Sending 2 bits by Periode T of the GUI tone, alternative static phasor to 0, -180º , 0º
im = 0;
tone_phase += tone_delta; // In BPSK-QSPK we are using to calculate each 1/4 of the periode.
} else if (modulation == 3) {
// Digital QPSK consecutive 00, 01, 10, 11,00, ...continuous cycle ,2 bits/symbol, at rate of 4 symbols / Freq Tone Periode. not random., without any Pulse shape at the moment .
switch (((tone_phase & 0xFF000000) >> 24)) {
case 0 ... 63: // equivalent to 1/4 of total 360º degrees.
/* "00" */
re = (sine_table_i8[32]); // we are sending symbol-phasor 45º during 1/4 of the total periode
im = (sine_table_i8[32 + 64]); // 32 index = rounded (45º/360º * 255 total sin table steps) = 31,875
break;
case 64 ... 127:
/* "01" */
re = (sine_table_i8[96]); // symbol-phasor 135º
im = (sine_table_i8[96 + 64]); // 96 index = 32 + 256/4
break;
break;
case 128 ... 191:
/* "10" */
re = (sine_table_i8[159]); // symbol-phasor 225º
im = (sine_table_i8[159 + 64]); // 159 rounded index = 96 + 256/4 = 159.3
break;
case 192 ... 255:
/* "11" */
re = (sine_table_i8[223]); // symbol-phasor 315º ; 223 rounded index = (315/360) * 255 =223.125
im = (sine_table_i8[((223 + 64) & 0xFF)]); // Max index 255, circular periodic conversion.
break;
default:
break;
}
tone_phase += tone_delta; // In BPSK-QSPK we are using to calculate each 1/4 of the periode.
} else { // Other modulations: FM, DSB, AM
if (tone_shape == 0) {
// Sine
sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]);
} else if (tone_shape == 2) {
} else if (tone_shape == 1) {
// Triangle
int8_t a = (tone_phase & 0xFF000000) >> 24;
sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80;
} else if (tone_shape == 3) {
} else if (tone_shape == 2) {
// Saw up
sample = ((tone_phase & 0xFF000000) >> 24);
} else if (tone_shape == 4) {
} else if (tone_shape == 3) {
// Saw down
sample = ((tone_phase & 0xFF000000) >> 24) ^ 0xFF;
} else if (tone_shape == 5) {
} else if (tone_shape == 4) {
// Square
sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128;
} else if (tone_shape == 6) {
} else if (tone_shape == 5) {
// Noise generator, pseudo random noise generator, 16 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci.
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register
// 16 bits LFSR .taps: 16, 15, 13, 4 ;feedback polynomial: x^16 + x^15 + x^13 + x^4 + 1
@@ -79,49 +118,13 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
if (counter == 15) {
counter = 0;
}
} else if (tone_shape == 7) {
// Digital BPSK consecutive 0,1,0,...continuous cycle, 1 bit/symbol, at rate of 2 symbols / Freq Tone Periode... without any Pulse shape at the moment .
re = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; // Sending 2 bits by Periode T of the GUI tone, alternative static phasor to 0, -180º , 0º
im = 0;
} else if (tone_shape == 8) {
// Digital QPSK consecutive 00, 01, 10, 11,00, ...continuous cycle ,2 bits/symbol, at rate of 4 symbols / Freq Tone Periode. not random., without any Pulse shape at the moment .
switch (((tone_phase & 0xFF000000) >> 24)) {
case 0 ... 63: // equivalent to 1/4 of total 360º degrees.
/* "00" */
re = (sine_table_i8[32]); // we are sending symbol-phasor 45º during 1/4 of the total periode
im = (sine_table_i8[32 + 64]); // 32 index = rounded (45º/360º * 255 total sin table steps) = 31,875
break;
case 64 ... 127:
/* "01" */
re = (sine_table_i8[96]); // symbol-phasor 135º
im = (sine_table_i8[96 + 64]); // 96 index = 32 + 256/4
break;
break;
case 128 ... 191:
/* "10" */
re = (sine_table_i8[159]); // symbol-phasor 225º
im = (sine_table_i8[159 + 64]); // 159 rounded index = 96 + 256/4 = 159.3
break;
case 192 ... 255:
/* "11" */
re = (sine_table_i8[223]); // symbol-phasor 315º ; 223 rounded index = (315/360) * 255 =223.125
im = (sine_table_i8[((223 + 64) & 0xFF)]); // Max index 255, circular periodic conversion.
break;
default:
break;
}
}
if (tone_shape != 6) { //(all except Pseudo Random White Noise). We are in (1):periodic signals or (2):BPSK/QPSK , in both cases ,we need Tone updated acum sum phases to modulate in FM / or control phasor phase (BPSK & QPSK.)
tone_phase += tone_delta; // In periodic signals(Sine/triangle/square) we are using to FM mod. in BPSK-QSPK we are using to calculate each 1/4 of the periode.
if (tone_shape != 5) { // All periodic except Pseudo Random White Noise.
tone_phase += tone_delta; // In periodic signals we are using phase to generate the tone to be modulated.
}
if (tone_shape < 7) { // All Option shape signals except BPSK(7) & QPSK(8) we are modulating in FM. (Those two has phase shift modulation XPSK , not FM )
if (modulation == 1) {
// Do FM modulation
delta = sample * fm_delta;
@@ -130,6 +133,21 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); // sin LUT is not dealing with decimals , output range [-128 ,...127]
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
} else if (modulation == 4) {
// Do Double Side Band modulation
re = sample;
im = 0;
} else if (modulation == 5) {
// Do AM modulation (100% mod index)
re = (127 >> 1) + (sample >> 1);
im = 0;
} else if (modulation == 6) {
// Do AM modulation (50% mod index)
re = 95 + (sample >> 2);
im = 0;
}
}
@@ -154,7 +172,8 @@ void SigGenProcessor::on_message(const Message* const msg) {
auto_off = false;
fm_delta = message.bw * (0xFFFFFFULL / 1536000);
tone_shape = message.shape;
tone_shape = message.shape & 0xF;
modulation = (message.shape & 0xF0) >> 4;
// lfsr = seed_value ; // Finally not used , init lfsr 8 bits.
lfsr_16 = seed_value_16; // init lfsr 16 bits.
+1 -1
View File
@@ -36,7 +36,7 @@ class SigGenProcessor : public BasebandProcessor {
bool configured{false};
uint32_t tone_delta{0}, fm_delta{}, tone_phase{0};
uint8_t tone_shape{};
uint8_t tone_shape{}, modulation{};
uint32_t sample_count{0};
bool auto_off{};
int32_t phase{0}, sphase{0}, delta{0}; // they may have sign in the pseudo random sample generation.
@@ -1060,6 +1060,7 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluste
}
}
/* go to default */
__attribute__ ((fallthrough));
#endif
default:
val = 1; /* Internal error */
+1
View File
@@ -231,6 +231,7 @@ void lcd_start_ram_write(
lcd_caset(p.x(), p.x() + s.width() - 1);
lcd_paset(p.y(), p.y() + s.height() - 1);
lcd_ramwr_start();
io.update_cached_values();
}
void lcd_start_ram_read(
+14
View File
@@ -77,10 +77,24 @@ void IO::reference_oscillator(const bool enable) {
io_write(1, io_reg);
}
bool IO::get_dark_cover() {
return portapack::persistent_memory::apply_fake_brightness() & (!portapack::persistent_memory::config_lcd_inverted_mode());
}
bool IO::get_is_inverted() {
return portapack::persistent_memory::config_lcd_inverted_mode();
}
uint8_t IO::get_brightness() {
return portapack::persistent_memory::fake_brightness_level();
}
void IO::update_cached_values() {
inverted_enabled = get_is_inverted();
dark_cover_enabled = get_dark_cover();
brightness = get_brightness();
}
uint32_t IO::io_update(const TouchPinsConfig write_value) {
/* Very touchy code to save context of PortaPack data bus while the
* resistive touch pin drive is changed. Order of operations is
+22
View File
@@ -162,6 +162,9 @@ class IO {
}
void lcd_write_pixel(ui::Color pixel) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
lcd_write_data(pixel.v);
}
@@ -170,12 +173,18 @@ class IO {
}
void lcd_write_pixels(ui::Color pixel, size_t n) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
while (n--) {
lcd_write_data(pixel.v);
}
}
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
auto v = pixel.v;
n >>= 3;
while (n--) {
@@ -222,7 +231,13 @@ class IO {
return switches_raw;
}
bool inverted_enabled = false;
bool dark_cover_enabled = false;
uint8_t brightness = 0;
bool get_is_inverted();
bool get_dark_cover();
uint8_t get_brightness();
void update_cached_values();
uint32_t io_update(const TouchPinsConfig write_value);
@@ -404,6 +419,13 @@ class IO {
const auto value_low = data_read();
uint32_t original_value = (value_high << 8) | value_low;
if (inverted_enabled) return original_value;
if (dark_cover_enabled) {
// this is read data, so if the fake brightness is enabled AKA get_dark_cover() == true,
// then shift to back side AKA UNDARKENED_PIXEL, to prevent read shifted darkern info
original_value = UNDARKENED_PIXEL(original_value, brightness);
}
return original_value;
}
@@ -109,7 +109,7 @@ struct ui_config_t {
bool hide_clock : 1;
bool clock_show_date : 1;
bool clkout_enabled : 1;
bool unused_FB : 1; // apply_fake_brightness
bool apply_fake_brightness : 1; // Fake brightness level, which eventually could be something along the lines of apply_pwm_brightness
bool stealth_mode : 1;
bool config_login : 1;
bool config_splash : 1;
@@ -130,7 +130,7 @@ struct ui_config2_t {
bool hide_sd_card : 1;
bool hide_mute : 1;
bool UNUSED_3 : 1;
bool hide_fake_brightness : 1;
bool hide_numeric_battery : 1;
bool hide_battery_icon : 1;
bool override_batt_calc : 1;
@@ -212,7 +212,7 @@ struct data_t {
bool updown_frequency_rx_correction;
bool updown_frequency_tx_correction;
bool lcd_inverted_mode : 1;
bool UNUSED_5 : 1;
bool encoder_dial_direction : 1; // false = normal, true = reverse
bool UNUSED_6 : 1;
bool UNUSED_7 : 1;
@@ -226,6 +226,9 @@ struct data_t {
// Rotary encoder dial sensitivity (encoder.cpp/hpp)
uint16_t encoder_dial_sensitivity : 4;
// fake brightness level (not switch, switch is in another place)
uint16_t fake_brightness_level : 4;
// Encoder rotation rate multiplier for larger increments when rotated rapidly
uint16_t encoder_rate_multiplier : 4;
@@ -287,7 +290,7 @@ struct data_t {
updown_frequency_rx_correction(false),
updown_frequency_tx_correction(false),
lcd_inverted_mode(false),
UNUSED_5(false),
encoder_dial_direction(false),
UNUSED_6(false),
UNUSED_7(false),
@@ -297,6 +300,7 @@ struct data_t {
frequency_tx_correction(0),
encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL),
fake_brightness_level(BRIGHTNESS_50),
encoder_rate_multiplier(1),
UNUSED(0),
@@ -413,7 +417,8 @@ void defaults() {
set_config_splash(true);
set_config_disable_external_tcxo(false);
set_encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL);
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
set_encoder_dial_direction(false); // false = normal, true = reverse
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
set_menu_color(Color::grey());
set_ui_hide_numeric_battery(true); // hide the numeric battery by default - no space to display it
@@ -465,6 +470,7 @@ void init() {
set_config_mode_storage_direct(config_mode_backup);
// Firmware upgrade handling - adjust newly defined fields where 0 is an invalid default
if (fake_brightness_level() == 0) set_fake_brightness_level(BRIGHTNESS_50);
if (menu_color().v == 0) set_menu_color(Color::grey());
}
@@ -645,6 +651,10 @@ bool stealth_mode() {
return data->ui_config.stealth_mode;
}
bool apply_fake_brightness() {
return data->ui_config.apply_fake_brightness;
}
bool config_login() {
return data->ui_config.config_login;
}
@@ -748,6 +758,10 @@ void set_config_backlight_timer(const backlight_config_t& new_value) {
data->ui_config.enable_backlight_timeout = static_cast<uint8_t>(new_value.timeout_enabled());
}
void set_apply_fake_brightness(const bool v) {
data->ui_config.apply_fake_brightness = v;
}
uint32_t pocsag_last_address() {
return data->pocsag_last_address;
}
@@ -934,7 +948,9 @@ bool ui_hide_clock() {
bool ui_hide_sd_card() {
return data->ui_config2.hide_sd_card;
}
bool ui_hide_fake_brightness() {
return data->ui_config2.hide_fake_brightness;
}
bool ui_hide_numeric_battery() {
return data->ui_config2.hide_numeric_battery;
}
@@ -977,6 +993,9 @@ void set_ui_hide_clock(bool v) {
void set_ui_hide_sd_card(bool v) {
data->ui_config2.hide_sd_card = v;
}
void set_ui_hide_fake_brightness(bool v) {
data->ui_config2.hide_fake_brightness = v;
}
void set_ui_hide_numeric_battery(bool v) {
data->ui_config2.hide_numeric_battery = v;
}
@@ -1062,6 +1081,13 @@ void set_encoder_rate_multiplier(uint8_t v) {
data->encoder_rate_multiplier = v;
}
bool encoder_dial_direction() {
return data->encoder_dial_direction;
}
void set_encoder_dial_direction(bool v) {
data->encoder_dial_direction = v;
}
// Recovery mode magic value storage
static data_t* data_direct_access = reinterpret_cast<data_t*>(memory::map::backup_ram.base());
@@ -1094,6 +1120,26 @@ void set_config_dst(dst_config_t v) {
rtc_time::dst_init();
}
// Fake brightness level (switch is in another place)
uint8_t fake_brightness_level() {
return data->fake_brightness_level;
}
void set_fake_brightness_level(uint8_t v) {
data->fake_brightness_level = v;
}
// Cycle through 4 brightness options: disabled -> enabled/50% -> enabled/25% -> enabled/12.5% -> disabled
void toggle_fake_brightness_level() {
bool fbe = apply_fake_brightness();
if (config_lcd_inverted_mode()) return; // for now only inverted mode OR fake brightness
if ((!fbe) || (data->fake_brightness_level >= BRIGHTNESS_12p5)) {
set_apply_fake_brightness(!fbe);
data->fake_brightness_level = BRIGHTNESS_50;
} else {
data->fake_brightness_level++;
}
}
// Menu Color Scheme
Color menu_color() {
return data->menu_color;
@@ -1208,9 +1254,11 @@ bool debug_dump() {
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(data->frequency_tx_correction));
pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity));
pmem_dump_file.write_line("encoder_rate_multiplier: " + to_string_dec_uint(data->encoder_rate_multiplier));
pmem_dump_file.write_line("encoder_dial_direction: " + to_string_dec_uint(data->encoder_dial_direction)); // 0 = normal, 1 = reverse
pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb));
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(data->config_mode_storage, 8));
pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)data->dst_config.v, 8));
pmem_dump_file.write_line("fake_brightness_level: " + to_string_dec_uint(data->fake_brightness_level));
pmem_dump_file.write_line("menu_color: 0x" + to_string_hex(data->menu_color.v, 4));
pmem_dump_file.write_line("touchscreen_threshold: " + to_string_dec_uint(data->touchscreen_threshold));
@@ -1227,6 +1275,7 @@ bool debug_dump() {
pmem_dump_file.write_line("ui_config hide_clock: " + to_string_dec_uint(data->ui_config.hide_clock));
pmem_dump_file.write_line("ui_config clock_with_date: " + to_string_dec_uint(data->ui_config.clock_show_date));
pmem_dump_file.write_line("ui_config clkout_enabled: " + to_string_dec_uint(data->ui_config.clkout_enabled));
pmem_dump_file.write_line("ui_config apply_fake_brightness: " + to_string_dec_uint(data->ui_config.apply_fake_brightness));
pmem_dump_file.write_line("ui_config stealth_mode: " + to_string_dec_uint(data->ui_config.stealth_mode));
pmem_dump_file.write_line("ui_config config_login: " + to_string_dec_uint(data->ui_config.config_login));
pmem_dump_file.write_line("ui_config config_splash: " + to_string_dec_uint(data->ui_config.config_splash));
@@ -1241,6 +1290,7 @@ bool debug_dump() {
pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(data->ui_config2.hide_clock));
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card));
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(data->ui_config2.hide_mute));
pmem_dump_file.write_line("ui_config2 hide_fake_brightness: " + to_string_dec_uint(data->ui_config2.hide_fake_brightness));
pmem_dump_file.write_line("ui_config2 hide_battery_icon: " + to_string_dec_uint(data->ui_config2.hide_battery_icon));
pmem_dump_file.write_line("ui_config2 hide_numeric_battery: " + to_string_dec_uint(data->ui_config2.hide_numeric_battery));
pmem_dump_file.write_line("ui_config2 theme_id: " + to_string_dec_uint(data->ui_config2.theme_id));
@@ -135,6 +135,12 @@ typedef union {
} dst_config_t;
static_assert(sizeof(dst_config_t) == sizeof(uint32_t));
enum fake_brightness_level_options {
BRIGHTNESS_50 = 1,
BRIGHTNESS_25 = 2,
BRIGHTNESS_12p5 = 3, // 12p5 is 12.5
};
namespace cache {
/* Set values in cache to sensible defaults. */
@@ -246,6 +252,8 @@ uint8_t encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);
uint8_t encoder_rate_multiplier();
void set_encoder_rate_multiplier(uint8_t v);
bool encoder_dial_direction();
void set_encoder_dial_direction(bool v);
uint32_t config_mode_storage_direct();
void set_config_mode_storage_direct(uint32_t v);
@@ -267,6 +275,15 @@ uint16_t clkout_freq();
dst_config_t config_dst();
void set_config_dst(dst_config_t v);
/* Fake brightness */
// switch (if do color change):
bool apply_fake_brightness();
void set_apply_fake_brightness(const bool v);
// level (color change level):
uint8_t fake_brightness_level();
void set_fake_brightness_level(uint8_t v);
void toggle_fake_brightness_level();
/* Touchscreen threshold */
uint16_t touchscreen_threshold();
void set_touchscreen_threshold(uint16_t v);
@@ -319,6 +336,7 @@ bool ui_hide_camera();
bool ui_hide_sleep();
bool ui_hide_bias_tee();
bool ui_hide_clock();
bool ui_hide_fake_brightness();
bool ui_hide_numeric_battery();
bool ui_hide_battery_icon();
bool ui_hide_sd_card();
@@ -332,6 +350,7 @@ void set_ui_hide_camera(bool v);
void set_ui_hide_sleep(bool v);
void set_ui_hide_bias_tee(bool v);
void set_ui_hide_clock(bool v);
void set_ui_hide_fake_brightness(bool v);
void set_ui_hide_numeric_battery(bool v);
void set_ui_hide_battery_icon(bool v);
void set_ui_hide_sd_card(bool v);
+11 -1
View File
@@ -35,6 +35,13 @@ struct JammerChannel {
uint32_t duration;
};
struct HopperChannel {
bool enabled;
uint64_t center;
uint32_t width;
uint32_t duration;
};
struct ToneDef {
uint32_t delta;
uint32_t duration;
@@ -61,7 +68,10 @@ struct SharedMemory {
union {
ToneData tones_data;
JammerChannel jammer_channels[24];
struct {
JammerChannel jammer_channels[24];
HopperChannel hopper_channels[24];
} dummy_seperate;
uint8_t data[512];
} bb_data{{{{0, 0}}, 0, {0}}};
+75
View File
@@ -170,6 +170,81 @@ temp_humid Packet::get_temp_humid() const {
result.humid = 0;
result.temp = 0;
if (type_ == Type::Meteomodem_M10) {
// https://github.com/projecthorus/radiosonde_auto_rx/blob/master/demod/mod/m10mod.c
// temp:
uint16_t ADC_Ti_raw = (reader_bi_m.read(0x49 * 8, 8) << 8) | reader_bi_m.read(0x48 * 8, 8) << 8; // int.temp.diode, ref: 4095->1.5V
if (ADC_Ti_raw != 0) {
// internal
float vti, ti;
// INCH1A (temp.diode), slau144
vti = ADC_Ti_raw / 4095.0 * 1.5; // V_REF+ = 1.5V, no calibration
ti = (vti - 0.986) / 0.00355; // 0.986/0.00355=277.75, 1.5/4095/0.00355=0.1032
result.temp = ti;
}
// NTC - Thermistor Shibaura PB5 - 41E
float p0 = 1.07303516e-03,
p1 = 2.41296733e-04,
p2 = 2.26744154e-06,
p3 = 6.52855181e-08;
// T/K = 1/( p0 + p1*ln(R) + p2*ln(R)^2 + p3*ln(R)^3 )
// range/scale 0, 1, 2: // M10-pcb
float Rs_T[3] = {12.1e3, 36.5e3, 475.0e3}; // bias/series
float Rp[3] = {1e20, 330.0e3, 2000.0e3}; // parallel, Rp[0]=inf
uint8_t scT; // {0,1,2}, range/scale voltage divider
uint16_t ADC_RT; // ADC12 P6.7(A7) , adr_0377h,adr_0376h
// uint16_t Tcal[2]; // adr_1000h[scT*4]
float adc_max = 4095.0; // ADC12
float x, R;
float T = 0; // T/Kelvin
scT = reader_bi_m.read(0x3E * 8, 8); // adr_0455h
ADC_RT = (reader_bi_m.read(0x40 * 8, 8) << 8) | reader_bi_m.read(0x3F * 8, 8);
if (ADC_RT != 0) {
ADC_RT -= 0xA000;
// Tcal[0] = (reader_bi_m.read(0x42 * 8, 8) << 8) | reader_bi_m.read(0x41 * 8, 8);
// Tcal[1] = (reader_bi_m.read(0x44 * 8, 8) << 8) | reader_bi_m.read(0x43 * 8, 8);
x = (adc_max - ADC_RT) / ADC_RT; // (Vcc-Vout)/Vout
if (scT < 3)
R = Rs_T[scT] / (x - Rs_T[scT] / Rp[scT]);
else
R = -1;
if (R > 0) T = 1 / (p0 + p1 * log(R) + p2 * log(R) * log(R) + p3 * log(R) * log(R) * log(R));
result.temp = T - 273.15;
}
// humidity:
// get count rh:
float TBCCR1 = (reader_bi_m.read(0x35 * 8, 8) | (reader_bi_m.read(0x36 * 8, 8) << 8) | (reader_bi_m.read(0x37 * 8, 8) << 16)) / 1000.0;
// get count 55:
float TBCREF = (reader_bi_m.read(0x32 * 8, 8) | (reader_bi_m.read(0x33 * 8, 8) << 8) | (reader_bi_m.read(0x34 * 8, 8) << 16)) / 1000.0;
if (TBCREF != 0) {
float cRHc55 = TBCCR1 / TBCREF; //;get_count_RH(gpx) / get_count_55(gpx); // CalRef 55%RH , T=20C ?
// get_Tntc2: --unused.
// float Rs = 22.1e3; // P5.6=Vcc
// float R25 = 2.2e3;
// float b = 3650.0; // B/Kelvin
// float T25 = 25.0 + 273.15; // T0=25C, R0=R25=5k
// -> Steinhart-Hart coefficients (polyfit):
// cRHc55_RH:
// float TH = get_Tntc2(gpx); --unused
float rh = (cRHc55 - 0.8955) / 0.002; // UPSI linear transfer function
// temperature compensation
float T0 = 0.0, T1 = -30.0; // T/C
if (result.temp < T0) rh += T0 - result.temp / 5.5; // approx/empirical
if (result.temp < T1) rh *= 1.0 + (T1 - result.temp) / 75.0; // approx/empirical
if (rh < 0.0) rh = 0.0;
if (rh > 100.0) rh = 100.0;
result.humid = rh;
}
}
if (type_ == Type::Vaisala_RS41_SG && crc_ok_RS41()) // Only process if packet is healthy
{
// memset(calfrchk, 0, 51); // is this necessary ? only if the sondeID changes (new sonde)
+2 -1
View File
@@ -71,7 +71,8 @@ enum app_location_t : uint32_t {
RX,
TX,
DEBUG,
HOME
HOME,
SETTINGS
};
struct standalone_application_information_t {
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
project(standalone_apps)
+1
View File
@@ -0,0 +1 @@
__pycache__/
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
for fname in $(ls ../graphics/); do
fname=$(echo "$fname" | awk -F'.' '{print $1}')
out=$(grep -r --exclude="bitmap.hpp" "$fname" ../* )
ret=$?
if [[ "$ret" -ne 0 ]]; then
echo "$fname not found"
fi
done
+21
View File
@@ -1,5 +1,26 @@
#!/bin/bash
#
# Copyright (C) 2024 u-foka
#
# 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.
#
set -e # exit immediatelly on any failure
build_make() {
@@ -0,0 +1,265 @@
# Copyleft Whiterose from the Dark Army
#
# 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.
# this tool allow you flash pp and copy the apps in fastest way. note that you need to have a sdcard switcher to switch sdcard to pc.
# although, if you don't have one, you still can take out the sdcard and plug it into your pc, but i think it would not pretty fast.
#!/usr/bin/env python3
import sys
import glob
import serial
import serial.tools.list_ports
import os
import shutil
import time
import subprocess
import tempfile
# config
SUDO_PASSWORD = ""
SDCARD_LABEL = "portapack"
def write_command(ser, command, wait_response=None, remove_echo=False, dont_wait_ch=False):
# clean buffer
while True:
try:
if ser.in_waiting:
ser.read(ser.in_waiting)
else:
break
except:
break
command_with_newline = command + "\r\n"
try:
ser.write(command_with_newline.encode())
except Exception as e:
return f"write err: {str(e)}"
response = ""
expected_echo = f"{command}\r\n"
wait_response_met = wait_response is None
while True:
try:
if ser.in_waiting:
msg = ser.read(ser.in_waiting).decode('utf-8')
response += msg
if wait_response and wait_response in response:
wait_response_met = True
if wait_response_met and (dont_wait_ch or "ch> " in response):
if remove_echo:
return response.replace(expected_echo, "")
return response
except serial.SerialTimeoutException:
continue
except Exception as e:
return f"e: {str(e)}"
def get_serial_devices():
ports = serial.tools.list_ports.comports()
if not ports:
print("no serial device found")
return None
portapack_device = None
for port in ports:
if port.product == "PortaPack Mayhem":
print(f"found your pp:")
print(f"device: {port.device}")
print(f"description: {port.description}")
print(f"hwid: {port.hwid}")
if port.manufacturer:
print(f"manufacturer: {port.manufacturer}")
if port.serial_number:
print(f"serial_number: {port.serial_number}")
portapack_device = port
break
if not portapack_device:
print("no pp device found")
return portapack_device
def run_sudo_command(cmd):
if not SUDO_PASSWORD:
print("sudo password not set, please hard code it in the script")
return
try:
process = subprocess.Popen(
['sudo', '-S'] + cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
stdout, stderr = process.communicate(input=SUDO_PASSWORD + '\n')
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, cmd, stdout, stderr)
return stdout
except subprocess.CalledProcessError as e:
print(f"sudo command failed: {e}")
raise
def get_pp_device_linux():
try:
cmd = ["lsblk", "-o", "NAME,LABEL,FSTYPE,SIZE,MOUNTPOINT", "-n", "-p"]
result = subprocess.run(cmd, capture_output=True, text=True)
# find your pp sd
for line in result.stdout.splitlines():
clean_line = line.replace('└─', '').replace('├─', '').strip()
parts = clean_line.split()
if len(parts) >= 2 and SDCARD_LABEL in parts[1]: # checker
device_path = parts[0]
# if path valid
if not os.path.exists(device_path):
continue
# if mounted, return mountpoint
if len(parts) >= 5:
mountpoint = parts[4]
return device_path, mountpoint
# if not, create temp dir and mount
mount_point = tempfile.mkdtemp()
print(f"mount {device_path} to {mount_point}")
run_sudo_command(["mount", "-o", f"uid={os.getuid()},gid={os.getgid()}", device_path, mount_point])
return device_path, mount_point
return None, None
except subprocess.CalledProcessError as e:
print(f"command failed: {str(e)}")
return None, None
def wait_for_pp_drive():
while True:
if os.name == 'nt': # win didn't test yet, TODO
drives = [d + ':' for d in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if os.path.exists(d + ':')]
for drive in drives:
if os.path.basename(os.path.abspath(drive)).lower() == SDCARD_LABEL:
return drive, None
else: # linux and mac
device_path, mount_point = get_pp_device_linux()
if device_path and mount_point:
return mount_point, device_path
time.sleep(1)
def run():
# worker 1: check device connected
device = get_serial_devices()
if not device:
print("no pp device found")
return
try:
# worker 2: open serial and send hackrf cmd
ser = serial.Serial(device.device, baudrate=115200, timeout=1)
for i in range(2):
write_command(ser, "hackrf")
ser.close()
# worker 3: hint user to swtich sdcard to pc
print("\033[92m\n\t+------------------------+")
print("\t| switch PP sdcard to PC |")
print("\t+------------------------+\033[0m")
print("wait PP sdcard...")
# worker 4: wait pp sd
mount_point, device_path = wait_for_pp_drive()
if not mount_point:
print("no pp sd found")
return
print(f"find pp sd: {mount_point}")
# worker 5: copy ppma and ppmp apps
apps_dir = os.path.join(mount_point, 'APPS')
if not os.path.exists(apps_dir):
os.makedirs(apps_dir)
firmware_dir = os.path.join('.', 'firmware', 'application')
for ext in ['ppma', 'ppmp']:
for file in glob.glob(os.path.join(firmware_dir, f'*.{ext}')):
dest = os.path.join(apps_dir, os.path.basename(file))
print(f"cp: {os.path.basename(file)}")
shutil.copy2(file, dest)
print("apps copy done")
# worker 6: umount pp sd
if os.name != 'nt' and device_path:
run_sudo_command(["umount", mount_point])
os.rmdir(mount_point) # 删除临时挂载点
# worker 7: run flash cmd with hackrf_spiflash util
print("\nflash...")
process = subprocess.Popen(
["hackrf_spiflash", "-w", "./firmware/portapack-mayhem-firmware.bin"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True
)
while True:
output = process.stdout.readline()
if output:
print(output.strip())
if process.poll() is not None:
break
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, "hackrf_spiflash")
# worker 8: hint user to swtich sdcard switcher back to pp
print("\033[92m\n\t+------------------------------------------------+")
print("\t| switch sdcard switcher back to pp, then reboot |")
print("\t+------------------------------------------------+\033[0m")
except serial.SerialException as e:
print(f"serial comm error: {str(e)}")
except OSError as e:
print(f"file operation error: {str(e)}")
except subprocess.CalledProcessError as e:
print(f"system command error: {str(e)}")
except Exception as e:
print(f"error: {str(e)}")
# ensure umount when error
if os.name != 'nt' and 'mount_point' in locals() and device_path:
try:
run_sudo_command(["umount", mount_point])
os.rmdir(mount_point)
except:
pass
if __name__ == "__main__":
try:
run()
except Exception as e:
print(f"error: {str(e)}")
sys.exit(1)
@@ -53,7 +53,7 @@ AboutView::AboutView(NavigationView& nav) {
button_ok.focus();
};
for (auto& authors_line : authors_list) {
for (auto& authors_line : mayhem_information_list) {
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
if (authors_line.size() > 0) {
if (authors_line[0] == '#') {
@@ -84,15 +84,13 @@ void AboutView::on_frame_sync() {
menu_view.set_highlighted(current + 1);
} else {
menu_view.set_highlighted(0);
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
menu_view.set_highlighted(2); // the first line that has human name
}
}
}
void AboutView::focus() {
button_ok.focus();
menu_view.set_highlighted(2); // the first line that has human name
menu_view.set_highlighted(3); // contributors block starting line
}
bool AboutView::on_touch(const TouchEvent) {
@@ -141,8 +139,12 @@ def get_contributors(url):
def generate_content(projects):
project_contrib = []
project_contrib.append("string_view authors_list[] = {\n")
project_contrib.append(" \"# * List of contributors * \",\n")
project_contrib.append("string_view mayhem_information_list[] = {\n")
project_contrib.append(" \"#****** Mayhem Community ******\",\n")
project_contrib.append(" \" \",\n")
project_contrib.append(" \" https://discord.mayhem.app\",\n")
project_contrib.append(" \" \",\n")
project_contrib.append(" \"#**** List of contributors ****\",\n")
for project in projects:
project_contrib.append(" \" \",\n")
project_contrib.append(f" \"#{project[0]}:\",\n")
@@ -170,7 +172,7 @@ def pp_create_ui_about_simple_cpp(cpp_file, cppheader, cppcontent, cppfooter):
def pp_change_ui_about_simple_cpp(cpp_file, cppcontent):
content = []
content_pattern = re.compile(r"string_view authors_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
content_pattern = re.compile(r"string_view mayhem_information_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
# Read original file
with open(cpp_file, 'r') as file:
+20
View File
@@ -161,3 +161,23 @@ write_image(spi_image, output_path)
percent_remaining = round(1000 * pad_size / spi_size) / 10;
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
# copy the fast flash script
build_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'build')
flash_py_path = os.path.join(build_dir, 'flash.py')
if not os.path.exists(flash_py_path):
current_dir = os.path.dirname(__file__)
source_file = os.path.join(current_dir, 'fast_flash_pp_and_copy_apps.py')
if not os.path.exists(build_dir):
os.makedirs(build_dir)
# cp
import shutil
print(f"\ncopy {source_file} to {flash_py_path}\n")
shutil.copy2(source_file, flash_py_path)
+275
View File
@@ -0,0 +1,275 @@
#
# copyleft Elliot Alderson from F society
# copyleft Darlene Alderson from F society
#
# 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.
#
import re
import tkinter as tk
from typing import Dict, List, Tuple
import argparse
from dataclasses import dataclass
"""
TODO: Multile class in one file seperation
TODO: Add more widget type
widget compatible guide:
1. add the preview color in the "widgets = {" table
(note that this color is only for easier to distinguish)
2. add the widget type and also regex in the "parsers" table
(note that your regex should apply all the possible constructor overload (re-impl))
"""
# pp hard coded vars
screen_width = 240
SCREEN_W = 240
screen_height = 320
CHARACTER_WIDTH = 8
LINE_HEIGHT = 16
topbar_offset = 16
# widgets actually drew after the top bar,
# for example if Y = 0 then Y on screen actually is 16
# scale factor
scale = 2
widgets = {
"Button": "lightgray",
"NewButton": "lightyellow",
"Text": "lightblue",
"Rectangle": "lightgreen",
"Image": "pink",
"ImageButton": "lightpink",
"NumberField": "peachpuff",
"ProgressBar": "lightcoral",
"Console": "wheat",
"Checkbox": "plum",
"Label": "lavender",
"TextField": "paleturquoise",
"OptionsField": "palegreen",
"VuMeter": "sandybrown",
"BigFrequency": "khaki"
}
@dataclass
class Widget:
name: str
widget_type: str
x: int
y: int
width: int
height: int
text: str = ""
class WidgetParser:
def __init__(self):
self.parsers: Dict[str, re.Pattern] = {
'Button': re.compile( # TODO: fix those that using language helper
r'Button\s+(\w+)\s*\{\s*\{([^}]+)\},\s*"([^"]+)"\s*\};',
re.MULTILINE
),
'NewButton': re.compile(
r'NewButton\s+(\w+)\s*\{\s*(?:\{([^}]+)\}|{}),\s*(?:"([^"]*)"|\{\}),\s*(?:[^,}]+(?:,\s*[^}]+)*|\{\})\};',
re.MULTILINE
),
'Text': re.compile(
r'Text\s+(\w+)\s*\{\s*\{([^}]+)\}(?:\s*,\s*"([^"]*)")?\s*\};',
re.MULTILINE
),
'ProgressBar': re.compile(
r'ProgressBar\s+(\w+)\s*\{\s*\{([^}]+)\}\s*\};',
re.MULTILINE
),
'Console': re.compile(
r'Console\s+(\w+)\s*\{\s*\{([^}]+)\}\s*\};',
re.MULTILINE
),
'Label': re.compile(
r'Label\s+(\w+)\s*\{\s*\{([^}]+)\},\s*"([^"]+)"\s*\};',
re.MULTILINE
),
'VuMeter': re.compile(
r'VuMeter\s+(\w+)\s*\{\s*\{([^}]+)\},\s*\d+,\s*(?:true|false)\s*\};',
re.MULTILINE
),
'BigFrequency': re.compile(
r'BigFrequency\s+(\w+)\s*\{\s*\{([^}]+)\},\s*\d+\s*\};',
re.MULTILINE
)
}
def parse_coordinates(self, coord_str: str) -> Tuple[int, int, int, int]:
"""Parse coordinate string like '2 * 8, 8 * 16, 8 * 8, 3 * 16'"""
if not coord_str or coord_str.isspace():
return (0, 0, 0, 0) # for empty constructor fallback
# split and evaluate each coordinate expression
coords = [eval(x.strip()) for x in coord_str.split(',')]
# ensure we have exactly 4 coordinates, pad with zeros if needed
while len(coords) < 4:
coords.append(0)
# only take the first 4 coordinates if there are more
return tuple(coords[:4])
def parse_file(self, filepath: str) -> List[Widget]:
with open(filepath, 'r') as f:
content = f.read()
widgets = []
for widget_type, pattern in self.parsers.items():
matches = pattern.finditer(content)
for match in matches:
name = match.group(1) # Widget name is always in group 1
coords = self.parse_coordinates(match.group(2) if match.group(2) else "")
# Only try to get text if the pattern has 3 or more groups
text = ""
try:
if match.lastindex >= 3:
text = match.group(3) if match.group(3) else ""
except IndexError:
pass
widgets.append(Widget(
name=name,
widget_type=widget_type,
x=coords[0],
y=coords[1],
width=coords[2],
height=coords[3],
text=text
))
return widgets
class WidgetPreview(tk.Tk):
def __init__(self, widgets: List[Widget]):
super().__init__()
self.title("Widget Preview")
self.canvas = tk.Canvas(self, width=screen_width * scale, height=screen_height * scale, bg='white')
self.canvas.pack(padx=10, pady=10)
self.all_text_elements = []
self.draw_widgets(widgets)
def draw_widgets(self, widgets: List[Widget]):
draw_top_bar(self)
for widget in widgets:
self.draw_widget(widget)
def draw_widget(self, widget: Widget):
print(f"Drawing widget: {widget.name} ({widget.widget_type})")
x1 = widget.x * scale
y1 = (widget.y + topbar_offset) * scale
x2 = x1 + (widget.width * scale)
y2 = y1 + (widget.height * scale)
print(f"Coordinates: ({x1}, {y1}), ({x2}, {y2})")
if widget.widget_type == "VuMeter":
segment_height = widget.height / 8 * scale
for i in range(8):
self.canvas.create_rectangle(
x1, y1 + (i * segment_height),
x2, y1 + ((i+1) * segment_height),
fill=widgets[widget.widget_type],
outline="gray"
)
elif widget.widget_type == "BigFrequency":
# Draw 7-segment style display
self.canvas.create_rectangle(
x1, y1, x2, y2,
fill=widgets[widget.widget_type],
outline="gray"
)
self.canvas.create_text(
(x1 + x2) / 2,
(y1 + y2) / 2,
text="433.92" # placeholder text
)
else:
# defualt rendering
rect_id = self.canvas.create_rectangle(
x1, y1, x2, y2,
fill=widgets[widget.widget_type]
)
type_text_id = self.canvas.create_text(
(x1 + x2) // 2,
(y1 + y2) // 2,
text=widget.widget_type
)
detail_text_id = self.canvas.create_text(
(x1 + x2) // 2,
(y1 + y2) // 2,
text=f"{widget.widget_type}|{widget.name}|{widget.text}",
state='hidden'
)
widget_texts = {
'type': type_text_id,
'detail': detail_text_id
}
self.all_text_elements.append(widget_texts)
# hover handlers
def on_enter(event):
for texts in self.all_text_elements:
self.canvas.itemconfig(texts['type'], state='hidden')
self.canvas.itemconfig(texts['detail'], state='hidden')
self.canvas.itemconfig(detail_text_id, state='normal')
self.canvas.tag_raise(detail_text_id)
def on_leave(event):
for texts in self.all_text_elements:
self.canvas.itemconfig(texts['type'], state='normal')
self.canvas.tag_raise(texts['type'])
self.canvas.itemconfig(texts['detail'], state='hidden')
for item_id in [rect_id, type_text_id, detail_text_id]:
self.canvas.tag_bind(item_id, '<Enter>', on_enter)
self.canvas.tag_bind(item_id, '<Leave>', on_leave)
def draw_top_bar(self):
self.canvas.create_rectangle(0, 0, screen_width * scale, topbar_offset * scale, fill='lightblue')
self.canvas.create_text(screen_width * scale // 2, topbar_offset * scale // 2, text='I\'m Top Bar, hover mouse on items to check details', fill='black')
def main():
parser = argparse.ArgumentParser(description='Preview UI widgets from hpp files')
parser.add_argument('file', help='Path to the hpp file')
args = parser.parse_args()
widget_parser = WidgetParser()
widgets = widget_parser.parse_file(args.file)
app = WidgetPreview(widgets)
app.mainloop()
if __name__ == "__main__":
main()
@@ -0,0 +1,561 @@
M48
METRIC,LZ,000.000
;FILE_FORMAT=3:3
;TYPE=PLATED
;Layer: PTH_Through
;EasyEDA v6.5.46, 2025-01-01 16:01:39
;1acd7fc2553046b79c71bda98b4923fc,10
;Gerber Generator version 0.2
;Holesize 1 = 0.300 mm
T01C0.300
;Holesize 2 = 0.400 mm
T02C0.400
;Holesize 3 = 0.500 mm
T03C0.500
;Holesize 4 = 0.700 mm
T04C0.700
;Holesize 5 = 0.712 mm
T05C0.712
;Holesize 6 = 0.800 mm
T06C0.800
;Holesize 7 = 1.000 mm
T07C1.000
;Holesize 8 = 1.450 mm
T08C1.450
;Holesize 9 = 1.501 mm
T09C1.501
;Holesize 10 = 1.600 mm
T10C1.600
;Holesize 11 = 3.200 mm
T11C3.200
;Holesize 12 = 3.300 mm
T12C3.300
;Holesize 13 = 4.000 mm
T13C4.000
%
G05
G90
T01
X033262Y064570
T02
X033746Y068890
X033746Y067630
X032496Y068890
X032496Y067640
X000503Y006140
X002078Y005493
X-001046Y005505
X-001757Y003880
X002700Y003931
X-001046Y002407
X000503Y001746
X002065Y002394
X112492Y006204
X114067Y005556
X110942Y005569
X110231Y003943
X114689Y003994
X110942Y002470
X112492Y001810
X114054Y002457
X112454Y073311
X114028Y072663
X110904Y072676
X110193Y071050
X114651Y071101
X110904Y069577
X112454Y068917
X114016Y069564
X000363Y073082
X001938Y072434
X-001186Y072447
X-001897Y070822
X002561Y070872
X-001186Y069348
X000363Y068688
X001926Y069336
X043182Y073180
X057290Y063879
X055639Y063879
X054115Y063879
X023541Y056040
X-000716Y008989
X040694Y054339
X039184Y054339
X037684Y054339
X036194Y054339
X034684Y054339
X033184Y054339
X031694Y054339
X030184Y054339
X028684Y054339
X040694Y057659
X039184Y057659
X037684Y057659
X036194Y057659
X034684Y057659
X033184Y057659
X031694Y057659
X030184Y057659
X028684Y057659
X027194Y057659
X025684Y057659
X024184Y057659
X-002604Y045352
X003264Y054799
X000826Y052072
X001374Y051759
X001394Y052349
X001394Y052349
X003674Y050949
X004474Y050949
X004474Y051819
X003674Y051819
X000794Y053369
X000804Y054169
X115384Y012549
X115384Y014149
X115384Y015569
X091384Y022329
X092884Y022329
X094384Y022329
X095884Y022329
X097384Y022329
X082084Y026189
X082084Y027159
X086521Y016440
X076134Y058229
X069384Y058229
X068854Y056279
X059895Y043988
X081972Y056970
X070944Y056949
X060837Y043271
X078771Y056139
X071254Y056139
X065404Y066959
X022844Y069129
X022854Y070029
X024785Y069289
X041284Y063829
X041284Y064749
X017594Y069039
X016784Y069039
X021954Y060729
X022604Y061599
X033484Y062559
X032884Y063169
X022684Y048469
X022684Y049489
X022684Y050469
X019781Y050798
X020551Y051588
X019071Y049998
X053284Y073469
X051784Y073469
X054784Y073469
X044284Y073469
X056284Y073469
X050284Y073469
X048784Y073469
X057784Y073469
X021882Y074350
X023382Y074350
X064450Y066340
X047962Y071820
X028462Y067260
X029172Y066460
X033902Y071829
X039922Y069890
X039929Y061954
X047970Y065068
X023182Y068229
X009236Y057012
X022652Y071790
X009622Y065100
X035742Y072490
X009572Y059860
X043912Y060994
X045552Y067320
X046412Y066020
X047212Y041900
X047212Y068570
X044692Y069849
X040972Y069849
X040992Y067830
X025171Y026068
X031191Y031448
X030271Y030328
X024261Y025278
X033391Y025758
X033401Y024428
X031911Y025798
X033391Y027298
X032081Y032348
X049684Y011368
X048184Y011358
X046684Y011348
X045184Y011358
X043684Y011348
X115384Y016969
X115384Y018169
X115364Y019329
X115384Y020659
X115214Y008229
X115204Y006729
X109384Y001239
X107884Y001249
X106384Y001259
X104884Y001249
X103384Y001249
X101884Y001239
X100384Y001229
X098884Y001239
X097384Y001239
X095884Y001229
X094384Y001229
X092884Y001229
X091384Y001229
X089884Y001229
X088384Y001229
X086884Y001219
X000785Y007999
X002284Y006879
X003484Y005729
X-002596Y008829
X-002616Y010329
X-002616Y011829
X-002616Y013329
X-002616Y014829
X-002616Y016329
X-000816Y065529
X-002316Y065529
X076084Y073484
X073272Y073460
X071284Y073475
X068284Y073484
X074584Y020369
X074584Y021658
X074584Y022719
X081484Y044478
X074365Y029879
X072535Y029828
X070635Y029808
X077555Y039659
X069335Y039858
X074145Y039309
X074145Y040279
X074345Y033929
X074335Y035788
X072555Y035769
X072545Y033948
X070655Y033938
X070685Y035769
X098884Y022329
X100384Y022329
X101884Y022329
X103384Y022329
X104884Y022329
X066784Y020168
X066784Y021048
X066784Y021959
X066784Y022808
X072121Y024198
X072121Y025078
X083535Y045249
X079544Y049619
X115384Y022329
X113884Y022329
X112384Y022329
X110884Y022329
X109384Y022329
X107884Y022329
X106384Y022329
X001584Y065529
X003284Y065529
X004984Y065529
X006384Y067929
X006384Y066729
X006384Y065529
X-002316Y067029
X-000816Y067029
X001385Y067429
X003184Y068429
X004184Y070029
X004385Y071629
X-002616Y032829
X-002616Y031329
X-002616Y029829
X-002616Y028329
X-002616Y026829
X-002616Y025329
X-002616Y023829
X-002616Y022329
X-002616Y020829
X-002616Y019329
X-002616Y017829
X-002516Y007529
X-002516Y006229
X004684Y004629
X005584Y003629
X006484Y002729
X007385Y001829
X002884Y000829
X004385Y000829
X005884Y000829
X007385Y000829
X008884Y000829
X010385Y000829
X011884Y000829
X013385Y000829
X014884Y000829
X016384Y000829
X017884Y000829
X019384Y000829
X020884Y000829
X022384Y000829
X023884Y000829
X025384Y000829
X026884Y000829
X028384Y000829
X029884Y000829
X031384Y000829
X032884Y000829
X034384Y000829
X035884Y000829
X037384Y000829
X038884Y000829
X040384Y000829
X041884Y000829
X043384Y000829
X044884Y000829
X046384Y000829
X047884Y000829
X049384Y000829
X050884Y000829
X052384Y000829
X053884Y000829
X055384Y000829
X056884Y000829
X058384Y000829
X059884Y000829
X061384Y000829
X062884Y000829
X064384Y000829
X065884Y000829
X067384Y000829
X068891Y000829
X070391Y000829
X071891Y000829
X073391Y000829
X074891Y000829
X076431Y000828
X078002Y000840
X079384Y001229
X080884Y001229
X082384Y001229
X083884Y001229
X085384Y001229
X004382Y074350
X005882Y074350
X007382Y074350
X008882Y074350
X014382Y074350
X015882Y074350
X017382Y074350
X020382Y074350
X018882Y074350
X024882Y074350
X026462Y074330
X027922Y074320
X029162Y074340
X030882Y074350
X032382Y074350
X033882Y074350
X035382Y074350
X036882Y074350
X038382Y074350
X039882Y074350
X041382Y074350
X045784Y073469
X047284Y073469
X059284Y073469
X060784Y073475
X062284Y073475
X063784Y073475
X065284Y073475
X066784Y073475
X078602Y073510
X081184Y073475
X082684Y073475
X084484Y073475
X085984Y073475
X087484Y073475
X088984Y073475
X090484Y073475
X091984Y073475
X093484Y073475
X094984Y073475
X096484Y073475
X097984Y073475
X099484Y073475
X100984Y073475
X102484Y073475
X103984Y073475
X105484Y073475
X106984Y073475
X108484Y073475
X027601Y019268
X023813Y020081
X028890Y022249
X026321Y021528
X017452Y004818
X085852Y017350
X077648Y008828
X034983Y005683
X076673Y015173
X033983Y015173
X068663Y007623
X068663Y004453
X063595Y015808
X036533Y015813
X063015Y016538
X031443Y015303
X062135Y007909
X032333Y005783
X061270Y008433
X014703Y005623
X060676Y014433
X014723Y014433
X059741Y013778
X012013Y014393
X-001718Y058110
X061644Y042569
X057445Y030168
X047615Y039378
X058814Y044899
X057445Y027618
X047615Y040929
X069195Y046318
X077135Y046328
X076515Y047028
X068495Y047039
X075865Y047689
X067745Y047689
X075155Y048389
X067165Y048389
X004574Y045538
X078065Y045868
X047924Y031737
X047879Y021582
X047893Y024121
X047895Y029199
X047897Y026660
X060794Y051359
X058555Y024128
X068304Y052019
X059624Y052679
X058264Y053329
X098233Y060306
X098221Y061405
X098225Y062489
X092008Y060306
X058359Y021582
X091955Y061388
X091955Y062489
X063594Y055499
X060064Y054079
X072164Y054799
X083563Y029081
X083532Y018290
X073148Y024448
X048131Y018289
X091127Y059240
X078791Y019948
X021611Y040768
X040324Y031329
X040344Y029829
X040344Y028329
X040384Y026729
X040384Y025109
X-002616Y034329
X-002616Y035829
X-002616Y037829
X-002616Y039329
X020884Y059739
X019984Y058749
X019024Y057729
X025085Y062139
X024894Y060729
X023884Y059619
X022634Y058629
X021484Y057629
X090962Y015440
X090962Y018290
X096042Y010710
X093452Y015640
X-002514Y051122
X-002594Y046042
X-002704Y050362
X004296Y056722
X-001114Y048042
T03
X014774Y046069
X069914Y059240
X086375Y059240
X004011Y018968
X014804Y064082
X021274Y018289
X101122Y005440
X090962Y006600
X099862Y006600
X089632Y005459
T04
X082571Y008808
T05
X013029Y015788G85X013034Y015788
X015569Y015788G85X015574Y015788
T06
X052422Y070860G85X052382Y070860
X054962Y070860G85X054922Y070860
X049882Y070860G85X049842Y070860
X052441Y003918G85X052401Y003918
X054981Y003918G85X054941Y003918
X049901Y003918G85X049861Y003918
X009484Y045729
T07
X069822Y069930G85X069822Y069930
X069822Y067390G85X069822Y067390
X077442Y069930G85X077442Y069930
X079982Y067390G85X079982Y067390
X077442Y067390G85X077442Y067390
X074902Y067390G85X074902Y067390
X079982Y069930G85X079982Y069930
X074902Y069930G85X074902Y069930
X072362Y067390G85X072362Y067390
X072362Y069930G85X072362Y069930
T08
X011293Y043946G85X011289Y043949
X021263Y053732G85X021259Y053736
X024204Y044129G85X024201Y044133
X014050Y021431G85X014047Y021435
X016546Y036471G85X016543Y036475
X024021Y031218G85X024017Y031221
X032767Y029853G85X032764Y029856
T09
X-001239Y059722G85X-001234Y059722
X005873Y059722G85X005878Y059722
T10
X014473Y040765
X020837Y034402
T11
X007048Y048190
T12
X000539Y003918G85X000544Y003918
X112527Y003981G85X112532Y003981
X112489Y071088G85X112494Y071088
X000399Y070860G85X000404Y070860
T13
X028261Y048190
X028261Y026977
M30
@@ -0,0 +1,515 @@
M48
METRIC,LZ,000.000
;FILE_FORMAT=3:3
;TYPE=PLATED
;Layer: PTH_Through_Via
;EasyEDA v6.5.46, 2025-01-01 16:01:39
;1acd7fc2553046b79c71bda98b4923fc,10
;Gerber Generator version 0.2
;Holesize 1 = 0.300 mm
T01C0.300
;Holesize 2 = 0.400 mm
T02C0.400
;Holesize 3 = 0.500 mm
T03C0.500
;Holesize 4 = 0.700 mm
T04C0.700
;Holesize 5 = 0.800 mm
T05C0.800
;Holesize 6 = 1.600 mm
T06C1.600
;Holesize 7 = 3.200 mm
T07C3.200
;Holesize 8 = 4.000 mm
T08C4.000
%
G05
G90
T01
X033262Y064570
T02
X033746Y068890
X033746Y067630
X032496Y068890
X032496Y067640
X000503Y006140
X002078Y005493
X-001046Y005505
X-001757Y003880
X002700Y003931
X-001046Y002407
X000503Y001746
X002065Y002394
X112492Y006204
X114067Y005556
X110942Y005569
X110231Y003943
X114689Y003994
X110942Y002470
X112492Y001810
X114054Y002457
X112454Y073311
X114028Y072663
X110904Y072676
X110193Y071050
X114651Y071101
X110904Y069577
X112454Y068917
X114016Y069564
X000363Y073082
X001938Y072434
X-001186Y072447
X-001897Y070822
X002561Y070872
X-001186Y069348
X000363Y068688
X001926Y069336
X043182Y073180
X057290Y063879
X055639Y063879
X054115Y063879
X023541Y056040
X-000716Y008989
X040694Y054339
X039184Y054339
X037684Y054339
X036194Y054339
X034684Y054339
X033184Y054339
X031694Y054339
X030184Y054339
X028684Y054339
X040694Y057659
X039184Y057659
X037684Y057659
X036194Y057659
X034684Y057659
X033184Y057659
X031694Y057659
X030184Y057659
X028684Y057659
X027194Y057659
X025684Y057659
X024184Y057659
X-002604Y045352
X003264Y054799
X000826Y052072
X001374Y051759
X001394Y052349
X001394Y052349
X003674Y050949
X004474Y050949
X004474Y051819
X003674Y051819
X000794Y053369
X000804Y054169
X115384Y012549
X115384Y014149
X115384Y015569
X091384Y022329
X092884Y022329
X094384Y022329
X095884Y022329
X097384Y022329
X082084Y026189
X082084Y027159
X086521Y016440
X076134Y058229
X069384Y058229
X068854Y056279
X059895Y043988
X081972Y056970
X070944Y056949
X060837Y043271
X078771Y056139
X071254Y056139
X065404Y066959
X022844Y069129
X022854Y070029
X024785Y069289
X041284Y063829
X041284Y064749
X017594Y069039
X016784Y069039
X021954Y060729
X022604Y061599
X033484Y062559
X032884Y063169
X022684Y048469
X022684Y049489
X022684Y050469
X019781Y050798
X020551Y051588
X019071Y049998
X053284Y073469
X051784Y073469
X054784Y073469
X044284Y073469
X056284Y073469
X050284Y073469
X048784Y073469
X057784Y073469
X021882Y074350
X023382Y074350
X064450Y066340
X047962Y071820
X028462Y067260
X029172Y066460
X033902Y071829
X039922Y069890
X039929Y061954
X047970Y065068
X023182Y068229
X009236Y057012
X022652Y071790
X009622Y065100
X035742Y072490
X009572Y059860
X043912Y060994
X045552Y067320
X046412Y066020
X047212Y041900
X047212Y068570
X044692Y069849
X040972Y069849
X040992Y067830
X025171Y026068
X031191Y031448
X030271Y030328
X024261Y025278
X033391Y025758
X033401Y024428
X031911Y025798
X033391Y027298
X032081Y032348
X049684Y011368
X048184Y011358
X046684Y011348
X045184Y011358
X043684Y011348
X115384Y016969
X115384Y018169
X115364Y019329
X115384Y020659
X115214Y008229
X115204Y006729
X109384Y001239
X107884Y001249
X106384Y001259
X104884Y001249
X103384Y001249
X101884Y001239
X100384Y001229
X098884Y001239
X097384Y001239
X095884Y001229
X094384Y001229
X092884Y001229
X091384Y001229
X089884Y001229
X088384Y001229
X086884Y001219
X000785Y007999
X002284Y006879
X003484Y005729
X-002596Y008829
X-002616Y010329
X-002616Y011829
X-002616Y013329
X-002616Y014829
X-002616Y016329
X-000816Y065529
X-002316Y065529
X076084Y073484
X073272Y073460
X071284Y073475
X068284Y073484
X074584Y020369
X074584Y021658
X074584Y022719
X081484Y044478
X074365Y029879
X072535Y029828
X070635Y029808
X077555Y039659
X069335Y039858
X074145Y039309
X074145Y040279
X074345Y033929
X074335Y035788
X072555Y035769
X072545Y033948
X070655Y033938
X070685Y035769
X098884Y022329
X100384Y022329
X101884Y022329
X103384Y022329
X104884Y022329
X066784Y020168
X066784Y021048
X066784Y021959
X066784Y022808
X072121Y024198
X072121Y025078
X083535Y045249
X079544Y049619
X115384Y022329
X113884Y022329
X112384Y022329
X110884Y022329
X109384Y022329
X107884Y022329
X106384Y022329
X001584Y065529
X003284Y065529
X004984Y065529
X006384Y067929
X006384Y066729
X006384Y065529
X-002316Y067029
X-000816Y067029
X001385Y067429
X003184Y068429
X004184Y070029
X004385Y071629
X-002616Y032829
X-002616Y031329
X-002616Y029829
X-002616Y028329
X-002616Y026829
X-002616Y025329
X-002616Y023829
X-002616Y022329
X-002616Y020829
X-002616Y019329
X-002616Y017829
X-002516Y007529
X-002516Y006229
X004684Y004629
X005584Y003629
X006484Y002729
X007385Y001829
X002884Y000829
X004385Y000829
X005884Y000829
X007385Y000829
X008884Y000829
X010385Y000829
X011884Y000829
X013385Y000829
X014884Y000829
X016384Y000829
X017884Y000829
X019384Y000829
X020884Y000829
X022384Y000829
X023884Y000829
X025384Y000829
X026884Y000829
X028384Y000829
X029884Y000829
X031384Y000829
X032884Y000829
X034384Y000829
X035884Y000829
X037384Y000829
X038884Y000829
X040384Y000829
X041884Y000829
X043384Y000829
X044884Y000829
X046384Y000829
X047884Y000829
X049384Y000829
X050884Y000829
X052384Y000829
X053884Y000829
X055384Y000829
X056884Y000829
X058384Y000829
X059884Y000829
X061384Y000829
X062884Y000829
X064384Y000829
X065884Y000829
X067384Y000829
X068891Y000829
X070391Y000829
X071891Y000829
X073391Y000829
X074891Y000829
X076431Y000828
X078002Y000840
X079384Y001229
X080884Y001229
X082384Y001229
X083884Y001229
X085384Y001229
X004382Y074350
X005882Y074350
X007382Y074350
X008882Y074350
X014382Y074350
X015882Y074350
X017382Y074350
X020382Y074350
X018882Y074350
X024882Y074350
X026462Y074330
X027922Y074320
X029162Y074340
X030882Y074350
X032382Y074350
X033882Y074350
X035382Y074350
X036882Y074350
X038382Y074350
X039882Y074350
X041382Y074350
X045784Y073469
X047284Y073469
X059284Y073469
X060784Y073475
X062284Y073475
X063784Y073475
X065284Y073475
X066784Y073475
X078602Y073510
X081184Y073475
X082684Y073475
X084484Y073475
X085984Y073475
X087484Y073475
X088984Y073475
X090484Y073475
X091984Y073475
X093484Y073475
X094984Y073475
X096484Y073475
X097984Y073475
X099484Y073475
X100984Y073475
X102484Y073475
X103984Y073475
X105484Y073475
X106984Y073475
X108484Y073475
X027601Y019268
X023813Y020081
X028890Y022249
X026321Y021528
X017452Y004818
X085852Y017350
X077648Y008828
X034983Y005683
X076673Y015173
X033983Y015173
X068663Y007623
X068663Y004453
X063595Y015808
X036533Y015813
X063015Y016538
X031443Y015303
X062135Y007909
X032333Y005783
X061270Y008433
X014703Y005623
X060676Y014433
X014723Y014433
X059741Y013778
X012013Y014393
X-001718Y058110
X061644Y042569
X057445Y030168
X047615Y039378
X058814Y044899
X057445Y027618
X047615Y040929
X069195Y046318
X077135Y046328
X076515Y047028
X068495Y047039
X075865Y047689
X067745Y047689
X075155Y048389
X067165Y048389
X004574Y045538
X078065Y045868
X047924Y031737
X047879Y021582
X047893Y024121
X047895Y029199
X047897Y026660
X060794Y051359
X058555Y024128
X068304Y052019
X059624Y052679
X058264Y053329
X098233Y060306
X098221Y061405
X098225Y062489
X092008Y060306
X058359Y021582
X091955Y061388
X091955Y062489
X063594Y055499
X060064Y054079
X072164Y054799
X083563Y029081
X083532Y018290
X073148Y024448
X048131Y018289
X091127Y059240
X078791Y019948
X021611Y040768
X040324Y031329
X040344Y029829
X040344Y028329
X040384Y026729
X040384Y025109
X-002616Y034329
X-002616Y035829
X-002616Y037829
X-002616Y039329
X020884Y059739
X019984Y058749
X019024Y057729
X025085Y062139
X024894Y060729
X023884Y059619
X022634Y058629
X021484Y057629
X090962Y015440
X090962Y018290
X096042Y010710
X093452Y015640
X-002514Y051122
X-002594Y046042
X-002704Y050362
X004296Y056722
X-001114Y048042
T03
X014774Y046069
X069914Y059240
X086375Y059240
X004011Y018968
X014804Y064082
X021274Y018289
X101122Y005440
X090962Y006600
X099862Y006600
X089632Y005459
T04
X082571Y008808
T05
X009484Y045729
T06
X014473Y040765
X020837Y034402
T07
X007048Y048190
T08
X028261Y048190
X028261Y026977
M30
@@ -0,0 +1,114 @@
G04 Layer: BoardOutlineLayer*
G04 EasyEDA v6.5.46, 2025-01-01 16:01:39*
G04 1acd7fc2553046b79c71bda98b4923fc,10*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%ADD10C,0.2540*%
D10*
X9285762Y5161592D02*
G01*
X11467622Y5161592D01*
G75*
G01*
X11490437Y6366413D02*
G03*
X11490435Y6365811I-114999J-10D01*
G75*
G01*
X3958438Y6366413D02*
G03*
X3958436Y6365811I-114999J-10D01*
G75*
G01*
X11489939Y1123414D02*
G03*
X11489937Y1122812I-114999J-10D01*
G75*
G01*
X11122802Y6341742D02*
G03*
X11122801Y6341376I-70000J11D01*
G75*
G01*
X11120262Y5539102D02*
G03*
X11120261Y5538736I-70000J11D01*
G75*
G01*
X3958438Y1124415D02*
G03*
X3958436Y1123813I-114999J-10D01*
G75*
G01*
X9285762Y5161591D02*
G03*
X9119393Y5006651I-4000J-162495D01*
G75*
G01*
X11467621Y5161591D02*
G03*
X11617482Y5315261I-7768J157484D01*
G75*
G01*
X9119393Y2454272D02*
G03*
X9259094Y2326002I137678J9736D01*
G75*
G01*
X11627643Y2179953D02*
G03*
X11486672Y2326004I-142230J3776D01*
G75*
G01*
X11294904Y2D02*
G03*
X11627644Y328931I-18455J351430D01*
G75*
G01*
X-346707Y320040D02*
G03*
X2Y-1I337962J18303D01*
G75*
G01*
X-11431Y7518226D02*
G03*
X-346708Y7186754I-19125J-315954D01*
G75*
G01*
X11617482Y7196922D02*
G03*
X11310144Y7518229I-289029J31179D01*
X-11432Y7518227D02*
G01*
X11310142Y7518227D01*
X11617482Y5315259D02*
G01*
X11617482Y7196922D01*
X9119392Y2454272D02*
G01*
X9119392Y5006649D01*
X9259092Y2326002D02*
G01*
X11486672Y2326002D01*
X11627642Y328929D02*
G01*
X11627642Y2179952D01*
X0Y0D02*
G01*
X11294902Y0D01*
X-346707Y320037D02*
G01*
X-346707Y7186752D01*
G75*
G01*
X11294902Y3D02*
G03*
X11627642Y328933I-18454J351430D01*
%LPD*%
M02*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
G04 Layer: BottomPasteMaskLayer*
G04 EasyEDA v6.5.46, 2025-01-01 16:01:39*
G04 1acd7fc2553046b79c71bda98b4923fc,10*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%AMMACRO1*21,1,$1,$2,0,0,$3*%
%ADD10MACRO1,5X2.3X135.0000*%
%ADD11MACRO1,5X2.3X-10.0001*%
%LPD*%
D10*
G01*
X209293Y4568317D03*
D11*
G01*
X32306Y3442335D03*
M02*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
G04 Layer: BottomSolderMaskLayer*
G04 EasyEDA v6.5.46, 2025-01-01 16:01:39*
G04 1acd7fc2553046b79c71bda98b4923fc,10*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%AMMACRO1*4,1,8,-0.8085,-0.8382,-0.8382,-0.8085,-0.8382,0.8084,-0.8085,0.8382,0.8084,0.8382,0.8382,0.8084,0.8382,-0.8085,0.8084,-0.8382,-0.8085,-0.8382,0*%
%AMMACRO2*4,1,8,0.9335,-2.6317,-2.6317,0.9335,-2.6317,0.9756,-0.9756,2.6317,-0.9335,2.6317,2.6317,-0.9335,2.6317,-0.9756,0.9756,-2.6317,0.9335,-2.6317,0*%
%AMMACRO3*4,1,8,2.2742,-1.6203,-2.6912,-0.7448,-2.7154,-0.7103,-2.3087,1.5962,-2.2742,1.6203,2.6912,0.7448,2.7154,0.7103,2.3087,-1.5962,2.2742,-1.6203,0*%
%ADD10C,1.6766*%
%ADD11C,1.6764*%
%ADD12MACRO1*%
%ADD13C,1.6016*%
%ADD14C,5.4016*%
%ADD15C,1.8016*%
%ADD17C,1.7016*%
%ADD18MACRO2*%
%ADD19MACRO3*%
%ADD20C,2.4016*%
%LPD*%
D10*
G01*
X6982152Y6992950D03*
G01*
X6982152Y6738950D03*
G01*
X7744152Y6992950D03*
D11*
G01*
X7998152Y6738950D03*
G01*
X7744152Y6738950D03*
G01*
X7490152Y6738950D03*
D12*
G01*
X7998152Y6992950D03*
D11*
G01*
X7490152Y6992950D03*
G01*
X7236152Y6738950D03*
G01*
X7236152Y6992950D03*
D13*
G01*
X-123675Y5972175D03*
G01*
X587524Y5972175D03*
D14*
G01*
X54124Y391795D03*
G01*
X11252984Y398145D03*
G01*
X11249174Y7108825D03*
G01*
X40154Y7085964D03*
D15*
G01*
X5240169Y7085964D03*
D17*
G01*
X5494169Y7085964D03*
G01*
X4986169Y7085964D03*
D11*
G01*
X1303195Y1578787D03*
G01*
X1557195Y1578787D03*
D15*
G01*
X5242125Y391795D03*
D17*
G01*
X5496125Y391795D03*
G01*
X4988125Y391795D03*
D18*
G01*
X209292Y4568315D03*
D19*
G01*
X32304Y3442333D03*
D20*
G01*
X1129078Y4394733D03*
G01*
X2126104Y5373370D03*
G01*
X2420236Y4413122D03*
G01*
X1404846Y2143302D03*
G01*
X1654451Y3647312D03*
G01*
X2401872Y3121939D03*
G01*
X3276546Y2985465D03*
M02*
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
Portapack H4 is an add-on for the HackRF Software Defined Radio (SDR) platform.
The improvements of H4:
1.GPIO port! The H4M adds a GPIO port so users can make their own add-ons for the H4M, just like the flipper.
2.Advanced battery management IC (battery %, voltage, current/draw, etc...)
3.Single power on/off switch.
4.When powered off, the battery will no longer phantom drain.
5.flat design, going back to the iPod style click wheel. (so no more broken buttons and encoders!)
6.Improved charge speeds.
7.Built in mic (and mic switch between internal and external).
8.Matte screen.
-11
View File
@@ -1,11 +0,0 @@
f=124700000,m=AM,bw=DSB 6k,d=BNE Approach NW
f=123500000,m=AM,bw=DSB 6k,d=BNE Approach 2
f=125600000,m=AM,bw=DSB 6k,d=BNE Approach 3
f=120500000,m=AM,bw=DSB 6k,d=BNE RWY 19L/01R
f=118000000,m=AM,bw=DSB 6k,d=BNE RWY 19R/01L
f=113200000,m=AM,bw=DSB 6k,d=BNE ATIS 1
f=125500000,m=AM,bw=DSB 6k,d=BNE ATIS 2
f=121700000,m=AM,bw=DSB 6k,d=BNE GRND 1
f=122250000,m=AM,bw=DSB 6k,d=BNE GRND 2
f=124050000,m=AM,bw=DSB 6k,d=BNE GRND 3
f=125700000,m=AM,bw=DSB 6k,d=OOL Approach E
-28
View File
@@ -1,28 +0,0 @@
f=132475000,m=AM,bw=DSB 6k,d=ATIS - Civil
f=130550000,m=AM,bw=DSB 6k,d=AbelagUnicom - Civil
f=118250000,m=AM,bw=DSB 6k,d=Approach - Civil
f=120100000,m=AM,bw=DSB 6k,d=Approach - Civil
f=122500000,m=AM,bw=DSB 6k,d=Approach Military Crossing
f=121950000,m=AM,bw=DSB 6k,d=Clearance Delivery - Civil
f=126625000,m=AM,bw=DSB 6k,d=Departure - Civil
f=127575000,m=AM,bw=DSB 6k,d=Final 25L - Civil
f=129725000,m=AM,bw=DSB 6k,d=Final 25R - Civil
f=118050000,m=AM,bw=DSB 6k,d=Ground - Civil
f=121700000,m=AM,bw=DSB 6k,d=Ground - Civil
f=121875000,m=AM,bw=DSB 6k,d=Ground - Civil
f=118600000,m=AM,bw=DSB 6k,d=Tower VHF - Civil
f=120775000,m=AM,bw=DSB 6k,d=Tower - Civil
f=127150000,m=AM,bw=DSB 6k,d=Tower Spare - Civil
f=240400000,m=AM,bw=DSB 6k,d=Air to Air - Military
f=241000000,m=AM,bw=DSB 6k,d=Air to Air - Military
f=289400000,m=AM,bw=DSB 6k,d=Military Approach
f=362300000,m=AM,bw=DSB 6k,d=Approach Military Crossing - Military
f=389300000,m=AM,bw=DSB 6k,d=Approach Military Crossing - Military
f=138250000,m=AM,bw=DSB 6k,d=Inflight Safety - Military
f=140575000,m=AM,bw=DSB 6k,d=Melsbroek Ops - Military
f=367950000,m=AM,bw=DSB 6k,d=Melsbroek Ops - Military
f=257800000,m=AM,bw=DSB 6k,d=Tower - Military
f=388500000,m=AM,bw=DSB 6k,d=Tower - Military
f=388525000,m=AM,bw=DSB 6k,d=Tower - Military
f=121725000,m=AM,bw=DSB 6k,d=Wing Ops - Military
f=336500000,m=AM,bw=DSB 6k,d=Wing Ops UHF - Military
-56
View File
@@ -1,56 +0,0 @@
f=87800000,m=WFM,bw=200k,d=France Inter
f=87600000,m=WFM,bw=200k,d=France Inter
f=88200000,m=WFM,bw=200k,d=Generations
f=88600000,m=WFM,bw=200k,d=Radio Soleil
f=89000000,m=WFM,bw=200k,d=RFI
f=89400000,m=WFM,bw=200k,d=Radio Libertaire
f=89900000,m=WFM,bw=200k,d=TSF Jazz
f=90400000,m=WFM,bw=200k,d=Nostalgie
f=90900000,m=WFM,bw=200k,d=Chante France
f=91300000,m=WFM,bw=200k,d=Cherie FM
f=91700000,m=WFM,bw=200k,d=France Musique
f=92100000,m=WFM,bw=200k,d=Mouv'
f=92600000,m=WFM,bw=200k,d=Tropiques FM
f=93100000,m=WFM,bw=200k,d=Cause Commune
f=93100000,m=WFM,bw=200k,d=Aligre FM
f=93500000,m=WFM,bw=200k,d=France Culture
f=93900000,m=WFM,bw=200k,d=Vivre FM
f=93900000,m=WFM,bw=200k,d=Radio Campus
f=94300000,m=WFM,bw=200k,d=Radio Orient
f=94800000,m=WFM,bw=200k,d=Radio Shalom
f=94800000,m=WFM,bw=200k,d=Radio J
f=94800000,m=WFM,bw=200k,d=RCJ
f=95200000,m=WFM,bw=200k,d=Ici Et Maintenant
f=95200000,m=WFM,bw=200k,d=Neo
f=95600000,m=WFM,bw=200k,d=Radio Courtoisie
f=96000000,m=WFM,bw=200k,d=Skyrock
f=96400000,m=WFM,bw=200k,d=BFM Business
f=96900000,m=WFM,bw=200k,d=Voltage
f=97400000,m=WFM,bw=200k,d=Rire Et Chansons
f=97800000,m=WFM,bw=200k,d=Ado
f=98200000,m=WFM,bw=200k,d=Radio FG
f=98600000,m=WFM,bw=200k,d=Radio Alfa
f=99000000,m=WFM,bw=200k,d=Latina
f=99500000,m=WFM,bw=200k,d=AYP FM
f=99500000,m=WFM,bw=200k,d=France Maghreb 2
f=99900000,m=WFM,bw=200k,d=Sud Radio +
f=100300000,m=WFM,bw=200k,d=NRJ
f=100700000,m=WFM,bw=200k,d=Frequence Protestante
f=100700000,m=WFM,bw=200k,d=Radio Notre-Dame
f=101100000,m=WFM,bw=200k,d=Radio Classique
f=101500000,m=WFM,bw=200k,d=Radio Nova
f=101900000,m=WFM,bw=200k,d=Fun Radio
f=102300000,m=WFM,bw=200k,d=Oui FM
f=102700000,m=WFM,bw=200k,d=M Radio
f=103100000,m=WFM,bw=200k,d=RMC
f=103500000,m=WFM,bw=200k,d=Europe 2
f=103900000,m=WFM,bw=200k,d=RFM
f=104300000,m=WFM,bw=200k,d=RTL
f=104700000,m=WFM,bw=200k,d=Europe 1
f=105100000,m=WFM,bw=200k,d=FIP
f=105500000,m=WFM,bw=200k,d=France Info
f=105900000,m=WFM,bw=200k,d=RTL2
f=106300000,m=WFM,bw=200k,d=Frequence Paris Plurielle
f=106700000,m=WFM,bw=200k,d=Beur FM
f=107100000,m=WFM,bw=200k,d=France Bleu
f=107500000,m=WFM,bw=200k,d=Africa Radio
-4
View File
@@ -1,4 +0,0 @@
f=92500000,m=WFM,bw=200k,d=NRK Oslo
f=94800000,m=WFM,bw=200k,d=NRK Bergen
f=97200000,m=WFM,bw=200k,d=NRK Melhus
f=98600000,m=WFM,bw=200k,d=NRK Arendal
-58
View File
@@ -1,58 +0,0 @@
f=88200000,m=WFM,bw=200k,d=RMF Polkowice
f=88200000,m=WFM,bw=200k,d=RMF Kielce
f=89200000,m=WFM,bw=200k,d=RMF Bielsko-Biala
f=89300000,m=WFM,bw=200k,d=RMF Lublin
f=89300000,m=WFM,bw=200k,d=RMF Koszalin
f=90600000,m=WFM,bw=200k,d=RMF Warsaw
f=91000000,m=WFM,bw=200k,d=RMF Warsaw
f=91300000,m=WFM,bw=200k,d=RMF Łobez
f=91500000,m=WFM,bw=200k,d=RMF Ostroleka
f=91700000,m=WFM,bw=200k,d=RMF Wagrowiec
f=91900000,m=WFM,bw=200k,d=RMF Siedlce
f=92600000,m=WFM,bw=200k,d=RMF Elbląg
f=92900000,m=WFM,bw=200k,d=RMF Wroclaw
f=93000000,m=WFM,bw=200k,d=RMF Katowice
f=93300000,m=WFM,bw=200k,d=RMF Bydgoszcz
f=93500000,m=WFM,bw=200k,d=RMF Lodz
f=93600000,m=WFM,bw=200k,d=RMF Ryki
f=93800000,m=WFM,bw=200k,d=RMF Luban
f=94300000,m=WFM,bw=200k,d=RMF Płock
f=94600000,m=WFM,bw=200k,d=RMF Poznan
f=94800000,m=WFM,bw=200k,d=RMF Zagan
f=95100000,m=WFM,bw=200k,d=RMF Suwałki
f=95300000,m=WFM,bw=200k,d=RMF Olsztyn
f=95400000,m=WFM,bw=200k,d=RMF Tarnow
f=96000000,m=WFM,bw=200k,d=RMF Krakow
f=96100000,m=WFM,bw=200k,d=RMF Legnica
f=96100000,m=WFM,bw=200k,d=RMF Gorzow Wielkopolski
f=96400000,m=WFM,bw=200k,d=RMF Bialogard
f=96600000,m=WFM,bw=200k,d=RMF Walcz
f=97100000,m=WFM,bw=200k,d=RMF Włoszczowa
f=98000000,m=WFM,bw=200k,d=RMF Kalisz
f=98400000,m=WFM,bw=200k,d=RMF Gdansk
f=98900000,m=WFM,bw=200k,d=RMF Konin
f=99300000,m=WFM,bw=200k,d=RMF Opole
f=99500000,m=WFM,bw=200k,d=RMF Kluczbork
f=100100000,m=WFM,bw=200k,d=RMF Rzeszow
f=100200000,m=WFM,bw=200k,d=RMF Bialystok
f=100800000,m=WFM,bw=200k,d=RMF Jelenia Gora
f=100900000,m=WFM,bw=200k,d=RMF Slupsk
f=101100000,m=WFM,bw=200k,d=RMF Solina
f=101200000,m=WFM,bw=200k,d=RMF Swinoujscie
f=101600000,m=WFM,bw=200k,d=RMF Klodzko
f=101800000,m=WFM,bw=200k,d=RMF Zakopane
f=101800000,m=WFM,bw=200k,d=RMF Leżajsk
f=102000000,m=WFM,bw=200k,d=RMF Gizycko
f=102900000,m=WFM,bw=200k,d=RMF Walbrzych
f=103200000,m=WFM,bw=200k,d=RMF Szczawnica
f=103400000,m=WFM,bw=200k,d=RMF Przemyśl
f=103400000,m=WFM,bw=200k,d=RMF Lebork
f=104700000,m=WFM,bw=200k,d=RMF Rabka
f=104900000,m=WFM,bw=200k,d=RMF Koszalin
f=105900000,m=WFM,bw=200k,d=RMF Krynica
f=105900000,m=WFM,bw=200k,d=RMF Czestochowa
f=106400000,m=WFM,bw=200k,d=RMF Zielona Gora
f=106500000,m=WFM,bw=200k,d=RMF Elk
f=106700000,m=WFM,bw=200k,d=RMF Szczecin
f=107400000,m=WFM,bw=200k,d=RMF Iława
f=107700000,m=WFM,bw=200k,d=RMF Zamosc
-28
View File
@@ -1,28 +0,0 @@
f=88000000,m=WFM,bw=200k,d=Radio Impuls
f=88500000,m=WFM,bw=200k,d=Radio Trinitas
f=89000000,m=WFM,bw=200k,d=Radio ZU
f=89500000,m=WFM,bw=200k,d=Dance FM
f=90800000,m=WFM,bw=200k,d=Magic FM
f=91700000,m=WFM,bw=200k,d=Național FM
f=92100000,m=WFM,bw=200k,d=One World Radio
f=93500000,m=WFM,bw=200k,d=RFI România
f=94200000,m=WFM,bw=200k,d=Radio Vocea Evangheliei
f=94800000,m=WFM,bw=200k,d=Radio Guerrilla
f=96100000,m=WFM,bw=200k,d=Kiss FM
f=96900000,m=WFM,bw=200k,d=Gold FM
f=97900000,m=WFM,bw=200k,d=Digi FM
f=98300000,m=WFM,bw=200k,d=București FM
f=99300000,m=WFM,bw=200k,d=Radio Itsy Bitsy
f=100200000,m=WFM,bw=200k,d=Virgin Radio
f=100600000,m=WFM,bw=200k,d=Rock FM
f=101300000,m=WFM,bw=200k,d=Radio România Cultural
f=101900000,m=WFM,bw=200k,d=Romantic FM
f=102800000,m=WFM,bw=200k,d=PRO FM
f=103400000,m=WFM,bw=200k,d=Radio Seven
f=103800000,m=WFM,bw=200k,d=Digi 24 FM
f=104800000,m=WFM,bw=200k,d=Radio România Muzical
f=105300000,m=WFM,bw=200k,d=Radio România Actualități
f=105800000,m=WFM,bw=200k,d=Sport Total FM
f=106200000,m=WFM,bw=200k,d=3 FM
f=106700000,m=WFM,bw=200k,d=Europa FM
f=107300000,m=WFM,bw=200k,d=Smart FM
-94
View File
@@ -1,94 +0,0 @@
f=91200000,m=WFM,bw=200k,d=Radio Slovensko Nitra
f=92200000,m=WFM,bw=200k,d=Radio Slovensko Poprad
f=96600000,m=WFM,bw=200k,d=Radio Slovensko Bratislava
f=96600000,m=WFM,bw=200k,d=Radio Slovensko Kosice
f=103500000,m=WFM,bw=200k,d=Radio Slovensko Zilina
f=99300000,m=WFM,bw=200k,d=Radio Regina Bratislava
f=100100000,m=WFM,bw=200k,d=Radio Regina Zilina
f=100300000,m=WFM,bw=200k,d=Radio Regina Kosice
f=96200000,m=WFM,bw=200k,d=Radio Devin Kosice
f=104400000,m=WFM,bw=200k,d=Radio Devin Bratislava
f=106700000,m=WFM,bw=200k,d=Radio Devin Presov
f=89300000,m=WFM,bw=200k,d=Radio _FM Bratislava
f=94500000,m=WFM,bw=200k,d=Radio _FM Zilina
f=101200000,m=WFM,bw=200k,d=Radio _FM Kosice
f=101500000,m=WFM,bw=200k,d=Radio _FM Presov
f=105400000,m=WFM,bw=200k,d=Radio _FM Banska Bystrica
f=98900000,m=WFM,bw=200k,d=Radio Patria Bratislava
f=102400000,m=WFM,bw=200k,d=Radio Patria Senec
f=98900000,m=WFM,bw=200k,d=Radio Slovakia International Bratislava
f=107600000,m=WFM,bw=200k,d=Radio Expres Bratislava
f=96500000,m=WFM,bw=200k,d=Radio Expres Zilina
f=93100000,m=WFM,bw=200k,d=Radio Expres Martin
f=93600000,m=WFM,bw=200k,d=Radio Expres Donovaly
f=95100000,m=WFM,bw=200k,d=Radio Expres Banska Stiavnica
f=95200000,m=WFM,bw=200k,d=Radio Expres Kosice
f=105200000,m=WFM,bw=200k,d=Radio Expres Presov
f=99500000,m=WFM,bw=200k,d=Radio Expres Vychodna
f=104800000,m=WFM,bw=200k,d=Europa 2 Bratislava
f=93500000,m=WFM,bw=200k,d=Europa 2 Martin
f=97200000,m=WFM,bw=200k,d=Europa 2 Presov
f=102000000,m=WFM,bw=200k,d=Europa 2 Kosice
f=106600000,m=WFM,bw=200k,d=Radio Melody Bratislava
f=88800000,m=WFM,bw=200k,d=Radio Melody Nitra
f=89800000,m=WFM,bw=200k,d=Radio Rock Presov
f=97600000,m=WFM,bw=200k,d=Radio Rock Bratislava
f=100300000,m=WFM,bw=200k,d=Radio Rock Trencin
f=94000000,m=WFM,bw=200k,d=Fun radio Nitra
f=94300000,m=WFM,bw=200k,d=Fun radio Bratislava
f=102900000,m=WFM,bw=200k,d=Fun radio Kosice
f=94800000,m=WFM,bw=200k,d=Radio Vlna Kosice
f=96100000,m=WFM,bw=200k,d=Radio Vlna Nitra
f=101800000,m=WFM,bw=200k,d=Radio Vlna Bratislava
f=104800000,m=WFM,bw=200k,d=Radio Vlna Poprad
f=104900000,m=WFM,bw=200k,d=Radio Vlna Martin
f=105100000,m=WFM,bw=200k,d=Radio Vlna Banska Stiavnica
f=105800000,m=WFM,bw=200k,d=Radio Vlna Presov
f=95600000,m=WFM,bw=200k,d=Best FM Bratislava
f=93800000,m=WFM,bw=200k,d=Best FM Kosice
f=88200000,m=WFM,bw=200k,d=Best FM Nitra
f=94000000,m=WFM,bw=200k,d=Best FM Presov
f=98900000,m=WFM,bw=200k,d=Best FM Trencin
f=93000000,m=WFM,bw=200k,d=Best FM Trnava
f=98000000,m=WFM,bw=200k,d=Best FM Zilina
f=89400000,m=WFM,bw=200k,d=Radio Lumen Martin
f=92900000,m=WFM,bw=200k,d=Radio Lumen Presov
f=93800000,m=WFM,bw=200k,d=Radio Lumen Bratislava
f=94400000,m=WFM,bw=200k,d=Radio Lumen Kosice
f=98100000,m=WFM,bw=200k,d=Radio Lumen Trnava
f=91700000,m=WFM,bw=200k,d=Radio 7 Bratislava
f=88600000,m=WFM,bw=200k,d=Radio 7 Kosice
f=103600000,m=WFM,bw=200k,d=Radio 7 Nitra
f=96400000,m=WFM,bw=200k,d=Radio 7 Poprad
f=107500000,m=WFM,bw=200k,d=Radio 7 Ruzomberok
f=101000000,m=WFM,bw=200k,d=Radio 7 Zilina
f=104100000,m=WFM,bw=200k,d=Dobre radio Presov
f=104200000,m=WFM,bw=200k,d=Dobre radio Domasa
f=104500000,m=WFM,bw=200k,d=Dobre radio Kosice
f=107000000,m=WFM,bw=200k,d=Dobre radio Bratislava
f=89700000,m=WFM,bw=200k,d=Radio Beta Zvolen
f=104300000,m=WFM,bw=200k,d=Radio Beta Trencin
f=96200000,m=WFM,bw=200k,d=Radio Frontinus Martin
f=104600000,m=WFM,bw=200k,d=Radio Frontinus Zilina
f=99600000,m=WFM,bw=200k,d=Radio Rebeca Martin
f=88600000,m=WFM,bw=200k,d=Radio Rebeca Zilina
f=96000000,m=WFM,bw=200k,d=Radio SiTy Bratislava
f=97700000,m=WFM,bw=200k,d=Sky Radio Kosice
f=91400000,m=WFM,bw=200k,d=Sky Radio Presov
f=106600000,m=WFM,bw=200k,d=Sky Radio Poprad
f=106200000,m=WFM,bw=200k,d=Trnavske radio Trnava
f=107900000,m=WFM,bw=200k,d=Trnavske radio Piestany
f=93300000,m=WFM,bw=200k,d=Radio VIVA Bratislava
f=101000000,m=WFM,bw=200k,d=Radio VIVA Trnava
f=93600000,m=WFM,bw=200k,d=Radio WOW Nitra
f=106700000,m=WFM,bw=200k,d=Radio WOW Trencin
f=89200000,m=WFM,bw=200k,d=Zahoracke radio Skalica
f=101500000,m=WFM,bw=200k,d=Zahoracke radio Senica
f=92400000,m=WFM,bw=200k,d=Radio Goldies Martin
f=92800000,m=WFM,bw=200k,d=Radio Goldies Trencin
f=104100000,m=WFM,bw=200k,d=Radio Kiss Presov
f=90100000,m=WFM,bw=200k,d=Radio Kosice Bratislava
f=90400000,m=WFM,bw=200k,d=Radio Kosice Presov
f=91700000,m=WFM,bw=200k,d=Radio Kosice Kosice
f=106400000,m=WFM,bw=200k,d=Radio Liptov Liptovsky Mikulas
f=106100000,m=WFM,bw=200k,d=Radio Modra
-28
View File
@@ -1,28 +0,0 @@
f=129400000,m=AM,bw=DSB 6k,d=Bucharest Information (LRBB_FSS)
f=134380000,m=AM,bw=DSB 6k,d=Bucharest Radar [sector consolidat] (LRBB_CTR)
f=119415000,m=AM,bw=DSB 6k,d=Bucharest Approach (LROP_APP)
f=127155000,m=AM,bw=DSB 6k,d=Bucharest Director (LROP_D_APP)
f=122905000,m=AM,bw=DSB 6k,d=Constanta Approach (LRCK_APP)
f=126430000,m=AM,bw=DSB 6k,d=NAPOC Approach (LRCL_APP)
f=126430000,m=AM,bw=DSB 6k,d=NAPOC North Approach (LRCL_N_APP)
f=119680000,m=AM,bw=DSB 6k,d=NAPOC South Approach (LRCL_S_APP)
f=123530000,m=AM,bw=DSB 6k,d=Arad Approach (LRAR_APP)
f=118230000,m=AM,bw=DSB 6k,d=Arad (LRAR_TWR)
f=120980000,m=AM,bw=DSB 6k,d=Bacau (LRBC_TWR)
f=118855000,m=AM,bw=DSB 6k,d=Baia Mare (LRBM_TWR)
f=125205000,m=AM,bw=DSB 6k,d=Baneasa (LRBS_TWR)
f=118705000,m=AM,bw=DSB 6k,d=Cluj (LRCL_TWR)
f=120030000,m=AM,bw=DSB 6k,d=Constanta (LRCK_TWR)
f=129530000,m=AM,bw=DSB 6k,d=Craiova (LRCV_TWR)
f=119955000,m=AM,bw=DSB 6k,d=Iasi (LRIA_TWR)
f=118805000,m=AM,bw=DSB 6k,d=Otopeni (LROP_TWR)
f=118455000,m=AM,bw=DSB 6k,d=Oradea (LROD_TWR)
f=119655000,m=AM,bw=DSB 6k,d=Satu Mare (LRSM_TWR)
f=121305000,m=AM,bw=DSB 6k,d=Sibiu (LRSB_TWR)
f=129955000,m=AM,bw=DSB 6k,d=Suceava (LRSV_TWR)
f=120105000,m=AM,bw=DSB 6k,d=Timisoara (LRTR_TWR)
f=119180000,m=AM,bw=DSB 6k,d=Targu Mures (LRTM_TWR)
f=119755000,m=AM,bw=DSB 6k,d=Tulcea (LRTC_TWR)
f=129950000,m=AM,bw=DSB 6k,d=Baneasa (LRBS_GND)
f=121855000,m=AM,bw=DSB 6k,d=Otopeni (LROP_GND)
f=121600000,m=AM,bw=DSB 6k,d=Timisoara (LRTR_GND)
-62
View File
@@ -1,62 +0,0 @@
# https://www.fcc.gov/wireless/bureau-divisions/mobility-division/paging
# Lower Paging Bands - Paired
f=152030000,d=USA LPBP-FA1
f=152060000,d=USA LPBP-FB1
f=152090000,d=USA LPBP-FC1
f=152120000,d=USA LPBP-FD1
f=152150000,d=USA LPBP-FE1
f=152180000,d=USA LPBP-FF1
f=152210000,d=USA LPBP-FG1
f=152510000,d=USA LPBP-FH1
f=152540000,d=USA LPBP-FI1
f=152570000,d=USA LPBP-FJ1
f=152600000,d=USA LPBP-FK1
f=152630000,d=USA LPBP-FL1
f=152660000,d=USA LPBP-FM1
f=152690000,d=USA LPBP-FN1
f=152720000,d=USA LPBP-FO1
f=152750000,d=USA LPBP-FP1
f=152780000,d=USA LPBP-FQ1
f=152810000,d=USA LPBP-FR1
f=158490000,d=USA LPBP-FA2
f=158520000,d=USA LPBP-FB2
f=158550000,d=USA LPBP-FC2
f=158580000,d=USA LPBP-FD2
f=158610000,d=USA LPBP-FE2
f=158640000,d=USA LPBP-FF2
f=158670000,d=USA LPBP-FG2
f=157770000,d=USA LPBP-FH2
f=157800000,d=USA LPBP-FI2
f=157830000,d=USA LPBP-FJ2
f=157860000,d=USA LPBP-FK2
f=157890000,d=USA LPBP-FL2
f=157920000,d=USA LPBP-FM2
f=157950000,d=USA LPBP-FN2
f=157980000,d=USA LPBP-FO2
f=158010000,d=USA LPBP-FP2
f=158040000,d=USA LPBP-FQ2
f=158070000,d=USA LPBP-FR2
f=454025000,d=USA LPBP-GA1
f=454050000,d=USA LPBP-GB1
f=454075000,d=USA LPBP-GC1
f=454100000,d=USA LPBP-GD1
f=454125000,d=USA LPBP-GE1
f=454150000,d=USA LPBP-GF1
f=454175000,d=USA LPBP-GG1
f=454200000,d=USA LPBP-GH1
f=454225000,d=USA LPBP-GI1
f=454250000,d=USA LPBP-GJ1
f=454275000,d=USA LPBP-GK1
f=454300000,d=USA LPBP-GL1
f=454325000,d=USA LPBP-GM1
f=152570000,d=USA LPBP-GN1
f=152600000,d=USA LPBP-GO1
f=152630000,d=USA LPBP-GP1
f=152660000,d=USA LPBP-GQ1
f=152690000,d=USA LPBP-GR1
f=152720000,d=USA LPBP-GS1
f=152750000,d=USA LPBP-GT1
f=152780000,d=USA LPBP-GU1
f=152810000,d=USA LPBP-GV1
f=152720000,d=USA LPBP-GX1
f=152750000,d=USA LPBP-GY1
-28
View File
@@ -1,28 +0,0 @@
# https://www.fcc.gov/wireless/bureau-divisions/mobility-division/paging
# Lower Paging Bands - Paired
f=152810000,d=USA LPBP-GZ1
f=459025000,d=USA LPBP-GA2
f=459050000,d=USA LPBP-GB2
f=459075000,d=USA LPBP-GC2
f=459100000,d=USA LPBP-GD2
f=459125000,d=USA LPBP-GE2
f=459150000,d=USA LPBP-GF2
f=459175000,d=USA LPBP-GG2
f=459200000,d=USA LPBP-GH2
f=459225000,d=USA LPBP-GI2
f=459250000,d=USA LPBP-GJ2
f=459275000,d=USA LPBP-GK2
f=459300000,d=USA LPBP-GL2
f=459325000,d=USA LPBP-GM2
f=157830000,d=USA LPBP-GN2
f=157860000,d=USA LPBP-GO2
f=157890000,d=USA LPBP-GP2
f=157920000,d=USA LPBP-GQ2
f=157950000,d=USA LPBP-GR2
f=157980000,d=USA LPBP-GS2
f=158010000,d=USA LPBP-GT2
f=158040000,d=USA LPBP-GU2
f=158070000,d=USA LPBP-GV2
f=157980000,d=USA LPBP-GX2
f=158010000,d=USA LPBP-GY2
f=158070000,d=USA LPBP-GZ2
-38
View File
@@ -1,38 +0,0 @@
# https://www.fcc.gov/wireless/bureau-divisions/mobility-division/paging
# Lower Paging Bands - Unpaired
f=35200000,d=USA LPBU-CA
f=35220000,d=USA LPBU-CB
f=35240000,d=USA LPBU-CC
f=35260000,d=USA LPBU-CD
f=35300000,d=USA LPBU-CE
f=35340000,d=USA LPBU-CF
f=35380000,d=USA LPBU-CG
f=35420000,d=USA LPBU-CH
f=35460000,d=USA LPBU-CI
f=35500000,d=USA LPBU-CJ
f=35540000,d=USA LPBU-CK
f=35560000,d=USA LPBU-CL
f=35580000,d=USA LPBU-CM
f=35600000,d=USA LPBU-CN
f=35620000,d=USA LPBU-CO
f=35660000,d=USA LPBU-CP
f=43200000,d=USA LPBU-DA
f=43220000,d=USA LPBU-DB
f=43240000,d=USA LPBU-DC
f=43260000,d=USA LPBU-DD
f=43300000,d=USA LPBU-DE
f=43340000,d=USA LPBU-DF
f=43380000,d=USA LPBU-DG
f=43420000,d=USA LPBU-DH
f=43460000,d=USA LPBU-DI
f=43500000,d=USA LPBU-DJ
f=43540000,d=USA LPBU-DK
f=43560000,d=USA LPBU-DL
f=43580000,d=USA LPBU-DM
f=43600000,d=USA LPBU-DN
f=43620000,d=USA LPBU-DO
f=43660000,d=USA LPBU-DP
f=152240000,d=USA LPBU-EA
f=152800000,d=USA LPBU-EB
f=158100000,d=USA LPBU-EC
f=158700000,d=USA LPBU-ED
-52
View File
@@ -1,52 +0,0 @@
# https://www.fcc.gov/wireless/bureau-divisions/mobility-division/paging
# Upper Paging Bands - Private Carrier Paging
f=929012500,d=USA PCP-A
f=929112500,d=USA PCP-B
f=929237500,d=USA PCP-C
f=929312500,d=USA PCP-D
f=929387500,d=USA PCP-E
f=929437500,d=USA PCP-F
f=929462500,d=USA PCP-G
f=929637500,d=USA PCP-H
f=929687500,d=USA PCP-I
f=929787500,d=USA PCP-J
f=929912500,d=USA PCP-K
f=929962500,d=USA PCP-L
# Upper Paging Bands - Common Carrier Paging
f=931012500,d=USA CCP-AA
f=931037500,d=USA CCP-AB
f=931062500,d=USA CCP-AC
f=931087500,d=USA CCP-AD
f=931112500,d=USA CCP-AE
f=931137500,d=USA CCP-AF
f=931162500,d=USA CCP-AG
f=931187500,d=USA CCP-AH
f=931212500,d=USA CCP-AI
f=931237500,d=USA CCP-AJ
f=931262500,d=USA CCP-AK
f=931287500,d=USA CCP-AL
f=931312500,d=USA CCP-AM
f=929462500,d=USA CCP-AN
f=929637500,d=USA CCP-AO
f=929687500,d=USA CCP-AP
f=929787500,d=USA CCP-AQ
f=929912500,d=USA CCP-AR
f=929962500,d=USA CCP-AS
f=929462500,d=USA CCP-AT
f=929637500,d=USA CCP-AU
f=929687500,d=USA CCP-AV
f=929787500,d=USA CCP-AW
f=929912500,d=USA CCP-AX
f=929962500,d=USA CCP-AY
f=929962500,d=USA CCP-AZ
f=931662500,d=USA CCP-BA
f=931687500,d=USA CCP-BB
f=931712500,d=USA CCP-BC
f=931737500,d=USA CCP-BD
f=931762500,d=USA CCP-BE
f=931787500,d=USA CCP-BF
f=931812500,d=USA CCP-BG
f=931837500,d=USA CCP-BH
f=931862500,d=USA CCP-BI
f=931962500,d=USA CCP-BJ
f=931987500,d=USA CCP-BK
+3
View File
@@ -0,0 +1,3 @@
2402000000
2426000000
2480000000

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