mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-15 19:13:00 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a14d04c91 | |||
| e17aef3724 | |||
| 4155216a7f | |||
| d9bc542b06 | |||
| 3dcfa4f8c1 | |||
| d36ae88284 | |||
| 69271632ae | |||
| e6a099913a | |||
| 280b8afc0a | |||
| b108d975c0 | |||
| fc08d4ead9 | |||
| 07cae4a298 | |||
| e88e0b5f8f | |||
| 131523d726 | |||
| 24d15c1643 | |||
| 4a83118557 | |||
| ff54f59d38 | |||
| 6b8274d792 | |||
| 1fc84e7eac | |||
| d31857c21e | |||
| 52b2cb241a | |||
| 64f8158134 | |||
| 59f72cbff1 |
@@ -54,7 +54,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -63,10 +63,10 @@ jobs:
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -47,10 +47,10 @@ jobs:
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -17,9 +17,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check these:
|
||||
|
||||
[<img alt="HackRF Portapack H4M - Getting Started Guide" src="https://img.youtube.com/vi/wzP0zWi85SI/maxresdefault.jpg" width="701">](https://grabify.link/F9MPOO)
|
||||
[<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="It’s TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://grabify.link/X4D5TF) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="172">](https://grabify.link/9UZGEW) [<img alt="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="It’s 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)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
@@ -27,7 +27,8 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
|
||||
## What to buy?
|
||||
|
||||
:heavy_check_mark:  The fabulous [PortaPack H4M Mayhem](https://opensourcesdrlab.com/products/h4m-receiver-and-spectrum-analyzer), 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://grabify.link/TUOLYI--->
|
||||
:heavy_check_mark:  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).
|
||||
|
||||
: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).
|
||||
|
||||
|
||||
+2
-11
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:xenial
|
||||
FROM ubuntu:noble
|
||||
|
||||
# Set location to download ARM toolkit from.
|
||||
# This will need to be changed over time or replaced with a static link to the latest release.
|
||||
@@ -11,19 +11,10 @@ WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 python3-pip python3-yaml ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
|
||||
+2
-11
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:xenial
|
||||
FROM ubuntu:noble
|
||||
|
||||
# Set location to download ARM toolkit from.
|
||||
# This will need to be changed over time or replaced with a static link to the latest release.
|
||||
@@ -11,19 +11,10 @@ WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 python3-pip python3-yaml ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
|
||||
set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
|
||||
set(LZ4 lz4)
|
||||
|
||||
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
|
||||
set(FIRMWARE_NAME portapack-mayhem-firmware)
|
||||
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
|
||||
set(PPFW_FILENAME "portapack-mayhem_OCI.ppfw.tar")
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ set(CPPSRC
|
||||
hw/si5351.cpp
|
||||
hw/spi_pp.cpp
|
||||
hw/touch_adc.cpp
|
||||
ook_file.cpp
|
||||
ui_baseband_stats_view.cpp
|
||||
ui_navigation.cpp
|
||||
ui_record_view.cpp
|
||||
@@ -302,7 +303,6 @@ set(CPPSRC
|
||||
apps/ui_rds.cpp
|
||||
apps/ui_recon_settings.cpp
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_remote.cpp
|
||||
apps/ui_scanner.cpp
|
||||
apps/ui_sd_over_usb.cpp
|
||||
apps/ui_sd_wipe.cpp
|
||||
|
||||
@@ -1,58 +1,60 @@
|
||||
#include "ui_about_simple.hpp"
|
||||
#include <string_view>
|
||||
|
||||
#define ROLL_SPEED_FRAME_PER_LINE 60
|
||||
// cuz frame rate of pp screen is probably 60, scroll per sec
|
||||
|
||||
namespace ui {
|
||||
|
||||
// TODO: Generate this automatically from github
|
||||
// Information: a line starting with a '#' will be yellow coloured
|
||||
const std::string authors_list[] = {
|
||||
constexpr std::string_view authors_list[] = {
|
||||
"# * List of contributors * ",
|
||||
" ",
|
||||
"#Mayhem:",
|
||||
"eried,euquiq,gregoryfenton",
|
||||
"johnelder,jwetzell,nnemanjan00",
|
||||
"N0vaPixel,klockee,gullradriel",
|
||||
"jamesshao8,ITAxReal,rascafr",
|
||||
"mcules,dqs105,strijar,fossum",
|
||||
"zhang00963,RedFox-Fr,aldude999",
|
||||
"East2West,ArjanOnwezen,jLynx",
|
||||
"vXxOinvizioNxX,teixeluis,n0xa",
|
||||
"Brumi-2021,texasyojimbo,zigad",
|
||||
"heurist1,intoxsick,ckuethe",
|
||||
"notpike,MichalLeonBorsuk",
|
||||
"kallanreed,bernd-herzog,zxkmm",
|
||||
"NotherNgineer,jimilinuxguy",
|
||||
"iNetro,HTotoo,BehleZebub,dc2dc",
|
||||
"arneluehrs,u-foka,Maescool",
|
||||
"nnesetto,formtapez,F33RNI",
|
||||
"rusty-labs,andrej-mk,taskinen",
|
||||
"MatiasFernandez,TQMatvey,KimIV",
|
||||
"RobertoD91,RndmNmbr,OpCode1300",
|
||||
"alex10791,GitClo,GermanAizek",
|
||||
"JimiHFord,jstockdale,aSmig",
|
||||
"KillerTurtleSoftware,Lpd738",
|
||||
"MattLodge,z3r0l1nk,joyel24",
|
||||
"#Mayhem-Firmware:",
|
||||
"jboone,eried,furrtek,",
|
||||
"NotherNgineer,gullradriel,",
|
||||
"jLynx,kallanreed,Brumi-2021,",
|
||||
"htotoo,bernd-herzog,zxkmm,",
|
||||
"ArjanOnwezen,euquiq,u-foka,",
|
||||
"iNetro,heurist1,dqs105,",
|
||||
"teixeluis,jwetzell,",
|
||||
"jimilinuxguy,gregoryfenton,",
|
||||
"notpike,strijar,BehleZebub,",
|
||||
"arneluehrs,rascafr,joyel24,",
|
||||
"ImDroided,zigad,johnelder,",
|
||||
"klockee,nnesetto,LupusE,",
|
||||
"argilo,dc2dc,formtapez,",
|
||||
"RocketGod-git,mrmookie,",
|
||||
"ITAxReal,F33RNI,F4GEV,",
|
||||
"rusty-labs,mjwaxios,andrej-mk,",
|
||||
"RedFox-Fr,nemanjan00,",
|
||||
"MichalLeonBorsuk,",
|
||||
"MatiasFernandez,Giorgiofox,",
|
||||
"ckuethe",
|
||||
" ",
|
||||
"#Havoc:",
|
||||
"furrtek,mrmookie,NotPike",
|
||||
"mjwaxios,ImDroided,Giorgiofox",
|
||||
"F4GEV,z4ziggy,xmycroftx",
|
||||
"troussos,silascutler",
|
||||
"nickbouwhuis,msoose,leres",
|
||||
"joakar,dhoetger,clem-42",
|
||||
"brianlechthaler,ZeroChaos-...",
|
||||
"jboone,furrtek,eried,argilo,",
|
||||
"mrmookie,Giorgiofox,ImDroided,",
|
||||
"mjwaxios,F4GEV,OpCode1300,",
|
||||
"ZeroChaos-,RndmNmbr,",
|
||||
"silascutler,troussos,z4ziggy,",
|
||||
"clem-42,dhoetger,NickBouwhuis,",
|
||||
"xmycroftx,Maescool,KimIV,",
|
||||
"joakar,leres,brianlechthaler,",
|
||||
"N0vaPixel",
|
||||
" ",
|
||||
"#PortaPack:",
|
||||
"jboone,argilo",
|
||||
"jboone,mossmann,martinling,",
|
||||
"argilo,eried,ZeroChaos-,",
|
||||
"RndmNmbr",
|
||||
" ",
|
||||
"#HackRF:",
|
||||
"mossmann,dominicgs,bvernoux",
|
||||
"bgamari,schneider42,miek",
|
||||
"willcode,hessu,Sec42",
|
||||
"yhetti,ckuethe,smunaut",
|
||||
"wishi,mrbubble62,scateu..."};
|
||||
"mossmann,jboone,dominicgs,",
|
||||
"martinling,bvernoux,miek,",
|
||||
"bgamari,schneider42,straithe,",
|
||||
"grvvy,willcode,hessu,yhetti,",
|
||||
"Sec42,ckuethe",
|
||||
" "};
|
||||
|
||||
AboutView::AboutView(NavigationView& nav) {
|
||||
add_children({&menu_view,
|
||||
@@ -70,18 +72,18 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
button_ok.focus();
|
||||
};
|
||||
|
||||
for (const std::string& authors_line : authors_list) {
|
||||
for (auto& authors_line : authors_list) {
|
||||
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
|
||||
if (authors_line.size() > 0) {
|
||||
if (authors_line[0] == '#') {
|
||||
menu_view.add_item(
|
||||
{authors_line.substr(1, authors_line.size() - 1),
|
||||
{(std::string)authors_line.substr(1, authors_line.size() - 1),
|
||||
ui::Theme::getInstance()->fg_yellow->foreground,
|
||||
nullptr,
|
||||
nullptr});
|
||||
} else {
|
||||
menu_view.add_item(
|
||||
{authors_line,
|
||||
{(std::string)authors_line,
|
||||
Theme::getInstance()->bg_darkest->foreground,
|
||||
nullptr,
|
||||
nullptr});
|
||||
|
||||
@@ -50,10 +50,10 @@ void BattinfoView::update_result() {
|
||||
text_voltage.set("UNKNOWN");
|
||||
text_current.set("-");
|
||||
text_charge.set("-");
|
||||
text_cycles.set("-");
|
||||
// text_cycles.set("-");
|
||||
text_ttef.set("-");
|
||||
text_method.set("-");
|
||||
text_warn.set("");
|
||||
// text_warn.set("");
|
||||
return;
|
||||
}
|
||||
bool uichg = false;
|
||||
@@ -96,22 +96,22 @@ void BattinfoView::update_result() {
|
||||
labels_opt.hidden(true);
|
||||
text_current.hidden(true);
|
||||
text_charge.hidden(true);
|
||||
text_cycles.hidden(true);
|
||||
// text_cycles.hidden(true);
|
||||
text_ttef.hidden(true);
|
||||
text_warn.set("");
|
||||
// text_warn.set("");
|
||||
}
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
|
||||
text_cycles.hidden(false);
|
||||
uint16_t cycles = battery::BatteryManagement::get_cycles();
|
||||
/* if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
|
||||
// text_cycles.hidden(false);
|
||||
uint16_t cycles = 0; // battery::BatteryManagement::get_cycles();
|
||||
if (cycles < 2)
|
||||
text_warn.set("SoC improves after 2 cycles");
|
||||
text_warn.set("SoC improves after each cycles");
|
||||
else
|
||||
text_warn.set("");
|
||||
text_cycles.set(to_string_dec_uint(cycles));
|
||||
// text_cycles.set(to_string_dec_uint(cycles));
|
||||
} else {
|
||||
text_cycles.hidden(true);
|
||||
// text_cycles.hidden(true);
|
||||
text_warn.set("");
|
||||
}
|
||||
} */
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
|
||||
text_ttef.hidden(false);
|
||||
float ttef = 0;
|
||||
@@ -160,8 +160,8 @@ BattinfoView::BattinfoView(NavigationView& nav)
|
||||
&text_method,
|
||||
&button_mode,
|
||||
&button_exit,
|
||||
&text_cycles,
|
||||
&text_warn,
|
||||
// &text_cycles,
|
||||
// &text_warn,
|
||||
&text_ttef});
|
||||
|
||||
button_exit.on_select = [this, &nav](Button&) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class BattinfoView : public View {
|
||||
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
|
||||
// {{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
|
||||
};
|
||||
|
||||
@@ -85,13 +85,13 @@ class BattinfoView : public View {
|
||||
Text text_ttef{
|
||||
{13 * 8, 6 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
Text text_cycles{
|
||||
/* Text text_cycles{
|
||||
{13 * 8, 7 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
|
||||
Text text_warn{
|
||||
{2 * 8, 8 * 16, 30 * 8, 2 * 16},
|
||||
""};
|
||||
{1 * 8, 8 * 16, 30 * 8, 2 * 16},
|
||||
""}; */
|
||||
|
||||
Button button_mode{
|
||||
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <algorithm>
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_playlist.hpp"
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_bmp_file_viewer.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
@@ -704,10 +703,11 @@ bool FileManagerView::handle_file_open() {
|
||||
|
||||
reload_current(false);
|
||||
return true;
|
||||
} else if (path_iequal(rem_ext, ext)) {
|
||||
}
|
||||
/*else if (path_iequal(rem_ext, ext)) {
|
||||
nav_.push<RemoteView>(path);
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -40,27 +40,39 @@ namespace fs = std::filesystem;
|
||||
|
||||
// TODO: Clean up after moving to better lookup tables.
|
||||
using options_t = OptionsField::options_t;
|
||||
extern options_t freqman_modulations;
|
||||
extern options_t freqman_bandwidths[4];
|
||||
extern options_t freqman_steps;
|
||||
extern options_t freqman_steps_short;
|
||||
|
||||
using option_db_t = std::pair<std::string_view, int32_t>;
|
||||
using options_db_t = std::vector<option_db_t>;
|
||||
|
||||
extern options_db_t freqman_modulations;
|
||||
extern options_db_t freqman_bandwidths[4];
|
||||
extern options_db_t freqman_steps;
|
||||
extern options_db_t freqman_steps_short;
|
||||
|
||||
options_t dboptions_to_options(const options_db_t& dboptions) {
|
||||
options_t options;
|
||||
for (const auto& dboption : dboptions) {
|
||||
options.emplace_back(dboption.first, dboption.second);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/* Set options. */
|
||||
void freqman_set_modulation_option(OptionsField& option) {
|
||||
option.set_options(freqman_modulations);
|
||||
option.set_options(dboptions_to_options(freqman_modulations));
|
||||
}
|
||||
|
||||
void freqman_set_bandwidth_option(freqman_index_t modulation, OptionsField& option) {
|
||||
if (is_valid(modulation))
|
||||
option.set_options(freqman_bandwidths[modulation]);
|
||||
option.set_options(dboptions_to_options(freqman_bandwidths[modulation]));
|
||||
}
|
||||
|
||||
void freqman_set_step_option(OptionsField& option) {
|
||||
option.set_options(freqman_steps);
|
||||
option.set_options(dboptions_to_options(freqman_steps));
|
||||
}
|
||||
|
||||
void freqman_set_step_option_short(OptionsField& option) {
|
||||
option.set_options(freqman_steps_short);
|
||||
option.set_options(dboptions_to_options(freqman_steps_short));
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
@@ -438,7 +450,7 @@ void FrequencyEditView::populate_bandwidth_options() {
|
||||
auto& bandwidths = freqman_bandwidths[entry_.modulation];
|
||||
for (auto i = 0u; i < bandwidths.size(); ++i) {
|
||||
auto& item = bandwidths[i];
|
||||
options.push_back({item.first, (OptionsField::value_t)i});
|
||||
options.push_back({(std::string)item.first, (OptionsField::value_t)i});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +463,7 @@ void FrequencyEditView::populate_step_options() {
|
||||
|
||||
for (auto i = 0u; i < freqman_steps.size(); ++i) {
|
||||
auto& item = freqman_steps[i];
|
||||
options.push_back({item.first, (OptionsField::value_t)i});
|
||||
options.push_back({(std::string)item.first, (OptionsField::value_t)i});
|
||||
}
|
||||
|
||||
field_step.set_options(std::move(options));
|
||||
|
||||
@@ -56,7 +56,7 @@ SIGFRXView::~SIGFRXView() {
|
||||
void SIGFRXView::paint(Painter& painter) {
|
||||
uint8_t i, xp;
|
||||
|
||||
// portapack::display.drawBMP({0, 302-160}, fox_bmp);
|
||||
// portapack::display.draw_bmp_from_bmp_hex_arr({0, 302-160}, fox_bmp);
|
||||
portapack::display.fill_rectangle({0, 16, 240, 160 - 16}, Theme::getInstance()->bg_darkest->foreground);
|
||||
for (i = 0; i < 6; i++) {
|
||||
xp = sigfrx_marks[i * 3];
|
||||
|
||||
@@ -119,7 +119,7 @@ bool SplashViewer::on_key(const KeyEvent key) {
|
||||
void SplashViewer::paint(Painter& painter) {
|
||||
painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black());
|
||||
|
||||
if (!portapack::display.drawBMP2({0, 0}, path_)) {
|
||||
if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, path_)) {
|
||||
painter.draw_string({10, 160}, *Theme::getInstance()->bg_darkest, "Not a valid splash image.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -31,6 +32,18 @@ using namespace ui;
|
||||
|
||||
namespace ui {
|
||||
|
||||
std::string SubGhzDRecentEntry::to_csv() {
|
||||
std::string csv = ";";
|
||||
csv += SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)sensorType);
|
||||
csv += ";" + to_string_dec_uint(bits) + ";";
|
||||
csv += to_string_hex(data, 64 / 4);
|
||||
return csv;
|
||||
}
|
||||
|
||||
void SubGhzDLogger::log_data(SubGhzDRecentEntry& data) {
|
||||
log_file.write_entry(data.to_csv());
|
||||
}
|
||||
|
||||
void SubGhzDRecentEntryDetailView::update_data() {
|
||||
// process protocol data
|
||||
parseProtocol();
|
||||
@@ -76,16 +89,25 @@ SubGhzDView::SubGhzDView(NavigationView& nav)
|
||||
&field_vga,
|
||||
&field_frequency,
|
||||
&button_clear_list,
|
||||
&check_log,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
|
||||
logger = std::make_unique<SubGhzDLogger>();
|
||||
|
||||
button_clear_list.on_select = [this](Button&) {
|
||||
recent.clear();
|
||||
recent_entries_view.set_dirty();
|
||||
};
|
||||
field_frequency.set_step(10000);
|
||||
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
if (logger && logging) {
|
||||
logger->append(logs_dir.string() + "/SUBGHZDLOG_" + to_string_timestamp(rtc_time::now()) + ".CSV");
|
||||
logger->write_header();
|
||||
}
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
|
||||
recent_entries_view.set_parent_rect(content_rect);
|
||||
recent_entries_view.on_select = [this](const SubGhzDRecentEntry& entry) {
|
||||
@@ -107,6 +129,9 @@ void SubGhzDView::on_tick_second() {
|
||||
|
||||
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
|
||||
SubGhzDRecentEntry key{data->sensorType, data->data, data->bits};
|
||||
if (logger && logging) {
|
||||
logger->log_data(key);
|
||||
}
|
||||
auto matching_recent = find(recent, key.key());
|
||||
if (matching_recent != std::end(recent)) {
|
||||
// Found within. Move to front of list, increment counter.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "../baseband/fprotos/subghztypes.hpp"
|
||||
@@ -68,6 +69,23 @@ struct SubGhzDRecentEntry {
|
||||
void reset_age() {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
std::string to_csv();
|
||||
};
|
||||
|
||||
class SubGhzDLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void log_data(SubGhzDRecentEntry& data);
|
||||
void write_header() {
|
||||
log_file.write_entry(";Type; Bits; Data;");
|
||||
}
|
||||
|
||||
private:
|
||||
LogFile log_file{};
|
||||
};
|
||||
using SubGhzDRecentEntries = RecentEntries<SubGhzDRecentEntry>;
|
||||
using SubGhzDRecentEntriesView = RecentEntriesView<SubGhzDRecentEntries>;
|
||||
@@ -93,10 +111,13 @@ class SubGhzDView : public View {
|
||||
1'750'000 /* bandwidth */,
|
||||
4'000'000 /* sampling rate */,
|
||||
ReceiverModel::Mode::AMAudio};
|
||||
bool logging = false;
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_subghzd",
|
||||
app_settings::Mode::RX,
|
||||
{}};
|
||||
{
|
||||
{"log"sv, &logging},
|
||||
}};
|
||||
|
||||
SubGhzDRecentEntries recent{};
|
||||
|
||||
@@ -118,8 +139,16 @@ class SubGhzDView : public View {
|
||||
{0, 16, 7 * 8, 32},
|
||||
"Clear"};
|
||||
|
||||
Checkbox check_log{
|
||||
{10 * 8, 18},
|
||||
3,
|
||||
"Log",
|
||||
true};
|
||||
|
||||
static constexpr auto header_height = 3 * 16;
|
||||
|
||||
std::unique_ptr<SubGhzDLogger> logger{};
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"Type", 19},
|
||||
{"Bits", 4},
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "../baseband/fprotos/fprotogeneral.hpp"
|
||||
|
||||
@@ -35,6 +36,21 @@ namespace pmem = portapack::persistent_memory;
|
||||
|
||||
namespace ui {
|
||||
|
||||
std::string WeatherRecentEntry::to_csv() {
|
||||
std::string csv = ";";
|
||||
csv += WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)sensorType);
|
||||
csv += ";" + to_string_dec_uint(id) + ";";
|
||||
csv += to_string_decimal(temp, 2) + ";";
|
||||
csv += to_string_dec_uint(humidity) + ";";
|
||||
csv += to_string_dec_uint(channel) + ";";
|
||||
csv += to_string_dec_uint(battery_low);
|
||||
return csv;
|
||||
}
|
||||
|
||||
void WeatherLogger::log_data(WeatherRecentEntry& data) {
|
||||
log_file.write_entry(data.to_csv());
|
||||
}
|
||||
|
||||
void WeatherRecentEntryDetailView::update_data() {
|
||||
// set text elements
|
||||
text_type.set(WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry_.sensorType));
|
||||
@@ -98,8 +114,11 @@ WeatherView::WeatherView(NavigationView& nav)
|
||||
&field_frequency,
|
||||
&options_temperature,
|
||||
&button_clear_list,
|
||||
&check_log,
|
||||
&recent_entries_view});
|
||||
|
||||
logger = std::make_unique<WeatherLogger>();
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_weather);
|
||||
|
||||
button_clear_list.on_select = [this](Button&) {
|
||||
@@ -114,6 +133,15 @@ WeatherView::WeatherView(NavigationView& nav)
|
||||
};
|
||||
options_temperature.set_selected_index(weather_units_fahr, false);
|
||||
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
if (logger && logging) {
|
||||
logger->append(logs_dir.string() + "/WEATHERLOG_" + to_string_timestamp(rtc_time::now()) + ".CSV");
|
||||
logger->write_header();
|
||||
}
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
|
||||
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
|
||||
recent_entries_view.set_parent_rect(content_rect);
|
||||
recent_entries_view.on_select = [this](const WeatherRecentEntry& entry) {
|
||||
@@ -140,6 +168,9 @@ void WeatherView::on_tick_second() {
|
||||
|
||||
void WeatherView::on_data(const WeatherDataMessage* data) {
|
||||
WeatherRecentEntry key = process_data(data);
|
||||
if (logger && logging) {
|
||||
logger->log_data(key);
|
||||
}
|
||||
// WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
|
||||
auto matching_recent = find(recent, key.key());
|
||||
if (matching_recent != std::end(recent)) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "../baseband/fprotos/weathertypes.hpp"
|
||||
@@ -78,7 +79,25 @@ struct WeatherRecentEntry {
|
||||
void reset_age() {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
std::string to_csv();
|
||||
};
|
||||
|
||||
class WeatherLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void log_data(WeatherRecentEntry& data);
|
||||
void write_header() {
|
||||
log_file.write_entry(";Type; id; Temp; Hum; CH; Batt");
|
||||
}
|
||||
|
||||
private:
|
||||
LogFile log_file{};
|
||||
};
|
||||
|
||||
using WeatherRecentEntries = RecentEntries<WeatherRecentEntry>;
|
||||
using WeatherRecentEntriesView = RecentEntriesView<WeatherRecentEntries>;
|
||||
|
||||
@@ -103,11 +122,13 @@ class WeatherView : public View {
|
||||
1'750'000 /* bandwidth */,
|
||||
2'000'000 /* sampling rate */,
|
||||
ReceiverModel::Mode::AMAudio};
|
||||
bool logging = false;
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_weather",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"units_fahr"sv, &weather_units_fahr},
|
||||
{"log"sv, &logging},
|
||||
}};
|
||||
|
||||
WeatherRecentEntries recent{};
|
||||
@@ -140,8 +161,16 @@ class WeatherView : public View {
|
||||
{0, 16, 7 * 8, 32},
|
||||
"Clear"};
|
||||
|
||||
Checkbox check_log{
|
||||
{10 * 8, 18},
|
||||
3,
|
||||
"Log",
|
||||
true};
|
||||
|
||||
static constexpr auto header_height = 3 * 16;
|
||||
|
||||
std::unique_ptr<WeatherLogger> logger{};
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"Type", 10},
|
||||
{"Temp", 5},
|
||||
|
||||
+705
-553
File diff suppressed because it is too large
Load Diff
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_acars_rx.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_acars */ {'P', 'A', 'C', 'A'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_adsbtx.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_adsbtx */ {'P', 'A', 'D', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_afsk_rx.application_information"), use
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_analogtv.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_am_tv */ {'P', 'A', 'M', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_audio_test.application_information"),
|
||||
},
|
||||
/*.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 */ {'P', 'A', 'B', 'P'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_blespam.application_information"), use
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_btle_tx */ {'P', 'B', 'T', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_calculator.application_information"),
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -76,6 +76,7 @@ __attribute__((section(".external_app.app_coasterp.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
+14
-13
@@ -17,17 +17,18 @@ void initialize_app(NavigationView& nav) {
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_cvs_spam.application_information"), used)) application_information_t _application_information_cvs_spam = {
|
||||
(uint8_t*)0x00000000,
|
||||
ui::external_app::cvs_spam::initialize_app,
|
||||
CURRENT_HEADER_VERSION,
|
||||
VERSION_MD5,
|
||||
"CVS Spam",
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81},
|
||||
ui::Color::red().v,
|
||||
app_location_t::TX,
|
||||
{'P', 'R', 'E', 'P'},
|
||||
0x00000000};
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::cvs_spam::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "CVS Spam",
|
||||
/*.bitmap_data = */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81},
|
||||
/*.icon_color = */ ui::Color::red().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'R', 'E', 'P'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+16
-1
@@ -115,9 +115,21 @@ set(EXTCPPSRC
|
||||
external/ookbrute/main.cpp
|
||||
external/ookbrute/ui_ookbrute.cpp
|
||||
|
||||
#ook_editor
|
||||
external/ook_editor/main.cpp
|
||||
external/ook_editor/ui_ook_editor.cpp
|
||||
|
||||
#cvs_spam
|
||||
external/cvs_spam/main.cpp
|
||||
external/cvs_spam/cvs_spam.cpp
|
||||
external/cvs_spam/cvs_spam.cpp
|
||||
|
||||
#flippertx
|
||||
external/flippertx/main.cpp
|
||||
external/flippertx/ui_flippertx.cpp
|
||||
|
||||
#remote
|
||||
external/remote/main.cpp
|
||||
external/remote/ui_remote.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -148,5 +160,8 @@ set(EXTAPPLIST
|
||||
random_password
|
||||
#acars_rx
|
||||
ookbrute
|
||||
ook_editor
|
||||
shoppingcart_lock
|
||||
flippertx
|
||||
remote
|
||||
)
|
||||
|
||||
+20
@@ -51,6 +51,9 @@ MEMORY
|
||||
ram_external_app_shoppingcart_lock(rwx) : org = 0xADCA0000, len = 32k
|
||||
ram_external_app_cvs_spam(rwx) : org = 0xADCB0000, len = 32k
|
||||
ram_external_app_ookbrute(rwx) : org = 0xADCC0000, len = 32k
|
||||
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
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -224,4 +227,21 @@ SECTIONS
|
||||
*(*ui*external_app*ookbrute*);
|
||||
} > ram_external_app_ookbrute
|
||||
|
||||
.external_app_ook_editor : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_ook_editor.application_information));
|
||||
*(*ui*external_app*ook_editor*);
|
||||
} > ram_external_app_ook_editor
|
||||
|
||||
.external_app_flippertx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_flippertx.application_information));
|
||||
*(*ui*external_app*flippertx*);
|
||||
} > ram_external_app_flippertx
|
||||
|
||||
.external_app_remote : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_remote.application_information));
|
||||
*(*ui*external_app*remote*);
|
||||
} > ram_external_app_remote
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_extsensors.application_information"),
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().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,83 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_flippertx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<FlipperTxView>();
|
||||
}
|
||||
} // namespace ui::external_app::flippertx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_flippertx.application_information"), used)) application_information_t _application_information_flippertx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::flippertx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "FlipperTx",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x30,
|
||||
0x0C,
|
||||
0x30,
|
||||
0x0C,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xE0,
|
||||
0x07,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ookstreaming */ {'P', 'O', 'S', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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_flippertx.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "buffer_exchange.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
|
||||
void FlipperTxView::focus() {
|
||||
button_browse.focus();
|
||||
}
|
||||
|
||||
void FlipperTxView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
FlipperTxView::FlipperTxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&button_startstop,
|
||||
&field_frequency,
|
||||
&tx_view,
|
||||
&field_filename,
|
||||
&button_browse});
|
||||
|
||||
button_startstop.on_select = [this](Button&) {
|
||||
if (is_running) {
|
||||
stop();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
button_browse.on_select = [this](Button&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".sub");
|
||||
open_view->push_dir(subghz_dir);
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
if (on_file_changed(new_file_path)) {
|
||||
; // tif (button_startstop.parent()) button_startstop.focus(); parent_ is null error.....
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
bool FlipperTxView::on_file_changed(std::filesystem::path new_file_path) {
|
||||
submeta = read_flippersub_file(new_file_path);
|
||||
filename = "";
|
||||
if (!submeta.is_valid()) {
|
||||
field_filename.set_text("File: err, bad file");
|
||||
return false;
|
||||
}
|
||||
proto = submeta.value().protocol;
|
||||
if (proto != FLIPPER_PROTO_RAW && proto != FLIPPER_PROTO_BINRAW) { // temp disabled
|
||||
field_filename.set_text("File: err, not supp. proto");
|
||||
return false;
|
||||
}
|
||||
preset = submeta.value().preset;
|
||||
if (preset != FLIPPER_PRESET_OOK) {
|
||||
field_filename.set_text("File: err, not supp. preset");
|
||||
return false;
|
||||
}
|
||||
te = submeta.value().te;
|
||||
binraw_bit_count = submeta.value().binraw_bit_count;
|
||||
/*if (binraw_bit_count >= 512 * 800) {
|
||||
field_filename.set_text("File: err, too long"); // should stream, but not in this scope YET
|
||||
return false;
|
||||
}*/
|
||||
field_filename.set_text("File: " + new_file_path.string());
|
||||
filename = new_file_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipperTxView::stop() {
|
||||
if (is_running && replay_thread) replay_thread.reset();
|
||||
is_running = false;
|
||||
ready_signal = false;
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_START]);
|
||||
}
|
||||
|
||||
bool FlipperTxView::start() {
|
||||
if (filename.empty()) return false;
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.enable();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_STOP]);
|
||||
// start thread
|
||||
replay_thread = std::make_unique<FlipperPlayThread>(
|
||||
filename,
|
||||
1024, 4,
|
||||
&ready_signal,
|
||||
submeta.value().protocol,
|
||||
submeta.value().te,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
is_running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipperTxView::on_tx_progress(const bool done) {
|
||||
if (done) {
|
||||
if (is_running) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlipperTxView::~FlipperTxView() {
|
||||
stop();
|
||||
}
|
||||
|
||||
FlipperPlayThread::FlipperPlayThread(
|
||||
std::filesystem::path filename,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
bool* ready_signal,
|
||||
FlipperProto proto,
|
||||
uint16_t te,
|
||||
std::function<void(uint32_t return_code)> terminate_callback)
|
||||
: config{read_size, buffer_count},
|
||||
filename{filename},
|
||||
ready_sig{ready_signal},
|
||||
proto{proto},
|
||||
te{te},
|
||||
terminate_callback{std::move(terminate_callback)} {
|
||||
thread = chThdCreateFromHeap(NULL, 6 * 1024, NORMALPRIO + 10, FlipperPlayThread::static_fn, this);
|
||||
}
|
||||
|
||||
FlipperPlayThread::~FlipperPlayThread() {
|
||||
if (thread) {
|
||||
chThdTerminate(thread);
|
||||
if (thread->p_refs) chThdWait(thread);
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
msg_t FlipperPlayThread::static_fn(void* arg) {
|
||||
auto obj = static_cast<FlipperPlayThread*>(arg);
|
||||
const auto return_code = obj->run();
|
||||
if (obj->terminate_callback) {
|
||||
obj->terminate_callback(return_code);
|
||||
}
|
||||
chThdExit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t FlipperPlayThread::run() {
|
||||
BasebandReplay replay{&config};
|
||||
BufferExchange buffers{&config};
|
||||
StreamBuffer* prefill_buffer{nullptr};
|
||||
int32_t read_result = 0;
|
||||
// assume it is ok, since prev we checked it.
|
||||
// open the sub file
|
||||
File f; // don't worry, destructor will close it.
|
||||
auto error = f.open(filename);
|
||||
if (error) return READ_ERROR;
|
||||
// seek to the first data
|
||||
if (proto == FLIPPER_PROTO_RAW)
|
||||
seek_flipper_raw_first_data(f);
|
||||
else {
|
||||
seek_flipper_binraw_first_data(f);
|
||||
}
|
||||
// Wait for FIFOs to be allocated in baseband
|
||||
// Wait for _view to tell us that the buffers are ready
|
||||
while (!(*ready_sig) && !chThdShouldTerminate()) {
|
||||
chThdSleep(100);
|
||||
};
|
||||
uint8_t readall = 0;
|
||||
int32_t endsignals[3] = {0, 42069, 613379}; // unlikely a valid data
|
||||
// While empty buffers fifo is not empty...
|
||||
while (!buffers.empty() && !chThdShouldTerminate()) {
|
||||
prefill_buffer = buffers.get_prefill();
|
||||
|
||||
if (prefill_buffer == nullptr) {
|
||||
buffers.put_app(prefill_buffer);
|
||||
} else {
|
||||
size_t c = 0;
|
||||
for (c = 0; c < config.read_size / 4;) {
|
||||
read_result = 0;
|
||||
if (0 == readall) {
|
||||
if (proto == FLIPPER_PROTO_RAW) {
|
||||
auto data = read_flipper_raw_next_data(f);
|
||||
if (!data.is_valid()) {
|
||||
((int32_t*)prefill_buffer->data())[c] = endsignals[++readall];
|
||||
} else {
|
||||
read_result = data.value();
|
||||
((int32_t*)prefill_buffer->data())[c] = read_result;
|
||||
}
|
||||
c++;
|
||||
|
||||
} else { // BINRAW
|
||||
auto data = read_flipper_binraw_next_data(f);
|
||||
if (!data.is_valid())
|
||||
readall = 1;
|
||||
else {
|
||||
read_result = data.value();
|
||||
// add 8 data
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
((int32_t*)prefill_buffer->data())[c + bit] = ((get_flipper_binraw_bitvalue(read_result, (7 - bit)) ? 1 : -1) * te);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
((int32_t*)prefill_buffer->data())[c] = endsignals[readall];
|
||||
if (readall == 1) readall++;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
prefill_buffer->set_size(config.read_size);
|
||||
buffers.put(prefill_buffer);
|
||||
}
|
||||
};
|
||||
baseband::set_fifo_data(nullptr);
|
||||
if (readall) return END_OF_FILE;
|
||||
|
||||
while (!chThdShouldTerminate()) {
|
||||
auto buffer = buffers.get();
|
||||
int32_t read_result = 0;
|
||||
for (size_t d = 0; d < buffer->capacity() / 4; d++) {
|
||||
read_result = -133469;
|
||||
if (!readall) {
|
||||
if (proto == FLIPPER_PROTO_RAW) {
|
||||
auto data = read_flipper_raw_next_data(f);
|
||||
if (!data.is_valid()) {
|
||||
readall = 1;
|
||||
} else {
|
||||
read_result = data.value();
|
||||
}
|
||||
} else {
|
||||
auto data = read_flipper_binraw_next_data(f);
|
||||
if (!data.is_valid())
|
||||
readall = 1;
|
||||
else {
|
||||
read_result = data.value();
|
||||
// add 8 data
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
((int32_t*)prefill_buffer->data())[d + bit] = ((get_flipper_binraw_bitvalue(read_result, (7 - bit)) ? 1 : -1) * te);
|
||||
}
|
||||
d += 7;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (readall >= 1 && readall <= 2) {
|
||||
read_result = endsignals[readall++];
|
||||
}
|
||||
((int32_t*)buffer->data())[d] = read_result;
|
||||
}
|
||||
buffer->set_size(buffer->capacity());
|
||||
buffers.put(buffer);
|
||||
if (readall == 3) return END_OF_FILE;
|
||||
}
|
||||
return TERMINATED;
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::flippertx
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_flippertx_H__
|
||||
#define __UI_flippertx_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "flipper_subfile.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
class FlipperPlayThread;
|
||||
class FlipperTxView : public View {
|
||||
public:
|
||||
FlipperTxView(NavigationView& nav);
|
||||
~FlipperTxView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "FlipperTx";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
nav_};
|
||||
TransmitterView2 tx_view{
|
||||
{11 * 8, 0 * 16},
|
||||
/*short_ui*/ true};
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_flippertx", app_settings::Mode::TX};
|
||||
|
||||
TextField field_filename{
|
||||
{0, 2 * 16, 300, 1 * 8},
|
||||
"File: -"};
|
||||
|
||||
Button button_startstop{
|
||||
{1, 6 * 16, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_START]};
|
||||
|
||||
Button button_browse{
|
||||
{1, 3 * 16 + 3, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_BROWSE]};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
void set_ready();
|
||||
void on_tx_progress(const bool done);
|
||||
bool on_file_changed(std::filesystem::path new_file_path);
|
||||
|
||||
std::filesystem::path filename = {};
|
||||
FlipperProto proto = FLIPPER_PROTO_UNSUPPORTED;
|
||||
FlipperPreset preset = FLIPPER_PRESET_UNK;
|
||||
uint16_t te = 0; // for binraw
|
||||
uint32_t binraw_bit_count = 0;
|
||||
bool ready_signal = false;
|
||||
|
||||
std::unique_ptr<FlipperPlayThread> replay_thread{};
|
||||
Optional<flippersub_metadata> submeta{};
|
||||
|
||||
const std::filesystem::path subghz_dir = u"subghz";
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.done);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
}
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_done{
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
// const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
(void)p;
|
||||
// stop(); //don't stop for now, stop when i get the tx finished msg
|
||||
}};
|
||||
};
|
||||
|
||||
struct BasebandReplay {
|
||||
BasebandReplay(ReplayConfig* const config) {
|
||||
baseband::replay_start(config);
|
||||
}
|
||||
|
||||
~BasebandReplay() {
|
||||
// baseband::replay_stop(); // wont, since we need to send out that is in the buffer
|
||||
}
|
||||
};
|
||||
|
||||
class FlipperPlayThread {
|
||||
public:
|
||||
FlipperPlayThread(
|
||||
std::filesystem::path,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
bool* ready_signal,
|
||||
FlipperProto proto,
|
||||
uint16_t te,
|
||||
std::function<void(uint32_t return_code)> terminate_callback);
|
||||
~FlipperPlayThread();
|
||||
|
||||
FlipperPlayThread(const FlipperPlayThread&) = delete;
|
||||
FlipperPlayThread(FlipperPlayThread&&) = delete;
|
||||
FlipperPlayThread& operator=(const FlipperPlayThread&) = delete;
|
||||
FlipperPlayThread& operator=(FlipperPlayThread&&) = delete;
|
||||
|
||||
const ReplayConfig& state() const {
|
||||
return config;
|
||||
};
|
||||
|
||||
enum FlipperPlayThread_return {
|
||||
READ_ERROR = 0,
|
||||
END_OF_FILE,
|
||||
TERMINATED
|
||||
};
|
||||
|
||||
private:
|
||||
ReplayConfig config;
|
||||
std::filesystem::path filename;
|
||||
bool* ready_sig;
|
||||
FlipperProto proto;
|
||||
uint16_t te;
|
||||
std::function<void(uint32_t return_code)> terminate_callback;
|
||||
Thread* thread{nullptr};
|
||||
|
||||
static msg_t static_fn(void* arg);
|
||||
|
||||
uint32_t run();
|
||||
};
|
||||
}; // namespace ui::external_app::flippertx
|
||||
|
||||
#endif /*__UI_flippertx_H__*/
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_font_viewer.application_information"),
|
||||
},
|
||||
/*.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
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_foxhunt_rx.application_information"),
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_am_audio */ {'P', 'A', 'M', 'A'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_gpssim.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_jammer.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_keyfob.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_keyfob */ {'P', 'O', 'O', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
+1
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_lcr.application_information"), used))
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk */ {'P', 'A', 'F', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
+1
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_lge.application_information"), used))
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_morse_tx.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -76,6 +76,7 @@ __attribute__((section(".external_app.app_nrf_rx.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_nrf_rx */ {'P', 'N', 'R', 'R'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* 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_ook_editor.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::ook_editor {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<OOKEditorAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::ook_editor
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_ook_editor.application_information"), used)) application_information_t _application_information_ook_editor = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::ook_editor::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "OOKEditor",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x30,
|
||||
0x0C,
|
||||
0x30,
|
||||
0x0C,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xE0,
|
||||
0x07,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ook */ {'P', 'O', 'O', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* 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_ook_editor.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui::external_app::ook_editor {
|
||||
|
||||
// give focus to set button
|
||||
void OOKEditorAppView::focus() {
|
||||
button_set.focus();
|
||||
}
|
||||
|
||||
// update internal ook_data with GUI values
|
||||
void OOKEditorAppView::update_ook_data_from_app() {
|
||||
ook_data.frequency = field_frequency.value();
|
||||
ook_data.sample_rate = field_sample_rate.selected_index_value();
|
||||
ook_data.symbol_rate = field_symbol_rate.value();
|
||||
ook_data.repeat = field_repeat.value();
|
||||
ook_data.pause_symbol_duration = field_pause_symbol_duration.value();
|
||||
}
|
||||
|
||||
// `start_tx` method: Configures and begins OOK data transmission with a specific message.
|
||||
void OOKEditorAppView::start_tx() {
|
||||
// check if there is a payload
|
||||
if (ook_data.payload.length() < 1) {
|
||||
text_app_status.set("Error: no payload to tx !!");
|
||||
return;
|
||||
}
|
||||
progressbar.set_max(field_repeat.value()); // Size the progress bar accordingly to the number of repeat
|
||||
is_transmitting = true; // set transmitting flag
|
||||
button_send_stop.set_text(LanguageHelper::currentMessages[LANG_STOP]); // set button back to initial "start" state
|
||||
start_ook_file_tx(ook_data); // start the transmission
|
||||
}
|
||||
|
||||
// `stop_tx` method: Stops the transmission and resets the progress bar.
|
||||
void OOKEditorAppView::stop_tx() {
|
||||
is_transmitting = false; // set transmitting flag
|
||||
stop_ook_file_tx(); // stop transmission
|
||||
progressbar.set_value(0); // Reset progress bar to 0
|
||||
button_send_stop.set_text(LanguageHelper::currentMessages[LANG_START]); // set button back to initial "start" state
|
||||
}
|
||||
|
||||
// `on_file_changed` method: Called when a new file is loaded; parses file data into variables
|
||||
void OOKEditorAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
ook_data.payload.clear(); // Clear previous payload content
|
||||
if (!read_ook_file(new_file_path, ook_data)) {
|
||||
text_app_status.set("Error loading " + new_file_path.filename().string());
|
||||
return;
|
||||
}
|
||||
field_frequency.set_value(ook_data.frequency);
|
||||
field_symbol_rate.set_value(ook_data.symbol_rate);
|
||||
field_repeat.set_value(ook_data.repeat);
|
||||
field_pause_symbol_duration.set_value(ook_data.pause_symbol_duration);
|
||||
field_sample_rate.set_by_value(ook_data.sample_rate);
|
||||
text_payload.set(ook_data.payload);
|
||||
button_send_stop.focus();
|
||||
text_app_status.set("Loaded: " + new_file_path.filename().string());
|
||||
}
|
||||
|
||||
// `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 (done) {
|
||||
stop_tx(); // Stop transmission when progress reaches maximum
|
||||
}
|
||||
}
|
||||
|
||||
// `draw_waveform` method: Draws the waveform on the UI based on the payload data
|
||||
void OOKEditorAppView::draw_waveform() {
|
||||
// Padding reason:
|
||||
// In real-world scenarios, the signal would always start low and return low after turning off the radio.
|
||||
// `waveform_buffer` only controls drawing; the actual send logic is handled by frame_fragments.
|
||||
size_t length = ook_data.payload.length();
|
||||
|
||||
// Ensure waveform length does not exceed buffer size
|
||||
if (length + (PADDING_LEFT + PADDING_RIGHT) >= WAVEFORM_BUFFER_SIZE) {
|
||||
length = WAVEFORM_BUFFER_SIZE - (PADDING_LEFT + PADDING_RIGHT);
|
||||
}
|
||||
|
||||
// Left padding
|
||||
for (size_t i = 0; i < PADDING_LEFT; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
// Draw the actual waveform
|
||||
for (size_t n = 0; n < length; n++) {
|
||||
waveform_buffer[n + PADDING_LEFT] = (ook_data.payload[n] == '0') ? 0 : 1;
|
||||
}
|
||||
|
||||
// Right padding
|
||||
for (size_t i = length + PADDING_LEFT; i < WAVEFORM_BUFFER_SIZE; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
waveform.set_length(length + PADDING_LEFT + PADDING_RIGHT);
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
// build a new path+file, make some tests, call save_ook_to_file
|
||||
void OOKEditorAppView::on_save_file(const std::string value) {
|
||||
// check if there is a payload, else Error
|
||||
if (ook_data.payload.length() < 1) {
|
||||
text_app_status.set("Err: can't save, no payload !");
|
||||
return;
|
||||
}
|
||||
ensure_directory(ook_editor_dir);
|
||||
auto new_path = ook_editor_dir / value + ".OOK";
|
||||
if (save_ook_to_file(new_path)) {
|
||||
text_app_status.set("Saved to " + new_path.string());
|
||||
} else {
|
||||
text_app_status.set("Error saving " + new_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
// update ook_data from GUI and save
|
||||
bool OOKEditorAppView::save_ook_to_file(const std::filesystem::path& path) {
|
||||
update_ook_data_from_app();
|
||||
return save_ook_file(ook_data, path);
|
||||
}
|
||||
|
||||
// Destructor for `OOKEditorAppView`: Disables the transmitter and shuts down the baseband
|
||||
OOKEditorAppView::~OOKEditorAppView() {
|
||||
stop_ook_file_tx();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
// Constructor for `OOKEditorAppView`: Sets up the app view and initializes UI elements
|
||||
OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
// load OOK baseband
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook);
|
||||
|
||||
// add all the widgets
|
||||
add_children({&field_frequency,
|
||||
&tx_view,
|
||||
&button_send_stop,
|
||||
&label_step,
|
||||
&field_step,
|
||||
&label_sample_rate,
|
||||
&field_sample_rate,
|
||||
&label_symbol_rate,
|
||||
&field_symbol_rate,
|
||||
&label_symbol_rate_unit,
|
||||
&text_payload,
|
||||
&button_set,
|
||||
&progressbar,
|
||||
&label_repeat,
|
||||
&field_repeat,
|
||||
&label_pause_symbol_duration,
|
||||
&field_pause_symbol_duration,
|
||||
&label_pause_symbol_duration_unit,
|
||||
&label_payload,
|
||||
&text_app_status,
|
||||
&label_waveform,
|
||||
&waveform,
|
||||
&button_open,
|
||||
&button_save});
|
||||
|
||||
// Initialize default values for controls
|
||||
field_symbol_rate.set_value(100);
|
||||
field_pause_symbol_duration.set_value(100);
|
||||
field_repeat.set_value(4);
|
||||
|
||||
// Configure open ook file button
|
||||
button_open.on_select = [this](Button&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".OOK");
|
||||
ensure_directory(ook_editor_dir);
|
||||
open_view->push_dir(ook_editor_dir);
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
// Postpone `on_file_changed` call until `FileLoadView` is closed
|
||||
nav_.set_on_pop([this, new_file_path]() {
|
||||
on_file_changed(new_file_path);
|
||||
button_send_stop.focus();
|
||||
draw_waveform();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Configure save to ook file button
|
||||
button_save.on_select = [this, &nav](const ui::Button&) {
|
||||
outputFileBuffer = "";
|
||||
text_prompt(
|
||||
nav,
|
||||
outputFileBuffer,
|
||||
64,
|
||||
[this](std::string& buffer) {
|
||||
on_save_file(buffer);
|
||||
});
|
||||
};
|
||||
|
||||
// clean out loaded file name if field is changed
|
||||
field_symbol_rate.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_repeat.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_pause_symbol_duration.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_sample_rate.on_change = [this](size_t, int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
|
||||
// setting up FrequencyField
|
||||
field_frequency.set_value(ook_editor_tx_freq);
|
||||
|
||||
// clean out loaded file name if field is changed, save ook_editor_tx_freq
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
ook_editor_tx_freq = f;
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
|
||||
// allow typing frequency number
|
||||
field_frequency.on_edit = [this]() {
|
||||
auto freq_view = nav_.push<FrequencyKeypadView>(field_frequency.value());
|
||||
freq_view->on_changed = [this](rf::Frequency f) {
|
||||
field_frequency.set_value(f);
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
};
|
||||
|
||||
// allow different steps on symbol_rate and pause_symbol_duration
|
||||
field_step.on_change = [this](size_t, int32_t value) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
field_symbol_rate.set_step(value);
|
||||
field_pause_symbol_duration.set_step(value);
|
||||
};
|
||||
|
||||
// Configure button to manually set payload through text input
|
||||
button_set.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
ook_data.payload,
|
||||
100,
|
||||
[this](std::string& s) {
|
||||
text_payload.set(s);
|
||||
draw_waveform();
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
});
|
||||
};
|
||||
|
||||
// Configure button to start or stop the transmission
|
||||
button_send_stop.on_select = [this](Button&) {
|
||||
if (!is_transmitting) {
|
||||
update_ook_data_from_app();
|
||||
start_tx(); // Begin transmission
|
||||
} else {
|
||||
stop_tx();
|
||||
}
|
||||
};
|
||||
|
||||
// initial waveform drawing (should be a single line)
|
||||
draw_waveform();
|
||||
}
|
||||
} // namespace ui::external_app::ook_editor
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_OOK_EDITOR_H__
|
||||
#define __UI_OOK_EDITOR_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "ook_file.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::ook_editor {
|
||||
|
||||
#define PADDING_LEFT 1 // waveform padding
|
||||
#define PADDING_RIGHT 1 // waveform padding
|
||||
#define OOK_SAMPLERATE_DEFAULT 2280000U // Set the default Sample Rate
|
||||
#define TRANSMISSION_FREQUENCY_DEFAULT 433920000U // Sets the default transmission frequency (27 MHz).
|
||||
#define WAVEFORM_BUFFER_SIZE 550
|
||||
|
||||
class OOKEditorAppView : public View {
|
||||
public:
|
||||
void focus() override;
|
||||
OOKEditorAppView(NavigationView& nav);
|
||||
|
||||
~OOKEditorAppView();
|
||||
|
||||
std::string title() const override {
|
||||
return "OOKEditor";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_; // Reference to the navigation system.
|
||||
uint32_t progress = 0; // Stores the current transmission progress.
|
||||
int16_t waveform_buffer[WAVEFORM_BUFFER_SIZE]; // Buffer for waveform data.
|
||||
bool is_transmitting = false; // State of transmission.
|
||||
rf::Frequency ook_editor_tx_freq{24000000}; // last used transmit frequency
|
||||
std::string outputFileBuffer{}; // buffer for output file
|
||||
ook_file_data ook_data = {0, 0, 0, 0, 0, ""}; // ook files handle
|
||||
|
||||
// draw current payload waveform
|
||||
void draw_waveform();
|
||||
|
||||
// update ook_data from GUI selection
|
||||
void update_ook_data_from_app();
|
||||
|
||||
// start transmission
|
||||
void start_tx();
|
||||
// stop transmission
|
||||
void stop_tx();
|
||||
|
||||
// Updates the transmission progress on the progress bar.
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
// Updates data when a new file is loaded.
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
|
||||
// Prepare a new std::filesystem::path from string value and call saveFile
|
||||
void on_save_file(const std::string value);
|
||||
|
||||
// Called by on_save_file to save the current OOK parameters to the file
|
||||
bool save_ook_to_file(const std::filesystem::path& path);
|
||||
|
||||
// Registers a message handler for transmission progress updates.
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress, // Transmission progress message ID.
|
||||
[this](const Message* const p) { // Callback to handle the message.
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}};
|
||||
|
||||
// Sets transmission frequency, bandwidth, and default sample rate for OOK.
|
||||
TxRadioState radio_state_{TRANSMISSION_FREQUENCY_DEFAULT, 1750000, OOK_SAMPLERATE_DEFAULT};
|
||||
|
||||
// Settings manager for app configuration.
|
||||
app_settings::SettingsManager settings_{
|
||||
"ook_editor",
|
||||
app_settings::Mode::TX,
|
||||
{{"ook_editor_tx_freq"sv, &ook_editor_tx_freq}}};
|
||||
|
||||
// UI components for frequency and transmitter view.
|
||||
FrequencyField field_frequency{{0 * 8, 0 * 16}};
|
||||
TransmitterView2 tx_view{{20 * 7, 0 * 16}, true};
|
||||
|
||||
// Labels for various fields such as sample rate and repeat count.
|
||||
Labels label_step{{{170, 20}, "Step:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_sample_rate{{{0, 20}, "SampleRate:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_symbol_rate{{{0, 40}, "SymbolRate:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_symbol_rate_unit{{{132, 40}, "/s", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_repeat{{{154, 40}, "Repeat:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_pause_symbol_duration{{{0, 60}, "PauseSymbol:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_pause_symbol_duration_unit{{{132, 60}, "us", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_payload{{{0, 80}, "Payload:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_waveform{{{0, 188}, "Waveform:", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
// Text field to display the various status message of the app
|
||||
Text text_app_status{{0, 160, 30 * 8, 16}, ""};
|
||||
|
||||
// OptionsField for selectable sample rates.
|
||||
OptionsField field_sample_rate{{96, 20}, 7, {{"250k", 250000U}, {"1M", 1000000U}, {"2M", 2000000U}, {"5M", 5000000U}, {"10M", 10000000U}, {"20M", 20000000U}}};
|
||||
// OptionsField for step symbol rates.
|
||||
OptionsField field_step{{210, 20}, 7, {{"1", 1}, {"10", 10}, {"100", 100}}};
|
||||
// Number fields for symbols, pause_symbol between repeat, and repeat count.
|
||||
NumberField field_symbol_rate{{96, 40}, 4, {0, 9999}, 1, '0', false};
|
||||
NumberField field_pause_symbol_duration{{96, 60}, 4, {0, 9999}, 1, '0', false};
|
||||
NumberField field_repeat{{210, 40}, 3, {1, 999}, 1, '0', false};
|
||||
|
||||
// Text field to display the payload data.
|
||||
Text text_payload{{0 * 8, 100, 30 * 8, 16}, ""};
|
||||
|
||||
// Buttons for setting configurations, opening files, and starting transmission.
|
||||
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
|
||||
Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]};
|
||||
Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]};
|
||||
Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]};
|
||||
|
||||
// Progress bar to display transmission progress.
|
||||
ProgressBar progressbar{{2 * 8, 250, 208, 16}};
|
||||
|
||||
// Waveform display using waveform buffer and yellow theme color.
|
||||
Waveform waveform{{0, 208, 240, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
|
||||
};
|
||||
|
||||
}; // namespace ui::external_app::ook_editor
|
||||
|
||||
#endif /*__UI_OOK_EDITOR_H__*/
|
||||
+3
-2
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<OokBruteView>();
|
||||
nav.push<OOKBruteView>();
|
||||
}
|
||||
} // namespace ui::external_app::ookbrute
|
||||
|
||||
@@ -38,7 +38,7 @@ __attribute__((section(".external_app.app_ookbrute.application_information"), us
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "OokBrute",
|
||||
/*.app_name = */ "OOKBrute",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_ookbrute.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ook */ {'P', 'O', 'O', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
+10
-10
@@ -31,11 +31,11 @@ using namespace ui;
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
|
||||
void OokBruteView::focus() {
|
||||
void OOKBruteView::focus() {
|
||||
button_startstop.focus();
|
||||
}
|
||||
|
||||
OokBruteView::OokBruteView(NavigationView& nav)
|
||||
OOKBruteView::OOKBruteView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({
|
||||
&button_startstop,
|
||||
@@ -69,7 +69,7 @@ OokBruteView::OokBruteView(NavigationView& nav)
|
||||
update_start_stop(0);
|
||||
}
|
||||
|
||||
void OokBruteView::update_start_stop(uint32_t proto) {
|
||||
void OOKBruteView::update_start_stop(uint32_t proto) {
|
||||
uint8_t bits = 12;
|
||||
switch (proto) {
|
||||
default:
|
||||
@@ -91,7 +91,7 @@ void OokBruteView::update_start_stop(uint32_t proto) {
|
||||
field_stop.set_value(max);
|
||||
}
|
||||
|
||||
void OokBruteView::validate_start_stop() {
|
||||
void OOKBruteView::validate_start_stop() {
|
||||
if (field_start.value() > field_stop.value()) {
|
||||
field_start.set_value(field_stop.value());
|
||||
}
|
||||
@@ -100,7 +100,7 @@ void OokBruteView::validate_start_stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void OokBruteView::generate_packet() {
|
||||
void OOKBruteView::generate_packet() {
|
||||
uint32_t protocol = options_atkmode.selected_index_value();
|
||||
uint8_t byte = 0;
|
||||
size_t bitstream_length = 0;
|
||||
@@ -210,13 +210,13 @@ void OokBruteView::generate_packet() {
|
||||
0);
|
||||
}
|
||||
|
||||
void OokBruteView::stop() {
|
||||
void OOKBruteView::stop() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_START]);
|
||||
}
|
||||
|
||||
void OokBruteView::start() {
|
||||
void OOKBruteView::start() {
|
||||
counter = field_start.value();
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.enable();
|
||||
@@ -224,7 +224,7 @@ void OokBruteView::start() {
|
||||
generate_packet();
|
||||
}
|
||||
|
||||
void OokBruteView::on_tx_progress(const bool done) {
|
||||
void OOKBruteView::on_tx_progress(const bool done) {
|
||||
if (done) {
|
||||
if (is_running) {
|
||||
counter++;
|
||||
@@ -238,7 +238,7 @@ void OokBruteView::on_tx_progress(const bool done) {
|
||||
}
|
||||
}
|
||||
|
||||
OokBruteView::~OokBruteView() {
|
||||
OOKBruteView::~OOKBruteView() {
|
||||
is_running = false;
|
||||
stop();
|
||||
}
|
||||
@@ -250,4 +250,4 @@ OokBruteView::~OokBruteView() {
|
||||
https://web.archive.org/web/20230331125843/https://phreakerclub.com/447
|
||||
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
+4
-4
@@ -24,15 +24,15 @@ namespace ui::external_app::ookbrute {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
class OokBruteView : public View {
|
||||
class OOKBruteView : public View {
|
||||
public:
|
||||
OokBruteView(NavigationView& nav);
|
||||
~OokBruteView();
|
||||
OOKBruteView(NavigationView& nav);
|
||||
~OOKBruteView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "OokBrute";
|
||||
return "OOKBrute";
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_protoview.application_information"), u
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_protoview */ {'P', 'P', 'V', 'W'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_random_password.application_informatio
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* 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_remote.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::remote {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<RemoteAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::remote
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_remote.application_information"), used)) application_information_t _application_information_remote = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::remote::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Remote",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x30,
|
||||
0x0C,
|
||||
0x30,
|
||||
0x0C,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xE0,
|
||||
0x07,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::HOME,
|
||||
/*.desired_menu_position = */ 4,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_replay */ {'P', 'R', 'E', 'P'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
Vendored
+25
-26
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include "ui_remote.hpp"
|
||||
|
||||
#include "binder.hpp"
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
@@ -34,11 +33,11 @@
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui::external_app::remote {
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
|
||||
static constexpr uint8_t text_edit_max = 30;
|
||||
|
||||
/* RemoteEntryModel **************************************/
|
||||
@@ -309,9 +308,9 @@ void RemoteEntryEditView::load_path(std::filesystem::path&& path) {
|
||||
entry_.metadata = {transmitter_model.target_frequency(), 500'000};
|
||||
}
|
||||
|
||||
/* RemoteView ********************************************/
|
||||
/* RemoteAppView ********************************************/
|
||||
|
||||
RemoteView::RemoteView(
|
||||
RemoteAppView::RemoteAppView(
|
||||
NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
@@ -365,27 +364,27 @@ RemoteView::RemoteView(
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
RemoteView::RemoteView(NavigationView& nav, fs::path path)
|
||||
: RemoteView(nav) {
|
||||
RemoteAppView::RemoteAppView(NavigationView& nav, fs::path path)
|
||||
: RemoteAppView(nav) {
|
||||
load_remote(std::move(path));
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
RemoteView::~RemoteView() {
|
||||
RemoteAppView::~RemoteAppView() {
|
||||
stop();
|
||||
baseband::shutdown();
|
||||
|
||||
save_remote(/*show_error*/ false);
|
||||
}
|
||||
|
||||
void RemoteView::focus() {
|
||||
void RemoteAppView::focus() {
|
||||
if (model_.entries.empty())
|
||||
button_add.focus();
|
||||
else
|
||||
buttons_[0]->focus();
|
||||
}
|
||||
|
||||
void RemoteView::create_buttons() {
|
||||
void RemoteAppView::create_buttons() {
|
||||
// Handler callbacks.
|
||||
auto handle_send = [this](RemoteButton& btn) {
|
||||
if (btn.entry()->path.empty())
|
||||
@@ -420,7 +419,7 @@ void RemoteView::create_buttons() {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteView::reset_buttons() {
|
||||
void RemoteAppView::reset_buttons() {
|
||||
// Whever the model's entries instance is invalidated,
|
||||
// all the pointers in the buttons will end up dangling.
|
||||
// TODO: This is pretty lame. Could maybe static alloc?
|
||||
@@ -428,7 +427,7 @@ void RemoteView::reset_buttons() {
|
||||
btn->set_entry(nullptr);
|
||||
}
|
||||
|
||||
void RemoteView::refresh_ui() {
|
||||
void RemoteAppView::refresh_ui() {
|
||||
field_title.set_text(model_.name);
|
||||
field_filename.set_text(remote_path_.stem().string());
|
||||
|
||||
@@ -441,7 +440,7 @@ void RemoteView::refresh_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteView::add_button() {
|
||||
void RemoteAppView::add_button() {
|
||||
if (model_.entries.size() >= max_buttons)
|
||||
return;
|
||||
|
||||
@@ -454,7 +453,7 @@ void RemoteView::add_button() {
|
||||
set_needs_save();
|
||||
}
|
||||
|
||||
void RemoteView::edit_button(RemoteButton& btn) {
|
||||
void RemoteAppView::edit_button(RemoteButton& btn) {
|
||||
// Don't let replay thread read the model while editing.
|
||||
stop();
|
||||
|
||||
@@ -471,7 +470,7 @@ void RemoteView::edit_button(RemoteButton& btn) {
|
||||
};
|
||||
}
|
||||
|
||||
void RemoteView::send_button(RemoteButton& btn) {
|
||||
void RemoteAppView::send_button(RemoteButton& btn) {
|
||||
// TODO: If this is called while is_sending() == true,
|
||||
// it just stops and doesn't start the new button?
|
||||
|
||||
@@ -510,14 +509,14 @@ void RemoteView::send_button(RemoteButton& btn) {
|
||||
});
|
||||
}
|
||||
|
||||
void RemoteView::stop() {
|
||||
void RemoteAppView::stop() {
|
||||
// This terminates the underlying chThread.
|
||||
replay_thread_.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal_ = false;
|
||||
}
|
||||
|
||||
void RemoteView::new_remote() {
|
||||
void RemoteAppView::new_remote() {
|
||||
save_remote();
|
||||
init_remote();
|
||||
refresh_ui();
|
||||
@@ -526,7 +525,7 @@ void RemoteView::new_remote() {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void RemoteView::open_remote() {
|
||||
void RemoteAppView::open_remote() {
|
||||
auto open_view = nav_.push<FileLoadView>(".REM");
|
||||
open_view->push_dir(remotes_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
@@ -536,7 +535,7 @@ void RemoteView::open_remote() {
|
||||
};
|
||||
}
|
||||
|
||||
void RemoteView::init_remote() {
|
||||
void RemoteAppView::init_remote() {
|
||||
model_ = {"<Unnamed Remote>", {}};
|
||||
reset_buttons();
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
|
||||
@@ -546,14 +545,14 @@ void RemoteView::init_remote() {
|
||||
show_error("Couldn't make new remote file.");
|
||||
}
|
||||
|
||||
bool RemoteView::load_remote(fs::path&& path) {
|
||||
bool RemoteAppView::load_remote(fs::path&& path) {
|
||||
set_remote_path(std::move(path));
|
||||
set_needs_save(false);
|
||||
reset_buttons();
|
||||
return model_.load(remote_path_);
|
||||
}
|
||||
|
||||
void RemoteView::save_remote(bool show_errors) {
|
||||
void RemoteAppView::save_remote(bool show_errors) {
|
||||
if (!needs_save_)
|
||||
return;
|
||||
|
||||
@@ -564,7 +563,7 @@ void RemoteView::save_remote(bool show_errors) {
|
||||
set_needs_save(false);
|
||||
}
|
||||
|
||||
void RemoteView::rename_remote(const std::string& new_name) {
|
||||
void RemoteAppView::rename_remote(const std::string& new_name) {
|
||||
auto folder = remote_path_.parent_path();
|
||||
auto ext = remote_path_.extension();
|
||||
auto new_path = folder / new_name + ext;
|
||||
@@ -581,7 +580,7 @@ void RemoteView::rename_remote(const std::string& new_name) {
|
||||
set_remote_path(std::move(new_path));
|
||||
}
|
||||
|
||||
void RemoteView::handle_replay_thread_done(uint32_t return_code) {
|
||||
void RemoteAppView::handle_replay_thread_done(uint32_t return_code) {
|
||||
if (return_code == ReplayThread::END_OF_FILE) {
|
||||
if (check_loop.value() && current_btn_) {
|
||||
send_button(*current_btn_);
|
||||
@@ -598,15 +597,15 @@ void RemoteView::handle_replay_thread_done(uint32_t return_code) {
|
||||
stop();
|
||||
}
|
||||
|
||||
void RemoteView::set_remote_path(fs::path&& path) {
|
||||
void RemoteAppView::set_remote_path(fs::path&& path) {
|
||||
// Unfortunately, have to keep these two in sync because
|
||||
// settings doesn't know about fs::path.
|
||||
remote_path_ = std::move(path);
|
||||
settings_.remote_path = remote_path_.string();
|
||||
}
|
||||
|
||||
void RemoteView::show_error(const std::string& msg) const {
|
||||
void RemoteAppView::show_error(const std::string& msg) const {
|
||||
nav_.display_modal("Error", msg);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::remote
|
||||
Vendored
+8
-8
@@ -41,7 +41,7 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::remote {
|
||||
|
||||
/* Maps icon index to bitmap. */
|
||||
class RemoteIcons {
|
||||
@@ -257,14 +257,14 @@ class RemoteEntryEditView : public View {
|
||||
};
|
||||
|
||||
/* App that allows for buttons to be bound to captures for playback. */
|
||||
class RemoteView : public View {
|
||||
class RemoteAppView : public View {
|
||||
public:
|
||||
RemoteView(NavigationView& nav);
|
||||
RemoteView(NavigationView& nav, std::filesystem::path path);
|
||||
~RemoteView();
|
||||
RemoteAppView(NavigationView& nav);
|
||||
RemoteAppView(NavigationView& nav, std::filesystem::path path);
|
||||
~RemoteAppView();
|
||||
|
||||
RemoteView(const RemoteView&) = delete;
|
||||
RemoteView& operator=(const RemoteView&) = delete;
|
||||
RemoteAppView(const RemoteAppView&) = delete;
|
||||
RemoteAppView& operator=(const RemoteAppView&) = delete;
|
||||
|
||||
std::string title() const override { return "Remote"; };
|
||||
void focus() override;
|
||||
@@ -385,4 +385,4 @@ class RemoteView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::remote
|
||||
+13
-10
@@ -16,12 +16,13 @@ void initialize_app(NavigationView& nav) {
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_shoppingcart_lock.application_information"), used)) application_information_t _application_information_shoppingcart_lock = {
|
||||
(uint8_t*)0x00000000,
|
||||
ui::external_app::shoppingcart_lock::initialize_app,
|
||||
CURRENT_HEADER_VERSION,
|
||||
VERSION_MD5,
|
||||
"Cart Lock",
|
||||
{
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::shoppingcart_lock::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Cart Lock",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
@@ -55,9 +56,11 @@ __attribute__((section(".external_app.app_shoppingcart_lock.application_informat
|
||||
0x7E,
|
||||
0x00,
|
||||
},
|
||||
ui::Color::red().v,
|
||||
app_location_t::UTILITIES,
|
||||
{'P', 'A', 'T', 'X'},
|
||||
0x00000000,
|
||||
/*.icon_color = */ ui::Color::red().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'T', 'X'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_spainter.application_information"), us
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_spainter */ {'P', 'S', 'P', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_sstvtx.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_sstvtx */ {'P', 'S', 'T', 'X'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_tetris.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_tpmsrx.application_information"), used
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_tpms */ {'P', 'T', 'P', 'M'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -75,6 +75,7 @@ __attribute__((section(".external_app.app_wardrivemap.application_information"),
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
|
||||
@@ -48,3 +48,4 @@ const std::filesystem::path splash_dir = u"SPLASH";
|
||||
const std::filesystem::path sstv_dir = u"SSTV";
|
||||
const std::filesystem::path wav_dir = u"WAV";
|
||||
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
|
||||
const std::filesystem::path ook_editor_dir = u"OOKFILES";
|
||||
|
||||
@@ -50,5 +50,6 @@ extern const std::filesystem::path splash_dir;
|
||||
extern const std::filesystem::path sstv_dir;
|
||||
extern const std::filesystem::path wav_dir;
|
||||
extern const std::filesystem::path whipcalc_dir;
|
||||
extern const std::filesystem::path ook_editor_dir;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
|
||||
@@ -29,15 +29,16 @@
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
const std::string_view filetype_name = "Filetype"sv;
|
||||
const std::string_view frequency_name = "Frequency"sv;
|
||||
const std::string_view latitude_name_old = "Latitute"sv;
|
||||
const std::string_view longitude_name_old = "Longitude"sv;
|
||||
const std::string_view latitude_name = "Lat"sv;
|
||||
const std::string_view longitude_name = "Lon"sv;
|
||||
const std::string_view protocol_name = "Protocol"sv;
|
||||
const std::string_view preset_name = "Preset"sv;
|
||||
const std::string_view te_name = "TE"sv; // only in BinRAW
|
||||
const std::string filetype_name = "Filetype";
|
||||
const std::string frequency_name = "Frequency";
|
||||
const std::string latitude_name_old = "Latitute";
|
||||
const std::string longitude_name_old = "Longitude";
|
||||
const std::string latitude_name = "Lat";
|
||||
const std::string longitude_name = "Lon";
|
||||
const std::string protocol_name = "Protocol";
|
||||
const std::string preset_name = "Preset";
|
||||
const std::string te_name = "TE"; // only in BinRAW
|
||||
const std::string bit_count_name = "Bit"; // for us, only in BinRAW
|
||||
|
||||
/*
|
||||
Filetype: Flipper SubGhz Key File
|
||||
@@ -72,39 +73,54 @@ raw_data- positive: carrier for n time, negative: no carrier for n time. (us)
|
||||
Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
|
||||
File f;
|
||||
auto error = f.open(path);
|
||||
|
||||
if (error)
|
||||
return {};
|
||||
|
||||
flippersub_metadata metadata{};
|
||||
|
||||
auto reader = FileLineReader(f);
|
||||
for (const auto& line : reader) {
|
||||
auto cols = split_string(line, ':');
|
||||
|
||||
if (cols.size() != 2)
|
||||
char ch = 0;
|
||||
std::string line = "";
|
||||
auto fr = f.read(&ch, 1);
|
||||
while (!fr.is_error() && fr.value() > 0) {
|
||||
if (line.length() < 130 && ch != '\n') line += ch;
|
||||
if (ch != '\n') {
|
||||
fr = f.read(&ch, 1);
|
||||
continue;
|
||||
}
|
||||
auto it = line.find(':', 0);
|
||||
if (it == std::string::npos) {
|
||||
fr = f.read(&ch, 1);
|
||||
continue; // Bad line.
|
||||
if (cols[1].length() <= 1) continue;
|
||||
std::string fixed = cols[1].data() + 1;
|
||||
}
|
||||
std::string fixed = line.data() + it + 1;
|
||||
fixed = trim(fixed);
|
||||
if (cols[0] == filetype_name) {
|
||||
std::string head = line.substr(0, it);
|
||||
line = "";
|
||||
|
||||
if (fixed.length() <= 1) {
|
||||
fr = f.read(&ch, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (head == filetype_name) {
|
||||
if (fixed != "Flipper SubGhz Key File" && fixed != "Flipper SubGhz RAW File") return {}; // not supported
|
||||
} else if (cols[0] == frequency_name)
|
||||
} else if (head == frequency_name)
|
||||
parse_int(fixed, metadata.center_frequency);
|
||||
else if (cols[0] == latitude_name)
|
||||
else if (head == latitude_name)
|
||||
parse_float_meta(fixed, metadata.latitude);
|
||||
else if (cols[0] == longitude_name)
|
||||
else if (head == longitude_name)
|
||||
parse_float_meta(fixed, metadata.longitude);
|
||||
else if (cols[0] == latitude_name_old)
|
||||
else if (head == latitude_name_old)
|
||||
parse_float_meta(fixed, metadata.latitude);
|
||||
else if (cols[0] == longitude_name_old)
|
||||
else if (head == longitude_name_old)
|
||||
parse_float_meta(fixed, metadata.longitude);
|
||||
else if (cols[0] == protocol_name) {
|
||||
else if (head == protocol_name) {
|
||||
if (fixed == "RAW") metadata.protocol = FLIPPER_PROTO_RAW;
|
||||
if (fixed == "BinRAW") metadata.protocol = FLIPPER_PROTO_BINRAW;
|
||||
} else if (cols[0] == te_name) {
|
||||
} else if (head == te_name) {
|
||||
metadata.te = atoi(fixed.c_str());
|
||||
} else if (cols[0] == preset_name) {
|
||||
} else if (head == bit_count_name) {
|
||||
metadata.binraw_bit_count = atol(fixed.c_str());
|
||||
} else if (head == preset_name) {
|
||||
if (fixed.find("FSK") != std::string::npos) {
|
||||
metadata.preset = FLIPPER_PRESET_2FSK;
|
||||
} else if (fixed.find("Ook") != std::string::npos) {
|
||||
@@ -112,12 +128,92 @@ Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
|
||||
} else if (fixed.find("Custom") != std::string::npos) {
|
||||
metadata.preset = FLIPPER_PRESET_CUSTOM;
|
||||
}
|
||||
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
fr = f.read(&ch, 1);
|
||||
}
|
||||
|
||||
f.close();
|
||||
if (metadata.center_frequency == 0) return {}; // Parse failed.
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
bool seek_flipper_raw_first_data(File& f) {
|
||||
f.seek(0);
|
||||
std::string chs = "";
|
||||
char ch;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue;
|
||||
if (ch == '\n') {
|
||||
chs = "";
|
||||
continue;
|
||||
};
|
||||
chs += ch;
|
||||
if (ch == 0) break;
|
||||
if (chs == "RAW_Data: ") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool seek_flipper_binraw_first_data(File& f, bool seekzero) {
|
||||
if (seekzero) f.seek(0);
|
||||
std::string chs = "";
|
||||
char ch;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue;
|
||||
if (ch == '\n') {
|
||||
chs = "";
|
||||
continue;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
if (chs == "Data_RAW: ") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<int32_t> read_flipper_raw_next_data(File& f) {
|
||||
// RAW_Data: 5832 -12188 130 -162
|
||||
std::string chs = "";
|
||||
char ch = 0;
|
||||
while (f.read(&ch, 1).is_ok()) {
|
||||
if (ch == '\r') continue; // should not present
|
||||
if ((ch == ' ') || ch == '\n') {
|
||||
if (chs == "RAW_Data:") {
|
||||
chs = "";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
}
|
||||
if (chs == "") return {};
|
||||
return atol(chs.c_str());
|
||||
}
|
||||
|
||||
Optional<uint8_t> read_flipper_binraw_next_data(File& f) {
|
||||
// Data_RAW: 02 10 84 BUT THERE ARE Bit_RAW lines to skip!
|
||||
std::string chs = "";
|
||||
char ch = 0;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue; // should not present
|
||||
if ((ch == ' ') || ch == '\n') {
|
||||
if (chs == "RAW_Data:") {
|
||||
chs = "";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
}
|
||||
if (chs == "") return {};
|
||||
return static_cast<uint8_t>(std::stoul(chs, nullptr, 16));
|
||||
}
|
||||
|
||||
bool get_flipper_binraw_bitvalue(uint8_t byte, uint8_t nthBit) {
|
||||
return (byte & (1 << nthBit)) != 0;
|
||||
}
|
||||
@@ -44,9 +44,15 @@ struct flippersub_metadata {
|
||||
FlipperProto protocol = FLIPPER_PROTO_UNSUPPORTED;
|
||||
FlipperPreset preset = FLIPPER_PRESET_UNK;
|
||||
uint16_t te = 0;
|
||||
uint32_t binraw_bit_count = 0;
|
||||
};
|
||||
|
||||
Optional<flippersub_metadata> read_flippersub_file(const std::filesystem::path& path);
|
||||
bool seek_flipper_raw_first_data(File& f);
|
||||
bool seek_flipper_binraw_first_data(File& f, bool seekzero = true);
|
||||
Optional<int32_t> read_flipper_raw_next_data(File& f);
|
||||
Optional<uint8_t> read_flipper_binraw_next_data(File& f);
|
||||
bool get_flipper_binraw_bitvalue(uint8_t byte, uint8_t nthBit);
|
||||
|
||||
// Maybe sometime there will be a data part reader / converter
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@
|
||||
using option_t = ui::OptionsField::option_t;
|
||||
using options_t = ui::OptionsField::options_t;
|
||||
|
||||
extern options_t freqman_steps;
|
||||
extern const option_t* find_by_index(const options_t& options, freqman_index_t index);
|
||||
using option_db_t = std::pair<std::string_view, int32_t>;
|
||||
using options_db_t = std::vector<option_db_t>;
|
||||
|
||||
extern options_db_t freqman_steps;
|
||||
extern const option_db_t* find_by_index(const options_db_t& options, freqman_index_t index);
|
||||
|
||||
/* Option value lookup. */
|
||||
int32_t freqman_entry_get_step_value(freqman_index_t step) {
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace fs = std::filesystem;
|
||||
const std::filesystem::path freqman_extension{u".TXT"};
|
||||
|
||||
// NB: Don't include UI headers to keep this code unit testable.
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using option_t = std::pair<std::string_view, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
|
||||
options_t freqman_modulations = {
|
||||
@@ -194,27 +194,27 @@ bool operator==(const freqman_entry& lhs, const freqman_entry& rhs) {
|
||||
|
||||
std::string freqman_entry_get_modulation_string(freqman_index_t modulation) {
|
||||
if (auto opt = find_by_index(freqman_modulations, modulation))
|
||||
return opt->first;
|
||||
return (std::string)opt->first;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_bandwidth_string(freqman_index_t modulation, freqman_index_t bandwidth) {
|
||||
if (modulation < freqman_modulations.size()) {
|
||||
if (auto opt = find_by_index(freqman_bandwidths[modulation], bandwidth))
|
||||
return opt->first;
|
||||
return (std::string)opt->first;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_step_string(freqman_index_t step) {
|
||||
if (auto opt = find_by_index(freqman_steps, step))
|
||||
return opt->first;
|
||||
return (std::string)opt->first;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_step_string_short(freqman_index_t step) {
|
||||
if (auto opt = find_by_index(freqman_steps_short, step))
|
||||
return opt->first;
|
||||
return (std::string)opt->first;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* 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 "ook_file.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/*
|
||||
struct of an OOK file:
|
||||
|
||||
Frequency SampleRate SymbolRate Repeat PauseSymbolDuration Payload
|
||||
|
||||
-Frequency is in hertz
|
||||
-SampleRate is one of 250k, 1M, 2M, 5M , 10M ,20M
|
||||
-SymbolRate is the number of symbols /s
|
||||
-Repeat is the number of times we will repeat the payload
|
||||
-PauseSymbolDuration is the duration of the pause between repeat, in usec
|
||||
-Payload is the payload in form of a string of 0 and 1
|
||||
*/
|
||||
|
||||
bool read_ook_file(const std::filesystem::path& path, ook_file_data& ook_data) {
|
||||
File data_file;
|
||||
auto open_result = data_file.open(path);
|
||||
if (open_result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileLineReader reader(data_file);
|
||||
|
||||
for (const auto& line : reader) {
|
||||
// Split the line into segments to extract each value
|
||||
size_t first_space = line.find(' ');
|
||||
size_t second_space = line.find(' ', first_space + 1);
|
||||
size_t third_space = line.find(' ', second_space + 1);
|
||||
size_t fourth_space = line.find(' ', third_space + 1);
|
||||
size_t fifth_space = line.find(' ', fourth_space + 1);
|
||||
|
||||
// Extract each component of the line
|
||||
std::string frequency_str = line.substr(0, first_space);
|
||||
std::string sample_rate_str = line.substr(first_space + 1, second_space - first_space - 1);
|
||||
std::string symbol_rate_str = line.substr(second_space + 1, third_space - second_space - 1);
|
||||
std::string repeat_str = line.substr(third_space + 1, fourth_space - third_space - 1);
|
||||
std::string pause_symbol_duration_str = line.substr(fourth_space + 1, fifth_space - fourth_space - 1);
|
||||
std::string payload_data = line.substr(fifth_space + 1); // Extract binary payload as final value
|
||||
|
||||
// Convert and assign frequency
|
||||
ook_data.frequency = std::stoull(frequency_str);
|
||||
// Convert and assign symbol_rate
|
||||
ook_data.symbol_rate = static_cast<unsigned int>(atoi(symbol_rate_str.c_str()));
|
||||
// Convert and assign repeat count
|
||||
ook_data.repeat = static_cast<unsigned int>(atoi(repeat_str.c_str()));
|
||||
// Convert and assign pause_symbol_duration
|
||||
ook_data.pause_symbol_duration = static_cast<unsigned int>(atoi(pause_symbol_duration_str.c_str()));
|
||||
// Select sample rate based on value read from file
|
||||
if (sample_rate_str == "250k") {
|
||||
ook_data.sample_rate = 250000U;
|
||||
} else if (sample_rate_str == "1M") {
|
||||
ook_data.sample_rate = 1000000U;
|
||||
} else if (sample_rate_str == "2M") {
|
||||
ook_data.sample_rate = 2000000U;
|
||||
} else if (sample_rate_str == "5M") {
|
||||
ook_data.sample_rate = 5000000U;
|
||||
} else if (sample_rate_str == "10M") {
|
||||
ook_data.sample_rate = 10000000U;
|
||||
} else if (sample_rate_str == "20M") {
|
||||
ook_data.sample_rate = 20000000U;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// Update payload with binary data
|
||||
ook_data.payload = std::move(payload_data);
|
||||
// Process only the first line
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool save_ook_file(ook_file_data& ook_data, const std::filesystem::path& path) {
|
||||
// delete file if it exists
|
||||
delete_file(path);
|
||||
|
||||
// Attempt to open, if it can't be opened. Create new.
|
||||
auto src = std::make_unique<File>();
|
||||
auto error = src->open(path, false, true);
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string sample_rate_str;
|
||||
if (ook_data.sample_rate == 250000U) {
|
||||
sample_rate_str = "250k";
|
||||
} else if (ook_data.sample_rate == 1000000U) {
|
||||
sample_rate_str = "1M";
|
||||
} else if (ook_data.sample_rate == 2000000U) {
|
||||
sample_rate_str = "2M";
|
||||
} else if (ook_data.sample_rate == 5000000U) {
|
||||
sample_rate_str = "5M";
|
||||
} else if (ook_data.sample_rate == 10000000U) {
|
||||
sample_rate_str = "10M";
|
||||
} else if (ook_data.sample_rate == 20000000U) {
|
||||
sample_rate_str = "20M";
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write informations
|
||||
src->write_line(to_string_dec_uint(ook_data.frequency) + " " +
|
||||
sample_rate_str + " " +
|
||||
to_string_dec_uint(ook_data.symbol_rate) + " " +
|
||||
to_string_dec_uint(ook_data.repeat) + " " +
|
||||
to_string_dec_uint(ook_data.pause_symbol_duration) + " " +
|
||||
ook_data.payload);
|
||||
|
||||
// Close files
|
||||
src.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void start_ook_file_tx(ook_file_data& ook_data) {
|
||||
size_t bitstream_length = encoders::make_bitstream(const_cast<std::string&>(ook_data.payload)); // Convert the message into a bitstream
|
||||
|
||||
transmitter_model.set_target_frequency(ook_data.frequency); // Set target frequency
|
||||
transmitter_model.set_sampling_rate(ook_data.sample_rate); // Set the OOK sampling rate
|
||||
transmitter_model.set_baseband_bandwidth(1750000); // Set the baseband bandwidth
|
||||
transmitter_model.enable(); // Enable the transmitter
|
||||
|
||||
// Configure OOK data and transmission characteristics
|
||||
baseband::set_ook_data(
|
||||
bitstream_length, // Length of the bitstream to transmit
|
||||
ook_data.sample_rate / ook_data.symbol_rate, // Calculate symbol period based on repetition rate
|
||||
ook_data.repeat, // Set the number of times the whole bitstream is repeated
|
||||
ook_data.pause_symbol_duration // Set the pause_symbol between reps
|
||||
);
|
||||
}
|
||||
|
||||
void stop_ook_file_tx() {
|
||||
transmitter_model.disable(); // Disable the transmitter
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* 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 __OOK_FILE_HPP__
|
||||
#define __OOK_FILE_HPP__
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "encoders.hpp" // Includes data encoding functions for transmission
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include <string_view>
|
||||
#include "transmitter_model.hpp"
|
||||
#include "baseband_api.hpp" // Includes baseband API for handling transmission settings
|
||||
|
||||
struct ook_file_data {
|
||||
rf::Frequency frequency = rf::Frequency(0); // Default frequency
|
||||
uint32_t sample_rate = 0; // Default sample rate
|
||||
uint16_t symbol_rate = 0; // Default bit duration
|
||||
uint16_t pause_symbol_duration = 0; // Default pause between repeat
|
||||
uint16_t repeat = 0; // Default repeat
|
||||
std::string payload = ""; // Default payload
|
||||
};
|
||||
|
||||
bool read_ook_file(const std::filesystem::path& path, ook_file_data& ook_data);
|
||||
bool save_ook_file(ook_file_data& data, const std::filesystem::path& path);
|
||||
void start_ook_file_tx(ook_file_data& ook_data);
|
||||
void stop_ook_file_tx();
|
||||
|
||||
#endif // __FLIPPER_SUBFILE_HPP__
|
||||
@@ -96,7 +96,7 @@ float tone_key_frequency(tone_index index) {
|
||||
std::string tone_key_string(tone_index index) {
|
||||
if (index < 0 || (unsigned)index >= tone_keys.size())
|
||||
return std::string("");
|
||||
return tone_keys[index].first;
|
||||
return (std::string)tone_keys[index].first;
|
||||
}
|
||||
|
||||
// Return string showing frequency only from specific table index
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace tonekey {
|
||||
@@ -35,7 +36,7 @@ namespace tonekey {
|
||||
#define F2Ix100(x) (int32_t)(x * 100.0 + 0.5) // add 0.5f to round vs truncate during FP->int conversion
|
||||
|
||||
using tone_index = int32_t;
|
||||
using tone_key_t = std::vector<std::pair<std::string, uint32_t>>;
|
||||
using tone_key_t = std::vector<std::pair<std::string_view, uint32_t>>;
|
||||
|
||||
extern const tone_key_t tone_keys;
|
||||
|
||||
|
||||
@@ -118,30 +118,36 @@ void BtnGridView::clear() {
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items, bool inhibit_update) {
|
||||
for (auto item : new_items) {
|
||||
if (!blacklisted_app(item))
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::add_item(GridItem new_item) {
|
||||
if (!blacklisted_app(new_item)) {
|
||||
menu_items.push_back(new_item);
|
||||
if (!inhibit_update) {
|
||||
update_items();
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::insert_item(GridItem new_item, uint8_t position) {
|
||||
void BtnGridView::add_item(const GridItem& new_item, bool inhibit_update) {
|
||||
if (!blacklisted_app(new_item)) {
|
||||
menu_items.push_back(new_item);
|
||||
if (!inhibit_update) {
|
||||
update_items();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::insert_item(const GridItem& new_item, size_t position, bool inhibit_update) {
|
||||
if (!blacklisted_app(new_item)) {
|
||||
if (position < menu_items.size()) {
|
||||
auto pos_iter = menu_items.begin() + position;
|
||||
menu_items.insert(pos_iter, new_item);
|
||||
update_items();
|
||||
} else {
|
||||
menu_items.push_back(new_item);
|
||||
}
|
||||
|
||||
if (!inhibit_update) {
|
||||
update_items();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ class BtnGridView : public View {
|
||||
|
||||
~BtnGridView();
|
||||
|
||||
void add_items(std::initializer_list<GridItem> new_items);
|
||||
void add_item(GridItem new_item);
|
||||
void insert_item(GridItem new_item, uint8_t position);
|
||||
void add_items(std::initializer_list<GridItem> new_items, bool inhibit_update = false);
|
||||
void add_item(const GridItem& new_item, bool inhibit_update = false);
|
||||
void insert_item(const GridItem& new_item, size_t position, bool inhibit_update = false);
|
||||
void set_max_rows(int rows);
|
||||
int rows();
|
||||
void clear();
|
||||
@@ -81,12 +81,13 @@ class BtnGridView : public View {
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
bool blacklisted_app(GridItem new_item);
|
||||
|
||||
void update_items();
|
||||
|
||||
protected:
|
||||
virtual void on_populate() = 0;
|
||||
|
||||
private:
|
||||
int rows_{3};
|
||||
void update_items();
|
||||
void on_tick_second();
|
||||
|
||||
bool keep_highlight{false};
|
||||
|
||||
@@ -34,7 +34,7 @@ void tone_keys_populate(OptionsField& field) {
|
||||
for (size_t c = 0; c < tone_keys.size(); c++) {
|
||||
auto f = tone_keys[c].second;
|
||||
if ((c != 0) && (f < 1000 * 100))
|
||||
tone_name = "CTCSS " + fx100_string(f) + " #" + tone_keys[c].first;
|
||||
tone_name = "CTCSS " + fx100_string(f) + " #" + (std::string)tone_keys[c].first;
|
||||
else
|
||||
tone_name = tone_keys[c].first;
|
||||
|
||||
|
||||
@@ -97,10 +97,10 @@ namespace ui {
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
|
||||
/* static */ std::vector<ExternalItemsMenuLoader::GridItemEx> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
|
||||
bitmaps.clear();
|
||||
|
||||
std::vector<GridItem> external_apps;
|
||||
std::vector<GridItemEx> external_apps;
|
||||
|
||||
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace ui {
|
||||
if (appInfo->header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
|
||||
continue;
|
||||
|
||||
GridItem gridItem = {};
|
||||
GridItemEx gridItem = {};
|
||||
gridItem.text = reinterpret_cast<char*>(&appInfo->app_name[0]);
|
||||
|
||||
gridItem.color = Color((uint16_t)appInfo->icon_color);
|
||||
@@ -150,6 +150,8 @@ namespace ui {
|
||||
nav.display_modal("Error", "Unable to download app.");
|
||||
};
|
||||
|
||||
gridItem.desired_position = -1; // TODO: Where should we put the module's app icon? First? Last? Also configurable?
|
||||
|
||||
external_apps.push_back(gridItem);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,7 @@ namespace ui {
|
||||
|
||||
bool versionMatches = VERSION_MD5 == application_information.app_version;
|
||||
|
||||
GridItem gridItem = {};
|
||||
GridItemEx gridItem = {};
|
||||
gridItem.text = reinterpret_cast<char*>(&application_information.app_name[0]);
|
||||
|
||||
if (versionMatches) {
|
||||
@@ -195,6 +197,8 @@ namespace ui {
|
||||
nav.display_modal("Error", "The .ppma file in your " + apps_dir.string() + "\nfolder can't be read. Please\nupdate your SD Card content.");
|
||||
}
|
||||
};
|
||||
|
||||
gridItem.desired_position = application_information.desired_menu_position;
|
||||
} else {
|
||||
gridItem.color = Theme::getInstance()->fg_light->foreground;
|
||||
|
||||
@@ -203,6 +207,8 @@ namespace ui {
|
||||
gridItem.on_select = [&nav]() {
|
||||
nav.display_modal("Error", "The .ppma file in your " + apps_dir.string() + "\nfolder is outdated. Please\nupdate your SD Card content.");
|
||||
};
|
||||
|
||||
gridItem.desired_position = application_information.desired_menu_position;
|
||||
}
|
||||
|
||||
external_apps.push_back(gridItem);
|
||||
@@ -228,7 +234,7 @@ namespace ui {
|
||||
if (application_information.header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION)
|
||||
continue;
|
||||
|
||||
GridItem gridItem = {};
|
||||
GridItemEx gridItem = {};
|
||||
gridItem.text = reinterpret_cast<char*>(&application_information.app_name[0]);
|
||||
|
||||
gridItem.color = Color((uint16_t)application_information.icon_color);
|
||||
@@ -243,6 +249,8 @@ namespace ui {
|
||||
}
|
||||
};
|
||||
|
||||
gridItem.desired_position = -1; // No desired position support for standalone apps yet
|
||||
|
||||
external_apps.push_back(gridItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,11 @@ class DynamicBitmap {
|
||||
|
||||
class ExternalItemsMenuLoader {
|
||||
public:
|
||||
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
|
||||
struct GridItemEx : GridItem {
|
||||
int32_t desired_position;
|
||||
};
|
||||
|
||||
static std::vector<GridItemEx> load_external_items(app_location_t, NavigationView&);
|
||||
ExternalItemsMenuLoader() = delete;
|
||||
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
|
||||
static bool run_standalone_app(ui::NavigationView&, std::filesystem::path);
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#include "ui_pocsag_tx.hpp"
|
||||
#include "ui_rds.hpp"
|
||||
#include "ui_recon.hpp"
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_scanner.hpp"
|
||||
#include "ui_sd_over_usb.hpp"
|
||||
#include "ui_sd_wipe.hpp"
|
||||
@@ -126,7 +125,6 @@ const NavigationView::AppList NavigationView::appList = {
|
||||
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
|
||||
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
|
||||
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
|
||||
{"remote", "Remote", HOME, ui::Color::green(), &bitmap_icon_remote, new ViewFactory<RemoteView>()},
|
||||
{"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
|
||||
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
|
||||
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
|
||||
@@ -754,13 +752,16 @@ static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc)
|
||||
grid.add_item({app.displayName, app.iconColor, app.icon,
|
||||
[&nav, &app]() {
|
||||
i2cdev::I2CDevManager::set_autoscan_interval(0); //if i navigate away from any menu, turn off autoscan
|
||||
nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }});
|
||||
nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }},
|
||||
true);
|
||||
}
|
||||
};
|
||||
|
||||
grid.update_items();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
void addExternalItems(NavigationView& nav, app_location_t location, BtnGridView& grid) {
|
||||
void add_external_items(NavigationView& nav, app_location_t location, BtnGridView& grid, uint8_t notice_pos) {
|
||||
auto externalItems = ExternalItemsMenuLoader::load_external_items(location, nav);
|
||||
if (externalItems.empty()) {
|
||||
grid.insert_item({"Notice!",
|
||||
@@ -773,11 +774,23 @@ void addExternalItems(NavigationView& nav, app_location_t location, BtnGridView&
|
||||
"see Mayhem wiki and copy apps\n"
|
||||
"to " + apps_dir.string() + " folder of SD card.");
|
||||
}},
|
||||
pmem::show_gui_return_icon() ? 1 : 0);
|
||||
notice_pos);
|
||||
} else {
|
||||
std::sort(externalItems.begin(), externalItems.end(), [](const auto &a, const auto &b)
|
||||
{
|
||||
return a.desired_position < b.desired_position;
|
||||
});
|
||||
|
||||
for (auto const& gridItem : externalItems) {
|
||||
grid.add_item(gridItem);
|
||||
if (gridItem.desired_position < 0) {
|
||||
grid.add_item(gridItem, true);
|
||||
} else {
|
||||
grid.insert_item(gridItem, gridItem.desired_position, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
grid.update_items();
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
@@ -788,13 +801,14 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav)
|
||||
: nav_(nav) {}
|
||||
|
||||
void ReceiversMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
bool return_icon = pmem::show_gui_return_icon();
|
||||
if (return_icon) {
|
||||
add_item({"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }});
|
||||
}
|
||||
|
||||
add_apps(nav_, *this, RX);
|
||||
|
||||
addExternalItems(nav_, app_location_t::RX, *this);
|
||||
add_external_items(nav_, app_location_t::RX, *this, return_icon ? 1 : 0);
|
||||
}
|
||||
|
||||
/* TransmittersMenuView **************************************************/
|
||||
@@ -803,13 +817,14 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav)
|
||||
: nav_(nav) {}
|
||||
|
||||
void TransmittersMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
bool return_icon = pmem::show_gui_return_icon();
|
||||
if (return_icon) {
|
||||
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
|
||||
add_apps(nav_, *this, TX);
|
||||
|
||||
addExternalItems(nav_, app_location_t::TX, *this);
|
||||
add_external_items(nav_, app_location_t::TX, *this, return_icon ? 1 : 0);
|
||||
}
|
||||
|
||||
/* UtilitiesMenuView *****************************************************/
|
||||
@@ -820,13 +835,14 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav)
|
||||
}
|
||||
|
||||
void UtilitiesMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
bool return_icon = pmem::show_gui_return_icon();
|
||||
if (return_icon) {
|
||||
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
|
||||
add_apps(nav_, *this, UTILITIES);
|
||||
|
||||
addExternalItems(nav_, app_location_t::UTILITIES, *this);
|
||||
add_external_items(nav_, app_location_t::UTILITIES, *this, return_icon ? 1 : 0);
|
||||
}
|
||||
|
||||
/* SystemMenuView ********************************************************/
|
||||
@@ -851,6 +867,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
|
||||
|
||||
void SystemMenuView::on_populate() {
|
||||
add_apps(nav_, *this, HOME);
|
||||
add_external_items(nav_, app_location_t::HOME, *this, 2);
|
||||
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
|
||||
}
|
||||
|
||||
@@ -902,7 +919,7 @@ SystemView::SystemView(
|
||||
navigation_view.push<SystemMenuView>();
|
||||
|
||||
if (pmem::config_splash()) {
|
||||
navigation_view.push<BMPView>();
|
||||
navigation_view.push<SplashScreenView>();
|
||||
}
|
||||
status_view.set_back_enabled(false);
|
||||
status_view.set_title_image_enabled(true);
|
||||
@@ -971,11 +988,11 @@ void SystemView::set_app_fullscreen(bool fullscreen) {
|
||||
|
||||
/* ***********************************************************************/
|
||||
|
||||
void BMPView::focus() {
|
||||
void SplashScreenView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
BMPView::BMPView(NavigationView& nav)
|
||||
SplashScreenView::SplashScreenView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
add_children({&button_done});
|
||||
|
||||
@@ -984,12 +1001,14 @@ BMPView::BMPView(NavigationView& nav)
|
||||
};
|
||||
}
|
||||
|
||||
void BMPView::paint(Painter&) {
|
||||
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
|
||||
portapack::display.drawBMP({0, 16}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
void SplashScreenView::paint(Painter&) {
|
||||
if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, splash_dot_bmp))
|
||||
// ^ try draw bmp file from sdcard at (0,0), and the (0,0) already bypassed the status bar, so actual pos is (0, STATUS_BAR_HEIGHT)
|
||||
portapack::display.draw_bmp_from_bmp_hex_arr({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
// ^ draw BMP HEX arr in firmware, note that the BMP HEX arr only cover the image part (instead of fill the screen with background, this position is located it in the center)
|
||||
}
|
||||
|
||||
bool BMPView::on_touch(const TouchEvent event) {
|
||||
bool SplashScreenView::on_touch(const TouchEvent event) {
|
||||
/* the event thing were resolved by HTotoo, talked here https://discord.com/channels/719669764804444213/956561375155589192/1287756910950486027
|
||||
* the touch screen policy can be better, talked here https://discord.com/channels/719669764804444213/956561375155589192/1198926225897443328
|
||||
* this workaround discussed here: https://discord.com/channels/719669764804444213/1170738202924044338/1295630640158478418
|
||||
@@ -1011,7 +1030,7 @@ bool BMPView::on_touch(const TouchEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BMPView::handle_pop() {
|
||||
void SplashScreenView::handle_pop() {
|
||||
if (nav_.is_valid()) {
|
||||
nav_.pop();
|
||||
}
|
||||
@@ -1080,7 +1099,7 @@ ModalMessageView::ModalMessageView(
|
||||
}
|
||||
|
||||
void ModalMessageView::paint(Painter& painter) {
|
||||
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
|
||||
if (!compact) portapack::display.draw_bmp_from_bmp_hex_arr({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
|
||||
|
||||
// Break lines.
|
||||
auto lines = split_string(message_, '\n');
|
||||
|
||||
@@ -332,9 +332,9 @@ class InformationView : public View {
|
||||
{86, 0, 19 * 8, 16}};
|
||||
};
|
||||
|
||||
class BMPView : public View {
|
||||
class SplashScreenView : public View {
|
||||
public:
|
||||
BMPView(NavigationView& nav);
|
||||
SplashScreenView(NavigationView& nav);
|
||||
void paint(Painter&) override;
|
||||
void focus() override;
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ bool strEndsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||
|
||||
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
|
||||
chprintf(chp, "Usage: flash /FIRMWARE/portapack-mayhem-firmware.bin\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -656,6 +656,14 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### OOKStream
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ook_stream_tx.cpp
|
||||
)
|
||||
DeclareTargets(POSK ookstream)
|
||||
|
||||
|
||||
|
||||
### HackRF "factory" firmware
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 "proc_ook_stream_tx.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
OOKProcessorStreamed::OOKProcessorStreamed() {
|
||||
configured = false;
|
||||
baseband_thread.start();
|
||||
}
|
||||
|
||||
inline void OOKProcessorStreamed::write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i) {
|
||||
int8_t re, im;
|
||||
|
||||
if (bit_value) {
|
||||
phase = (phase + 200); // What ?
|
||||
sphase = phase + (64 << 18);
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured || !stream) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
if (rem_samples <= curr_samples) {
|
||||
// get a new sample from stream
|
||||
int32_t sample = -13346;
|
||||
rem_samples = 0; // reset my pointer
|
||||
curr_samples = 0;
|
||||
size_t readed = 0;
|
||||
if (configured) readed = stream->read(&sample, 4); // read from stream // todo htotoo error handling?!
|
||||
if (readed == 0) {
|
||||
txprogress_message.progress = -10;
|
||||
shared_memory.application_queue.push(txprogress_message); // debug
|
||||
} else {
|
||||
if (sample == endsignals[readerrs + 1]) { // if no more samples, stop
|
||||
readerrs++;
|
||||
if (readerrs == 2) {
|
||||
configured = false;
|
||||
txprogress_message.done = true;
|
||||
txprogress_message.progress = 100;
|
||||
curr_hilow = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
} else {
|
||||
if (sample < 0) {
|
||||
rem_samples = OOK_SAMPLERATE * ((-1 * sample) / 1000000.0);
|
||||
curr_hilow = false;
|
||||
} else {
|
||||
rem_samples = OOK_SAMPLERATE * (sample / 1000000.0);
|
||||
curr_hilow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++curr_samples;
|
||||
write_sample(buffer, curr_hilow, i);
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::on_message(const Message* const message) {
|
||||
switch (message->id) {
|
||||
case Message::ID::ReplayConfig:
|
||||
configured = false;
|
||||
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::FIFOData:
|
||||
configured = true;
|
||||
txprogress_message.progress = -4;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::replay_config(const ReplayConfigMessage& message) {
|
||||
if (message.config) {
|
||||
txprogress_message.progress = -2;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
stream = std::make_unique<StreamOutput>(message.config);
|
||||
// Tell application that the buffers and FIFO pointers are ready, prefill
|
||||
RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest};
|
||||
shared_memory.application_queue.push(sig_message);
|
||||
} else {
|
||||
txprogress_message.progress = -3;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher{std::make_unique<OOKProcessorStreamed>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
For usage check the FlipperTX app. Don't forget to "close" the tx by sending 42069, 613379 values at the end of the stream.
|
||||
Only close baseband when you get the TX done msg, not on replay thread done, because, there can still data in the buffer.
|
||||
*/
|
||||
|
||||
#ifndef __PROC_OOKSTREAMTX_HPP__
|
||||
#define __PROC_OOKSTREAMTX_HPP__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "stream_output.hpp"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
class OOKProcessorStreamed : public BasebandProcessor {
|
||||
public:
|
||||
OOKProcessorStreamed();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
uint32_t rem_samples = 0;
|
||||
uint32_t curr_samples = 0;
|
||||
bool curr_hilow = false;
|
||||
|
||||
uint32_t phase{0}, sphase{0};
|
||||
void write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i);
|
||||
|
||||
std::unique_ptr<StreamOutput> stream{};
|
||||
bool configured{false};
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
|
||||
int32_t endsignals[3] = {0, 42069, 613379}; // 0 is skipped, count from 1, don't ask...
|
||||
uint8_t readerrs = 0; // to count in the array
|
||||
|
||||
TXProgressMessage txprogress_message{};
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{OOK_SAMPLERATE, this, baseband::Direction::Transmit};
|
||||
};
|
||||
|
||||
#endif /*__PROC_OOKSTREAMTX_HPP__*/
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "spi_image.hpp"
|
||||
#include "standalone_app.hpp"
|
||||
|
||||
#define CURRENT_HEADER_VERSION 0x00000002
|
||||
#define CURRENT_HEADER_VERSION 0x00000003
|
||||
#define MIN_HEADER_VERSION_FOR_CHECKSUM 0x00000002
|
||||
|
||||
typedef void (*externalAppEntry_t)(ui::NavigationView& nav);
|
||||
@@ -42,6 +42,7 @@ struct application_information_t {
|
||||
uint8_t bitmap_data[32];
|
||||
uint32_t icon_color;
|
||||
app_location_t menu_location;
|
||||
int32_t desired_menu_position;
|
||||
|
||||
portapack::spi_flash::image_tag_t m4_app_tag;
|
||||
uint32_t m4_app_offset;
|
||||
|
||||
@@ -202,16 +202,14 @@ bool I2cDev_MAX17055::init(uint8_t addr_) {
|
||||
model = I2CDEVMDL_MAX17055;
|
||||
query_interval = BATTERY_WIDGET_REFRESH_INTERVAL;
|
||||
if (detect()) {
|
||||
bool return_status = true;
|
||||
if (needsInitialization()) {
|
||||
// First-time or POR initialization
|
||||
full_reset_and_init();
|
||||
} else {
|
||||
// Subsequent boot
|
||||
partialInit();
|
||||
return_status = full_reset_and_init();
|
||||
}
|
||||
partialInit();
|
||||
// statusClear(); I am not sure if this should be here or not
|
||||
return true;
|
||||
partialInit(); // If you always want hibernation disabled
|
||||
// statusClear(); // I am not sure if this should be here or not (Clear all bits in the Status register (0x00))
|
||||
return return_status;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -220,19 +218,15 @@ bool I2cDev_MAX17055::full_reset_and_init() {
|
||||
if (!soft_reset()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initialize_custom_parameters()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!load_custom_parameters()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clear_por()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -298,8 +292,7 @@ void I2cDev_MAX17055::partialInit() {
|
||||
bool I2cDev_MAX17055::reset_learned() {
|
||||
// this if for reset all the learned parameters by ic
|
||||
// the full inis should do this
|
||||
full_reset_and_init();
|
||||
return true;
|
||||
return full_reset_and_init();
|
||||
}
|
||||
|
||||
bool I2cDev_MAX17055::detect() {
|
||||
|
||||
@@ -345,19 +345,19 @@ void ILI9341::render_box(const ui::Point p, const ui::Size s, const ui::Color* l
|
||||
|
||||
// RLE_4 BMP loader (delta not implemented)
|
||||
/* draw transparent, pass transparent color as arg, usage inline anonymous obj
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){41, 24, 22}); // dec, out of {255, 255, 255}
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){0x29, 0x18, 0x16}); //hex out of {0xFF, 0xFF, 0xFF}
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, (const uint8_t[]){41, 24, 22}); // dec, out of {255, 255, 255}
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, (const uint8_t[]){0x29, 0x18, 0x16}); //hex out of {0xFF, 0xFF, 0xFF}
|
||||
*
|
||||
* draw transparent, pass transparent color as arg, usage pass uint8_t[] obj
|
||||
* const uint8_t c[] = {41, 24, 22}; or const uint8_t c[3] = {0x29, 0x18, 0x16};
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, c);
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, c);
|
||||
*
|
||||
* don't draw transparent, pass nullptr as arg, usage
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, nullptr);
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, nullptr);
|
||||
*
|
||||
* if your image use RLE compress as transparency methods, pass any valid color as arg, it doesn't matter (like the modal bmp. TODO: write RLE transparency generator)
|
||||
* */
|
||||
void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color) {
|
||||
void ILI9341::draw_bmp_from_bmp_hex_arr(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color) {
|
||||
const bmp_header_t* bmp_header = (const bmp_header_t*)bitmap;
|
||||
uint32_t data_idx;
|
||||
uint8_t by, c, count, transp_idx = 0;
|
||||
@@ -463,7 +463,7 @@ void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* t
|
||||
* 24bpp RGB
|
||||
* 32bpp ARGB
|
||||
*/
|
||||
bool ILI9341::drawBMP2(const ui::Point p, const std::filesystem::path& file) {
|
||||
bool ILI9341::draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem::path& file) {
|
||||
File bmpimage;
|
||||
size_t file_pos = 0;
|
||||
uint16_t pointer = 0;
|
||||
@@ -512,7 +512,9 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::filesystem::path& file) {
|
||||
|
||||
file_pos = bmp_header.image_data;
|
||||
|
||||
py = height + 16;
|
||||
py = height + 16 - 1;
|
||||
/* ^ this is for to "start" AKA "image end" draw at the 17th line,
|
||||
* because the render_line logic below is start with p.y() + py until "end" AKA "image start"*/
|
||||
|
||||
while (1) {
|
||||
while (px < width) {
|
||||
|
||||
@@ -62,8 +62,8 @@ class ILI9341 {
|
||||
const ui::Color background);
|
||||
|
||||
void draw_pixel(const ui::Point p, const ui::Color color);
|
||||
void drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color);
|
||||
bool drawBMP2(const ui::Point p, const std::filesystem::path& file);
|
||||
void draw_bmp_from_bmp_hex_arr(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color);
|
||||
bool draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem::path& file);
|
||||
void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer);
|
||||
void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer);
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ constexpr image_tag_t image_tag_fskrx{'P', 'F', 'S', 'R'};
|
||||
constexpr image_tag_t image_tag_jammer{'P', 'J', 'A', 'M'};
|
||||
constexpr image_tag_t image_tag_mic_tx{'P', 'M', 'T', 'X'};
|
||||
constexpr image_tag_t image_tag_ook{'P', 'O', 'O', 'K'};
|
||||
constexpr image_tag_t image_tag_ookstream{'P', 'O', 'S', 'K'};
|
||||
constexpr image_tag_t image_tag_rds{'P', 'R', 'D', 'S'};
|
||||
constexpr image_tag_t image_tag_replay{'P', 'R', 'E', 'P'};
|
||||
constexpr image_tag_t image_tag_gps{'P', 'G', 'P', 'S'};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ui_language.hpp"
|
||||
|
||||
// use the exact position in this array! the enum's value is the identifier. Best to add to the end
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset", "Pause", "Resume", "Flood", "Show QR", "Save", "Lock", "Unlock"};
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset", "Pause", "Resume", "Flood", "Show QR", "Save", "Lock", "Unlock", "Browse", "Set", "Open File", "Save File", "Send", "Receive"};
|
||||
|
||||
// multi language support will changes (not in use for now)
|
||||
const char** LanguageHelper::currentMessages = englishMessages;
|
||||
|
||||
@@ -60,7 +60,13 @@ enum LangConsts {
|
||||
LANG_SHOWQR,
|
||||
LANG_SAVE,
|
||||
LANG_LOCK,
|
||||
LANG_UNLOCK
|
||||
LANG_UNLOCK,
|
||||
LANG_BROWSE,
|
||||
LANG_SET,
|
||||
LANG_OPEN_FILE,
|
||||
LANG_SAVE_FILE,
|
||||
LANG_SEND,
|
||||
LANG_RECV
|
||||
};
|
||||
|
||||
class LanguageHelper {
|
||||
@@ -73,4 +79,4 @@ class LanguageHelper {
|
||||
static const char* englishMessages[];
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -91,8 +91,8 @@ cmake_objcopy = sys.argv[3]
|
||||
|
||||
memory_location_header_position = 0
|
||||
externalAppEntry_header_position = 4
|
||||
m4_app_tag_header_position = 72
|
||||
m4_app_offset_header_position = 76
|
||||
m4_app_tag_header_position = 76
|
||||
m4_app_offset_header_position = 80
|
||||
|
||||
for external_image_prefix in sys.argv[4:]:
|
||||
|
||||
@@ -123,7 +123,10 @@ for external_image_prefix in sys.argv[4:]:
|
||||
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
|
||||
continue
|
||||
|
||||
print(chunk_data)
|
||||
chunk_tag = chunk_data.decode("utf-8")
|
||||
print(chunk_tag)
|
||||
print("{}/../baseband/{}.bin".format(binary_dir, chunk_tag))
|
||||
m4_image = read_image("{}/../baseband/{}.bin".format(binary_dir, chunk_tag))
|
||||
app_image_len = len(external_application_image)
|
||||
external_application_image += m4_image
|
||||
|
||||
Regular → Executable
+7
-6
@@ -26,6 +26,7 @@ import requests
|
||||
import sys
|
||||
|
||||
cppheader = """#include "ui_about_simple.hpp"
|
||||
#include <string_view>
|
||||
|
||||
#define ROLL_SPEED_FRAME_PER_LINE 60
|
||||
// cuz frame rate of pp screen is probably 60, scroll per sec
|
||||
@@ -33,7 +34,7 @@ cppheader = """#include "ui_about_simple.hpp"
|
||||
namespace ui {
|
||||
|
||||
// Information: a line starting with a '#' will be yellow coloured
|
||||
const std::"""
|
||||
constexpr std::"""
|
||||
|
||||
cppfooter = """
|
||||
AboutView::AboutView(NavigationView& nav) {
|
||||
@@ -52,18 +53,18 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
button_ok.focus();
|
||||
};
|
||||
|
||||
for (const std::string& authors_line : authors_list) {
|
||||
for (auto& authors_line : authors_list) {
|
||||
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
|
||||
if (authors_line.size() > 0) {
|
||||
if (authors_line[0] == '#') {
|
||||
menu_view.add_item(
|
||||
{authors_line.substr(1, authors_line.size() - 1),
|
||||
{(std::string)authors_line.substr(1, authors_line.size() - 1),
|
||||
ui::Theme::getInstance()->fg_yellow->foreground,
|
||||
nullptr,
|
||||
nullptr});
|
||||
} else {
|
||||
menu_view.add_item(
|
||||
{authors_line,
|
||||
{(std::string)authors_line,
|
||||
Theme::getInstance()->bg_darkest->foreground,
|
||||
nullptr,
|
||||
nullptr});
|
||||
@@ -140,7 +141,7 @@ def get_contributors(url):
|
||||
|
||||
def generate_content(projects):
|
||||
project_contrib = []
|
||||
project_contrib.append("string authors_list[] = {\n")
|
||||
project_contrib.append("string_view authors_list[] = {\n")
|
||||
project_contrib.append(" \"# * List of contributors * \",\n")
|
||||
for project in projects:
|
||||
project_contrib.append(" \" \",\n")
|
||||
@@ -169,7 +170,7 @@ def pp_create_ui_about_simple_cpp(cpp_file, cppheader, cppcontent, cppfooter):
|
||||
|
||||
def pp_change_ui_about_simple_cpp(cpp_file, cppcontent):
|
||||
content = []
|
||||
content_pattern = re.compile(r"string authors_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
|
||||
content_pattern = re.compile(r"string_view authors_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
|
||||
|
||||
# Read original file
|
||||
with open(cpp_file, 'r') as file:
|
||||
|
||||
@@ -12,8 +12,8 @@ pause
|
||||
echo.
|
||||
|
||||
REM Check if the firmware file exists
|
||||
if not exist portapack-h1_h2-mayhem.bin (
|
||||
echo The firmware file "portapack-h1_h2-mayhem.bin" does not exist.
|
||||
if not exist portapack-mayhem-firmware.bin (
|
||||
echo The firmware file "portapack-mayhem-firmware.bin" does not exist.
|
||||
echo Please ensure that you have downloaded the latest release from:
|
||||
echo https://github.com/portapack-mayhem/mayhem-firmware/releases/
|
||||
echo.
|
||||
@@ -21,7 +21,7 @@ if not exist portapack-h1_h2-mayhem.bin (
|
||||
exit /b
|
||||
)
|
||||
|
||||
"utils/hackrf_update.exe" portapack-h1_h2-mayhem.bin
|
||||
"utils/hackrf_update.exe" portapack-mayhem-firmware.bin
|
||||
echo.
|
||||
echo If your device never boot after flashing, please refer to https://github.com/portapack-mayhem/mayhem-firmware/wiki/Won't-boot
|
||||
echo.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
27000000 1M 1076 4 100 01110111011101110101010101010101010101110111011101110101
|
||||
Reference in New Issue
Block a user