mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-07 07:19:07 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3db8a5ac5b | |||
| 266a626acf | |||
| 411e2fb93a | |||
| 6ed83c08e3 | |||
| 9929376acf | |||
| 5135b6404d | |||
| 790669c242 | |||
| 4bb8b1882c | |||
| 56702ca7c2 | |||
| 9be4b9596d | |||
| 37c6b35fb7 | |||
| 5cd7a09797 | |||
| a1856b9bb0 | |||
| 29a36c8658 | |||
| 264b1b38ba | |||
| 65e71508ac | |||
| e5546159c5 | |||
| d7c961ea4f | |||
| ad987f2b5e | |||
| defb934c3f | |||
| 5a850984b9 | |||
| 1185b67b28 | |||
| abb42e8df4 | |||
| 7fdb1af69d | |||
| 78713cc2af |
@@ -49,7 +49,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 && zip -r sdcard-no-map.zip sdcard
|
||||
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 && zip -r sdcard-no-map.zip sdcard
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.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 && zip -r sdcard.zip sdcard
|
||||
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 && zip -r sdcard.zip sdcard
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
@@ -33,7 +33,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 && zip -r sdcard-no-map.zip sdcard
|
||||
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 && zip -r sdcard-no-map.zip sdcard
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.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 && zip -r sdcard.zip sdcard
|
||||
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 && zip -r sdcard.zip sdcard
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.7.3
|
||||
v1.7.4
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.7.4
|
||||
v1.8.0
|
||||
|
||||
@@ -42,6 +42,12 @@ if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
endif (GIT_VERSION_FOUND)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND bash "-c" "echo ${VERSION} | md5sum | head -c 8"
|
||||
OUTPUT_VARIABLE VERSION_MD5
|
||||
)
|
||||
set(VERSION_MD5 "0x${VERSION_MD5}")
|
||||
|
||||
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
|
||||
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
|
||||
# Quick overview
|
||||
# What is this?
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, there is an awesome introductory video by [Tech Minds](https://www.youtube.com/channel/UC9a8Z6Sp6eb2s3O79pX5Zvg) available:
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
|
||||
|
||||
[](https://www.youtube.com/watch?v=kjFB58Y1TAo)
|
||||
[](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
||||
+10
-2
@@ -26,6 +26,7 @@ set(CHIBIOS ${PROJECT_SOURCE_DIR}/chibios)
|
||||
set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
|
||||
|
||||
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
|
||||
set(EXPORT_EXTERNAL_APP_IMAGES ${PROJECT_SOURCE_DIR}/tools/export_external_apps.py)
|
||||
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)
|
||||
@@ -55,8 +56,15 @@ add_custom_target(
|
||||
program
|
||||
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary)
|
||||
COMMAND sleep 3s
|
||||
COMMAND hackrf_spiflash -w ${FIRMWARE_FILENAME}
|
||||
DEPENDS ${FIRMWARE_FILENAME}
|
||||
COMMAND hackrf_spiflash -i -R -w ${FIRMWARE_FILENAME}
|
||||
DEPENDS firmware ${FIRMWARE_FILENAME}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
program-external-apps
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tools/copy_external_apps.sh
|
||||
DEPENDS program
|
||||
)
|
||||
|
||||
# TODO: Bad hack to fix location of LICENSE file for tar.
|
||||
|
||||
@@ -32,7 +32,7 @@ include(CheckCXXCompilerFlag)
|
||||
project(application)
|
||||
|
||||
# Compiler options here.
|
||||
set(USE_OPT "-Os -g --specs=nano.specs")
|
||||
set(USE_OPT "-Os -g --specs=nano.specs -specs=nosys.specs")
|
||||
|
||||
# C specific options here (added to USE_OPT).
|
||||
set(USE_COPT "-std=gnu99")
|
||||
@@ -97,6 +97,7 @@ include(${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0/port.cmake)
|
||||
include(${CHIBIOS}/os/kernel/kernel.cmake)
|
||||
include(${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs.cmake)
|
||||
include(${CHIBIOS}/test/test.cmake)
|
||||
include(external/external.cmake)
|
||||
|
||||
# Define linker script file here
|
||||
set(LDSCRIPT ${PORTLD}/LPC43xx_M0.ld)
|
||||
@@ -256,7 +257,6 @@ set(CPPSRC
|
||||
apps/ui_about_simple.cpp
|
||||
apps/ui_adsb_rx.cpp
|
||||
apps/ui_adsb_tx.cpp
|
||||
apps/ui_afsk_rx.cpp
|
||||
apps/ui_aprs_rx.cpp
|
||||
apps/ui_aprs_tx.cpp
|
||||
apps/ui_bht_tx.cpp
|
||||
@@ -322,6 +322,9 @@ set(CPPSRC
|
||||
${CPLD_20150901_DATA_CPP}
|
||||
${CPLD_20170522_DATA_CPP}
|
||||
${HACKRF_CPLD_DATA_CPP}
|
||||
ui_external_items_menu_loader.cpp
|
||||
|
||||
${EXTCPPSRC}
|
||||
)
|
||||
|
||||
# C sources to be compiled in ARM mode regardless of the global setting.
|
||||
@@ -397,7 +400,7 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
|
||||
# List all default C defines here, like -D_DEBUG=1
|
||||
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
||||
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"'")
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5}")
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
set(DADEFS)
|
||||
@@ -466,12 +469,13 @@ set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
|
||||
add_definitions(${DEFS})
|
||||
include_directories(. ${INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS} "-L${CMAKE_CURRENT_LIST_DIR}/external")
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.bin
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
|
||||
COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin --remove-section=.external_app_*
|
||||
COMMAND ${EXPORT_EXTERNAL_APP_IMAGES} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_OBJCOPY} ${EXTAPPLIST}
|
||||
DEPENDS ${PROJECT_NAME}.elf
|
||||
)
|
||||
|
||||
|
||||
@@ -244,8 +244,7 @@ SettingsManager::SettingsManager(
|
||||
: app_name_{app_name},
|
||||
settings_{},
|
||||
bindings_{},
|
||||
loaded_{false},
|
||||
radio_loaded_{false} {
|
||||
loaded_{false} {
|
||||
settings_.mode = mode;
|
||||
settings_.options = options;
|
||||
|
||||
@@ -253,53 +252,50 @@ SettingsManager::SettingsManager(
|
||||
additional_settings.reserve(17 + additional_settings.size());
|
||||
bindings_ = std::move(additional_settings);
|
||||
|
||||
// Additional settings should always be loaded because apps now rely
|
||||
// on being able to store UI settings, config, etc. The radio settings
|
||||
// are only loaded if the global option has been enabled.
|
||||
// Add the radio setting bindings if either load or save are enabled.
|
||||
if (portapack::persistent_memory::load_app_settings() ||
|
||||
portapack::persistent_memory::save_app_settings()) {
|
||||
// Transmitter model settings.
|
||||
if (flags_enabled(mode, Mode::TX)) {
|
||||
bindings_.emplace_back("tx_frequency"sv, &settings_.tx_frequency);
|
||||
bindings_.emplace_back("tx_amp"sv, &settings_.tx_amp);
|
||||
bindings_.emplace_back("tx_gain"sv, &settings_.tx_gain);
|
||||
bindings_.emplace_back("channel_bandwidth"sv, &settings_.channel_bandwidth);
|
||||
}
|
||||
// Settings should always be loaded because apps now rely
|
||||
// on being able to store UI settings, config, etc.
|
||||
|
||||
// Receiver model settings.
|
||||
if (flags_enabled(mode, Mode::RX)) {
|
||||
bindings_.emplace_back("rx_frequency"sv, &settings_.rx_frequency);
|
||||
bindings_.emplace_back("lna"sv, &settings_.lna);
|
||||
bindings_.emplace_back("vga"sv, &settings_.vga);
|
||||
bindings_.emplace_back("rx_amp"sv, &settings_.rx_amp);
|
||||
bindings_.emplace_back("modulation"sv, &settings_.modulation);
|
||||
bindings_.emplace_back("am_config_index"sv, &settings_.am_config_index);
|
||||
bindings_.emplace_back("nbfm_config_index"sv, &settings_.nbfm_config_index);
|
||||
bindings_.emplace_back("wfm_config_index"sv, &settings_.wfm_config_index);
|
||||
bindings_.emplace_back("squelch"sv, &settings_.squelch);
|
||||
}
|
||||
|
||||
// Common model settings.
|
||||
bindings_.emplace_back("baseband_bandwidth"sv, &settings_.baseband_bandwidth);
|
||||
bindings_.emplace_back("sampling_rate"sv, &settings_.sampling_rate);
|
||||
bindings_.emplace_back("step"sv, &settings_.step);
|
||||
bindings_.emplace_back("volume"sv, &settings_.volume);
|
||||
// Transmitter model settings.
|
||||
if (flags_enabled(mode, Mode::TX)) {
|
||||
bindings_.emplace_back("tx_frequency"sv, &settings_.tx_frequency);
|
||||
bindings_.emplace_back("tx_amp"sv, &settings_.tx_amp);
|
||||
bindings_.emplace_back("tx_gain"sv, &settings_.tx_gain);
|
||||
bindings_.emplace_back("channel_bandwidth"sv, &settings_.channel_bandwidth);
|
||||
}
|
||||
|
||||
// Receiver model settings.
|
||||
if (flags_enabled(mode, Mode::RX)) {
|
||||
bindings_.emplace_back("rx_frequency"sv, &settings_.rx_frequency);
|
||||
bindings_.emplace_back("lna"sv, &settings_.lna);
|
||||
bindings_.emplace_back("vga"sv, &settings_.vga);
|
||||
bindings_.emplace_back("rx_amp"sv, &settings_.rx_amp);
|
||||
bindings_.emplace_back("modulation"sv, &settings_.modulation);
|
||||
bindings_.emplace_back("am_config_index"sv, &settings_.am_config_index);
|
||||
bindings_.emplace_back("nbfm_config_index"sv, &settings_.nbfm_config_index);
|
||||
bindings_.emplace_back("wfm_config_index"sv, &settings_.wfm_config_index);
|
||||
bindings_.emplace_back("squelch"sv, &settings_.squelch);
|
||||
}
|
||||
|
||||
// Common model settings.
|
||||
bindings_.emplace_back("baseband_bandwidth"sv, &settings_.baseband_bandwidth);
|
||||
bindings_.emplace_back("sampling_rate"sv, &settings_.sampling_rate);
|
||||
bindings_.emplace_back("step"sv, &settings_.step);
|
||||
bindings_.emplace_back("volume"sv, &settings_.volume);
|
||||
|
||||
// RadioState should have initialized default radio parameters before this function;
|
||||
// copy them to settings_ before checking settings .ini file (in case the file doesn't exist
|
||||
// or doesn't include all parameters). Settings in the file can overwrite all, or a subset of parameters.
|
||||
copy_from_radio_model(settings_);
|
||||
|
||||
loaded_ = load_settings(app_name_, bindings_);
|
||||
|
||||
// Only copy to the radio if load was successful.
|
||||
if (loaded_ && portapack::persistent_memory::load_app_settings()) {
|
||||
radio_loaded_ = true;
|
||||
if (loaded_)
|
||||
copy_to_radio_model(settings_);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager() {
|
||||
// Only save radio settings when the option is enabled.
|
||||
if (portapack::persistent_memory::save_app_settings())
|
||||
copy_from_radio_model(settings_);
|
||||
copy_from_radio_model(settings_);
|
||||
|
||||
save_settings(app_name_, bindings_);
|
||||
}
|
||||
|
||||
@@ -170,8 +170,6 @@ class SettingsManager {
|
||||
/* True if settings were successfully loaded from file. */
|
||||
bool loaded() const { return loaded_; }
|
||||
|
||||
/* True if radio settings were successfully loaded from file. */
|
||||
bool radio_loaded() const { return radio_loaded_; }
|
||||
Mode mode() const { return settings_.mode; }
|
||||
|
||||
AppSettings& raw() { return settings_; }
|
||||
@@ -181,7 +179,6 @@ class SettingsManager {
|
||||
AppSettings settings_;
|
||||
SettingBindings bindings_;
|
||||
bool loaded_;
|
||||
bool radio_loaded_;
|
||||
};
|
||||
|
||||
} // namespace app_settings
|
||||
|
||||
@@ -60,11 +60,12 @@ class ACARSAppView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
131550000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_acars.hpp", app_settings::Mode::RX};
|
||||
"rx_acars", app_settings::Mode::RX};
|
||||
|
||||
bool logging{false};
|
||||
uint32_t packet_counter{0};
|
||||
|
||||
@@ -382,9 +382,6 @@ AISAppView::AISAppView(NavigationView& nav)
|
||||
|
||||
recent_entry_detail_view.hidden(true);
|
||||
|
||||
if (!settings_.radio_loaded())
|
||||
receiver_model.set_target_frequency(initial_target_frequency);
|
||||
|
||||
receiver_model.enable();
|
||||
|
||||
options_channel.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
|
||||
@@ -162,9 +162,8 @@ class AISAppView : public View {
|
||||
std::string title() const override { return "AIS RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 162025000;
|
||||
|
||||
RxRadioState radio_state_{
|
||||
162025000 /* frequency*/,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -110,9 +110,6 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
&recent_entries_view,
|
||||
});
|
||||
|
||||
if (!settings_.radio_loaded())
|
||||
receiver_model.set_target_frequency(initial_target_frequency);
|
||||
|
||||
receiver_model.enable();
|
||||
|
||||
logger = std::make_unique<ERTLogger>();
|
||||
|
||||
@@ -123,12 +123,11 @@ class ERTAppView : public View {
|
||||
std::string title() const override { return "ERT RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 911600000;
|
||||
|
||||
ERTRecentEntries recent{};
|
||||
std::unique_ptr<ERTLogger> logger{};
|
||||
|
||||
RxRadioState radio_state_{
|
||||
911600000 /* frequency */,
|
||||
2500000 /* bandwidth */,
|
||||
4194304 /* sampling rate */};
|
||||
app_settings::SettingsManager settings_{
|
||||
|
||||
@@ -74,7 +74,10 @@ void GpsSimAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
auto duration = ms_duration(file_size, transmitter_model.sampling_rate(), 2);
|
||||
text_duration.set(to_string_time_ms(duration));
|
||||
|
||||
button_play.focus();
|
||||
// TODO: fix in UI framework with 'try_focus()'?
|
||||
// Hack around focus getting called by ctor before parent is set.
|
||||
if (parent())
|
||||
button_play.focus();
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_tx_progress(const uint32_t progress) {
|
||||
@@ -173,11 +176,6 @@ GpsSimAppView::GpsSimAppView(
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
if (!settings_.radio_loaded()) {
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
transmitter_model.set_sampling_rate(2600000);
|
||||
}
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
|
||||
@@ -51,21 +51,17 @@ class GpsSimAppView : public View {
|
||||
std::string title() const override { return "GPS Sim TX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 1575420000;
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
3000000 /* bandwidth */,
|
||||
500000 /* sampling rate */
|
||||
TxRadioState radio_state_{
|
||||
1575420000 /* frequency */,
|
||||
15000000 /* bandwidth */,
|
||||
2600000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_gps", app_settings::Mode::TX};
|
||||
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
int32_t tx_gain{47};
|
||||
bool rf_amp{true}; // aux private var to store temporal, same as Replay App rf_amp user selection.
|
||||
static constexpr uint32_t baseband_bandwidth = 3000000; // filter bandwidth
|
||||
const size_t read_size{16384};
|
||||
const size_t buffer_count{3};
|
||||
|
||||
|
||||
@@ -238,6 +238,10 @@ void LGEView::start_tx() {
|
||||
tx_view.set_dirty();
|
||||
}
|
||||
|
||||
/* By experimental test, previous fw 1.7.4 seems to have setting , tx LPF = 5Mhz
|
||||
This LGE - seems to be for controlling a "laser tag equipment" (Havoc app) , modulated FSK , low bauds,
|
||||
and using max GUI fm dev 150k , we are still in NBFM, let's reduce slightly original TX LPF 5M ->2M5*/
|
||||
transmitter_model.set_baseband_bandwidth(2'500'000);
|
||||
transmitter_model.enable();
|
||||
|
||||
chThdSleep(100);
|
||||
|
||||
@@ -51,6 +51,7 @@ class LGEView : public View {
|
||||
};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
868067000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -114,9 +114,6 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||
? FILTER_NONE
|
||||
: FILTER_DROP;
|
||||
}
|
||||
if (!app_settings_.radio_loaded()) {
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
}
|
||||
|
||||
logger.append(LOG_ROOT_DIR "/POCSAG.TXT");
|
||||
|
||||
|
||||
@@ -197,14 +197,17 @@ class POCSAGAppView : public View {
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 466'175'000;
|
||||
bool logging() const { return settings_.enable_logging; };
|
||||
bool logging_raw() const { return settings_.enable_raw_log; };
|
||||
bool hide_bad_data() const { return settings_.hide_bad_data; };
|
||||
bool hide_addr_only() const { return settings_.hide_addr_only; };
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
RxRadioState radio_state_{
|
||||
466175000 /* frequency*/,
|
||||
max283x::filter::bandwidth_minimum /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
|
||||
// Settings
|
||||
POCSAGSettings settings_{};
|
||||
|
||||
@@ -50,6 +50,7 @@ class SoundBoardView : public View {
|
||||
|
||||
private:
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -157,9 +157,6 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
&field_vga,
|
||||
&recent_entries_view});
|
||||
|
||||
if (!settings_.radio_loaded())
|
||||
receiver_model.set_target_frequency(initial_target_frequency);
|
||||
|
||||
receiver_model.enable();
|
||||
|
||||
options_band.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
|
||||
@@ -102,9 +102,8 @@ class TPMSAppView : public View {
|
||||
std::string title() const override { return "TPMS RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 315000000;
|
||||
|
||||
RxRadioState radio_state_{
|
||||
315000000 /* frequency*/,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */};
|
||||
app_settings::SettingsManager settings_{
|
||||
@@ -135,7 +134,7 @@ class TPMSAppView : public View {
|
||||
3,
|
||||
{
|
||||
{"315", 315000000},
|
||||
{"433", 433920000},
|
||||
{"434", 433920000},
|
||||
}};
|
||||
|
||||
OptionsField options_pressure{
|
||||
|
||||
@@ -34,6 +34,8 @@ using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool ac_details_view_active{false};
|
||||
|
||||
template <>
|
||||
void RecentEntriesTable<AircraftRecentEntries>::draw(
|
||||
const Entry& entry,
|
||||
@@ -182,6 +184,7 @@ ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView(
|
||||
break;
|
||||
}
|
||||
button_close.on_select = [&nav](Button&) {
|
||||
ac_details_view_active = false;
|
||||
nav.pop();
|
||||
};
|
||||
};
|
||||
@@ -217,6 +220,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
|
||||
}
|
||||
|
||||
ADSBRxDetailsView::~ADSBRxDetailsView() {
|
||||
ac_details_view_active = false;
|
||||
on_close_();
|
||||
}
|
||||
|
||||
@@ -260,6 +264,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
|
||||
|
||||
button_aircraft_details.on_select = [this, &nav](Button&) {
|
||||
ac_details_view_active = true;
|
||||
aircraft_details_view = nav.push<ADSBRxAircraftDetailsView>(entry_copy, [this]() { send_updates = false; });
|
||||
send_updates = false;
|
||||
};
|
||||
@@ -433,6 +438,11 @@ void ADSBRxView::updateDetailsAndMap(int ageStep) {
|
||||
ui::GeoMarker marker;
|
||||
bool storeNewMarkers = false;
|
||||
|
||||
// NB: Temporarily pausing updates in rtc_timer_tick context when viewing AC Details screen (kludge for some Guru faults)
|
||||
// TODO: More targeted blocking of updates in rtc_timer_tick when ADSB processes are running
|
||||
if (ac_details_view_active)
|
||||
return;
|
||||
|
||||
// Sort and truncate the entries, grouped, newest group first
|
||||
sort_entries_by_state();
|
||||
truncate_entries(recent);
|
||||
@@ -511,7 +521,6 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
|
||||
baseband::set_adsb();
|
||||
|
||||
receiver_model.set_target_frequency(1'090'000'000);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +342,7 @@ class ADSBRxView : public View {
|
||||
|
||||
private:
|
||||
RxRadioState radio_state_{
|
||||
1090000000 /* frequency */,
|
||||
2500000 /* bandwidth */,
|
||||
2000000 /* sampling rate */,
|
||||
ReceiverModel::Mode::SpectrumAnalysis};
|
||||
|
||||
@@ -296,6 +296,9 @@ void ADSBTxView::generate_frames() {
|
||||
void ADSBTxView::start_tx() {
|
||||
generate_frames();
|
||||
|
||||
/* Already tested , with SDR Angel + another Hackrf RX and dump1090 + SDR RLT. Final conclusion is TX LPF 6 Mhz is
|
||||
* the best settings to fulfill ADSB transponder spectrum mask requirements (<=-20 dB's at +-7Mhz , <=-40 dB's at +-23Mhz )
|
||||
* and not showing any ADSB data decoding degradation.*/
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_adsb();
|
||||
|
||||
@@ -203,7 +203,8 @@ class ADSBTxView : public View {
|
||||
};*/
|
||||
|
||||
TxRadioState radio_state_{
|
||||
10000000 /* bandwidth */,
|
||||
1090000000 /* frequency */,
|
||||
6000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
|
||||
@@ -190,7 +190,11 @@ class APRSRxView : public View {
|
||||
bool reset_console = false;
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
RxRadioState radio_state_{
|
||||
144390000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_aprs", app_settings::Mode::RX};
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class APRSTXView : public View {
|
||||
|
||||
private:
|
||||
TxRadioState radio_state_{
|
||||
144390000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
AFSK_TX_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -54,7 +54,6 @@ BTLERxView::BTLERxView(NavigationView& nav)
|
||||
&console});
|
||||
|
||||
// Auto-configure modem for LCR RX (TODO: remove later)
|
||||
field_frequency.set_value(2426000000);
|
||||
auto def_bell202 = &modem_defs[0];
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
|
||||
serial_format_t serial_format;
|
||||
|
||||
@@ -50,6 +50,7 @@ class BTLERxView : public View {
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
2426000000 /* frequency */,
|
||||
4000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */,
|
||||
ReceiverModel::Mode::WidebandFMAudio};
|
||||
|
||||
@@ -52,11 +52,12 @@ class CoasterPagerView : public View {
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
433920000, /* frequency */
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_coaster", app_settings::Mode::TX};
|
||||
"tx_burger", app_settings::Mode::TX};
|
||||
|
||||
void start_tx();
|
||||
void generate_frame();
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_external_items_menu_loader.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@@ -400,7 +401,6 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
add_items({
|
||||
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }},
|
||||
{"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { portapack::persistent_memory::debug_dump(); }},
|
||||
//{"Fonts Viewer", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<DebugFontsView>(); }}, // temporarily disabled to conserve ROM space
|
||||
{"M0 Stack Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { stack_dump(); }},
|
||||
{"Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugMemoryView>(); }},
|
||||
{"P.Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }},
|
||||
@@ -410,6 +410,11 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<DebugScreenTest>(); }},
|
||||
});
|
||||
|
||||
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav)) {
|
||||
add_item(gridItem);
|
||||
};
|
||||
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -469,41 +474,6 @@ uint32_t DebugPmemView::registers_widget_feed(const size_t register_number) {
|
||||
return data.regfile[(page_size * page + register_number) / 4] >> (register_number % 4 * 8);
|
||||
}
|
||||
|
||||
/* DebugFontsView *******************************************************/
|
||||
|
||||
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name) {
|
||||
auto char_width{font_style->font.char_width()};
|
||||
auto char_height{font_style->font.line_height()};
|
||||
auto cpl{((screen_width / char_width) - 6) & 0xF8}; // Display a multiple of 8 characters per line
|
||||
uint16_t line_pos{y_offset};
|
||||
|
||||
painter.draw_string({0, y_offset}, *font_style, font_name);
|
||||
|
||||
// Displaying ASCII+extended characters from 0x20 to 0xFF
|
||||
for (uint8_t c = 0; c <= 0xDF; c++) {
|
||||
line_pos = y_offset + ((c / cpl) + 2) * char_height;
|
||||
|
||||
if ((c % cpl) == 0)
|
||||
painter.draw_string({0, line_pos}, *font_style, "Ox" + to_string_hex(c + 0x20, 2));
|
||||
|
||||
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
|
||||
}
|
||||
|
||||
return line_pos + char_height;
|
||||
}
|
||||
|
||||
void DebugFontsView::paint(Painter& painter) {
|
||||
int16_t line_pos;
|
||||
|
||||
line_pos = display_font(painter, 32, &Styles::white, "Fixed 8x16");
|
||||
display_font(painter, line_pos + 16, &Styles::white_small, "Fixed 5x8");
|
||||
}
|
||||
|
||||
DebugFontsView::DebugFontsView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
/* DebugScreenTest ****************************************************/
|
||||
|
||||
DebugScreenTest::DebugScreenTest(NavigationView& nav)
|
||||
|
||||
@@ -310,17 +310,6 @@ class DebugPmemView : public View {
|
||||
uint32_t registers_widget_feed(const size_t register_number);
|
||||
};
|
||||
|
||||
class DebugFontsView : public View {
|
||||
public:
|
||||
DebugFontsView(NavigationView& nav);
|
||||
void paint(Painter& painter) override;
|
||||
std::string title() const override { return "Fonts"; };
|
||||
|
||||
private:
|
||||
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name);
|
||||
NavigationView& nav_;
|
||||
};
|
||||
|
||||
class DebugScreenTest : public View {
|
||||
public:
|
||||
DebugScreenTest(NavigationView& nav);
|
||||
|
||||
@@ -92,7 +92,9 @@ DfuMenu2::DfuMenu2(NavigationView& nav)
|
||||
&text_info_line_5,
|
||||
&text_info_line_6,
|
||||
&text_info_line_7,
|
||||
&text_info_line_8});
|
||||
&text_info_line_8,
|
||||
&text_info_line_9,
|
||||
&text_info_line_10});
|
||||
}
|
||||
|
||||
void DfuMenu2::paint(Painter& painter) {
|
||||
@@ -103,10 +105,12 @@ void DfuMenu2::paint(Painter& painter) {
|
||||
text_info_line_5.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
|
||||
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
|
||||
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
|
||||
text_info_line_8.set("");
|
||||
text_info_line_8.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
|
||||
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
|
||||
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
|
||||
|
||||
constexpr auto margin = 5;
|
||||
constexpr auto lines = 8 + 2;
|
||||
constexpr auto lines = 10 + 2;
|
||||
|
||||
painter.fill_rectangle(
|
||||
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
|
||||
|
||||
@@ -77,17 +77,19 @@ class DfuMenu2 : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 8 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Receiver"};
|
||||
Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 14 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Radio Settings"};
|
||||
|
||||
Labels labels{
|
||||
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "Tgt freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "Bandwidt:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "Sampl Rt:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "RX Freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "RX BW:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "RX SampR:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "", Color::dark_cyan()}};
|
||||
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()}};
|
||||
|
||||
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_2{{14 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
@@ -97,6 +99,8 @@ class DfuMenu2 : public View {
|
||||
Text text_info_line_6{{14 * CHARACTER_WIDTH, 10 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_7{{14 * CHARACTER_WIDTH, 11 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_8{{14 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_9{{14 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_10{{14 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -269,6 +269,9 @@ void EncodersView::start_tx(const bool scan) {
|
||||
repeat_index = 1;
|
||||
update_progress();
|
||||
|
||||
/* Setting TX LPF 1M75 in this TX OOK App , We got same results as fw 1.7.4
|
||||
* Looking max BW of this app, we tested , Selecting OOK type 145026 with CLK 455K and max DEV. 150k,
|
||||
* and we got BW +-2Mhz , with that TX LPF 1M75, it is fine.*/
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_ook_data(
|
||||
|
||||
@@ -185,6 +185,7 @@ class EncodersView : public View {
|
||||
};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
#include "ui_iq_trim.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
@@ -566,6 +567,7 @@ FileManagerView::FileManagerView(
|
||||
&button_new_file,
|
||||
&button_open_notepad,
|
||||
&button_rename_timestamp,
|
||||
&button_open_iq_trim,
|
||||
});
|
||||
|
||||
menu_view.on_highlight = [this]() {
|
||||
@@ -648,6 +650,14 @@ FileManagerView::FileManagerView(
|
||||
} else
|
||||
nav_.display_modal("Timestamp Rename", "Can't rename that.");
|
||||
};
|
||||
|
||||
button_open_iq_trim.on_select = [this]() {
|
||||
auto path = get_selected_full_path();
|
||||
if (selected_is_valid() && !get_selected_entry().is_directory && is_cxx_capture_file(path)) {
|
||||
nav_.push<IQTrimView>(path);
|
||||
} else
|
||||
nav_.display_modal("IQ Trim", "Not a capture file.");
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -272,6 +272,12 @@ class FileManagerView : public FileManBaseView {
|
||||
&bitmap_icon_options_datetime,
|
||||
Color::orange(),
|
||||
/*vcenter*/ true};
|
||||
|
||||
NewButton button_open_iq_trim{
|
||||
{9 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_trim,
|
||||
Color::orange()};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -153,28 +153,14 @@ FrequencySaveView::FrequencySaveView(
|
||||
add_children(
|
||||
{&labels,
|
||||
&big_display,
|
||||
&button_clear,
|
||||
&button_edit,
|
||||
&button_save,
|
||||
&text_description});
|
||||
&field_description});
|
||||
|
||||
entry_.type = freqman_type::Single;
|
||||
entry_.frequency_a = value;
|
||||
entry_.description = to_string_timestamp(rtc_time::now());
|
||||
refresh_ui();
|
||||
|
||||
button_clear.on_select = [this, &nav](Button&) {
|
||||
entry_.description = "";
|
||||
refresh_ui();
|
||||
};
|
||||
|
||||
button_edit.on_select = [this, &nav](Button&) {
|
||||
temp_buffer_ = entry_.description;
|
||||
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
|
||||
entry_.description = new_desc;
|
||||
refresh_ui();
|
||||
});
|
||||
};
|
||||
bind(field_description, entry_.description, nav);
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
db_.insert_entry(db_.entry_count(), entry_);
|
||||
@@ -182,9 +168,13 @@ FrequencySaveView::FrequencySaveView(
|
||||
};
|
||||
}
|
||||
|
||||
void FrequencySaveView::focus() {
|
||||
refresh_ui();
|
||||
FreqManBaseView::focus();
|
||||
}
|
||||
|
||||
void FrequencySaveView::refresh_ui() {
|
||||
big_display.set(entry_.frequency_a);
|
||||
text_description.set(entry_.description);
|
||||
}
|
||||
|
||||
/* FrequencyLoadView *************************************/
|
||||
|
||||
@@ -87,9 +87,9 @@ class FrequencySaveView : public FreqManBaseView {
|
||||
public:
|
||||
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
|
||||
std::string title() const override { return "Save freq"; }
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
std::string temp_buffer_{};
|
||||
freqman_entry entry_{};
|
||||
|
||||
void refresh_ui();
|
||||
@@ -101,15 +101,9 @@ class FrequencySaveView : public FreqManBaseView {
|
||||
Labels labels{
|
||||
{{0 * 8, 6 * 16}, "Description:", Color::white()}};
|
||||
|
||||
Text text_description{{0 * 8, 7 * 16, 30 * 8, 1 * 16}};
|
||||
|
||||
Button button_clear{
|
||||
{4 * 8, 10 * 16, 10 * 8, 2 * 16},
|
||||
"Clear"};
|
||||
|
||||
Button button_edit{
|
||||
{16 * 8, 10 * 16, 10 * 8, 2 * 16},
|
||||
"Edit"};
|
||||
TextField field_description{
|
||||
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
|
||||
""};
|
||||
|
||||
Button button_save{
|
||||
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
|
||||
|
||||
@@ -47,10 +47,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
path_ = std::move(path);
|
||||
profile_capture();
|
||||
compute_range();
|
||||
refresh_ui();
|
||||
open_file(path);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -84,6 +81,18 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
};
|
||||
}
|
||||
|
||||
IQTrimView::IQTrimView(NavigationView& nav, const fs::path& path)
|
||||
: IQTrimView(nav) {
|
||||
open_file(path);
|
||||
}
|
||||
|
||||
void IQTrimView::open_file(const std::filesystem::path& path) {
|
||||
path_ = std::move(path);
|
||||
profile_capture();
|
||||
compute_range();
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
void IQTrimView::paint(Painter& painter) {
|
||||
if (info_) {
|
||||
uint32_t power_cutoff = field_cutoff.value() * static_cast<uint64_t>(info_->max_power) / 100;
|
||||
|
||||
@@ -68,12 +68,15 @@ class TrimProgressUI {
|
||||
class IQTrimView : public View {
|
||||
public:
|
||||
IQTrimView(NavigationView& nav);
|
||||
IQTrimView(NavigationView& nav, const std::filesystem::path& path);
|
||||
|
||||
std::string title() const override { return "IQ Trim"; }
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
void open_file(const std::filesystem::path& path);
|
||||
|
||||
/* Update controls with latest values. */
|
||||
void refresh_ui();
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ void JammerView::start_tx() {
|
||||
|
||||
transmitter_model.set_rf_amp(field_amp.value());
|
||||
transmitter_model.set_tx_gain(field_gain.value());
|
||||
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||
@@ -288,7 +289,8 @@ void JammerView::on_timer() {
|
||||
mscounter = 0;
|
||||
if (jamming) {
|
||||
if (cooling) {
|
||||
if (++seconds >= field_timepause.value()) { // Re-start TX
|
||||
if (++seconds >= field_timepause.value()) { // Re-start TX
|
||||
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
|
||||
transmitter_model.enable();
|
||||
button_transmit.set_text("STOP");
|
||||
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||
|
||||
@@ -101,6 +101,7 @@ class JammerView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
3500000 /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -222,8 +222,6 @@ KeyfobView::KeyfobView(
|
||||
|
||||
options_make.set_selected_index(0);
|
||||
|
||||
transmitter_model.set_target_frequency(433920000); // Fixed 433.92MHz
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
|
||||
@@ -44,6 +44,7 @@ class KeyfobView : public View {
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -127,6 +127,8 @@ void LCRView::start_tx(const bool scan) {
|
||||
|
||||
modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
|
||||
|
||||
/* It is AFSK modulation , measuring original fw 1.7.4 spectrum BW is just around 30khz , NBFM */
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF 1M75, same spectrum as previous fw 1.7.4
|
||||
transmitter_model.enable();
|
||||
|
||||
memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data));
|
||||
|
||||
@@ -82,6 +82,7 @@ class LCRView : public View {
|
||||
};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
AFSK_TX_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -87,6 +87,10 @@ void MicTXView::set_tx(bool enable) {
|
||||
transmitting = true;
|
||||
configure_baseband();
|
||||
transmitter_model.set_target_frequency(tx_frequency); // Now, no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp);
|
||||
|
||||
/* The max. Power Spectrum Densitiy in WFM with High tone mod level (80%) and high 32kHZ subtone as fmod. with max fdeviation 150k ,
|
||||
BW aprox = 2 *(150K + 32K) = 364khz, then we just select the minimum TX LPF 1M75. */
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000);
|
||||
transmitter_model.enable();
|
||||
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
|
||||
} else {
|
||||
|
||||
@@ -82,6 +82,7 @@ class MicTXView : public View {
|
||||
|
||||
RxRadioState rx_radio_state_{};
|
||||
TxRadioState tx_radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
sampling_rate /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -117,6 +117,8 @@ bool MorseView::start_tx() {
|
||||
}
|
||||
|
||||
progressbar.set_max(symbol_count);
|
||||
// By experimental spectrum measurment, we left exactly same TX LPF settings as previous fw 1.7.4, TX LPF= 1M75 (min).
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF .already tested in FM morse max tone 9,999k , max dev 150khz
|
||||
transmitter_model.enable();
|
||||
|
||||
if (modulation == CW) {
|
||||
|
||||
@@ -70,6 +70,7 @@ class MorseView : public View {
|
||||
uint32_t time_units{0};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -54,7 +54,6 @@ NRFRxView::NRFRxView(NavigationView& nav)
|
||||
&console});
|
||||
|
||||
// Auto-configure modem for LCR RX (will be removed later)
|
||||
field_frequency.set_value(2480000000);
|
||||
auto def_bell202 = &modem_defs[0];
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
|
||||
serial_format_t serial_format;
|
||||
|
||||
@@ -50,6 +50,7 @@ class NRFRxView : public View {
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
2480000000 /* frequency */,
|
||||
4000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */,
|
||||
ReceiverModel::Mode::WidebandFMAudio};
|
||||
|
||||
@@ -56,6 +56,7 @@ class NumbersStationView : public View {
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -56,6 +56,7 @@ class NuoptixView : public View {
|
||||
};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -273,7 +273,7 @@ void PlaylistView::send_current_track() {
|
||||
// ReplayThread starts immediately on construction; must be set before creating.
|
||||
transmitter_model.set_target_frequency(current()->metadata.center_frequency);
|
||||
transmitter_model.set_sampling_rate(get_actual_sample_rate(current()->metadata.sample_rate));
|
||||
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
transmitter_model.set_baseband_bandwidth(current()->metadata.sample_rate <= 500'000 ? 1'750'000 : 2'500'000); // TX LPF min 1M75 for SR <=500K, and 2M5 (by experimental test) for SR >500K
|
||||
transmitter_model.enable();
|
||||
|
||||
// Reset the transmit progress bar.
|
||||
|
||||
@@ -60,7 +60,6 @@ class PlaylistView : public View {
|
||||
|
||||
// More header == less spectrum view.
|
||||
static constexpr ui::Dim header_height = 6 * 16;
|
||||
static constexpr uint32_t baseband_bandwidth = 2'500'000;
|
||||
|
||||
struct playlist_entry {
|
||||
std::filesystem::path path{};
|
||||
|
||||
@@ -81,6 +81,8 @@ bool POCSAGTXView::start_tx() {
|
||||
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_tx_gain(40);
|
||||
// We left exactly same TX LPF settings as previous fw 1.7.4, TX LPF= 1M75 (min). It is fine even in max FM dev. 150khz and 2400 bauds.
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF . Pocsag is NBFM , using BW channel 25khz or 12khz
|
||||
transmitter_model.enable();
|
||||
|
||||
uint8_t* data_ptr = shared_memory.bb_data.data;
|
||||
|
||||
@@ -60,6 +60,7 @@ class POCSAGTXView : public View {
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
466175000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -142,6 +142,7 @@ class RDSView : public View {
|
||||
RDS_flags rds_flags{};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -103,7 +103,6 @@ void ReconView::reset_indexes() {
|
||||
void ReconView::update_description() {
|
||||
if (frequency_list.empty() || current_entry().description.empty()) {
|
||||
description = "...no description...";
|
||||
return;
|
||||
} else {
|
||||
switch (current_entry().type) {
|
||||
case freqman_type::Range:
|
||||
@@ -117,7 +116,6 @@ void ReconView::update_description() {
|
||||
}
|
||||
description += current_entry().description;
|
||||
}
|
||||
|
||||
desc_cycle.set(description);
|
||||
}
|
||||
|
||||
@@ -496,6 +494,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
|
||||
button_manual_recon.on_select = [this](Button&) {
|
||||
button_remove.set_text("DELETE");
|
||||
button_add.hidden(false);
|
||||
scanner_mode = false;
|
||||
manual_mode = true;
|
||||
recon_pause();
|
||||
@@ -539,7 +538,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
button_scanner_mode.set_style(&Styles::white);
|
||||
button_scanner_mode.set_text("MANUAL");
|
||||
file_name.set_style(&Styles::white);
|
||||
file_name.set("MANUAL RANGE RECON");
|
||||
file_name.set("MANUAL => " + output_file);
|
||||
desc_cycle.set_style(&Styles::white);
|
||||
|
||||
last_entry.modulation = freqman_invalid_index;
|
||||
@@ -608,12 +607,12 @@ ReconView::ReconView(NavigationView& nav)
|
||||
scanner_mode = false;
|
||||
button_scanner_mode.set_style(&Styles::blue);
|
||||
button_scanner_mode.set_text("RECON");
|
||||
button_remove.set_text("DELETE");
|
||||
button_remove.set_text("REMOVE");
|
||||
} else {
|
||||
scanner_mode = true;
|
||||
button_scanner_mode.set_style(&Styles::red);
|
||||
button_scanner_mode.set_text("SCAN");
|
||||
button_scanner_mode.set_text("REMOVE");
|
||||
button_remove.set_text("DELETE");
|
||||
}
|
||||
frequency_file_load(true);
|
||||
if (autostart) {
|
||||
@@ -621,12 +620,17 @@ ReconView::ReconView(NavigationView& nav)
|
||||
} else {
|
||||
recon_pause();
|
||||
}
|
||||
button_add.hidden(scanner_mode);
|
||||
if (scanner_mode) // only needed when hiding, UI mayhem
|
||||
set_dirty();
|
||||
};
|
||||
|
||||
button_config.on_select = [this, &nav](Button&) {
|
||||
if (is_recording) // disabling config while recording
|
||||
return;
|
||||
|
||||
clear_freqlist_for_ui_action();
|
||||
|
||||
freq_lock = 0;
|
||||
timer = 0;
|
||||
auto open_view = nav.push<ReconSetupView>(input_file, output_file);
|
||||
@@ -733,30 +737,22 @@ void ReconView::frequency_file_load(bool) {
|
||||
button_scanner_mode.set_text("RECON");
|
||||
}
|
||||
|
||||
file_name.set(file_input + "=>" + output_file);
|
||||
|
||||
freqman_load_options options{
|
||||
.load_freqs = load_freqs,
|
||||
.load_ranges = load_ranges,
|
||||
.load_hamradios = load_hamradios};
|
||||
if (!load_freqman_file(file_input, frequency_list, options)) {
|
||||
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
|
||||
file_name.set_style(&Styles::red);
|
||||
desc_cycle.set(" NO " + file_input + ".TXT FILE ...");
|
||||
file_name.set("=> NO DATA");
|
||||
} else {
|
||||
file_name.set(file_input + "=>" + output_file);
|
||||
if (frequency_list.size() == 0) {
|
||||
file_name.set_style(&Styles::red);
|
||||
desc_cycle.set("/0 no entries in list");
|
||||
file_name.set("BadOrEmpty " + file_input);
|
||||
} else {
|
||||
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
|
||||
file_name.set_style(&Styles::yellow);
|
||||
}
|
||||
}
|
||||
desc_cycle.set("...empty file...");
|
||||
frequency_list.clear();
|
||||
text_cycle.set_text(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (frequency_list.empty()) {
|
||||
text_cycle.set_text(" ");
|
||||
return; // Can't really do much.
|
||||
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
|
||||
file_name.set_style(&Styles::yellow);
|
||||
}
|
||||
|
||||
reset_indexes();
|
||||
@@ -781,19 +777,8 @@ void ReconView::frequency_file_load(bool) {
|
||||
}
|
||||
|
||||
void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
systime_t time_interval = 100;
|
||||
uint32_t local_recon_lock_duration = recon_lock_duration;
|
||||
|
||||
chrono_end = chTimeNow();
|
||||
if (field_mode.selected_index_value() == SPEC_MODULATION) {
|
||||
time_interval = chrono_end - chrono_start;
|
||||
if (field_lock_wait.value() == 0) {
|
||||
if (time_interval <= 1) // capping here to avoid freeze because too quick
|
||||
local_recon_lock_duration = 2; // minimum working tested value
|
||||
else
|
||||
local_recon_lock_duration = time_interval;
|
||||
}
|
||||
}
|
||||
systime_t time_interval = chrono_end - chrono_start;
|
||||
chrono_start = chrono_end;
|
||||
|
||||
// hack to reload the list if it was cleared by going into CONFIG
|
||||
@@ -813,7 +798,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (!timer) {
|
||||
status = 0;
|
||||
freq_lock = 0;
|
||||
timer = local_recon_lock_duration;
|
||||
timer = recon_lock_duration;
|
||||
}
|
||||
if (freq_lock < recon_lock_nb_match) // LOCKING
|
||||
{
|
||||
@@ -1214,14 +1199,6 @@ void ReconView::handle_remove_current_item() {
|
||||
if (current_is_valid()) {
|
||||
frequency_list.erase(frequency_list.begin() + current_index);
|
||||
}
|
||||
|
||||
if (frequency_list.size() > 0) {
|
||||
current_index = clip<int32_t>(current_index, 0u, frequency_list.size() - 1);
|
||||
text_cycle.set_text(to_string_dec_uint(current_index + 1, 3));
|
||||
update_description();
|
||||
} else {
|
||||
current_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// In Scanner or Manual mode, remove the entry from the output file.
|
||||
@@ -1232,6 +1209,17 @@ void ReconView::handle_remove_current_item() {
|
||||
|
||||
freq_db.delete_entry(entry);
|
||||
}
|
||||
|
||||
// Clip
|
||||
if (frequency_list.size() > 0) {
|
||||
current_index = clip<int32_t>(current_index, 0u, frequency_list.size() - 1);
|
||||
text_cycle.set_text(to_string_dec_uint(current_index + 1, 3));
|
||||
} else {
|
||||
current_index = 0;
|
||||
text_cycle.set_text(" ");
|
||||
}
|
||||
|
||||
update_description();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -303,7 +303,7 @@ class ReconView : public View {
|
||||
NumberField field_lock_wait{
|
||||
{25 * 8, 24 * 8 + 4},
|
||||
4,
|
||||
{0, RECON_MAX_LOCK_DURATION},
|
||||
{STATS_UPDATE_INTERVAL, RECON_MAX_LOCK_DURATION},
|
||||
STATS_UPDATE_INTERVAL,
|
||||
' ',
|
||||
};
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#include "ui_search.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "binder.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -47,8 +49,61 @@ void RecentEntriesTable<SearchRecentEntries>::draw(
|
||||
painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration);
|
||||
}
|
||||
|
||||
void SearchView::focus() {
|
||||
field_frequency_min.focus();
|
||||
/* SearchView ********************************************/
|
||||
|
||||
SearchView::SearchView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_threshold,
|
||||
&text_mean,
|
||||
&text_slices,
|
||||
&text_rate,
|
||||
&text_infos,
|
||||
&vu_max,
|
||||
&progress_timers,
|
||||
&check_snap,
|
||||
&options_snap,
|
||||
&big_display,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
||||
|
||||
recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, 12 * 8});
|
||||
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
||||
nav.push<FrequencySaveView>(entry.frequency);
|
||||
};
|
||||
|
||||
text_mean.set_style(&Styles::grey);
|
||||
text_slices.set_style(&Styles::grey);
|
||||
text_rate.set_style(&Styles::grey);
|
||||
progress_timers.set_style(&Styles::grey);
|
||||
big_display.set_style(&Styles::grey);
|
||||
|
||||
field_frequency_min.set_step(100'000);
|
||||
bind(field_frequency_min, settings_.freq_min, nav, [this](auto) {
|
||||
on_range_changed();
|
||||
});
|
||||
|
||||
field_frequency_max.set_step(100'000);
|
||||
bind(field_frequency_max, settings_.freq_max, nav, [this](auto) {
|
||||
on_range_changed();
|
||||
});
|
||||
|
||||
bind(field_threshold, settings_.power_threshold);
|
||||
bind(check_snap, settings_.snap_search);
|
||||
bind(options_snap, settings_.snap_step);
|
||||
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
|
||||
on_range_changed();
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
SearchView::~SearchView() {
|
||||
@@ -56,6 +111,18 @@ SearchView::~SearchView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void SearchView::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void SearchView::focus() {
|
||||
field_frequency_min.focus();
|
||||
}
|
||||
|
||||
void SearchView::do_detection() {
|
||||
uint8_t power_max = 0;
|
||||
int32_t bin_max = -1;
|
||||
@@ -83,7 +150,7 @@ void SearchView::do_detection() {
|
||||
if (power > overall_power_max)
|
||||
overall_power_max = power;
|
||||
|
||||
if ((power >= mean_power + power_threshold) && (power > power_max)) {
|
||||
if ((power >= mean_power + settings_.power_threshold) && (power > power_max)) {
|
||||
power_max = power;
|
||||
bin_max = slices[slice].max_index;
|
||||
slice_max = slice;
|
||||
@@ -104,7 +171,7 @@ void SearchView::do_detection() {
|
||||
}
|
||||
|
||||
// Check range
|
||||
if ((resolved_frequency >= f_min) && (resolved_frequency <= f_max)) {
|
||||
if ((resolved_frequency >= settings_.freq_min) && (resolved_frequency <= settings_.freq_max)) {
|
||||
duration = 0;
|
||||
|
||||
auto& entry = ::on_packet(recent, resolved_frequency);
|
||||
@@ -159,16 +226,47 @@ void SearchView::do_detection() {
|
||||
}
|
||||
}
|
||||
|
||||
void SearchView::add_spectrum_pixel(Color color) {
|
||||
// Is avoiding floats really necessary ?
|
||||
bin_skip_acc += bin_skip_frac;
|
||||
if (bin_skip_acc < 0x10000)
|
||||
return;
|
||||
void SearchView::do_timers() {
|
||||
if (timing_div >= 60) {
|
||||
// ~1Hz
|
||||
|
||||
bin_skip_acc -= 0x10000;
|
||||
timing_div = 0;
|
||||
|
||||
if (pixel_index < 240)
|
||||
spectrum_row[pixel_index++] = color;
|
||||
// Update scan rate
|
||||
text_rate.set(to_string_dec_uint(search_counter, 3));
|
||||
search_counter = 0;
|
||||
}
|
||||
|
||||
if (timing_div % 12 == 0) {
|
||||
// ~5Hz
|
||||
|
||||
// Update power levels
|
||||
text_mean.set(to_string_dec_uint(mean_power, 3));
|
||||
|
||||
vu_max.set_value(overall_power_max);
|
||||
vu_max.set_mark(mean_power + settings_.power_threshold);
|
||||
}
|
||||
|
||||
if (timing_div % 6 == 0) {
|
||||
// ~10Hz
|
||||
|
||||
// Update timing indicator
|
||||
if (locked) {
|
||||
progress_timers.set_max(RELEASE_DELAY);
|
||||
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
||||
} else {
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
progress_timers.set_value(detect_timer);
|
||||
}
|
||||
|
||||
// Increment timers
|
||||
if (detect_timer < DETECT_DELAY) detect_timer++;
|
||||
if (release_timer < RELEASE_DELAY) release_timer++;
|
||||
|
||||
if (locked) duration++;
|
||||
}
|
||||
|
||||
timing_div++;
|
||||
}
|
||||
|
||||
void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
@@ -221,22 +319,14 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_show() {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void SearchView::on_hide() {
|
||||
baseband::spectrum_streaming_stop();
|
||||
}
|
||||
|
||||
void SearchView::on_range_changed() {
|
||||
rf::Frequency slices_span, center_frequency;
|
||||
rf::Frequency slices_span;
|
||||
rf::Frequency center_frequency;
|
||||
int64_t offset;
|
||||
size_t slice;
|
||||
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = abs(f_max - f_min);
|
||||
// TODO: enforce min < max?
|
||||
search_span = abs(settings_.freq_max - settings_.freq_min);
|
||||
|
||||
if (search_span > SEARCH_SLICE_WIDTH) {
|
||||
// ex: 100M~115M (15M span):
|
||||
@@ -253,14 +343,14 @@ void SearchView::on_range_changed() {
|
||||
// offset = 0 + 2.5/2 = 1.25M
|
||||
offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2);
|
||||
// slice_start = 100M + 1.25M = 101.25M
|
||||
center_frequency = std::min(f_min, f_max) + offset;
|
||||
center_frequency = std::min(settings_.freq_min, settings_.freq_max) + offset;
|
||||
|
||||
for (slice = 0; slice < slices_nb; slice++) {
|
||||
slices[slice].center_frequency = center_frequency;
|
||||
center_frequency += SEARCH_SLICE_WIDTH;
|
||||
}
|
||||
} else {
|
||||
slices[0].center_frequency = (f_max + f_min) / 2;
|
||||
slices[0].center_frequency = (settings_.freq_max + settings_.freq_min) / 2;
|
||||
receiver_model.set_target_frequency(slices[0].center_frequency);
|
||||
|
||||
slices_nb = 1;
|
||||
@@ -272,139 +362,16 @@ void SearchView::on_range_changed() {
|
||||
slice_counter = 0;
|
||||
}
|
||||
|
||||
void SearchView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
void SearchView::add_spectrum_pixel(Color color) {
|
||||
// Is avoiding floats really necessary?
|
||||
bin_skip_acc += bin_skip_frac;
|
||||
if (bin_skip_acc < 0x10000)
|
||||
return;
|
||||
|
||||
void SearchView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
bin_skip_acc -= 0x10000;
|
||||
|
||||
void SearchView::do_timers() {
|
||||
if (timing_div >= 60) {
|
||||
// ~1Hz
|
||||
|
||||
timing_div = 0;
|
||||
|
||||
// Update scan rate
|
||||
text_rate.set(to_string_dec_uint(search_counter, 3));
|
||||
search_counter = 0;
|
||||
}
|
||||
|
||||
if (timing_div % 12 == 0) {
|
||||
// ~5Hz
|
||||
|
||||
// Update power levels
|
||||
text_mean.set(to_string_dec_uint(mean_power, 3));
|
||||
|
||||
vu_max.set_value(overall_power_max);
|
||||
vu_max.set_mark(mean_power + power_threshold);
|
||||
}
|
||||
|
||||
if (timing_div % 6 == 0) {
|
||||
// ~10Hz
|
||||
|
||||
// Update timing indicator
|
||||
if (locked) {
|
||||
progress_timers.set_max(RELEASE_DELAY);
|
||||
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
||||
} else {
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
progress_timers.set_value(detect_timer);
|
||||
}
|
||||
|
||||
// Increment timers
|
||||
if (detect_timer < DETECT_DELAY) detect_timer++;
|
||||
if (release_timer < RELEASE_DELAY) release_timer++;
|
||||
|
||||
if (locked) duration++;
|
||||
}
|
||||
|
||||
timing_div++;
|
||||
}
|
||||
|
||||
SearchView::SearchView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_threshold,
|
||||
&text_mean,
|
||||
&text_slices,
|
||||
&text_rate,
|
||||
&text_infos,
|
||||
&vu_max,
|
||||
&progress_timers,
|
||||
&check_snap,
|
||||
&options_snap,
|
||||
&big_display,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
||||
|
||||
recent_entries_view.set_parent_rect({0, 28 * 8, 240, 12 * 8});
|
||||
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
||||
nav.push<FrequencyKeypadView>(entry.frequency);
|
||||
};
|
||||
|
||||
text_mean.set_style(&Styles::grey);
|
||||
text_slices.set_style(&Styles::grey);
|
||||
text_rate.set_style(&Styles::grey);
|
||||
progress_timers.set_style(&Styles::grey);
|
||||
big_display.set_style(&Styles::grey);
|
||||
|
||||
check_snap.set_value(true);
|
||||
options_snap.set_selected_index(1); // 12.5kHz
|
||||
|
||||
field_threshold.set_value(80);
|
||||
field_threshold.on_change = [this](int32_t value) {
|
||||
power_threshold = value;
|
||||
};
|
||||
|
||||
field_frequency_min.set_value(receiver_model.target_frequency() - 1000000);
|
||||
field_frequency_min.set_step(100000);
|
||||
field_frequency_min.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
field_frequency_min.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() - 1000000);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->field_frequency_min.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(receiver_model.target_frequency() + 1000000);
|
||||
field_frequency_max.set_step(100000);
|
||||
field_frequency_max.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
field_frequency_max.on_edit = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() + 1000000);
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->field_frequency_max.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
progress_timers.set_max(DETECT_DELAY);
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.enable();
|
||||
if (pixel_index < screen_width)
|
||||
spectrum_row[pixel_index++] = color;
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -91,10 +90,31 @@ class SearchView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
100'000'000 /* frequency */,
|
||||
2500000 /* bandwidth */,
|
||||
SEARCH_SLICE_WIDTH /* sampling rate */,
|
||||
ReceiverModel::Mode::SpectrumAnalysis};
|
||||
|
||||
// Settings
|
||||
struct SearchSettings {
|
||||
uint32_t power_threshold = 80;
|
||||
rf::Frequency freq_min = 100'000'000;
|
||||
rf::Frequency freq_max = 400'000'000;
|
||||
bool snap_search = true;
|
||||
uint32_t snap_step = 12'500;
|
||||
};
|
||||
SearchSettings settings_{};
|
||||
app_settings::SettingsManager app_settings_{
|
||||
"rx_search"sv,
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"power_threshold"sv, &settings_.power_threshold},
|
||||
{"freq_min"sv, &settings_.freq_min},
|
||||
{"freq_max"sv, &settings_.freq_max},
|
||||
{"snap_search"sv, &settings_.snap_search},
|
||||
{"snap_step"sv, &settings_.snap_step},
|
||||
}};
|
||||
|
||||
struct slice_t {
|
||||
rf::Frequency center_frequency;
|
||||
uint8_t max_power;
|
||||
@@ -103,33 +123,36 @@ class SearchView : public View {
|
||||
int16_t index;
|
||||
} slices[32];
|
||||
|
||||
uint32_t bin_skip_acc{0}, bin_skip_frac{};
|
||||
uint32_t pixel_index{0};
|
||||
std::array<Color, 240> spectrum_row = {0};
|
||||
ChannelSpectrumFIFO* fifo{nullptr};
|
||||
rf::Frequency f_min{0}, f_max{0};
|
||||
uint8_t detect_timer{0}, release_timer{0}, timing_div{0};
|
||||
uint8_t overall_power_max{0};
|
||||
uint32_t mean_power{0}, mean_acc{0};
|
||||
uint32_t duration{0};
|
||||
uint32_t power_threshold{80}; // Todo: Put this in persistent / settings
|
||||
rf::Frequency slice_start{0};
|
||||
uint8_t slices_nb{0};
|
||||
uint8_t slice_counter{0};
|
||||
int16_t last_bin{0};
|
||||
uint32_t last_slice{0};
|
||||
Coord last_tick_pos{0};
|
||||
rf::Frequency search_span{0}, resolved_frequency{0};
|
||||
uint16_t locked_bin{0};
|
||||
uint8_t search_counter{0};
|
||||
bool locked{false};
|
||||
uint32_t bin_skip_acc = 0;
|
||||
uint32_t bin_skip_frac = 0;
|
||||
uint32_t pixel_index = 0;
|
||||
std::array<Color, 240> spectrum_row{};
|
||||
ChannelSpectrumFIFO* fifo = nullptr;
|
||||
|
||||
uint8_t detect_timer = 0;
|
||||
uint8_t release_timer = 0;
|
||||
uint8_t timing_div = 0;
|
||||
uint8_t overall_power_max{0};
|
||||
uint32_t mean_power = 0;
|
||||
uint32_t mean_acc = 0;
|
||||
uint32_t duration = 0;
|
||||
|
||||
rf::Frequency slice_start = 0;
|
||||
uint8_t slices_nb = 0;
|
||||
uint8_t slice_counter = 0;
|
||||
int16_t last_bin = 0;
|
||||
uint32_t last_slice = 0;
|
||||
Coord last_tick_pos = 0;
|
||||
rf::Frequency search_span = 0;
|
||||
rf::Frequency resolved_frequency = 0;
|
||||
uint16_t locked_bin = 0;
|
||||
uint8_t search_counter = 0;
|
||||
bool locked = false;
|
||||
|
||||
void do_detection();
|
||||
void do_timers();
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void on_range_changed();
|
||||
void do_detection();
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void do_timers();
|
||||
void add_spectrum_pixel(Color color);
|
||||
|
||||
const RecentEntriesColumns columns{{{"Frequency", 9},
|
||||
@@ -150,6 +173,7 @@ class SearchView : public View {
|
||||
{1 * 8, 1 * 16}};
|
||||
FrequencyField field_frequency_max{
|
||||
{11 * 8, 1 * 16}};
|
||||
|
||||
LNAGainField field_lna{
|
||||
{22 * 8, 1 * 16}};
|
||||
VGAGainField field_vga{
|
||||
@@ -191,10 +215,10 @@ class SearchView : public View {
|
||||
{17 * 8, 15 * 8}, // Position
|
||||
7, // Length
|
||||
{ // Options
|
||||
{"25kHz ", 25000},
|
||||
{"12.5kHz", 12500},
|
||||
{"8.33kHz", 8333},
|
||||
{"2.5kHz", 2500},
|
||||
{"25kHz ", 25'000},
|
||||
{"12.5kHz", 12'500},
|
||||
{"8.33kHz", 8'333},
|
||||
{"2.5kHz", 2'500},
|
||||
{"500Hz", 500}}};
|
||||
|
||||
BigFrequency big_display{
|
||||
@@ -207,6 +231,7 @@ class SearchView : public View {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->fifo = message.fifo;
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
|
||||
@@ -343,34 +343,6 @@ void SetUIView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* SetAppSettingsView ************************************/
|
||||
|
||||
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
|
||||
add_children({
|
||||
&labels,
|
||||
&checkbox_load_app_settings,
|
||||
&checkbox_save_app_settings,
|
||||
&button_save,
|
||||
&button_cancel,
|
||||
});
|
||||
|
||||
checkbox_load_app_settings.set_value(pmem::load_app_settings());
|
||||
checkbox_save_app_settings.set_value(pmem::save_app_settings());
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
pmem::set_load_app_settings(checkbox_load_app_settings.value());
|
||||
pmem::set_save_app_settings(checkbox_save_app_settings.value());
|
||||
nav.pop();
|
||||
};
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void SetAppSettingsView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* SetConverterSettingsView ******************************/
|
||||
|
||||
SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
|
||||
@@ -654,7 +626,6 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SetAppSettingsView>(); }},
|
||||
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push<SetAudioView>(); }},
|
||||
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push<TouchCalibrationView>(); }},
|
||||
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetConverterSettingsView>(); }},
|
||||
|
||||
@@ -310,41 +310,6 @@ class SetUIView : public View {
|
||||
};
|
||||
};
|
||||
|
||||
class SetAppSettingsView : public View {
|
||||
public:
|
||||
SetAppSettingsView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "App Settings"; };
|
||||
|
||||
private:
|
||||
Labels labels{
|
||||
{{1 * 8, 1 * 16}, "App settings are saved to", Color::light_grey()},
|
||||
{{1 * 8, 2 * 16}, "the SD card in /SETTINGS.", Color::light_grey()},
|
||||
{{1 * 8, 3 * 16}, "Radio settings may also be", Color::light_grey()},
|
||||
{{1 * 8, 4 * 16}, "loaded or saved per app.", Color::light_grey()},
|
||||
};
|
||||
|
||||
Checkbox checkbox_load_app_settings{
|
||||
{3 * 8, 6 * 16},
|
||||
25,
|
||||
"Load radio settings"};
|
||||
|
||||
Checkbox checkbox_save_app_settings{
|
||||
{3 * 8, 8 * 16},
|
||||
25,
|
||||
"Save radio settings"};
|
||||
|
||||
Button button_save{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Save"};
|
||||
|
||||
Button button_cancel{
|
||||
{16 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Cancel",
|
||||
};
|
||||
};
|
||||
|
||||
class SetConverterSettingsView : public View {
|
||||
public:
|
||||
SetConverterSettingsView(NavigationView& nav);
|
||||
|
||||
@@ -51,6 +51,7 @@ class SigGenView : public View {
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
TxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -66,9 +66,6 @@ SondeView::SondeView(NavigationView& nav)
|
||||
&button_see_qr,
|
||||
&button_see_map});
|
||||
|
||||
if (!settings_.radio_loaded())
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
|
||||
field_frequency.set_step(500); // euquiq: was 10000, but we are using this for fine-tunning
|
||||
|
||||
geopos.set_read_only(true);
|
||||
|
||||
@@ -68,6 +68,7 @@ class SondeView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
402700000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
@@ -75,7 +76,6 @@ class SondeView : public View {
|
||||
"rx_sonde", app_settings::Mode::RX};
|
||||
|
||||
std::unique_ptr<SondeLogger> logger{};
|
||||
uint32_t target_frequency_{402700000};
|
||||
bool logging{false};
|
||||
bool use_crc{false};
|
||||
bool beep{false};
|
||||
|
||||
@@ -85,7 +85,11 @@ SpectrumPainterView::SpectrumPainterView(
|
||||
|
||||
if (tx_mode == 0 && image_input_avaliable == false)
|
||||
return;
|
||||
|
||||
/* By experimental test measurement, we got a good painted spectrum quality when selecting
|
||||
a BW GUI App range from 100k ... 2M aprox. In that range , the best TX LPF filter = 1M75 (the min)
|
||||
With this min TX LPF filter , we minimized the harmonics aliasing multipicture overlapped.
|
||||
So original fw 1.7.4., was already selecting the best setting (1M75) */
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Already tested, is the best setting for this App.
|
||||
transmitter_model.enable();
|
||||
|
||||
if (persistent_memory::stealth_mode()) {
|
||||
|
||||
@@ -160,6 +160,7 @@ void SSTVTXView::start_tx() {
|
||||
scanline_counter = 0;
|
||||
prepare_scanline(); // Preload one scanline
|
||||
|
||||
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF 1M75, same spectrum as previous fw 1.7.4
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_sstv_data(
|
||||
|
||||
@@ -62,6 +62,7 @@ class TestView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
0 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 * 2 /* sampling rate */
|
||||
};
|
||||
|
||||
@@ -85,7 +85,6 @@ void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
// transmission events.
|
||||
void TouchTunesView::start_ew() {
|
||||
// Radio
|
||||
transmitter_model.set_target_frequency(433920000);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_tx_gain(47);
|
||||
transmitter_model.enable();
|
||||
@@ -151,10 +150,7 @@ void TouchTunesView::start_tx(const uint32_t button_index) {
|
||||
|
||||
size_t bitstream_length = make_bitstream(fragments);
|
||||
|
||||
transmitter_model.set_target_frequency(433920000);
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_ook_data(
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "encoders.hpp"
|
||||
|
||||
// The coding in notpike's script is quite complex, using multiple LUTs to form the data sent to the YSO.
|
||||
// The format is actually very simple if it is rather seen as short and long gaps between pulses (as seen in many OOK remotes).
|
||||
@@ -115,8 +116,9 @@ class TouchTunesView : public View {
|
||||
|
||||
private:
|
||||
TxRadioState radio_state_{
|
||||
3500000 /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
uint32_t scan_button_index{};
|
||||
|
||||
@@ -329,6 +329,30 @@ void run_image(const spi_flash::image_tag_t image_tag) {
|
||||
}
|
||||
}
|
||||
|
||||
void run_prepared_image(const uint32_t m4_code) {
|
||||
if (baseband_image_running) {
|
||||
chDbgPanic("BBRunning");
|
||||
}
|
||||
|
||||
creg::m4txevent::clear();
|
||||
shared_memory.clear_baseband_ready();
|
||||
|
||||
m4_init_prepared(m4_code, false);
|
||||
baseband_image_running = true;
|
||||
|
||||
creg::m4txevent::enable();
|
||||
|
||||
if constexpr (enforce_core_sync) {
|
||||
// Wait up to 3 seconds for baseband to start handling events.
|
||||
auto count = 3'000u;
|
||||
while (!shared_memory.baseband_ready && --count)
|
||||
chThdSleepMilliseconds(1);
|
||||
|
||||
if (count == 0)
|
||||
chDbgPanic("Baseband Sync Fail");
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (!baseband_image_running) {
|
||||
return;
|
||||
|
||||
@@ -89,6 +89,7 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
|
||||
void request_beep();
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||
void run_prepared_image(const uint32_t m4_code);
|
||||
void shutdown();
|
||||
|
||||
void spectrum_streaming_start();
|
||||
|
||||
@@ -52,6 +52,15 @@ struct NoOp {
|
||||
* adequate lifetime of the referenced instances.
|
||||
*/
|
||||
|
||||
template <typename T, typename Fn = NoOp>
|
||||
void bind(Checkbox& check, T& value, Fn fn = Fn{}) {
|
||||
check.set_value(value);
|
||||
check.on_select = [&value, fn](Checkbox&, bool b) {
|
||||
value = b;
|
||||
fn(value);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename Fn = NoOp>
|
||||
void bind(NumberField& field, T& value, Fn fn = Fn{}) {
|
||||
field.set_value(value);
|
||||
|
||||
@@ -5645,6 +5645,44 @@ static constexpr Bitmap bitmap_icon_shift{
|
||||
{16, 16},
|
||||
bitmap_icon_shift_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_trim_data[] = {
|
||||
0x10,
|
||||
0x10,
|
||||
0x30,
|
||||
0x12,
|
||||
0x50,
|
||||
0x15,
|
||||
0x50,
|
||||
0x15,
|
||||
0x50,
|
||||
0x15,
|
||||
0x52,
|
||||
0x95,
|
||||
0x56,
|
||||
0xD5,
|
||||
0x5F,
|
||||
0xF5,
|
||||
0x56,
|
||||
0xD5,
|
||||
0x52,
|
||||
0x95,
|
||||
0x50,
|
||||
0x15,
|
||||
0x50,
|
||||
0x15,
|
||||
0x50,
|
||||
0x15,
|
||||
0x90,
|
||||
0x18,
|
||||
0x10,
|
||||
0x10,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_trim{
|
||||
{16, 16},
|
||||
bitmap_icon_trim_data};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__BITMAP_HPP__*/
|
||||
|
||||
@@ -59,6 +59,19 @@ void m4_init(const spi_flash::image_tag_t image_tag, const memory::region_t to,
|
||||
chDbgPanic("NoImg");
|
||||
}
|
||||
|
||||
void m4_init_prepared(const uint32_t m4_code, const bool full_reset) {
|
||||
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
|
||||
* with its address space and RAM without concern.
|
||||
*/
|
||||
LPC_CREG->M4MEMMAP = m4_code;
|
||||
|
||||
/* Reset M4 core and optionally all peripherals */
|
||||
LPC_RGU->RESET_CTRL[0] = (full_reset) ? (1 << 1) // PERIPH_RST
|
||||
: (1 << 13); // M4_RST
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void m4_request_shutdown() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "spi_image.hpp"
|
||||
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
|
||||
void m4_init_prepared(const uint32_t m4_code, const bool full_reset);
|
||||
void m4_request_shutdown();
|
||||
|
||||
void m0_halt();
|
||||
|
||||
@@ -57,6 +57,9 @@ uint32_t fault_address{0};
|
||||
void draw_guru_meditation_header(uint8_t source, const char* hint) {
|
||||
Painter painter;
|
||||
|
||||
// disable scroll in case a waterfall/spectrum display was active (scroll would mess up the fault display)
|
||||
portapack::display.scroll_disable();
|
||||
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
Color::red());
|
||||
@@ -134,6 +137,10 @@ void runtime_error(uint8_t source) {
|
||||
|
||||
led.off();
|
||||
|
||||
// wait for DFU button release if pressed, so we don't immediately jump into stack dump
|
||||
while (swizzled_switches() & (1 << (int)Switch::Dfu))
|
||||
;
|
||||
|
||||
while (true) {
|
||||
volatile size_t n = 1000000U;
|
||||
while (n--)
|
||||
@@ -243,10 +250,6 @@ void draw_stack_dump() {
|
||||
p = &__process_stack_base__;
|
||||
break;
|
||||
}
|
||||
|
||||
// If SEL button pressed, try writing to file (likely to hang in fault condition but heh)
|
||||
if (switches & (1 << (int)Switch::Sel))
|
||||
stack_dump();
|
||||
}
|
||||
|
||||
// clear screen to allow new range to be displayed, if we're not at the end
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_afsk_rx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::afsk_rx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<AFSKRxView>();
|
||||
}
|
||||
} // namespace ui::external_app::afsk_rx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_afsk_rx.application_information"), used)) application_information_t _application_information_afsk_rx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::afsk_rx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "AFSK",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xF8,
|
||||
0x1F,
|
||||
0x04,
|
||||
0x20,
|
||||
0x02,
|
||||
0x40,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xAB,
|
||||
0xDF,
|
||||
0xAB,
|
||||
0xDF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
Vendored
+5
-4
@@ -32,20 +32,21 @@
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::afsk_rx {
|
||||
|
||||
void AFSKLogger::log_raw_data(const std::string& data) {
|
||||
log_file.write_entry(data);
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
|
||||
void AFSKRxView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
AFSKRxView::AFSKRxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({&rssi,
|
||||
&channel,
|
||||
@@ -147,4 +148,4 @@ AFSKRxView::~AFSKRxView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::afsk_rx
|
||||
Vendored
+5
-3
@@ -33,6 +33,10 @@
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::afsk_rx {
|
||||
|
||||
class AFSKLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
@@ -45,8 +49,6 @@ class AFSKLogger {
|
||||
LogFile log_file{};
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AFSKRxView : public View {
|
||||
public:
|
||||
AFSKRxView(NavigationView& nav);
|
||||
@@ -116,6 +118,6 @@ class AFSKRxView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::afsk_rx
|
||||
|
||||
#endif /*__UI_AFSK_RX_H__*/
|
||||
+2072
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_calculator.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::calculator {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<CalculatorView>();
|
||||
}
|
||||
} // namespace ui::external_app::calculator
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_calculator.application_information"), used)) application_information_t _application_information_calculator = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::calculator::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Calculator",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0xF8,
|
||||
0x1F,
|
||||
0xF8,
|
||||
0x1F,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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_calculator.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::calculator {
|
||||
|
||||
uint8_t current_key = 255;
|
||||
char display_string[10];
|
||||
uint8_t fgm = 0;
|
||||
|
||||
// interface to external code
|
||||
unsigned int mp;
|
||||
|
||||
char CHARMAP[] = {
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'H',
|
||||
'I',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'c',
|
||||
's',
|
||||
't',
|
||||
' ',
|
||||
'.',
|
||||
'*',
|
||||
'+',
|
||||
'-',
|
||||
'/',
|
||||
'!',
|
||||
'<',
|
||||
'=',
|
||||
'>',
|
||||
'^',
|
||||
'f',
|
||||
'n',
|
||||
'p',
|
||||
's',
|
||||
'm',
|
||||
'w',
|
||||
|
||||
};
|
||||
|
||||
void printcat(uint8_t c, uint8_t x) { // Print char c at position x
|
||||
display_string[x] = CHARMAP[c];
|
||||
}
|
||||
uint8_t getkey(void) { // Read keypad
|
||||
return current_key;
|
||||
}
|
||||
|
||||
#include "ivt.hpp"
|
||||
|
||||
void step() {
|
||||
loop();
|
||||
}
|
||||
|
||||
void CalculatorView::focus() {
|
||||
button_F.focus();
|
||||
}
|
||||
|
||||
CalculatorView::CalculatorView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&button_F,
|
||||
&button_7,
|
||||
&button_8,
|
||||
&button_9,
|
||||
&button_E,
|
||||
&button_4,
|
||||
&button_5,
|
||||
&button_6,
|
||||
&button_N,
|
||||
&button_1,
|
||||
&button_2,
|
||||
&button_3,
|
||||
&button_C,
|
||||
&button_0,
|
||||
&button_P,
|
||||
&button_D,
|
||||
&console});
|
||||
|
||||
button_F.on_select = [&nav, this](Button&) { on_button_press(0); };
|
||||
button_7.on_select = [&nav, this](Button&) { on_button_press(1); };
|
||||
button_8.on_select = [&nav, this](Button&) { on_button_press(2); };
|
||||
button_9.on_select = [&nav, this](Button&) { on_button_press(3); };
|
||||
|
||||
button_E.on_select = [&nav, this](Button&) { on_button_press(4); };
|
||||
button_4.on_select = [&nav, this](Button&) { on_button_press(5); };
|
||||
button_5.on_select = [&nav, this](Button&) { on_button_press(6); };
|
||||
button_6.on_select = [&nav, this](Button&) { on_button_press(7); };
|
||||
|
||||
button_N.on_select = [&nav, this](Button&) { on_button_press(8); };
|
||||
button_1.on_select = [&nav, this](Button&) { on_button_press(9); };
|
||||
button_2.on_select = [&nav, this](Button&) { on_button_press(10); };
|
||||
button_3.on_select = [&nav, this](Button&) { on_button_press(11); };
|
||||
|
||||
button_C.on_select = [&nav, this](Button&) { on_button_press(12); };
|
||||
button_0.on_select = [&nav, this](Button&) { on_button_press(13); };
|
||||
button_P.on_select = [&nav, this](Button&) { on_button_press(14); };
|
||||
button_D.on_select = [&nav, this](Button&) { on_button_press(15); };
|
||||
}
|
||||
|
||||
void CalculatorView::on_button_press(uint8_t button) {
|
||||
auto pre_fgm = fgm;
|
||||
for (int i = 0; i < 10; i++)
|
||||
display_string[i] = ' ';
|
||||
|
||||
current_key = button;
|
||||
step();
|
||||
|
||||
do {
|
||||
current_key = 255;
|
||||
step();
|
||||
} while (mp);
|
||||
|
||||
std::string d(&display_string[0], 10);
|
||||
|
||||
if (pre_fgm && button != 0) {
|
||||
console.write(" ");
|
||||
switch (button) {
|
||||
case 1:
|
||||
console.write("SUM+");
|
||||
break;
|
||||
case 2:
|
||||
console.write("PRG");
|
||||
break;
|
||||
case 3:
|
||||
console.write("/");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
console.write("SWAP");
|
||||
break;
|
||||
case 5:
|
||||
console.write("DICT");
|
||||
break;
|
||||
case 6:
|
||||
console.write("USR");
|
||||
break;
|
||||
case 7:
|
||||
console.write("*");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
console.write("ROT");
|
||||
break;
|
||||
case 9:
|
||||
console.write("RCL");
|
||||
break;
|
||||
case 10:
|
||||
console.write("STO");
|
||||
break;
|
||||
case 11:
|
||||
console.write("-");
|
||||
break;
|
||||
|
||||
case 12:
|
||||
console.write("CA");
|
||||
break;
|
||||
case 13:
|
||||
console.write("PI");
|
||||
break;
|
||||
case 14:
|
||||
console.write("INT");
|
||||
break;
|
||||
case 15:
|
||||
console.write("+");
|
||||
break;
|
||||
}
|
||||
console.writeln(d);
|
||||
} else if (button == 15) {
|
||||
console.write("\r");
|
||||
console.writeln(d);
|
||||
} else {
|
||||
console.write("\r");
|
||||
console.write(d);
|
||||
}
|
||||
|
||||
update_button_labels();
|
||||
}
|
||||
|
||||
void CalculatorView::update_button_labels() {
|
||||
if (fgm) {
|
||||
button_F.set_text("MENU");
|
||||
button_7.set_text("SUM+");
|
||||
button_8.set_text("PRG");
|
||||
button_9.set_text("/");
|
||||
|
||||
button_E.set_text("SWAP");
|
||||
button_4.set_text("DICT");
|
||||
button_5.set_text("USR");
|
||||
button_6.set_text("*");
|
||||
|
||||
button_N.set_text("ROT");
|
||||
button_1.set_text("RCL");
|
||||
button_2.set_text("STO");
|
||||
button_3.set_text("-");
|
||||
|
||||
button_C.set_text("CA");
|
||||
button_0.set_text("PI");
|
||||
button_P.set_text("INT");
|
||||
button_D.set_text("+");
|
||||
|
||||
} else {
|
||||
button_F.set_text("F");
|
||||
button_7.set_text("7");
|
||||
button_8.set_text("8");
|
||||
button_9.set_text("9");
|
||||
|
||||
button_E.set_text("E");
|
||||
button_4.set_text("4");
|
||||
button_5.set_text("5");
|
||||
button_6.set_text("6");
|
||||
|
||||
button_N.set_text("N");
|
||||
button_1.set_text("1");
|
||||
button_2.set_text("2");
|
||||
button_3.set_text("3");
|
||||
|
||||
button_C.set_text("C");
|
||||
button_0.set_text("0");
|
||||
button_P.set_text(".");
|
||||
button_D.set_text("D");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::calculator
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_calculator_H__
|
||||
#define __UI_calculator_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::calculator {
|
||||
|
||||
class CalculatorView : public View {
|
||||
public:
|
||||
CalculatorView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Calculator"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
Button button_F{{0 * 8, 11 * 16, 6 * 8, 16 + 8}, "F"};
|
||||
Button button_7{{8 * 8, 11 * 16, 6 * 8, 16 + 8}, "7"};
|
||||
Button button_8{{16 * 8, 11 * 16, 6 * 8, 16 + 8}, "8"};
|
||||
Button button_9{{24 * 8, 11 * 16, 6 * 8, 16 + 8}, "9"};
|
||||
Button button_E{{0 * 8, 13 * 16, 6 * 8, 16 + 8}, "E"};
|
||||
Button button_4{{8 * 8, 13 * 16, 6 * 8, 16 + 8}, "4"};
|
||||
Button button_5{{16 * 8, 13 * 16, 6 * 8, 16 + 8}, "5"};
|
||||
Button button_6{{24 * 8, 13 * 16, 6 * 8, 16 + 8}, "6"};
|
||||
Button button_N{{0 * 8, 15 * 16, 6 * 8, 16 + 8}, "N"};
|
||||
Button button_1{{8 * 8, 15 * 16, 6 * 8, 16 + 8}, "1"};
|
||||
Button button_2{{16 * 8, 15 * 16, 6 * 8, 16 + 8}, "2"};
|
||||
Button button_3{{24 * 8, 15 * 16, 6 * 8, 16 + 8}, "3"};
|
||||
Button button_C{{0 * 8, 17 * 16, 6 * 8, 16 + 8}, "C"};
|
||||
Button button_0{{8 * 8, 17 * 16, 6 * 8, 16 + 8}, "0"};
|
||||
Button button_P{{16 * 8, 17 * 16, 6 * 8, 16 + 8}, "."};
|
||||
Button button_D{{24 * 8, 17 * 16, 6 * 8, 16 + 8}, "D"};
|
||||
|
||||
Console console{
|
||||
{0 * 8, 0 * 16, screen_width, 10 * 16}};
|
||||
|
||||
void on_button_press(uint8_t button);
|
||||
void update_button_labels();
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::calculator
|
||||
|
||||
#endif /*__UI_calculator_H__*/
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
set(EXTCPPSRC
|
||||
|
||||
#pacman
|
||||
external/pacman/main.cpp
|
||||
external/pacman/ui_pacman.cpp
|
||||
|
||||
#afsk_rx
|
||||
external/afsk_rx/main.cpp
|
||||
external/afsk_rx/ui_afsk_rx.cpp
|
||||
|
||||
#calculator
|
||||
external/calculator/main.cpp
|
||||
external/calculator/ui_calculator.cpp
|
||||
|
||||
#font_viewer
|
||||
external/font_viewer/main.cpp
|
||||
external/font_viewer/ui_font_viewer.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
pacman
|
||||
afsk_rx
|
||||
calculator
|
||||
font_viewer
|
||||
)
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (C) 2023 Bernd Herzog
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* external apps: regions can't overlap so addresses are corrected after build */
|
||||
ram_external_app_pacman (rwx) : org = 0xEEE90000, len = 32k
|
||||
ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 32k
|
||||
ram_external_app_calculator (rwx) : org = 0xEEEB0000, len = 32k
|
||||
ram_external_app_font_viewer(rwx) : org = 0xEEEC0000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.external_app_pacman : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_pacman.application_information));
|
||||
*(*ui*external_app*pacman*);
|
||||
} > ram_external_app_pacman
|
||||
|
||||
.external_app_afsk_rx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_afsk_rx.application_information));
|
||||
*(*ui*external_app*afsk_rx*);
|
||||
} > ram_external_app_afsk_rx
|
||||
|
||||
.external_app_calculator : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_calculator.application_information));
|
||||
*(*ui*external_app*calculator*);
|
||||
} > ram_external_app_calculator
|
||||
|
||||
.external_app_font_viewer : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_font_viewer.application_information));
|
||||
*(*ui*external_app*font_viewer*);
|
||||
} > ram_external_app_font_viewer
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
*
|
||||
* 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_font_viewer.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::font_viewer {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<DebugFontsView>();
|
||||
}
|
||||
} // namespace ui::external_app::font_viewer
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_font_viewer.application_information"), used)) application_information_t _application_information_font_viewer = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::font_viewer::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Font Viewer",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x10,
|
||||
0x0C,
|
||||
0x38,
|
||||
0x0C,
|
||||
0x38,
|
||||
0x0C,
|
||||
0x6C,
|
||||
0x0C,
|
||||
0x6C,
|
||||
0x0C,
|
||||
0xC6,
|
||||
0x7C,
|
||||
0xFE,
|
||||
0xFC,
|
||||
0xFF,
|
||||
0x8D,
|
||||
0x83,
|
||||
0x8D,
|
||||
0x83,
|
||||
0xFD,
|
||||
0x01,
|
||||
0x7D,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::cyan().v,
|
||||
/*.menu_location = */ app_location_t::DEBUG,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
*
|
||||
* 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_font_viewer.hpp"
|
||||
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::font_viewer {
|
||||
|
||||
/* DebugFontsView *******************************************************/
|
||||
|
||||
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name) {
|
||||
auto char_width{font_style->font.char_width()};
|
||||
auto char_height{font_style->font.line_height()};
|
||||
auto cpl{((screen_width / char_width) - 6) & 0xF8}; // Display a multiple of 8 characters per line
|
||||
uint16_t line_pos{y_offset};
|
||||
|
||||
painter.draw_string({0, y_offset}, *font_style, font_name);
|
||||
|
||||
// Displaying ASCII+extended characters from 0x20 to 0xFF
|
||||
for (uint8_t c = 0; c <= 0xDF; c++) {
|
||||
line_pos = y_offset + ((c / cpl) + 2) * char_height;
|
||||
|
||||
if ((c % cpl) == 0)
|
||||
painter.draw_string({0, line_pos}, *font_style, "Ox" + to_string_hex(c + 0x20, 2));
|
||||
|
||||
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
|
||||
}
|
||||
|
||||
return line_pos + char_height;
|
||||
}
|
||||
|
||||
void DebugFontsView::paint(Painter& painter) {
|
||||
int16_t line_pos;
|
||||
|
||||
line_pos = display_font(painter, 32, &Styles::white, "Fixed 8x16");
|
||||
display_font(painter, line_pos + 16, &Styles::white_small, "Fixed 5x8");
|
||||
}
|
||||
|
||||
DebugFontsView::DebugFontsView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
} /* namespace ui::external_app::font_viewer */
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_DEBUG_H__
|
||||
#define __UI_DEBUG_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::font_viewer {
|
||||
|
||||
class DebugFontsView : public View {
|
||||
public:
|
||||
DebugFontsView(NavigationView& nav);
|
||||
void paint(Painter& painter) override;
|
||||
std::string title() const override { return "Fonts"; };
|
||||
|
||||
private:
|
||||
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name);
|
||||
NavigationView& nav_;
|
||||
};
|
||||
|
||||
} /* namespace ui::external_app::font_viewer */
|
||||
|
||||
#endif /*__UI_FONT_VIEWER_H__*/
|
||||
@@ -0,0 +1,66 @@
|
||||
/******************************************************************************/
|
||||
/* */
|
||||
/* PACMAN GAME FOR ARDUINO DUE */
|
||||
/* */
|
||||
/******************************************************************************/
|
||||
/* Copyright (c) 2014 Dr. NCX (mirracle.mxx@gmail.com) */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL */
|
||||
/* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED */
|
||||
/* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR */
|
||||
/* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES */
|
||||
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
|
||||
/* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
|
||||
/* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS */
|
||||
/* SOFTWARE. */
|
||||
/* */
|
||||
/* MIT license, all text above must be included in any redistribution. */
|
||||
|
||||
// #include "ili9328.h"
|
||||
|
||||
typedef uint16_t ushort;
|
||||
|
||||
#define C16(_rr, _gg, _bb) ((ushort)(((_rr & 0xF8) << 8) | ((_gg & 0xFC) << 3) | ((_bb & 0xF8) >> 3)))
|
||||
|
||||
uint16_t _paletteW[] =
|
||||
{
|
||||
C16(0, 0, 0),
|
||||
C16(255, 0, 0), // 1 red
|
||||
C16(222, 151, 81), // 2 brown
|
||||
C16(255, 0, 255), // 3 pink
|
||||
|
||||
C16(0, 0, 0),
|
||||
C16(0, 255, 255), // 5 cyan
|
||||
C16(71, 84, 255), // 6 mid blue
|
||||
C16(255, 184, 81), // 7 lt brown
|
||||
|
||||
C16(0, 0, 0),
|
||||
C16(255, 255, 0), // 9 yellow
|
||||
C16(0, 0, 0),
|
||||
C16(33, 33, 255), // 11 blue
|
||||
|
||||
C16(0, 255, 0), // 12 green
|
||||
C16(71, 84, 174), // 13 aqua
|
||||
C16(255, 184, 174), // 14 lt pink
|
||||
C16(222, 222, 255), // 15 whiteish
|
||||
};
|
||||
|
||||
void drawIndexedmap(uint8_t* indexmap, int16_t x, uint16_t y) {
|
||||
ui::Painter painter;
|
||||
|
||||
byte i = 0;
|
||||
word color = (word)_paletteW[indexmap[0]];
|
||||
for (byte tmpY = 0; tmpY < 8; tmpY++) {
|
||||
byte width = 1;
|
||||
for (byte tmpX = 0; tmpX < 8; tmpX++) {
|
||||
word next_color = (word)_paletteW[indexmap[++i]];
|
||||
if ((color != next_color && width >= 1) || tmpX == 7) {
|
||||
painter.draw_hline({x + tmpX - width + 1, y + tmpY}, width, ui::Color(color));
|
||||
|
||||
color = next_color;
|
||||
width = 0;
|
||||
}
|
||||
width++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2462
File diff suppressed because it is too large
Load Diff
+2858
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user