Compare commits

...

43 Commits

Author SHA1 Message Date
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
jLynx eb22af84ff Update version.txt 2024-12-20 20:05:01 +13:00
jLynx 6dc25813e2 Update past_version.txt 2024-12-20 20:04:46 +13:00
Bernd Herzog 4ee00c0031 Enable exFAT feature (#2437)
* enabled exFAT in ffconf
* added exFAT to sd card check
* solved todo
2024-12-20 07:55:39 +01:00
sommermorgentraum 55525ed4ad fix wrong cast in morse app (#2436)
* fix wrong cast

* sorry, forgot to use enum
2024-12-20 07:53:15 +01:00
Erwin Ried 84dcfe29a7 Update README.md link to h4m was broken again 2024-12-18 23:44:35 +01:00
Erwin Ried 1ebe956557 h4m photo was broken 2024-12-18 23:35:42 +01:00
gullradriel ae656f07bf Usb fix (#2433)
* revert custom compiler definition since we are now having correct hackrf m0 implementation

* update submodule

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-12-18 21:55:53 +01:00
sommermorgentraum d5c894b322 fix namingspace for an app, remove too old images and links (#2432) 2024-12-18 11:20:53 +01:00
sommermorgentraum 9cea76a9f3 add metronome app (merge tuner firstly to satisfy conflict) (#2431)
* _

* _
2024-12-18 09:25:02 +00:00
sommermorgentraum ead9449609 add tuner app (#2429)
* _

* icon
2024-12-18 10:06:25 +01:00
Totoo fff63e056a FM Radio ext app (#2428) 2024-12-17 18:53:26 +01:00
sommermorgentraum 42da7443b4 fix gray box & remove multiple line text support for NewButton (#2426) 2024-12-15 18:22:19 +01:00
gullradriel ee10081854 update submodule (#2424) 2024-12-14 18:21:13 +00:00
gullradriel b38d1012fa Fix usb.c and usb_queue.c warnings in our CMakeLists instead of in the files (#2423) 2024-12-14 19:14:39 +01:00
sommermorgentraum 5075349d2d sdcard format detect and warn user (#2420) 2024-12-14 09:39:39 +01:00
gullradriel 8497b62c13 updated submodule (#2422)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-12-14 09:38:45 +01:00
jLynx fdeaf380d1 Update README.md 2024-12-09 20:41:43 +13:00
gullradriel 67eb8e9104 update submodule (#2413) 2024-12-07 15:43:16 +01:00
sommermorgentraum 989414d4df prevent show 1970 when focusing the paging item in fileman app (#2412)
* _

* fix map view button overlap
2024-12-07 13:13:06 +01:00
sommermorgentraum 509bda2472 move Temprature debug app into external (#2411)
* remove commented out code
2024-12-07 12:38:15 +01:00
Erwin Ried f5e6cb0d96 Update README.md
Removing the upper warning of scams, since it has been calm last half year and temporal link after another H2 item was taken down
2024-12-07 11:01:41 +01:00
Bernd Herzog 8e2442f196 Add fresh build of hackrf firmware to firmware zip (#2409)
* added fresh build of hackrf firmware to firmware zip

* refactoring

* refactoring
2024-12-06 13:02:00 +01:00
sommermorgentraum ac89ca2a2a RSSI draw/ Level app opt (#2403)
* change peak from green to orange
* add db
2024-12-05 19:24:27 +01:00
gullradriel 498369b77d updated submodule (#2407) 2024-12-05 18:19:30 +01:00
Bernd Herzog 2e64fae953 updated submodule (#2405) 2024-12-05 13:18:33 +01:00
Bernd Herzog 2d8e0f88a3 Replace hackrf submodule (#2404)
* replaced hackrf submodule
* updated hackrf submodule
* replaced hackrf overlay with mayhem fork
2024-12-05 12:31:45 +01:00
Bernd Herzog 874eba8b36 Add portapack cpld write usb serial command for AG256SL100 devices (#2401) 2024-12-05 08:12:14 +01:00
cygeus c553df7170 Corrected two small typos. (#1932) 2024-11-30 21:49:33 +13:00
sommermorgentraum 67a804c5ba fix progress bar still ongoing after user interraced, in OOKEdit app (#2397)
* _

* _

* format
2024-11-28 09:39:02 +01:00
93 changed files with 53009 additions and 1381 deletions
+2 -1
View File
@@ -1 +1,2 @@
build
*
!firmware/tools/docker-entrypoint.sh
@@ -61,6 +61,9 @@ jobs:
- name: Unzip world map
run: |
unzip world_map.zip -d sdcard/ADSB
- name: Prepare Firmware ZIP
run: |
cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/utils/hackrf_one_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/utils/hackrf_one_usb.bin
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
@@ -45,6 +45,9 @@ jobs:
- name: Unzip world map
run: |
unzip world_map.zip -d sdcard/ADSB
- name: Prepare Firmware ZIP
run: |
cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/utils/hackrf_one_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/utils/hackrf_one_usb.bin
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
+1 -1
View File
@@ -1 +1 @@
v2.0.1
v2.0.2
+1 -1
View File
@@ -1 +1 @@
v2.0.2
v2.1.0
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "hackrf"]
path = hackrf
url = https://github.com/mossmann/hackrf.git
url = https://github.com/portapack-mayhem/hackrf.git
+2 -1
View File
@@ -65,10 +65,11 @@ message("Building version: ${VERSION} MD5: ${VERSION_MD5}")
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
add_subdirectory(hackrf-portapack)
add_subdirectory(hackrf/firmware)
set(HACKRF_FIRMWARE_DFU_FILENAME hackrf_usb.dfu)
set(HACKRF_FIRMWARE_BIN_FILENAME hackrf_usb_ram.bin)
set(HACKRF_FIRMWARE_FILENAME hackrf_usb.bin)
set(HACKRF_CPLD_XSVF_FILENAME default.xsvf)
set(HACKRF_PATH ${CMAKE_CURRENT_LIST_DIR}/hackrf)
+9 -11
View File
@@ -1,25 +1,23 @@
<!---
> [!WARNING]
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED VERSIONS, YOU'RE BEING SCAMMED.__
>
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.
> 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)
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.
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://grabify.link/P9CO0J) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure)*
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition) [<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
# What is this?
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="The Latest HackRF & Portapak Combo - H4M The Flipper Zero Killer?" src="https://img.youtube.com/vi/Ew2qDgm2hf0/maxresdefault.jpg" width="701">](https://grabify.link/6HKX9A)
[<img alt="The Latest HackRF & Portapak Combo - H4M The Flipper Zero Killer?" src="https://img.youtube.com/vi/Ew2qDgm2hf0/maxresdefault.jpg" width="701">](https://share.hackrf.app/6HKX9A)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://grabify.link/X4D5TF) [<img alt="HackRF Portapack H4M - Getting Started Guide" src="https://img.youtube.com/vi/wzP0zWi85SI/maxresdefault.jpg" width="172">](https://grabify.link/F9MPOO) [<img alt="The new HackRF Portapack H4M" src="https://img.youtube.com/vi/onQRdCITmuk/maxresdefault.jpg" width="172">](https://grabify.link/0JUHZ6) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="172">](https://grabify.link/C0J6ZR)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://share.hackrf.app/X4D5TF) [<img alt="HackRF Portapack H4M - Getting Started Guide" src="https://img.youtube.com/vi/wzP0zWi85SI/maxresdefault.jpg" width="172">](https://share.hackrf.app/F9MPOO) [<img alt="The new HackRF Portapack H4M" src="https://img.youtube.com/vi/onQRdCITmuk/maxresdefault.jpg" width="172">](https://share.hackrf.app/0JUHZ6) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="172">](https://share.hackrf.app/C0J6ZR)
# Frequently Asked Questions
@@ -27,12 +25,12 @@ 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://grabify.link/TUOLYI--->
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/0STXCA), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://grabify.link/99SAMT). Join their giveaways on discord (check the badge on top).
<!---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/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://lab401.com/products/portapack-h4m).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/P9CO0J), 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: 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://grabify.link/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:
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
@@ -64,7 +64,7 @@ add_custom_command(
add_custom_target(
firmware
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME} ${HACKRF_FIRMWARE_FILENAME}
)
if(${GCC_VERSION_MISMATCH})
+1 -1
View File
@@ -377,7 +377,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
hw
apps
protocols
bitmaps
bmps
)
#
+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},
-88
View File
@@ -74,91 +74,6 @@ void DebugMemoryView::focus() {
button_done.focus();
}
/* TemperatureWidget *****************************************************/
void TemperatureWidget::paint(Painter& painter) {
const auto logger = portapack::temperature_logger;
const auto rect = screen_rect();
const Color color_background{0, 0, 64};
const Color color_foreground = Theme::getInstance()->fg_green->foreground;
const Color color_reticle{128, 128, 128};
const auto graph_width = static_cast<int>(logger.capacity()) * bar_width;
const Rect graph_rect{
rect.left() + (rect.width() - graph_width) / 2, rect.top() + 8,
graph_width, rect.height()};
const Rect frame_rect{
graph_rect.left() - 1, graph_rect.top() - 1,
graph_rect.width() + 2, graph_rect.height() + 2};
painter.draw_rectangle(frame_rect, color_reticle);
painter.fill_rectangle(graph_rect, color_background);
const auto history = logger.history();
for (size_t i = 0; i < history.size(); i++) {
const Coord x = graph_rect.right() - (history.size() - i) * bar_width;
const auto sample = history[i];
const auto temp = temperature(sample);
const auto y = screen_y(temp, graph_rect);
const Dim bar_height = graph_rect.bottom() - y;
painter.fill_rectangle({x, y, bar_width, bar_height}, color_foreground);
}
if (!history.empty()) {
const auto sample = history.back();
const auto temp = temperature(sample);
const auto last_y = screen_y(temp, graph_rect);
const Coord x = graph_rect.right() + 8;
const Coord y = last_y - 8;
painter.draw_string({x, y}, style(), temperature_str(temp));
}
const auto display_temp_max = display_temp_min + (graph_rect.height() / display_temp_scale);
for (auto temp = display_temp_min; temp <= display_temp_max; temp += 10) {
const int32_t tick_length = 6;
const auto tick_x = graph_rect.left() - tick_length;
const auto tick_y = screen_y(temp, graph_rect);
painter.fill_rectangle({tick_x, tick_y, tick_length, 1}, color_reticle);
const auto text_x = graph_rect.left() - temp_len * 8 - 8;
const auto text_y = tick_y - 8;
painter.draw_string({text_x, text_y}, style(), temperature_str(temp));
}
}
TemperatureWidget::temperature_t TemperatureWidget::temperature(const sample_t sensor_value) const {
// Scaling is different for MAX2837 vs MAX2839 so it's now done in the respective chip-specific module
return sensor_value;
}
std::string TemperatureWidget::temperature_str(const temperature_t temperature) const {
return to_string_dec_int(temperature, temp_len - 2) + STR_DEGREES_C;
}
Coord TemperatureWidget::screen_y(
const temperature_t temperature,
const Rect& rect) const {
int y_raw = rect.bottom() - ((temperature - display_temp_min) * display_temp_scale);
const auto y_limit = std::min(rect.bottom(), std::max(rect.top(), y_raw));
return y_limit;
}
/* TemperatureView *******************************************************/
TemperatureView::TemperatureView(NavigationView& nav) {
add_children({
&text_title,
&temperature_widget,
&button_done,
});
button_done.on_select = [&nav](Button&) { nav.pop(); };
}
void TemperatureView::focus() {
button_done.focus();
}
/* RegistersWidget *******************************************************/
RegistersWidget::RegistersWidget(
@@ -507,12 +422,9 @@ void DebugMenuView::on_populate() {
{"Debug Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { portapack::persistent_memory::debug_dump(); }},
{"M0 Stack Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { stack_dump(); }},
{"Memory Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryDumpView>(); }},
//{"Memory Usage", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryView>(); }},
{"Peripherals", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
{"Pers. Memory", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugPmemView>(); }},
//{ "Radio State", ui::Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ nav_.push<NotImplementedView>(); } },
{"SD Card", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_sdcard, [this]() { nav_.push<SDCardDebugView>(); }},
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
{"Ext Module", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<ExternalModuleView>(); }},
-47
View File
@@ -86,53 +86,6 @@ class DebugMemoryView : public View {
"Done"};
};
class TemperatureWidget : public Widget {
public:
explicit TemperatureWidget(
Rect parent_rect)
: Widget{parent_rect} {
}
void paint(Painter& painter) override;
private:
using sample_t = uint32_t;
using temperature_t = int32_t;
temperature_t temperature(const sample_t sensor_value) const;
Coord screen_y(const temperature_t temperature, const Rect& screen_rect) const;
std::string temperature_str(const temperature_t temperature) const;
static constexpr temperature_t display_temp_min = -10; // Accomodate negative values, present in cold startup cases
static constexpr temperature_t display_temp_scale = 3;
static constexpr int bar_width = 1;
static constexpr int temp_len = 5; // Now scale shows up to 5 chars ("-10ºC")
};
class TemperatureView : public View {
public:
explicit TemperatureView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Temperature"; };
private:
Text text_title{
{76, 16, 240, 16},
"Temperature",
};
TemperatureWidget temperature_widget{
{0, 40, 240, 180},
};
Button button_done{
{72, 264, 96, 24},
"Done"};
};
typedef enum {
CT_PMEM,
CT_RFFC5072,
@@ -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;
+10 -3
View File
@@ -751,10 +751,17 @@ FileManagerView::FileManagerView(
text_date.set("Too many files!");
} else {
text_date.set_style(Theme::getInstance()->fg_medium);
if (selected_is_valid())
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") + to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
if (selected_is_valid()) {
if (get_selected_entry().path == str_back) {
text_date.set("Go page " + std::to_string(pagination + 1 - 1)); // for better explain, pagination start with 0 AKA real page - 1
} else if (get_selected_entry().path == str_next) {
text_date.set("Go page " + std::to_string(pagination + 1 + 1)); // when show this, it should display current AKA (pagination + 1) + 1 AKA next page
} else {
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") + to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
}
} else {
text_date.set("");
}
}
};
@@ -95,7 +95,7 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
menu_view.add_item({filename.string().substr(0, max_filename_length),
color,
&bitmap_icon_temperature,
&bitmap_icon_peripherals_details,
[this, path](KeyEvent) {
this->firmware_selected(path);
}});
+1
View File
@@ -187,6 +187,7 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
if (last_max_db != statistics.max_db) {
last_max_db = statistics.max_db;
freq_stats_db.set("Power: " + to_string_dec_int(statistics.max_db) + " db");
rssi.set_db(statistics.max_db);
}
// refresh rssi
if (last_min_rssi != rssi_graph.get_graph_min() || last_avg_rssi != rssi_graph.get_graph_avg() || last_max_rssi != rssi_graph.get_graph_max()) {
+47 -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,11 +707,28 @@ 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_sensitivity.set_by_value(pmem::encoder_dial_sensitivity());
field_encoder_rate_multiplier.set_value(pmem::encoder_rate_multiplier());
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());
@@ -777,13 +799,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 +821,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 +994,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 +1075,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 +1099,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 */
+43 -43
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{
@@ -607,6 +611,22 @@ class SetEncoderDialView : public View {
1,
' '};
Button button_dial_sensitivity_plus{
{20 * 8, 4 * 16, 16, 16},
"+"};
Button button_dial_sensitivity_minus{
{20 * 8, 6 * 16, 16, 16},
"-"};
Button button_rate_multiplier_plus{
{20 * 8, 11 * 16, 16, 16},
"+"};
Button button_rate_multiplier_minus{
{20 * 8, 13 * 16, 16, 16},
"-"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -705,6 +725,7 @@ class SetConfigModeView : public View {
"Cancel",
};
};
using portapack::persistent_memory::fake_brightness_level_options;
class SetDisplayView : public View {
public:
@@ -715,8 +736,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 +904,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 +987,7 @@ class SettingsMenuView : public BtnGridView {
private:
NavigationView& nav_;
void on_populate() override;
};
+76
View File
@@ -4827,6 +4827,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,
@@ -5739,6 +5777,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,
+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__
+2 -2
View File
@@ -26,7 +26,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::audio_test {
AudioTestView::AudioTestView(NavigationView& nav)
: nav_{nav} {
@@ -103,4 +103,4 @@ void AudioTestView::update_audio_beep() {
baseband::request_beep_stop();
}
} /* namespace ui */
} /* namespace ui::external_app::audio_test */
+2 -2
View File
@@ -25,7 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
namespace ui {
namespace ui::external_app::audio_test {
class AudioTestView : public View {
public:
@@ -96,6 +96,6 @@ class AudioTestView : public View {
Theme::getInstance()->bg_dark->background};
};
} /* namespace ui */
} /* namespace ui::external_app::audio_test */
#endif /*__UI_AUDIO_TEST_H__*/
+27 -2
View File
@@ -130,6 +130,26 @@ set(EXTCPPSRC
#remote
external/remote/main.cpp
external/remote/ui_remote.cpp
#mcu_temperature
external/mcu_temperature/main.cpp
external/mcu_temperature/mcu_temperature.cpp
#fmradio
external/fmradio/main.cpp
external/fmradio/ui_fmradio.cpp
#tuner
external/tuner/main.cpp
external/tuner/ui_tuner.cpp
#metronome
external/metronome/main.cpp
external/metronome/ui_metronome.cpp
#app_manager
external/app_manager/main.cpp
external/app_manager/ui_app_manager.cpp
)
set(EXTAPPLIST
@@ -163,5 +183,10 @@ set(EXTAPPLIST
ook_editor
shoppingcart_lock
flippertx
remote
)
remote
mcu_temperature
fmradio
tuner
metronome
app_manager
)
+35
View File
@@ -54,6 +54,11 @@ MEMORY
ram_external_app_flippertx(rwx) : org = 0xADCD0000, len = 32k
ram_external_app_ook_editor(rwx) : org = 0xADCE0000, len = 32k
ram_external_app_remote(rwx) : org = 0xADCF0000, len = 32k
ram_external_app_mcu_temperature(rwx) : org = 0xADD00000, len = 32k
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
}
SECTIONS
@@ -244,4 +249,34 @@ SECTIONS
KEEP(*(.external_app.app_remote.application_information));
*(*ui*external_app*remote*);
} > ram_external_app_remote
.external_app_mcu_temperature : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_mcu_temperature.application_information));
*(*ui*external_app*mcu_temperature*);
} > ram_external_app_mcu_temperature
.external_app_fmradio : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_fmradio.application_information));
*(*ui*external_app*fmradio*);
} > ram_external_app_fmradio
.external_app_tuner : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_tuner.application_information));
*(*ui*external_app*tuner*);
} > ram_external_app_tuner
.external_app_metronome : ALIGN(4) SUBALIGN(4)
{
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
}
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_fmradio.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::fmradio {
void initialize_app(ui::NavigationView& nav) {
nav.push<FmRadioView>();
}
} // namespace ui::external_app::fmradio
extern "C" {
__attribute__((section(".external_app.app_fmradio.application_information"), used)) application_information_t _application_information_fmradio = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::fmradio::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "FM Radio",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0x04,
0x20,
0x12,
0x48,
0x8A,
0x51,
0xCA,
0x53,
0xCA,
0x53,
0x8A,
0x51,
0x12,
0x48,
0x84,
0x21,
0xC0,
0x03,
0x40,
0x02,
0x60,
0x06,
0x20,
0x04,
0x30,
0x0C,
0xF0,
0x0F,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_wfm_audio */ {'P', 'W', 'F', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_fmradio.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace modems;
using namespace ui;
namespace ui::external_app::fmradio {
void FmRadioView::focus() {
field_frequency.focus();
}
FmRadioView::FmRadioView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_volume,
&field_frequency,
&btn_fav_save,
&txt_save_help,
&btn_fav_0,
&btn_fav_1,
&btn_fav_2,
&btn_fav_3,
&btn_fav_4,
&btn_fav_5,
&btn_fav_6,
&btn_fav_7,
&btn_fav_8,
&btn_fav_9,
&audio,
&waveform});
txt_save_help.visible(false);
for (uint8_t i = 0; i < 12; ++i) {
if (freq_fav_list[i] == 0) {
freq_fav_list[i] = 87000000;
}
}
if (field_frequency.value() == 0) {
field_frequency.set_value(87000000);
}
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
field_frequency.set_step(25000);
receiver_model.enable();
audio::output::start();
btn_fav_0.on_select = [this](Button&) {
on_btn_clicked(0);
};
btn_fav_1.on_select = [this](Button&) {
on_btn_clicked(1);
};
btn_fav_2.on_select = [this](Button&) {
on_btn_clicked(2);
};
btn_fav_3.on_select = [this](Button&) {
on_btn_clicked(3);
};
btn_fav_4.on_select = [this](Button&) {
on_btn_clicked(4);
};
btn_fav_5.on_select = [this](Button&) {
on_btn_clicked(5);
};
btn_fav_6.on_select = [this](Button&) {
on_btn_clicked(6);
};
btn_fav_7.on_select = [this](Button&) {
on_btn_clicked(7);
};
btn_fav_8.on_select = [this](Button&) {
on_btn_clicked(8);
};
btn_fav_9.on_select = [this](Button&) {
on_btn_clicked(9);
};
btn_fav_save.on_select = [this](Button&) {
save_fav = !save_fav;
txt_save_help.set_text(save_fav ? "Select slot" : "");
txt_save_help.visible(save_fav);
txt_save_help.set_dirty();
};
update_fav_btn_texts();
}
void FmRadioView::on_btn_clicked(uint8_t i) {
if (save_fav) {
save_fav = false;
freq_fav_list[i] = field_frequency.value();
update_fav_btn_texts();
txt_save_help.visible(save_fav);
txt_save_help.set_text("");
txt_save_help.set_dirty();
return;
}
field_frequency.set_value(freq_fav_list[i]);
}
std::string FmRadioView::to_nice_freq(rf::Frequency freq) {
std::string nice = to_string_dec_uint(freq / 1000000);
nice += ".";
nice += to_string_dec_uint((freq / 10000) % 100);
return nice;
}
void FmRadioView::update_fav_btn_texts() {
btn_fav_0.set_text(to_nice_freq(freq_fav_list[0]));
btn_fav_1.set_text(to_nice_freq(freq_fav_list[1]));
btn_fav_2.set_text(to_nice_freq(freq_fav_list[2]));
btn_fav_3.set_text(to_nice_freq(freq_fav_list[3]));
btn_fav_4.set_text(to_nice_freq(freq_fav_list[4]));
btn_fav_5.set_text(to_nice_freq(freq_fav_list[5]));
btn_fav_6.set_text(to_nice_freq(freq_fav_list[6]));
btn_fav_7.set_text(to_nice_freq(freq_fav_list[7]));
btn_fav_8.set_text(to_nice_freq(freq_fav_list[8]));
btn_fav_9.set_text(to_nice_freq(freq_fav_list[9]));
}
FmRadioView::~FmRadioView() {
receiver_model.disable();
baseband::shutdown();
audio::output::stop();
}
void FmRadioView::on_audio_spectrum() {
for (size_t i = 0; i < audio_spectrum_data->db.size(); i++)
audio_spectrum[i] = ((int16_t)audio_spectrum_data->db[i] - 127) * 256;
waveform.set_dirty();
}
} // namespace ui::external_app::fmradio
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
FUTURE TODO: implement search function.
*/
#ifndef __UI_fmradio_H__
#define __UI_fmradio_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_geomap.hpp"
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
using namespace ui;
namespace ui::external_app::fmradio {
#define FMR_BTNGRID_TOP 60
class FmRadioView : public View {
public:
FmRadioView(NavigationView& nav);
FmRadioView& operator=(const FmRadioView&) = delete;
FmRadioView(const FmRadioView&) = delete;
~FmRadioView();
void focus() override;
std::string title() const override { return "FM radio"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{};
int16_t audio_spectrum[128]{0};
bool audio_spectrum_update = false;
AudioSpectrum* audio_spectrum_data{nullptr};
rf::Frequency freq_fav_list[12] = {0};
app_settings::SettingsManager settings_{
"rx_fmradio",
app_settings::Mode::RX,
{{"favlist0"sv, &freq_fav_list[0]},
{"favlist1"sv, &freq_fav_list[1]},
{"favlist2"sv, &freq_fav_list[2]},
{"favlist3"sv, &freq_fav_list[3]},
{"favlist4"sv, &freq_fav_list[4]},
{"favlist5"sv, &freq_fav_list[5]},
{"favlist6"sv, &freq_fav_list[6]},
{"favlist7"sv, &freq_fav_list[7]},
{"favlist8"sv, &freq_fav_list[8]},
{"favlist9"sv, &freq_fav_list[9]},
{"favlist10"sv, &freq_fav_list[10]},
{"favlist11"sv, &freq_fav_list[11]}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
TextField txt_save_help{
{2, FMR_BTNGRID_TOP + 6 * 34 - 20, 12 * 8, 16},
" "};
Audio audio{
{21 * 8, 10, 6 * 8, 4}};
Waveform waveform{
{0, 20, 30 * 8, 2 * 16},
audio_spectrum,
128,
0,
false,
Theme::getInstance()->bg_darkest->foreground};
Button btn_fav_0{{2, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_1{{2 + 15 * 8, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_2{{2, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_3{{2 + 15 * 8, FMR_BTNGRID_TOP + 1 * 34, 10 * 8, 28}, "---"};
Button btn_fav_4{{2, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_5{{2 + 15 * 8, FMR_BTNGRID_TOP + 2 * 34, 10 * 8, 28}, "---"};
Button btn_fav_6{{2, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_7{{2 + 15 * 8, FMR_BTNGRID_TOP + 3 * 34, 10 * 8, 28}, "---"};
Button btn_fav_8{{2, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_9{{2 + 15 * 8, FMR_BTNGRID_TOP + 4 * 34, 10 * 8, 28}, "---"};
Button btn_fav_save{{2, FMR_BTNGRID_TOP + 6 * 34, 7 * 8, 1 * 28}, "Save"};
bool save_fav = false;
void on_btn_clicked(uint8_t i);
void update_fav_btn_texts();
std::string to_nice_freq(rf::Frequency freq);
void on_audio_spectrum();
MessageHandlerRegistration message_handler_audio_spectrum{
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
audio_spectrum_data = message.data;
audio_spectrum_update = true;
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if (audio_spectrum_update) {
audio_spectrum_update = false;
on_audio_spectrum();
}
}};
};
} // namespace ui::external_app::fmradio
#endif /*__UI_fmradio_H__*/
+83
View File
@@ -0,0 +1,83 @@
/*
* 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 "mcu_temperature.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::mcu_temperature {
void initialize_app(ui::NavigationView& nav) {
nav.push<McuTemperatureView>();
}
} // namespace ui::external_app::mcu_temperature
extern "C" {
__attribute__((section(".external_app.app_mcu_temperature.application_information"), used)) application_information_t _application_information_mcu_temperature = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::mcu_temperature::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Temperature",
/*.bitmap_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,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::DEBUG,
/*.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,91 @@
#include "mcu_temperature.hpp"
#include "ui_painter.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui::external_app::mcu_temperature {
void McuTemperatureWidget::paint(Painter& painter) {
const auto logger = portapack::temperature_logger;
const auto rect = screen_rect();
const Color color_background{0, 0, 64};
const Color color_foreground = Theme::getInstance()->fg_green->foreground;
const Color color_reticle{128, 128, 128};
const auto graph_width = static_cast<int>(logger.capacity()) * bar_width;
const Rect graph_rect{
rect.left() + (rect.width() - graph_width) / 2, rect.top() + 8,
graph_width, rect.height()};
const Rect frame_rect{
graph_rect.left() - 1, graph_rect.top() - 1,
graph_rect.width() + 2, graph_rect.height() + 2};
painter.draw_rectangle(frame_rect, color_reticle);
painter.fill_rectangle(graph_rect, color_background);
const auto history = logger.history();
for (size_t i = 0; i < history.size(); i++) {
const Coord x = graph_rect.right() - (history.size() - i) * bar_width;
const auto sample = history[i];
const auto temp = temperature(sample);
const auto y = screen_y(temp, graph_rect);
const Dim bar_height = graph_rect.bottom() - y;
painter.fill_rectangle({x, y, bar_width, bar_height}, color_foreground);
}
if (!history.empty()) {
const auto sample = history.back();
const auto temp = temperature(sample);
const auto last_y = screen_y(temp, graph_rect);
const Coord x = graph_rect.right() + 8;
const Coord y = last_y - 8;
painter.draw_string({x, y}, style(), temperature_str(temp));
}
const auto display_temp_max = display_temp_min + (graph_rect.height() / display_temp_scale);
for (auto temp = display_temp_min; temp <= display_temp_max; temp += 10) {
const int32_t tick_length = 6;
const auto tick_x = graph_rect.left() - tick_length;
const auto tick_y = screen_y(temp, graph_rect);
painter.fill_rectangle({tick_x, tick_y, tick_length, 1}, color_reticle);
const auto text_x = graph_rect.left() - temp_len * 8 - 8;
const auto text_y = tick_y - 8;
painter.draw_string({text_x, text_y}, style(), temperature_str(temp));
}
}
McuTemperatureWidget::temperature_t McuTemperatureWidget::temperature(const sample_t sensor_value) const {
// Scaling is different for MAX2837 vs MAX2839 so it's now done in the respective chip-specific module
return sensor_value;
}
std::string McuTemperatureWidget::temperature_str(const temperature_t temperature) const {
return to_string_dec_int(temperature, temp_len - 2) + STR_DEGREES_C;
}
Coord McuTemperatureWidget::screen_y(
const temperature_t temperature,
const Rect& rect) const {
int y_raw = rect.bottom() - ((temperature - display_temp_min) * display_temp_scale);
const auto y_limit = std::min(rect.bottom(), std::max(rect.top(), y_raw));
return y_limit;
}
McuTemperatureView::McuTemperatureView(NavigationView& nav) {
add_children({
&text_title,
&temperature_widget,
&button_done,
});
button_done.on_select = [&nav](Button&) { nav.pop(); };
}
void McuTemperatureView::focus() {
button_done.focus();
}
} // namespace ui::external_app::mcu_temperature
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __MCU_TEMPERATURE_H__
#define __MCU_TEMPERATURE_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
#include "portapack.hpp"
#include "memory_map.hpp"
#include "irq_controls.hpp"
#include <functional>
#include <utility>
namespace ui::external_app::mcu_temperature {
class McuTemperatureWidget : public Widget {
public:
explicit McuTemperatureWidget(
Rect parent_rect)
: Widget{parent_rect} {
}
void paint(Painter& painter) override;
private:
using sample_t = uint32_t;
using temperature_t = int32_t;
temperature_t temperature(const sample_t sensor_value) const;
Coord screen_y(const temperature_t temperature, const Rect& screen_rect) const;
std::string temperature_str(const temperature_t temperature) const;
static constexpr temperature_t display_temp_min = -10; // Accomodate negative values, present in cold startup cases
static constexpr temperature_t display_temp_scale = 3;
static constexpr int bar_width = 1;
static constexpr int temp_len = 5; // Now scale shows up to 5 chars ("-10ºC")
};
class McuTemperatureView : public View {
public:
explicit McuTemperatureView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Temperature"; };
private:
Text text_title{
{76, 16, 240, 16},
"Temperature",
};
McuTemperatureWidget temperature_widget{
{0, 40, 240, 180},
};
Button button_done{
{72, 264, 96, 24},
"Done"};
};
} // namespace ui::external_app::mcu_temperature
#endif /*__MCU_TEMPERATURE_H__*/
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2024 Bernd
*
* 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_metronome.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::metronome {
void initialize_app(ui::NavigationView& nav) {
nav.push<MetronomeView>();
}
} // namespace ui::external_app::metronome
extern "C" {
__attribute__((section(".external_app.app_metronome.application_information"), used)) application_information_t _application_information_metronome = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::metronome::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Metronome",
/*.bitmap_data = */ {
0x00,
0x00,
0xC0,
0x43,
0x20,
0x66,
0xA0,
0x34,
0x20,
0x18,
0xB0,
0x0C,
0x10,
0x04,
0x10,
0x06,
0x10,
0x0B,
0x98,
0x19,
0x08,
0x10,
0xF8,
0x1F,
0xF8,
0x1F,
0xF8,
0x1F,
0xF0,
0x0F,
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 */ {'P', 'A', 'B', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+177
View File
@@ -0,0 +1,177 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_metronome.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui::external_app::metronome {
MetronomeView::MetronomeView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // proc_audio_beep baseband is external too
add_children({
&labels,
&field_volume,
&button_play_stop,
&field_rythm_unaccent_time,
&field_rythm_accent_time,
&field_accent_beep_tune,
&field_unaccent_beep_tune,
&field_beep_flash_duration,
&field_bpm,
&progressbar,
});
field_bpm.set_value(120);
field_rythm_accent_time.set_value(4);
field_rythm_unaccent_time.set_value(4);
field_accent_beep_tune.set_value(880);
field_unaccent_beep_tune.set_value(440);
field_beep_flash_duration.set_value(100);
button_play_stop.on_select = [this]() {
if (playing_) {
stop_play();
} else {
play();
}
};
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
field_volume.set_value(99);
audio::set_rate(audio::Rate::Hz_48000);
audio::output::start();
}
MetronomeView::~MetronomeView() {
should_exit = true;
if (thread) {
chThdWait(thread);
thread = nullptr;
}
receiver_model.disable();
baseband::shutdown();
audio::output::stop();
}
void MetronomeView::focus() {
field_bpm.focus();
}
void MetronomeView::stop_play() {
if (playing_) {
playing_ = false;
button_play_stop.set_bitmap(&bitmap_icon_replay);
baseband::request_beep_stop();
progressbar.set_value(0);
progressbar.set_style(Theme::getInstance()->fg_light);
}
}
void MetronomeView::play() {
if (!playing_) {
playing_ = true;
current_beat_ = 0;
button_play_stop.set_bitmap(&bitmap_icon_sleep);
if (!thread) {
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, MetronomeView::static_fn, this);
}
}
}
void MetronomeView::beep_accent_beat() {
baseband::request_audio_beep(field_accent_beep_tune.value(), 48000, field_beep_flash_duration.value());
}
void MetronomeView::beep_unaccent_beat() {
baseband::request_audio_beep(field_unaccent_beep_tune.value(), 48000, field_beep_flash_duration.value());
}
// TODO: draw the beat
// void MetronomeView::paint(Painter& painter) {
// View::paint(painter);
// painter.fill_rectangle(
// {visual_x, visual_y, visual_width, visual_height},
// Theme::getInstance()->bg_darkest->background);
// if (playing_) {
// const bool is_accent_beat = (current_beat_ % field_rythm_accent_time.value()) == 0;
// const Color beat_color = is_accent_beat ?
// Theme::getInstance()->fg_red->foreground :
// Theme::getInstance()->fg_green->foreground;
// painter.fill_rectangle(
// {visual_x + visual_width/4,
// visual_y + visual_height/4,
// visual_width/2,
// visual_height/2},
// beat_color);
// }
// }
msg_t MetronomeView::static_fn(void* arg) {
auto obj = static_cast<MetronomeView*>(arg);
obj->run();
return 0;
}
void MetronomeView::run() {
while (!should_exit) {
if (!playing_) {
chThdSleepMilliseconds(100);
continue;
}
uint32_t base_interval = (60 * 1000) / field_bpm.value(); // quarter note as 1 beat
uint32_t beats_per_measure = field_rythm_unaccent_time.value(); // how many beates per bar
progressbar.set_max(beats_per_measure);
uint32_t beat_unit = field_rythm_accent_time.value(); // which note type (quarter, eighth, etc.) as 1 beat
uint32_t actual_interval = (base_interval * 4) / beat_unit; // e.g. when beat_unit==8 it's 1/2 of base_interval AKA eighths notes
uint32_t beat_in_measure = current_beat_ % beats_per_measure; // current beat in this bar (need to decide accent or unaccent)
progressbar.set_value(beat_in_measure + 1);
// accent beat is the first beat of this bar
if (beat_in_measure == 0) {
beep_accent_beat();
progressbar.set_style(Theme::getInstance()->fg_red);
} else {
beep_unaccent_beat();
progressbar.set_style(Theme::getInstance()->fg_green);
}
current_beat_++;
chThdSleepMilliseconds(actual_interval);
}
}
} // namespace ui::external_app::metronome
+126
View File
@@ -0,0 +1,126 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_METRONOME_H__
#define __UI_METRONOME_H__
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "audio.hpp"
#include "ch.h"
namespace ui::external_app::metronome {
class MetronomeView : public View {
public:
MetronomeView(NavigationView& nav);
~MetronomeView();
MetronomeView(const MetronomeView& other) = delete;
MetronomeView& operator=(const MetronomeView& other) = delete;
void focus() override;
std::string title() const override { return "Metronome"; };
private:
NavigationView& nav_;
void beep_accent_beat(); // e.g. 3 of 3/4 beat
void beep_unaccent_beat(); // e.g. 4 of 3/4 beat
void stop_play();
void play();
// void paint(Painter& painter) override;
Thread* thread{nullptr};
bool should_exit{false};
static msg_t static_fn(void* arg);
void run();
bool playing_{false};
uint32_t current_beat_{0};
Labels labels{
{{0 * 8, 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground},
{{(sizeof("Rhythm:") + 1) * 8 + 4 * 8, 4 * 16}, "/", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
NumberField field_bpm{
{(sizeof("BPM:") + 1) * 8, 1 * 16},
4,
{1, 1000},
1,
' '};
NumberField field_rythm_unaccent_time{// e.g. 3 in 3/4 beat
{(sizeof("Rhythm:") + 1) * 8, 4 * 16},
2,
{1, 99},
1,
' '};
NumberField field_rythm_accent_time{// e.g. 4 in 3/4 beat
{(sizeof("Rhythm:") + 1) * 8 + 5 * 8, 4 * 16},
2,
{1, 99},
1,
' '};
NumberField field_beep_flash_duration{
{(sizeof("Beep Flash Duration:") + 1) * 8, 5 * 16},
3,
{10, 999},
1,
' '};
NumberField field_accent_beep_tune{
{(sizeof("Accent Beep Tune:") + 1) * 8, 2 * 16},
5,
{380, 24000},
20,
' '};
NumberField field_unaccent_beep_tune{
{(sizeof("Unaccent Beep Tune:") + 1) * 8, 3 * 16},
5,
{380, 24000},
20,
' '};
AudioVolumeField field_volume{
{(sizeof("Volume:") + 1) * 8, 6 * 16}};
NewButton button_play_stop{
{0 * 16, 16 * 16, screen_width, screen_height - 16 * 16},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_red->foreground};
ProgressBar progressbar{
{0 * 16, 8 * 16, screen_width, screen_height - 14 * 16}};
};
} // namespace ui::external_app::metronome
#endif /*__UI_METRONOME_H__*/
+2 -3
View File
@@ -232,9 +232,8 @@ MorseView::MorseView(
set_foxhunt(foxhunt_code);
};
options_modulation.on_change = [this](size_t i, int32_t value) {
(void)i; // avoid unused warning
mode_cw = (bool)value;
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
mode_cw = (bool)v;
};
options_loop.on_change = [this](size_t i, uint32_t value) {
+7 -2
View File
@@ -41,6 +41,11 @@ using namespace morse;
namespace ui::external_app::morse_tx {
enum Modulation {
FM = 0,
CW = 1
};
class MorseView : public View {
public:
MorseView(NavigationView& nav);
@@ -137,8 +142,8 @@ class MorseView : public View {
OptionsField options_modulation{
{15 * 8, 10 * 8},
2,
{{"CW", true},
{"FM", false}}};
{{"CW", Modulation::CW},
{"FM", Modulation::FM}}};
OptionsField options_loop{
{9 * 8, 12 * 8},
+2 -1
View File
@@ -57,6 +57,7 @@ void OOKEditorAppView::start_tx() {
// `stop_tx` method: Stops the transmission and resets the progress bar.
void OOKEditorAppView::stop_tx() {
// TODO: model stopped but message still spamming.
is_transmitting = false; // set transmitting flag
stop_ook_file_tx(); // stop transmission
progressbar.set_value(0); // Reset progress bar to 0
@@ -82,7 +83,7 @@ void OOKEditorAppView::on_file_changed(const fs::path& new_file_path) {
// `on_tx_progress` method: Updates the progress bar based on transmission progress.
void OOKEditorAppView::on_tx_progress(const uint32_t progress, const bool done) {
progressbar.set_value(progress); // Update progress bar value
if (is_transmitting) progressbar.set_value(progress); // Update progress bar value
if (done) {
stop_tx(); // Stop transmission when progress reaches maximum
}
+1 -1
View File
@@ -84,7 +84,7 @@ class RemoteIcons {
&bitmap_icon_sonde,
&bitmap_icon_stealth,
&bitmap_icon_tetra,
&bitmap_icon_temperature};
&bitmap_icon_peripherals_details};
};
// TODO: Use RGB colors instead?
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2024 Bernd
*
* 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_tuner.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::tuner {
void initialize_app(ui::NavigationView& nav) {
nav.push<TunerView>();
}
} // namespace ui::external_app::tuner
extern "C" {
__attribute__((section(".external_app.app_tuner.application_information"), used)) application_information_t _application_information_tuner = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::tuner::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Tuner",
/*.bitmap_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,
},
/*.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 */ {'P', 'A', 'B', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+256
View File
@@ -0,0 +1,256 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_tuner.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui::external_app::tuner {
TunerView::TunerView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // proc_audio_beep baseband is external too
add_children({
&labels,
&field_volume,
&options_instrument,
&options_note,
&button_play_stop,
&text_note_frequency,
&text_note_octave_shift,
});
audio::set_rate(audio::Rate::Hz_24000);
options_instrument.on_change = [this](size_t, int32_t value) {
const Instrument* selected_instrument = nullptr;
switch (value) {
case 0:
selected_instrument = &guitar;
break;
case 1:
selected_instrument = &violin;
break;
case 2:
selected_instrument = &pitch_fork;
break;
}
if (selected_instrument) {
update_notes_for_instrument(*selected_instrument);
}
update_current_note();
};
options_note.on_change = [this](size_t, int32_t index) {
const Instrument* current_instrument = nullptr;
switch (options_instrument.selected_index_value()) {
case 0:
current_instrument = &guitar;
break;
case 1:
current_instrument = &violin;
break;
case 2:
current_instrument = &pitch_fork;
break;
}
if (current_instrument) {
auto it = current_instrument->notes.begin();
std::advance(it, index);
if (it != current_instrument->notes.end()) {
current_note_frequency = it->second.frequency;
current_note_sample_rate = it->second.sample_rate;
current_note_octave_shift = it->second.octave_shift;
}
}
update_current_note();
};
button_play_stop.on_select = [this]() {
if (current_note_playing) {
stop_play();
} else {
play_change_note();
}
};
options_instrument.set_selected_index(0);
update_notes_for_instrument(guitar);
update_current_note();
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
field_volume.set_value(99);
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
TunerView::~TunerView() {
receiver_model.disable();
baseband::shutdown();
audio::output::stop();
}
void TunerView::focus() {
options_instrument.focus();
}
void TunerView::update_notes_for_instrument(const Instrument& instrument) {
std::vector<OptionsField::option_t> note_options;
size_t index = 0;
for (const auto& note_pair : instrument.notes) {
note_options.emplace_back(OptionsField::option_t{
note_pair.first,
index++});
}
options_note.set_options(note_options);
if (!note_options.empty()) {
options_note.set_selected_index(0);
}
}
void TunerView::update_current_note() {
const Instrument* current_instrument = nullptr;
switch (options_instrument.selected_index_value()) {
case 0:
current_instrument = &guitar;
break;
case 1:
current_instrument = &violin;
break;
case 2:
current_instrument = &pitch_fork;
break;
}
if (current_instrument) {
std::string note_name = options_note.selected_index_name();
// map
auto note_it = current_instrument->notes.find(note_name);
if (note_it != current_instrument->notes.end()) {
current_note_frequency = note_it->second.frequency;
current_note_sample_rate = note_it->second.sample_rate;
current_note_octave_shift = note_it->second.octave_shift;
text_note_frequency.set(std::to_string(current_note_frequency));
text_note_octave_shift.set(std::to_string(current_note_octave_shift));
set_dirty();
if (current_note_playing) {
play_change_note();
}
}
}
}
void TunerView::stop_play() {
baseband::request_beep_stop();
current_note_playing = false;
button_play_stop.set_bitmap(&bitmap_icon_replay);
}
void TunerView::play_change_note() {
if (current_note_playing) {
baseband::request_beep_stop();
audio::set_rate(current_note_sample_rate);
baseband::request_audio_beep(current_note_frequency, protected_sample_rate(current_note_sample_rate), 0);
} else {
audio::set_rate(current_note_sample_rate);
baseband::request_audio_beep(current_note_frequency, protected_sample_rate(current_note_sample_rate), 0);
}
current_note_playing = true;
button_play_stop.set_bitmap(&bitmap_icon_sleep);
set_dirty();
}
uint32_t TunerView::protected_sample_rate(audio::Rate r) {
switch (r) {
case audio::Rate::Hz_12000:
return 12000;
case audio::Rate::Hz_24000:
return 24000;
case audio::Rate::Hz_48000:
return 48000;
default:
return 24000;
}
}
void TunerView::paint(Painter& painter) {
View::paint(painter);
painter.fill_rectangle(
{screen_width / 4, 8 * 16, screen_width / 2, 6 * 16},
Theme::getInstance()->bg_darkest->background);
if (!current_note_playing) return;
painter.fill_rectangle(
{screen_width / 4, 10 * 16, 2, 2 * 16},
Theme::getInstance()->fg_light->foreground);
painter.fill_rectangle(
{screen_width / 4, 12 * 16, screen_width / 4 * 2 + 2, 2},
Theme::getInstance()->fg_light->foreground);
painter.fill_rectangle(
{(screen_width / 4) * 3, 10 * 16, 2, 2 * 16},
Theme::getInstance()->fg_light->foreground);
painter.draw_string(
{screen_width / 4 - 2 * 8, 8 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(current_note_frequency));
int16_t real_frequency = current_note_frequency;
if (current_note_octave_shift > 0) {
for (int i = 0; i < current_note_octave_shift; i++) {
real_frequency *= 2;
}
} else if (current_note_octave_shift < 0) {
for (int i = 0; i > current_note_octave_shift; i--) {
real_frequency /= 2;
}
}
painter.draw_string({(screen_width / 4) * 3 - 2 * 8, 8 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(real_frequency));
painter.draw_string({screen_width / 2 - 3 * 16, 13 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(current_note_octave_shift) + " * 8ev");
}
} // namespace ui::external_app::tuner
+145
View File
@@ -0,0 +1,145 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_TUNER_H__
#define __UI_TUNER_H__
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "audio.hpp"
namespace ui::external_app::tuner {
struct Instrument {
std::string name;
struct NoteInfo {
uint16_t frequency;
audio::Rate sample_rate; // tune samplerate to allow for freqs
int8_t octave_shift;
// PP hardware can't handle some extremely low/high frequencies, this indicates how much the freq that were played is shifted up/down,
// for example, if shift is -2 and note is A3, it means the real string should play A5 but PP's speaker plays A3
};
std::map<std::string, NoteInfo> notes; // this is for fast looking : O(log(n)) , usage `auto note = guitar.notes["A4"];`
};
class TunerView : public View {
public:
TunerView(NavigationView& nav);
~TunerView();
TunerView(const TunerView& other) = delete;
TunerView& operator=(const TunerView& other) = delete;
void focus() override;
void update_audio_beep();
std::string title() const override { return "Tuner"; };
private:
NavigationView& nav_;
bool beep{false};
void update_notes_for_instrument(const Instrument& instrument);
void play_change_note();
void stop_play();
void update_current_note();
uint32_t protected_sample_rate(audio::Rate r);
void paint(Painter& painter) override;
uint16_t current_note_frequency{440};
audio::Rate current_note_sample_rate{audio::Rate::Hz_12000};
int8_t current_note_octave_shift{0};
bool current_note_playing{false};
Labels labels{
{{0 * 8, 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
Text text_note_frequency{
{(sizeof("Note Frequency:") + 1) * 8, 3 * 16, screen_width - (sizeof("Note Frequency:") + 1) * 8, 16},
"",
};
Text text_note_octave_shift{
{(sizeof("Note Octave Shift:") + 1) * 8, 4 * 16, screen_width - (sizeof("Note Octave Shift:") + 1) * 8, 16},
"",
};
AudioVolumeField field_volume{
{(sizeof("Volume:") + 1) * 8, 5 * 16}};
// TODO: load list runtime
OptionsField options_instrument{
{(sizeof("Instrument:") + 1) * 8, 1 * 16},
screen_width - (sizeof("Instrument:") + 1) * 8,
{{"Guitar", 0},
{"Violin", 1},
{"Pitch Fork", 2}}};
OptionsField options_note{
{(sizeof("Note:") + 1) * 8, 2 * 16},
screen_width - (sizeof("Note:") + 1) * 8,
{}};
NewButton button_play_stop{
{0 * 8, 16 * 16, screen_width, screen_height - 16 * 16},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_red->foreground};
// TODO: load from file DB
const Instrument guitar = {
.name = "Folk Guitar",
.notes = {
{"E2", {165, audio::Rate::Hz_12000, -1}}, // actual: E2, PP speaker: E3
{"A2", {110, audio::Rate::Hz_12000, 0}},
{"D3", {147, audio::Rate::Hz_12000, 0}},
{"G3", {196, audio::Rate::Hz_24000, 0}},
{"B3", {247, audio::Rate::Hz_24000, 0}},
{"E4", {330, audio::Rate::Hz_24000, 0}}}};
const Instrument violin = {
.name = "Violin 440 Standard, 12ET",
.notes = {
{"G3", {196, audio::Rate::Hz_12000, 0}},
{"D4", {294, audio::Rate::Hz_24000, 0}},
{"A4", {440, audio::Rate::Hz_48000, 0}},
{"E5", {659, audio::Rate::Hz_48000, 0}}}};
const Instrument pitch_fork = {// freq copied from flipper app
.name = "Pitch Fork",
.notes = {
{"12ET A4", {440, audio::Rate::Hz_48000, 0}},
{"Sarti's A4", {436, audio::Rate::Hz_48000, 0}},
{"1858 A4", {435, audio::Rate::Hz_48000, 0}},
{"Verdi's A4", {432, audio::Rate::Hz_48000, 0}}}};
std::map<std::string, Instrument> instruments{
{"Guitar", guitar},
{"Violin", violin},
{"Pitch Fork", pitch_fork}};
};
} // namespace ui::external_app::tuner
#endif /*__UI_TUNER_H__*/
+78 -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("PAGE UP");
}
} 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("PAGE DOWN");
}
} 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,19 @@ NewButton* BtnGridView::item_view(size_t index) const {
return menu_item_views[index].get();
}
void BtnGridView::show_arrows_enabled(bool enabled) {
show_arrows = enabled;
}
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 +264,8 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
if (visible())
item_view(highlighted_item - offset)->focus();
show_hide_arrows();
return true;
}
@@ -235,21 +279,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};
+1 -1
View File
@@ -344,7 +344,7 @@ class GeoMapView : public View {
{0, GeoMap::banner_height, GeoMap::geomap_rect_width, GeoMap::geomap_rect_height}};
Button button_ok{
{20 * 8, 8, 8 * 8, 2 * 16},
{screen_width - 15 * 8, 0, 15 * 8, 1 * 16},
"OK"};
};
+29 -4
View File
@@ -100,8 +100,19 @@ void RSSI::paint(Painter& painter) {
const Rect r5{r.left() + peak - 3, r.top(), 3, r.height()};
painter.fill_rectangle(
r5,
Theme::getInstance()->fg_green->foreground);
Theme::getInstance()->fg_orange->foreground);
}
// dB - x
constexpr int db_min = -80;
constexpr int db_max = 10;
constexpr int db_delta = db_max - db_min;
const range_t<int> x_db_range{0, r.width() - 1};
const int16_t x_db = x_db_range.clip((db_ - db_min) * r.width() / db_delta);
const Rect r_db{r.left() + x_db, r.top(), 1, r.height()};
if (db_) painter.fill_rectangle(r_db, Color::green());
} else {
// vertical bottom to top level meters
const range_t<int> y_avg_range{0, r.height() - 1};
@@ -115,7 +126,7 @@ void RSSI::paint(Painter& painter) {
// y_min
const Rect r0{r.left(), r.bottom() - y_min, r.width(), y_min};
painter.fill_rectangle(
painter.fill_rectangle( // TODO: the blue plot is broken in vertical bars, not from the dB PR (#2403)
r0,
Color::blue());
@@ -149,8 +160,18 @@ void RSSI::paint(Painter& painter) {
const Rect r5{r.left(), r.bottom() - peak - 3, r.width(), 3};
painter.fill_rectangle(
r5,
Color::green());
Color::orange());
}
// dB - y
constexpr int db_min = -80;
constexpr int db_max = 10;
constexpr int db_delta = db_max - db_min;
const range_t<int> y_db_range{0, r.height() - 1};
const int16_t y_db = y_db_range.clip((db_ - db_min) * r.height() / db_delta);
const Rect r_db{r.left(), r.bottom() - y_db, r.width(), 3};
if (db_) painter.fill_rectangle(r_db, Color::green());
}
if (pitch_rssi_enabled) {
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
@@ -159,7 +180,7 @@ void RSSI::paint(Painter& painter) {
const Rect r6{r.left(), r.top(), r.width(), r.height()};
painter.draw_rectangle(
r6,
Color::white());
Color::white()); // TODO this and all the following Color struct call should satisfy the new "theme" system ref
}
}
@@ -500,4 +521,8 @@ bool RSSI::on_touch(const TouchEvent event) {
return false;
}
}
void RSSI::set_db(int16_t db) {
db_ = db;
}
} /* namespace ui */
+2
View File
@@ -61,6 +61,7 @@ class RSSI : public Widget {
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
void set_db(int16_t db);
private:
int8_t min_ = 0;
@@ -68,6 +69,7 @@ class RSSI : public Widget {
int8_t max_ = 0;
int8_t peak_ = 0;
size_t peak_duration_ = 0;
int16_t db_ = 0;
bool instant_exec_{false};
bool pitch_rssi_enabled = false;
+32 -13
View File
@@ -174,7 +174,7 @@ const NavigationView::AppList NavigationView::appList = {
{"wavview", "Wav View", UTILITIES, Color::yellow(), &bitmap_icon_soundboard, new ViewFactory<ViewWavView>()},
// Dangerous apps.
{nullptr, "Flash Utility", UTILITIES, Color::red(), &bitmap_icon_temperature, new ViewFactory<FlashUtilityView>()},
{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>()},
};
@@ -310,6 +310,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();
};
@@ -330,6 +339,7 @@ SystemStatusView::SystemStatusView(
audio::output::stop();
audio::output::update_audio_mute();
refresh();
}
@@ -372,6 +382,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()) {
@@ -404,6 +415,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();
}
@@ -746,7 +760,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,
@@ -761,10 +775,10 @@ static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc)
}
// clang-format off
void add_external_items(NavigationView& nav, app_location_t location, BtnGridView& grid, uint8_t notice_pos) {
void add_external_items(NavigationView& nav, app_location_t location, BtnGridView& grid, uint8_t error_tile_pos) {
auto externalItems = ExternalItemsMenuLoader::load_external_items(location, nav);
if (externalItems.empty()) {
grid.insert_item({"Notice!",
grid.insert_item({"ExtAppErr",
Theme::getInstance()->error_dark->foreground,
nullptr,
[&nav]() {
@@ -774,7 +788,7 @@ void add_external_items(NavigationView& nav, app_location_t location, BtnGridVie
"see Mayhem wiki and copy apps\n"
"to " + apps_dir.string() + " folder of SD card.");
}},
notice_pos);
error_tile_pos);
} else {
std::sort(externalItems.begin(), externalItems.end(), [](const auto &a, const auto &b)
{
@@ -795,6 +809,12 @@ void add_external_items(NavigationView& nav, app_location_t location, BtnGridVie
}
// clang-format on
bool verify_sdcard_format() {
FATFS* fs = &sd_card::fs;
return (fs->fs_type == FS_FAT32 || fs->fs_type == FS_EXFAT) || !(sd_card::status() == sd_card::Status::Mounted);
/* ^ to satisfy those users that not use an sd*/
}
/* ReceiversMenuView *****************************************************/
ReceiversMenuView::ReceiversMenuView(NavigationView& nav)
@@ -805,9 +825,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);
}
@@ -821,9 +839,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);
}
@@ -839,9 +855,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);
}
@@ -862,12 +876,17 @@ 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() {
if (!verify_sdcard_format()) {
add_item({"SDCard Error", Theme::getInstance()->error_dark->foreground, nullptr, [this]() {
nav_.display_modal("Error", "SD Card is not exFAT/FAT32,\nformat to exFAT or FAT32 on PC");
}});
}
add_apps(nav_, *this, HOME);
add_external_items(nav_, app_location_t::HOME, *this, 2);
add_external_items(nav_, app_location_t::HOME, *this, 0);
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
}
+12 -1
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,
@@ -197,6 +201,7 @@ class SystemStatusView : public View {
static constexpr auto default_title = "";
bool batt_was_inited = false; // if the battery was off on tart, but later turned on.
bool batt_info_up = false; // to prevent show multiple batt info dialog
NavigationView& nav_;
Rectangle backdrop{
@@ -279,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}};
@@ -322,7 +333,7 @@ class InformationView : public View {
Rectangle backdrop{
{0, 0 * 16, 240, 16},
{33, 33, 33}};
Theme::getInstance()->bg_darker->background};
Text version{
{2, 0, 11 * 8, 16},
+44 -7
View File
@@ -240,21 +240,16 @@ namespace ui {
SDCardDebugView::SDCardDebugView(NavigationView& nav) {
add_children({
&text_title,
&text_csd_title,
&labels,
&text_format,
&text_csd_value_3,
&text_csd_value_2,
&text_csd_value_1,
&text_csd_value_0,
&text_bus_width_title,
&text_bus_width_value,
&text_card_type_title,
&text_card_type_value,
&text_block_size_title,
&text_block_size_value,
&text_block_count_title,
&text_block_count_value,
&text_capacity_title,
&text_capacity_value,
&text_test_write_time_title,
&text_test_write_time_value,
@@ -374,6 +369,14 @@ void SDCardDebugView::on_status(const sd_card::Status) {
text_csd_value_1.set(to_string_hex(csd[1], 8));
text_csd_value_0.set(to_string_hex(csd[0], 8));
text_format.set(fetch_sdcard_format());
if (fetch_sdcard_format().find("FAT32") != std::string::npos) {
// to satisfy the intendent style in this app, the text contains padding space, thus can't use ==
text_format.set_style(Theme::getInstance()->fg_green);
} else {
text_format.set_style(Theme::getInstance()->error_dark);
}
BlockDeviceInfo block_device_info;
if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) {
text_block_size_value.set(to_string_dec_uint(block_device_info.blk_size, 5));
@@ -447,4 +450,38 @@ void SDCardDebugView::on_test() {
}
}
std::string SDCardDebugView::fetch_sdcard_format() {
const size_t max_len = sizeof("Undefined: 255") + 1;
auto padding = [max_len](std::string s) {
if (s.length() < max_len) {
return std::string(max_len - s.length(), ' ') + s;
}
return s;
};
FATFS* fs = &sd_card::fs;
std::string resault;
switch (fs->fs_type) {
case FS_FAT12:
resault = "FAT12";
break;
case FS_FAT16:
resault = "FAT16";
break;
case FS_FAT32:
resault = "FAT32";
break;
case FS_EXFAT:
resault = "ExFAT";
break;
default:
resault = "Undefined: " + to_string_dec_uint(fs->fs_type, 1);
break;
}
return padding(resault);
}
} /* namespace ui */
+14 -31
View File
@@ -45,15 +45,23 @@ class SDCardDebugView : public View {
void on_status(const sd_card::Status status);
void on_test();
std::string fetch_sdcard_format();
Labels labels{
{{0 * 8, 1 * 16}, "Format", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "CSD", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Card type", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Block size", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 9 * 16}, "Block count", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 10 * 16}, "Capacity", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Bus width", Theme::getInstance()->fg_light->foreground},
Text text_title{
{(240 - (7 * 8)) / 2, 1 * 16, (7 * 8), 16},
"SD Card",
};
Text text_csd_title{
{0, 3 * 16, (8 * 8), 16},
"CSD",
Text text_format{
{240 - ((sizeof("Undefined: 255") + 1) * 8), 1 * 16, (sizeof("Undefined: 255") + 1) * 8, 16},
"Unknown",
};
Text text_csd_value_3{
@@ -78,11 +86,6 @@ class SDCardDebugView : public View {
static constexpr size_t bus_width_characters = 1;
Text text_bus_width_title{
{0, 5 * 16, (9 * 8), 16},
"Bus width",
};
Text text_bus_width_value{
{240 - (bus_width_characters * 8), 5 * 16, (bus_width_characters * 8), 16},
"",
@@ -90,11 +93,6 @@ class SDCardDebugView : public View {
static constexpr size_t card_type_characters = 13;
Text text_card_type_title{
{0, 6 * 16, (9 * 8), 16},
"Card type",
};
Text text_card_type_value{
{240 - (card_type_characters * 8), 6 * 16, (card_type_characters * 8), 16},
"",
@@ -102,11 +100,6 @@ class SDCardDebugView : public View {
static constexpr size_t block_size_characters = 5;
Text text_block_size_title{
{0, 8 * 16, (10 * 8), 16},
"Block size",
};
Text text_block_size_value{
{240 - (block_size_characters * 8), 8 * 16, (block_size_characters * 8), 16},
"",
@@ -114,11 +107,6 @@ class SDCardDebugView : public View {
static constexpr size_t block_count_characters = 9;
Text text_block_count_title{
{0, 9 * 16, (11 * 8), 16},
"Block count",
};
Text text_block_count_value{
{240 - (block_count_characters * 8), 9 * 16, (block_count_characters * 8), 16},
"",
@@ -126,11 +114,6 @@ class SDCardDebugView : public View {
static constexpr size_t capacity_characters = 10;
Text text_capacity_title{
{0, 10 * 16, (8 * 8), 16},
"Capacity",
};
Text text_capacity_value{
{240 - (capacity_characters * 8), 10 * 16, (capacity_characters * 8), 16},
"",
+186
View File
@@ -907,6 +907,191 @@ static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
}
}
static uint32_t reverse(uint32_t x) {
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8);
x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16);
return x;
}
static void cmd_cpld_write(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: cpld_write <device> <target> <file>\r\n"
" device can be: hackrf, portapack\r\n"
" target can be: sram (hackrf only), eeprom\r\n"
" currently only \"cpld_write portapack eeprom <file>\" is supported\r\n";
if (argc != 3) {
chprintf(chp, usage);
return;
}
if (strncmp(argv[0], "hackrf", 5) == 0) {
chprintf(chp, usage);
} else if (strncmp(argv[0], "portapack", 5) == 0) {
if (strncmp(argv[1], "eeprom", 5) == 0) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo};
jtag::JTAG jtag{target};
portapack::cpld::CPLD cpld{jtag};
cpld.reset();
cpld.run_test_idle();
uint32_t idcode = cpld.get_idcode();
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
if (idcode == 0x00025610) {
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
if (cpld.AGM_enter_maintenance_mode() == false) {
return;
}
File* file = new File();
auto path = path_from_string8(argv[2]);
auto error = file->open(path);
if (error.is_valid()) {
chprintf(chp, "Error opening file: %s %d %s\r\n", argv[2], error.value().code(), error.value().what().c_str());
delete file;
cpld.AGM_exit_maintenance_mode();
return;
}
auto data = std::make_unique<std::array<uint32_t, 1801>>();
uint32_t magic = 0;
bool magic_found = false;
uint32_t expected_address = 0;
auto readData = std::vector<uint8_t>();
chprintf(chp, "Reading file...\r\n");
file->seek(0);
while (!file->eof().value()) {
uint32_t remainingData = readData.size();
uint32_t bytesToRead = 512 - remainingData;
readData.resize(512);
auto result = file->read(readData.data() + remainingData, bytesToRead);
if (result.is_error()) {
chprintf(chp, "Error reading file: %d %s\r\n", result.error().code(), result.error().what().c_str());
cpld.AGM_exit_maintenance_mode();
file->close();
delete file;
return;
}
if (result.value() != 512)
readData.resize(remainingData + result.value());
do {
auto it = std::find(readData.begin(), readData.end(), '\n');
if (it != readData.end()) {
std::string line(readData.begin(), it);
readData.erase(readData.begin(), it + 1);
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
auto prefix = line.find("sdr 64 -tdi ", 0);
auto suffix = line.find("0040", line.size() - 4);
if (prefix == 0 && suffix == line.size() - 4) {
std::string dataString = line.substr(line.size() - 16, 16);
uint32_t address = reverse(std::stoul(dataString.substr(8, 8), nullptr, 16) - 64) / 4;
uint32_t value = std::stoul(dataString.substr(0, 8), nullptr, 16);
if (expected_address == 299 && address == 0) {
magic = value;
magic_found = true;
chprintf(chp, "Magic found: %08X\r\n", magic);
continue;
}
if (expected_address != address) {
chprintf(chp, "Error: expected address %d, got %d\r\n", expected_address, address);
cpld.AGM_exit_maintenance_mode();
file->close();
delete file;
return;
}
(*data)[expected_address] = value;
expected_address++;
if (expected_address == 1801) {
if (!magic_found) {
chprintf(chp, "Error: magic not found\r\n");
cpld.AGM_exit_maintenance_mode();
file->close();
delete file;
return;
}
chprintf(chp, "Writing data to CPLD...\r\n");
file->close();
delete file;
cpld.AGM_write(*data, magic);
cpld.AGM_enter_read_mode();
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
for (size_t i = 0; i < 2048; i++) {
uint32_t data = cpld.AGM_read(i);
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
cpld.AGM_exit_maintenance_mode();
chprintf(chp, "New CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
m4_request_shutdown();
chThdSleepMilliseconds(1000);
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
WWDT_TC = 100000 & 0xFFFFFF;
WWDT_FEED_SEQUENCE;
return;
break;
}
}
} else {
break;
}
} while (true);
}
file->close();
delete file;
cpld.AGM_exit_maintenance_mode();
} else {
chprintf(chp, "CPLD Model: unknown\r\n");
}
} else {
chprintf(chp, usage);
}
} else {
chprintf(chp, usage);
}
}
static void cmd_gotgps(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: gotgps <lat> <lon> [altitude] [speed] [satinuse]\r\n";
if (argc < 2 || argc > 5) {
@@ -1203,6 +1388,7 @@ static const ShellCommand commands[] = {
{"rtcset", cmd_rtcset},
{"cpld_info", cpld_info},
{"cpld_read", cmd_cpld_read},
{"cpld_write", cmd_cpld_write},
{"accessibility_readall", cmd_accessibility_readall},
{"accessibility_readcurr", cmd_accessibility_readcurr},
{"applist", cmd_applist},
+4 -1
View File
@@ -271,6 +271,7 @@ macro(DeclareTargets chunk_tag name)
project("baseband_${name}")
include(${RULESPATH}/rules.cmake)
set_source_files_properties(${MODE_CPPSRC} PROPERTIES COMPILE_FLAGS "${MODE_FLAGS}")
add_executable(${PROJECT_NAME}.elf $<TARGET_OBJECTS:baseband_shared> ${MODE_CPPSRC} ${HALSRC} ${PLATFORMSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
add_definitions(${DEFS})
@@ -313,6 +314,7 @@ macro(DeclareTargets chunk_tag name)
endmacro()
set(add_to_firmware TRUE)
set(MODE_FLAGS "-O3")
### ADS-B RX
@@ -498,6 +500,7 @@ set(MODE_CPPSRC
)
DeclareTargets(PWTH weather)
set(MODE_FLAGS "-Os")
### Flash Utility
@@ -559,7 +562,7 @@ DeclareTargets(PUSB sd_over_usb)
### Place external app and disabled images below so they don't get added to the firmware
set(add_to_firmware FALSE)
set(MODE_FLAGS "-O3")
### ACARS RX
+35
View File
@@ -332,5 +332,40 @@ uint32_t CPLD::AGM_read(uint32_t address) {
return jtag.shift_dr(32, encoded_address, 0x0);
}
void CPLD::AGM_write(const std::array<uint32_t, 1801>& block, uint32_t magic_value) {
shift_ir(instruction_t::AGM_SET_REGISTER);
jtag.runtest_tck(100);
jtag.shift_dr(8, 0xf0);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_ERASE);
jtag.runtest_tck(100);
jtag.runtest_ms(500);
shift_ir(instruction_t::AGM_SET_REGISTER);
jtag.runtest_tck(100);
jtag.shift_dr(8, 0xf0);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_PROGRAM);
jtag.runtest_tck(100);
auto data = block.data();
for (size_t i = 0; i < 0x12B; i++) {
auto address = AGM_encode_address(i * 4, 0x40);
jtag.shift_dr(32, address, data[i]);
jtag.runtest_ms(2);
}
jtag.shift_dr(32, 0x00000040, magic_value);
jtag.runtest_ms(2);
for (size_t i = 0x12B; i < block.size(); i++) {
auto address = AGM_encode_address(i * 4, 0x40);
jtag.shift_dr(32, address, data[i]);
jtag.runtest_ms(2);
}
}
} /* namespace max5 */
} /* namespace cpld */
+1
View File
@@ -99,6 +99,7 @@ class CPLD {
void AGM_enter_read_mode();
uint32_t AGM_encode_address(uint32_t address, uint32_t trailer);
uint32_t AGM_read(uint32_t address);
void AGM_write(const std::array<uint32_t, 1801>& block, uint32_t magic_value);
private:
using idcode_t = uint32_t;
+2 -2
View File
@@ -90,7 +90,7 @@
/ 950 - Traditional Chinese (DBCS)
*/
#define _USE_LFN 2
#define _USE_LFN 3
#define _MAX_LFN 255
/* The _USE_LFN switches the support of long file name (LFN).
/
@@ -189,7 +189,7 @@
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the file system object (FATFS) is used for the file data transfer. */
#define _FS_EXFAT 0
#define _FS_EXFAT 1
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
+11 -1
View File
@@ -23,6 +23,7 @@
#define __JTAG_H__
#include "jtag_target.hpp"
#include "ch.h"
#include <cstdint>
#include <cstddef>
@@ -51,7 +52,16 @@ class JTAG {
}
void runtest_tck(const size_t count) {
target.delay(count);
for (size_t i = 0; i < count; i++) {
target.clock(0, 0);
}
}
void runtest_ms(const size_t count) {
auto starttime = chTimeNow();
while ((chTimeNow() - starttime) < (count + 1))
target.clock(0, 0);
}
uint32_t shift_ir(const size_t count, const uint32_t value) {
+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(
+5 -1
View File
@@ -101,7 +101,11 @@ class MessageQueue {
}
bool push(const void* const buf, const size_t len) {
chMtxLock(&mutex_write);
bool lock_success = chMtxTryLock(&mutex_write);
if (!lock_success) {
return false;
}
const auto result = fifo.in_r(buf, len);
chMtxUnlock();
+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;
@@ -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;
@@ -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),
@@ -465,6 +469,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 +650,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 +757,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 +947,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 +992,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;
}
@@ -1094,6 +1112,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;
@@ -1211,6 +1249,7 @@ bool debug_dump() {
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 +1266,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 +1281,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. */
@@ -267,6 +273,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 +334,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 +348,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);
+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 {
Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

+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() {
Binary file not shown.
Binary file not shown.
+1 -1
Submodule hackrf updated: d4ed8474e5...65bf67f9b5
-93
View File
@@ -1,93 +0,0 @@
# Copyright 2012 Jared Boone <jared@sharebrained.com>
#
# This file is part of HackRF.
#
# 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.
#
cmake_minimum_required(VERSION 3.5)
set(CMAKE_TOOLCHAIN_FILE ../toolchain-arm-cortex-m.cmake)
project(hackrf_usb C)
include(../hackrf/firmware/hackrf-common.cmake)
set(PATH_HACKRF_USB ../hackrf/firmware/hackrf_usb)
add_custom_command(
OUTPUT ${PATH_HACKRF_CPLD_DATA_C}
COMMAND ${PATH_CPLD_BITSTREAM_TOOL} --xsvf ${PATH_HACKRF_CPLD_XSVF} --hackrf-data ${PATH_HACKRF_CPLD_DATA_C}
DEPENDS ${PATH_CPLD_BITSTREAM_TOOL} ${PATH_HACKRF_CPLD_XSVF}
)
set(SRC_M4
${PATH_HACKRF_USB}/hackrf_usb.c
"${PATH_HACKRF_FIRMWARE_COMMON}/tuning.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/streaming.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/usb.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/usb_request.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/usb_standard_request.c"
${PATH_HACKRF_USB}/usb_descriptor.c
${PATH_HACKRF_USB}/usb_device.c
${PATH_HACKRF_USB}/usb_endpoint.c
${PATH_HACKRF_USB}/usb_api_cpld.c
${PATH_HACKRF_USB}/usb_api_m0_state.c
${PATH_HACKRF_USB}/usb_api_register.c
${PATH_HACKRF_USB}/usb_api_spiflash.c
${PATH_HACKRF_USB}/usb_api_transceiver.c
${PATH_HACKRF_USB}/usb_api_operacake.c
${PATH_HACKRF_USB}/usb_api_sweep.c
${PATH_HACKRF_USB}/usb_api_ui.c
"${PATH_HACKRF_FIRMWARE_COMMON}/usb_queue.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/fault_handler.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/cpld_xc2c.c"
"${PATH_HACKRF_CPLD_DATA_C}"
"${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/lenval.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/micro.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/ports.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/crc.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rom_iap.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/operacake.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/operacake_sctimer.c"
)
set(SRC_M0 ${PATH_HACKRF_USB}/sgpio_m0.s)
if(BOARD STREQUAL "HACKRF_ONE")
SET(SRC_M4
${SRC_M4}
usb_api_board_info.c
portapack.c
"${PATH_HACKRF_FIRMWARE_COMMON}/ui_portapack.c"
)
endif()
if(BOARD STREQUAL "RAD1O")
SET(SRC_M4
${SRC_M4}
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/display.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/print.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/render.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/decoder.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/smallfonts.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/draw.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/rad1o/ubuntu18.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/ui_rad1o.c"
)
endif()
DeclareTargets()
-640
View File
@@ -1,640 +0,0 @@
/*
* Copyright 2018-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2018 Jared Boone
*
* This file is part of HackRF.
*
* 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 "portapack.h"
#include "hackrf_core.h"
#include "gpio_lpc.h"
#include <libopencm3/lpc43xx/scu.h>
static void portapack_sleep_milliseconds(const uint32_t milliseconds)
{
/* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */
delay(milliseconds * 40800);
}
// clang-format off
static struct gpio_t gpio_io_stbx = GPIO(5, 0); /* P2_0 */
static struct gpio_t gpio_addr = GPIO(5, 1); /* P2_1 */
__attribute__((unused))
static struct gpio_t gpio_lcd_te = GPIO(5, 3); /* P2_3 */
__attribute__((unused))
static struct gpio_t gpio_unused = GPIO(5, 7); /* P2_8 */
static struct gpio_t gpio_lcd_rdx = GPIO(5, 4); /* P2_4 */
static struct gpio_t gpio_lcd_wrx = GPIO(1, 10); /* P2_9 */
static struct gpio_t gpio_dir = GPIO(1, 13); /* P2_13 */
// clang-format on
typedef struct portapack_if_t {
gpio_t gpio_dir;
gpio_t gpio_lcd_rdx;
gpio_t gpio_lcd_wrx;
gpio_t gpio_io_stbx;
gpio_t gpio_addr;
gpio_port_t* const gpio_port_data;
uint8_t io_reg;
} portapack_if_t;
static portapack_if_t portapack_if = {
.gpio_dir = &gpio_dir,
.gpio_lcd_rdx = &gpio_lcd_rdx,
.gpio_lcd_wrx = &gpio_lcd_wrx,
.gpio_io_stbx = &gpio_io_stbx,
.gpio_addr = &gpio_addr,
.gpio_port_data = GPIO_LPC_PORT(3),
.io_reg = 0x03,
};
/* NOTE: Code below assumes the shift value is "8". */
#define GPIO_DATA_SHIFT (8)
static const uint32_t gpio_data_mask = 0xFFU << GPIO_DATA_SHIFT;
static void portapack_data_mask_set()
{
portapack_if.gpio_port_data->mask = ~gpio_data_mask;
}
static void portapack_data_write_low(const uint32_t value)
{
portapack_if.gpio_port_data->mpin = (value << GPIO_DATA_SHIFT);
}
static void portapack_data_write_high(const uint32_t value)
{
/* NOTE: Assumes no other bits in the port are masked. */
/* NOTE: Assumes that bits 15 through 8 are masked. */
portapack_if.gpio_port_data->mpin = value;
}
static void portapack_dir_read()
{
portapack_if.gpio_port_data->dir &= ~gpio_data_mask;
gpio_set(portapack_if.gpio_dir);
}
static void portapack_dir_write()
{
gpio_clear(portapack_if.gpio_dir);
portapack_if.gpio_port_data->dir |= gpio_data_mask;
/* TODO: Manipulating DIR[3] makes me queasy. The RFFC5072 DATA pin
* is also on port 3, and switches direction periodically...
* Time to resort to bit-banding to enforce atomicity? But then, how
* to change direction on eight bits efficiently? Or do I care, since
* the PortaPack data bus shouldn't change direction too frequently?
*/
}
__attribute__((unused)) static void portapack_lcd_rd_assert()
{
gpio_clear(portapack_if.gpio_lcd_rdx);
}
static void portapack_lcd_rd_deassert()
{
gpio_set(portapack_if.gpio_lcd_rdx);
}
static void portapack_lcd_wr_assert()
{
gpio_clear(portapack_if.gpio_lcd_wrx);
}
static void portapack_lcd_wr_deassert()
{
gpio_set(portapack_if.gpio_lcd_wrx);
}
static void portapack_io_stb_assert()
{
gpio_clear(portapack_if.gpio_io_stbx);
}
static void portapack_io_stb_deassert()
{
gpio_set(portapack_if.gpio_io_stbx);
}
static void portapack_addr(const bool value)
{
gpio_write(portapack_if.gpio_addr, value);
}
static void portapack_lcd_command(const uint32_t value)
{
portapack_data_write_high(0); /* Drive high byte (with zero -- don't care) */
portapack_dir_write(); /* Turn around data bus, MCU->CPLD */
portapack_addr(0); /* Indicate command */
__asm__("nop");
__asm__("nop");
__asm__("nop");
portapack_lcd_wr_assert(); /* Latch high byte */
portapack_data_write_low(value); /* Drive low byte (pass-through) */
__asm__("nop");
__asm__("nop");
__asm__("nop");
portapack_lcd_wr_deassert(); /* Complete write operation */
portapack_addr(1); /* Set up for data phase (most likely after a command) */
}
static void portapack_lcd_write_data(const uint32_t value)
{
// NOTE: Assumes and DIR=0 and ADDR=1 from command phase.
portapack_data_write_high(value); /* Drive high byte */
__asm__("nop");
portapack_lcd_wr_assert(); /* Latch high byte */
portapack_data_write_low(value); /* Drive low byte (pass-through) */
__asm__("nop");
__asm__("nop");
__asm__("nop");
portapack_lcd_wr_deassert(); /* Complete write operation */
}
static void portapack_io_write(const bool address, const uint_fast16_t value)
{
portapack_data_write_low(value);
portapack_dir_write();
portapack_addr(address);
__asm__("nop");
__asm__("nop");
__asm__("nop");
portapack_io_stb_assert();
__asm__("nop");
__asm__("nop");
__asm__("nop");
portapack_io_stb_deassert();
}
static void portapack_if_init()
{
portapack_data_mask_set();
portapack_data_write_high(0);
portapack_dir_read();
portapack_lcd_rd_deassert();
portapack_lcd_wr_deassert();
portapack_io_stb_deassert();
portapack_addr(0);
gpio_output(portapack_if.gpio_dir);
gpio_output(portapack_if.gpio_lcd_rdx);
gpio_output(portapack_if.gpio_lcd_wrx);
gpio_output(portapack_if.gpio_io_stbx);
gpio_output(portapack_if.gpio_addr);
/* gpio_input(portapack_if.gpio_rot_a); */
/* gpio_input(portapack_if.gpio_rot_b); */
scu_pinmux(SCU_PINMUX_PP_D0, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D1, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D2, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D3, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D4, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D5, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D6, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_D7, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
scu_pinmux(SCU_PINMUX_PP_DIR, SCU_CONF_FUNCTION0 | SCU_GPIO_NOPULL);
scu_pinmux(SCU_PINMUX_PP_LCD_RDX, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
scu_pinmux(SCU_PINMUX_PP_LCD_WRX, SCU_CONF_FUNCTION0 | SCU_GPIO_NOPULL);
scu_pinmux(SCU_PINMUX_PP_IO_STBX, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
scu_pinmux(SCU_PINMUX_PP_ADDR, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
/* scu_pinmux(SCU_PINMUX_PP_LCD_TE, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL); */
/* scu_pinmux(SCU_PINMUX_PP_UNUSED, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL); */
}
static void portapack_lcd_reset_state(const bool active)
{
portapack_if.io_reg = (portapack_if.io_reg & 0xfe) | (active ? (1 << 0) : 0);
portapack_io_write(1, portapack_if.io_reg);
}
static void portapack_lcd_data_write_command_and_data(
const uint_fast8_t command,
const uint8_t* data,
const size_t data_count)
{
portapack_lcd_command(command);
for (size_t i = 0; i < data_count; i++) {
portapack_lcd_write_data(data[i]);
}
}
static void portapack_lcd_sleep_out()
{
const uint8_t cmd_11[] = {};
portapack_lcd_data_write_command_and_data(0x11, cmd_11, ARRAY_SIZEOF(cmd_11));
// "It will be necessary to wait 120msec after sending Sleep Out
// command (when in Sleep In Mode) before Sleep In command can be
// sent."
portapack_sleep_milliseconds(120);
}
static void portapack_lcd_display_on()
{
const uint8_t cmd_29[] = {};
portapack_lcd_data_write_command_and_data(0x29, cmd_29, ARRAY_SIZEOF(cmd_29));
}
static void portapack_lcd_ramwr_start()
{
const uint8_t cmd_2c[] = {};
portapack_lcd_data_write_command_and_data(0x2c, cmd_2c, ARRAY_SIZEOF(cmd_2c));
}
static void portapack_lcd_set(
const uint_fast8_t command,
const uint_fast16_t start,
const uint_fast16_t end)
{
const uint8_t data[] = {(start >> 8), (start & 0xff), (end >> 8), (end & 0xff)};
portapack_lcd_data_write_command_and_data(command, data, ARRAY_SIZEOF(data));
}
static void portapack_lcd_caset(
const uint_fast16_t start_column,
const uint_fast16_t end_column)
{
portapack_lcd_set(0x2a, start_column, end_column);
}
static void portapack_lcd_paset(
const uint_fast16_t start_page,
const uint_fast16_t end_page)
{
portapack_lcd_set(0x2b, start_page, end_page);
}
static void portapack_lcd_start_ram_write(const ui_rect_t rect)
{
portapack_lcd_caset(rect.point.x, rect.point.x + rect.size.width - 1);
portapack_lcd_paset(rect.point.y, rect.point.y + rect.size.height - 1);
portapack_lcd_ramwr_start();
}
static void portapack_lcd_write_pixel(const ui_color_t pixel)
{
portapack_lcd_write_data(pixel.v);
}
static void portapack_lcd_write_pixels_color(const ui_color_t c, size_t n)
{
while (n--) {
portapack_lcd_write_data(c.v);
}
}
static void portapack_lcd_wake()
{
portapack_lcd_sleep_out();
portapack_lcd_display_on();
}
static void portapack_lcd_reset()
{
portapack_lcd_reset_state(false);
portapack_sleep_milliseconds(1);
portapack_lcd_reset_state(true);
portapack_sleep_milliseconds(10);
portapack_lcd_reset_state(false);
portapack_sleep_milliseconds(120);
}
static void portapack_lcd_init()
{
// LCDs are configured for IM[2:0] = 001
// 8080-I system, 16-bit parallel bus
//
// 0x3a: DBI[2:0] = 101
// MDT[1:0] = XX (if not in 18-bit mode, right?)
// Power control B
// 0
// PCEQ=1, DRV_ena=0, Power control=3
const uint8_t cmd_cf[] = {0x00, 0xD9, 0x30};
portapack_lcd_data_write_command_and_data(0xCF, cmd_cf, ARRAY_SIZEOF(cmd_cf));
// Power on sequence control
const uint8_t cmd_ed[] = {0x64, 0x03, 0x12, 0x81};
portapack_lcd_data_write_command_and_data(0xED, cmd_ed, ARRAY_SIZEOF(cmd_ed));
// Driver timing control A
const uint8_t cmd_e8[] = {0x85, 0x10, 0x78};
portapack_lcd_data_write_command_and_data(0xE8, cmd_e8, ARRAY_SIZEOF(cmd_e8));
// Power control A
const uint8_t cmd_cb[] = {0x39, 0x2C, 0x00, 0x34, 0x02};
portapack_lcd_data_write_command_and_data(0xCB, cmd_cb, ARRAY_SIZEOF(cmd_cb));
// Pump ratio control
const uint8_t cmd_f7[] = {0x20};
portapack_lcd_data_write_command_and_data(0xF7, cmd_f7, ARRAY_SIZEOF(cmd_f7));
// Driver timing control B
const uint8_t cmd_ea[] = {0x00, 0x00};
portapack_lcd_data_write_command_and_data(0xEA, cmd_ea, ARRAY_SIZEOF(cmd_ea));
const uint8_t cmd_b1[] = {0x00, 0x1B};
portapack_lcd_data_write_command_and_data(0xB1, cmd_b1, ARRAY_SIZEOF(cmd_b1));
// Blanking Porch Control
// VFP = 0b0000010 = 2 (number of HSYNC of vertical front porch)
// VBP = 0b0000010 = 2 (number of HSYNC of vertical back porch)
// HFP = 0b0001010 = 10 (number of DOTCLOCK of horizontal front porch)
// HBP = 0b0010100 = 20 (number of DOTCLOCK of horizontal back porch)
const uint8_t cmd_b5[] = {0x02, 0x02, 0x0a, 0x14};
portapack_lcd_data_write_command_and_data(0xB5, cmd_b5, ARRAY_SIZEOF(cmd_b5));
// Display Function Control
// PT[1:0] = 0b10
// PTG[1:0] = 0b10
// ISC[3:0] = 0b0010 (scan cycle interval of gate driver: 5 frames)
// SM = 0 (gate driver pin arrangement in combination with GS)
// SS = 1 (source output scan direction S720 -> S1)
// GS = 0 (gate output scan direction G1 -> G320)
// REV = 1 (normally white)
// NL = 0b100111 (default)
// PCDIV = 0b000000 (default?)
const uint8_t cmd_b6[] = {0x0A, 0xA2, 0x27, 0x00};
portapack_lcd_data_write_command_and_data(0xB6, cmd_b6, ARRAY_SIZEOF(cmd_b6));
// Power Control 1
//VRH[5:0]
const uint8_t cmd_c0[] = {0x1B};
portapack_lcd_data_write_command_and_data(0xC0, cmd_c0, ARRAY_SIZEOF(cmd_c0));
// Power Control 2
//SAP[2:0];BT[3:0]
const uint8_t cmd_c1[] = {0x12};
portapack_lcd_data_write_command_and_data(0xC1, cmd_c1, ARRAY_SIZEOF(cmd_c1));
// VCOM Control 1
const uint8_t cmd_c5[] = {0x32, 0x3C};
portapack_lcd_data_write_command_and_data(0xC5, cmd_c5, ARRAY_SIZEOF(cmd_c5));
// VCOM Control 2
const uint8_t cmd_c7[] = {0x9B};
portapack_lcd_data_write_command_and_data(0xC7, cmd_c7, ARRAY_SIZEOF(cmd_c7));
// Memory Access Control
// Invert X and Y memory access order, so upper-left of
// screen is (0,0) when writing to display.
const uint8_t cmd_36[] = {
(1 << 7) | // MY=1
(1 << 6) | // MX=1
(0 << 5) | // MV=0
(1 << 4) | // ML=1: reverse vertical refresh to simplify scrolling logic
(1 << 3) // BGR=1: For Kingtech LCD, BGR filter.
};
portapack_lcd_data_write_command_and_data(0x36, cmd_36, ARRAY_SIZEOF(cmd_36));
// COLMOD: Pixel Format Set
// DPI=101 (16 bits/pixel), DBI=101 (16 bits/pixel)
const uint8_t cmd_3a[] = {0x55};
portapack_lcd_data_write_command_and_data(0x3A, cmd_3a, ARRAY_SIZEOF(cmd_3a));
//portapack_lcd_data_write_command_and_data(0xF6, { 0x01, 0x30 });
// WEMODE=1 (reset column and page number on overflow)
// MDT[1:0]
// EPF[1:0]=00 (use channel MSB for LSB)
// RIM=0 (If COLMOD[6:4]=101 (65k color), 16-bit RGB interface (1 transfer/pixel))
// RM=0 (system interface/VSYNC interface)
// DM[1:0]=00 (internal clock operation)
// ENDIAN=0 (doesn't matter with 16-bit interface)
const uint8_t cmd_f6[] = {0x01, 0x30, 0x00};
portapack_lcd_data_write_command_and_data(0xF6, cmd_f6, ARRAY_SIZEOF(cmd_f6));
// 3Gamma Function Disable
const uint8_t cmd_f2[] = {0x00};
portapack_lcd_data_write_command_and_data(0xF2, cmd_f2, ARRAY_SIZEOF(cmd_f2));
// Gamma curve selected
const uint8_t cmd_26[] = {0x01};
portapack_lcd_data_write_command_and_data(0x26, cmd_26, ARRAY_SIZEOF(cmd_26));
// Set Gamma
const uint8_t cmd_e0[] = {
0x0F,
0x1D,
0x19,
0x0E,
0x10,
0x07,
0x4C,
0x63,
0x3F,
0x03,
0x0D,
0x00,
0x26,
0x24,
0x04};
portapack_lcd_data_write_command_and_data(0xE0, cmd_e0, ARRAY_SIZEOF(cmd_e0));
// Set Gamma
const uint8_t cmd_e1[] = {
0x00,
0x1C,
0x1F,
0x02,
0x0F,
0x03,
0x35,
0x25,
0x47,
0x04,
0x0C,
0x0B,
0x29,
0x2F,
0x05};
portapack_lcd_data_write_command_and_data(0xE1, cmd_e1, ARRAY_SIZEOF(cmd_e1));
portapack_lcd_wake();
// Turn on Tearing Effect Line (TE) output signal.
const uint8_t cmd_35[] = {0b00000000};
portapack_lcd_data_write_command_and_data(0x35, cmd_35, ARRAY_SIZEOF(cmd_35));
}
void portapack_backlight(const bool on)
{
portapack_if.io_reg = (portapack_if.io_reg & 0x7f) | (on ? (1 << 7) : 0);
portapack_io_write(1, portapack_if.io_reg);
}
void portapack_reference_oscillator(const bool on)
{
const uint8_t mask = 1 << 6;
portapack_if.io_reg = (portapack_if.io_reg & ~mask) | (on ? mask : 0);
portapack_io_write(1, portapack_if.io_reg);
}
void portapack_fill_rectangle(const ui_rect_t rect, const ui_color_t color)
{
portapack_lcd_start_ram_write(rect);
portapack_lcd_write_pixels_color(color, rect.size.width * rect.size.height);
}
void portapack_clear_display(const ui_color_t color)
{
const ui_rect_t rect_screen = {{0, 0}, {240, 320}};
portapack_fill_rectangle(rect_screen, color);
}
void portapack_draw_bitmap(
const ui_point_t point,
const ui_bitmap_t bitmap,
const ui_color_t foreground,
const ui_color_t background)
{
const ui_rect_t rect = {.point = point, .size = bitmap.size};
portapack_lcd_start_ram_write(rect);
const size_t count = bitmap.size.width * bitmap.size.height;
for (size_t i = 0; i < count; i++) {
const uint8_t pixel = bitmap.data[i >> 3] & (1U << (i & 0x7));
portapack_lcd_write_pixel(pixel ? foreground : background);
}
}
ui_bitmap_t portapack_font_glyph(const ui_font_t* const font, const char c)
{
if (c >= font->c_start) {
const uint_fast8_t index = c - font->c_start;
if (index < font->c_count) {
const ui_bitmap_t bitmap = {
.size = font->glyph_size,
.data = &font->data[index * font->data_stride]};
return bitmap;
}
}
const ui_bitmap_t bitmap = {
.size = font->glyph_size,
.data = font->data,
};
return bitmap;
}
static bool jtag_pp_tck(const bool tms_value)
{
gpio_write(jtag_cpld.gpio->gpio_pp_tms, tms_value);
// 8 ns TMS/TDI to TCK setup
__asm__("nop");
__asm__("nop");
__asm__("nop");
gpio_set(jtag_cpld.gpio->gpio_tck);
// 15 ns TCK to TMS/TDI hold time
// 20 ns TCK high time
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
gpio_clear(jtag_cpld.gpio->gpio_tck);
// 20 ns TCK low time
// 25 ns TCK falling edge to TDO valid
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
__asm__("nop");
return gpio_read(jtag_cpld.gpio->gpio_pp_tdo);
}
static uint32_t jtag_pp_shift(const uint32_t tms_bits, const size_t count)
{
uint32_t result = 0;
size_t bit_in_index = count - 1;
size_t bit_out_index = 0;
while (bit_out_index < count) {
const uint32_t tdo = jtag_pp_tck((tms_bits >> bit_in_index) & 1) & 1;
result |= (tdo << bit_out_index);
bit_in_index--;
bit_out_index++;
}
return result;
}
static uint32_t jtag_pp_idcode(void)
{
cpld_jtag_take(&jtag_cpld);
/* TODO: Check if PortaPack TMS is floating or driven by an external device. */
gpio_output(jtag_cpld.gpio->gpio_pp_tms);
/* Test-Logic/Reset -> Run-Test/Idle -> Select-DR/Scan -> Capture-DR */
jtag_pp_shift(0b11111010, 8);
/* Shift-DR */
const uint32_t idcode = jtag_pp_shift(0, 32);
/* Exit1-DR -> Update-DR -> Run-Test/Idle -> ... -> Test-Logic/Reset */
jtag_pp_shift(0b11011111, 8);
cpld_jtag_release(&jtag_cpld);
return idcode;
}
static bool portapack_detect(void)
{
const uint32_t idcode = jtag_pp_idcode();
return idcode == 0x00025610 || idcode == 0x020A50DD;
}
static const portapack_t portapack_instance = {};
static const portapack_t* portapack_pointer = NULL;
const portapack_t* portapack(void)
{
return portapack_pointer;
}
void portapack_init(void)
{
if (portapack_detect()) {
portapack_if_init();
portapack_lcd_reset();
portapack_lcd_init();
portapack_pointer = &portapack_instance;
} else {
portapack_pointer = NULL;
}
}
-189
View File
@@ -1,189 +0,0 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
* Copyright 2013 Benjamin Vernoux
*
* This file is part of HackRF.
*
* 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 "usb_api_board_info.h"
#include "platform_detect.h"
#include "firmware_info.h"
#include <hackrf_core.h>
#include <rom_iap.h>
#include <usb_queue.h>
#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/wwdt.h>
#include <stddef.h>
#include <string.h>
#ifdef HACKRF_ONE
#include "gpio_lpc.h"
static struct gpio_t gpio_h1r9_clkout_en = GPIO(0, 9);
static struct gpio_t gpio_h1r9_mcu_clk_en = GPIO(0, 8);
static struct gpio_t gpio_h1r9_rx = GPIO(0, 7);
#endif
usb_request_status_t usb_vendor_request_read_board_id(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
endpoint->buffer[0] = detected_platform();
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_read_version_string(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
uint8_t length;
if (stage == USB_TRANSFER_STAGE_SETUP) {
length = (uint8_t)strlen(firmware_info.version_string);
// The USB peripheral doesn't seem to be able to read directly from flash,
// so copy the version string into ram first.
memcpy(&endpoint->buffer,
firmware_info.version_string,
sizeof(firmware_info.version_string));
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
length,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
static read_partid_serialno_t read_partid_serialno;
usb_request_status_t usb_vendor_request_read_partid_serialno(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
uint8_t length;
iap_cmd_res_t iap_cmd_res;
if (stage == USB_TRANSFER_STAGE_SETUP) {
/* Read IAP Part Number Identification */
iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_PART_ID_NO;
iap_cmd_call(&iap_cmd_res);
if (iap_cmd_res.status_res.status_ret != CMD_SUCCESS) {
return USB_REQUEST_STATUS_STALL;
}
read_partid_serialno.part_id[0] = iap_cmd_res.status_res.iap_result[0];
read_partid_serialno.part_id[1] = iap_cmd_res.status_res.iap_result[1];
/* Read IAP Serial Number Identification */
iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_SERIAL_NO;
iap_cmd_call(&iap_cmd_res);
if (iap_cmd_res.status_res.status_ret != CMD_SUCCESS) {
return USB_REQUEST_STATUS_STALL;
}
read_partid_serialno.serial_no[0] = iap_cmd_res.status_res.iap_result[0];
read_partid_serialno.serial_no[1] = iap_cmd_res.status_res.iap_result[1];
read_partid_serialno.serial_no[2] = iap_cmd_res.status_res.iap_result[2];
read_partid_serialno.serial_no[3] = iap_cmd_res.status_res.iap_result[3];
length = (uint8_t)sizeof(read_partid_serialno_t);
usb_transfer_schedule_block(
endpoint->in,
&read_partid_serialno,
length,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_reset(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
#ifdef HACKRF_ONE
/*
* Set boot pins as inputs so that the bootloader reads them
* correctly after the reset.
*/
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
gpio_input(&gpio_h1r9_mcu_clk_en);
gpio_input(&gpio_h1r9_clkout_en);
gpio_input(&gpio_h1r9_rx);
}
#endif
usb_transfer_schedule_ack(endpoint->in);
delay(50 * 40800);
SCU_SFSP2_8 = (SCU_SFSP2_8 & ~(7)) | 4;
struct gpio_t dfu = GPIO(5, 7);
gpio_output(&dfu);
gpio_clear(&dfu);
wwdt_reset(100000);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_read_board_rev(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
endpoint->buffer[0] = detected_revision();
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_read_supported_platform(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t platform = supported_platform();
endpoint->buffer[0] = (platform >> 24) & 0xff;
endpoint->buffer[1] = (platform >> 16) & 0xff;
endpoint->buffer[2] = (platform >> 8) & 0xff;
endpoint->buffer[3] = platform & 0xff;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
4,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
-56
View File
@@ -1,56 +0,0 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
* Copyright 2013 Benjamin Vernoux
*
* This file is part of HackRF.
*
* 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 __USB_API_BOARD_INFO_H__
#define __USB_API_BOARD_INFO_H__
#include <stdint.h>
#include <usb_type.h>
#include <usb_request.h>
typedef struct {
uint32_t part_id[2];
uint32_t serial_no[4];
} read_partid_serialno_t;
usb_request_status_t usb_vendor_request_read_board_id(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
usb_request_status_t usb_vendor_request_read_version_string(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
usb_request_status_t usb_vendor_request_read_partid_serialno(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
usb_request_status_t usb_vendor_request_reset(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
usb_request_status_t usb_vendor_request_read_board_rev(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
usb_request_status_t usb_vendor_request_read_supported_platform(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
#endif /* end of include guard: __USB_API_BOARD_INFO_H__ */
@@ -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.
+2 -2
View File
@@ -1,13 +1,13 @@
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=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 - Civik
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