Compare commits

...

20 Commits

Author SHA1 Message Date
Bernd Herzog 3998dc124a added cpld info shell commands (#1703)
* added cpld info shell commands

* fixed reset
2024-01-02 00:18:53 +01:00
jLynx 3d2da9c0db Serial info command (#1700)
* WIP adding more info

* Added source output & R9 detection

* WIP

* WIP

* Moved hackrf back

* Fixed output error
2024-01-02 07:25:36 +13:00
Erwin Ried 3189d3af4c Update FUNDING.yml (#1702)
Removing bountysource, since they scammed everyone: https://github.com/bountysource/core/issues/1586
2024-01-01 15:14:37 +01:00
E.T 1b3a99cc5e Gcc warning onscreen (#1701)
* Fix GCC version detection for old CMAKE

* Yellow tint version number / hash on gcc mismatch
2023-12-31 15:46:26 -06:00
E.T 935c43e88f Gcc version warning (#1698)
* Compiler warning
* Fix warning message
2023-12-31 16:36:19 +01:00
gullradriel 2893c031ab Recon repeat delay (#1699)
- Recon repeat delay, range: from 0s to 254s, timer is played before each TX
- fixed missing '<' '>' around button names for DELETE and REMOVE name to indicate that they can also be used to skip in the list
- added missing pmem recon dump entries
2023-12-30 22:32:41 +01:00
E.T 94cdb16ca9 Mention external apps in stable release notes (#1694) 2023-12-29 15:11:40 -06:00
jLynx 84d4066986 V1.9.1 (#1691)
* Update past_version.txt

* Update version.txt
2023-12-29 22:07:43 +13:00
Mark Thompson 2153c2dd10 ERT app - Fix freq field overlapping amp setting (#1689) 2023-12-29 07:28:42 +01:00
gullradriel a7393c3492 fix for freq_b equal zero in repeater entries (#1686) 2023-12-28 20:10:38 +01:00
gullradriel 794fece8cc Recon raw repeat (#1658)
* UI for Recon Repeater config
* persistent recon repeat settings
* record_view: added possibility to force keep the provided record filename
* working auto record to same file
* Recon TX 
* added Repeater type
* adding yellow coloring on Repeater config+a modal, comments in the code
* default repeater values
2023-12-28 11:25:53 +01:00
Totoo 1bf95e85a0 Fixed #1670 (#1676) 2023-12-26 07:16:54 +01:00
Brumi-2021 a85357a8af Minor Improvement (Extending_min_CLK_out_from_10khz_to_4khz) (#1675) 2023-12-26 07:11:14 +01:00
Totoo 7bf3e02f6c Faster usb serial file download (#1674)
* Made file read much faster via USB serial
* Remove old code
2023-12-25 17:25:16 +01:00
Mark Thompson 459e8d0b24 Disallow CLKOUT freq changes on r9 (#1672) 2023-12-24 17:28:22 +01:00
Mark Thompson 93585d846a CLKOUT workaround for r9 boards (#1671) 2023-12-24 11:06:11 +01:00
Mark Thompson 4eb5c4603e Simplify CLKOUT frequency entry (#1668) 2023-12-24 08:55:15 +01:00
Mark Thompson f42ad5ffc6 Configure CLKOUT at power-up per PMEM setting (#1669) 2023-12-24 08:54:56 +01:00
gullradriel d0bd8e50be Added USB serial checkbox (#1664) 2023-12-22 11:00:27 +01:00
Mark Thompson 337e3f4449 Enhance Debug->Peripherals to view all registers of SI5351 chip (#1663) 2023-12-22 08:45:05 +01:00
46 changed files with 2230 additions and 734 deletions
+1 -1
View File
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
custom: # https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
+3 -2
View File
@@ -74,11 +74,12 @@ jobs:
**Full Changelog**: https://github.com/eried/portapack-mayhem/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
## Installation
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
__Warning:__ Since release 1.8.0, some applications has been moved to the SD card as we ran out of flash space.
### MicroSD card files
For certain functionality, like the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
For certain functionality, like external apps, the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
draft: true
prerelease: false
- name: Upload Firmware Asset
+1 -1
View File
@@ -1 +1 @@
v1.8.0
v1.9.0
+1 -1
View File
@@ -1 +1 @@
v1.9.0
v1.9.1
+2
View File
@@ -25,6 +25,8 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex
project(portapack-h1)
set(EXPECTED_GCC_VERSION "9.2.1")
set(VERSION "$ENV{VERSION_STRING}")
if ("$ENV{VERSION_STRING}" STREQUAL "")
execute_process(
+20
View File
@@ -34,6 +34,17 @@ set(LZ4 lz4)
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
# In our current build container cmake need a little help to get the version :)
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
execute_process(COMMAND bash "-c" "arm-none-eabi-g++ -v 2>&1 | grep 'gcc version' | awk '{print $3}'" OUTPUT_VARIABLE CMAKE_CXX_COMPILER_VERSION)
string(STRIP ${CMAKE_CXX_COMPILER_VERSION} CMAKE_CXX_COMPILER_VERSION)
endif()
set(GCC_VERSION_MISMATCH 0)
if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
set(GCC_VERSION_MISMATCH 1)
endif()
add_subdirectory(application)
add_subdirectory(baseband)
add_subdirectory(test)
@@ -52,6 +63,15 @@ add_custom_target(
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
)
if(${GCC_VERSION_MISMATCH})
set(COMPILER_MISMATCH_MESSAGE "WARNING: Compiler version mismatch, please use the official compiler version ${EXPECTED_GCC_VERSION} when sharing builds! Current compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
message(${COMPILER_MISMATCH_MESSAGE})
add_custom_command(
TARGET firmware POST_BUILD
COMMAND echo ${COMPILER_MISMATCH_MESSAGE}
VERBATIM)
endif()
add_custom_target(
program
COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf
+2 -2
View File
@@ -122,7 +122,6 @@ set(CSRC
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
${HACKRF_PATH}/firmware/common/usb_request.c
${HACKRF_PATH}/firmware/common/usb_standard_request.c
${CHIBIOS}/os/various/shell.c
${CHIBIOS}/os/various/chprintf.c
)
@@ -130,6 +129,7 @@ set(CSRC
# setting.
set(CPPSRC
main.cpp
shell.cpp
${COMMON}/acars_packet.cpp
${COMMON}/adsb.cpp
${COMMON}/adsb_frame.cpp
@@ -424,7 +424,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}\"' -DVERSION_MD5=${VERSION_MD5}")
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} -D'GCC_VERSION_MISMATCH=${GCC_VERSION_MISMATCH}'")
# List all default ASM defines here, like -D_DEBUG=1
set(DADEFS)
+15 -5
View File
@@ -426,6 +426,7 @@ BLERxView::BLERxView(NavigationView& nav)
&options_sort,
&label_found,
&text_found_count,
&check_serial_log,
&button_filter,
&button_save_list,
&button_clear_list,
@@ -436,7 +437,15 @@ BLERxView::BLERxView(NavigationView& nav)
nav_.push<BleRecentEntryDetailView>(entry);
};
usb_serial_thread = std::make_unique<UsbSerialThread>();
check_serial_log.on_select = [this](Checkbox&, bool v) {
serial_logging = v;
if (v) {
usb_serial_thread = std::make_unique<UsbSerialThread>();
} else {
usb_serial_thread.reset();
}
};
check_serial_log.set_value(serial_logging);
ensure_directory(find_packet_path);
ensure_directory(log_packets_path);
@@ -456,8 +465,6 @@ BLERxView::BLERxView(NavigationView& nav)
logger = std::make_unique<BLELogger>();
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
str_log = "";
logging = v;
@@ -468,6 +475,7 @@ BLERxView::BLERxView(NavigationView& nav)
"/BLELOG_" +
to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_log.set_value(logging);
button_save_list.on_select = [this, &nav](const ui::Button&) {
listFileBuffer = "";
@@ -723,8 +731,10 @@ void BLERxView::on_data(BlePacketData* packet) {
logger->log_raw_data(str_console + "\r\n");
}
usb_serial_thread->serial_str = str_console + "\r\n";
usb_serial_thread->str_ready = true;
if (serial_logging) {
usb_serial_thread->serial_str = str_console + "\r\n";
usb_serial_thread->str_ready = true;
}
str_console = "";
if (!searchList.empty()) {
+9
View File
@@ -215,6 +215,7 @@ class BLERxView : public View {
uint8_t sort_index{0};
std::string filter{};
bool logging{false};
bool serial_logging{false};
bool name_enable{true};
app_settings::SettingsManager settings_{
@@ -225,6 +226,8 @@ class BLERxView : public View {
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
// disabled to always start without USB serial activated until we can make it non blocking if not connected
// {"serial_log"sv, &serial_logging},
{"name"sv, &name_enable},
}};
@@ -320,6 +323,12 @@ class BLERxView : public View {
{11 * 8, 3 * 16, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{17 * 8, 3 * 16 - 2},
7,
"USB Log",
true};
Console console{
{0, 4 * 16, 240, 240}};
+1 -1
View File
@@ -146,7 +146,7 @@ class ERTAppView : public View {
static constexpr auto header_height = 1 * 16;
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
{0 * 8, 0 * 16},
nav_};
RFAmpField field_rf_amp{
+25 -14
View File
@@ -176,12 +176,15 @@ void RegistersWidget::paint(Painter& painter) {
void RegistersWidget::draw_legend(const Coord left, Painter& painter) {
const auto pos = screen_pos();
const std::string spaces(config.legend_length(), ' ');
for (uint32_t i = 0; i < config.registers_per_page; i += config.registers_per_row()) {
uint32_t r = page_number * config.registers_per_page + i;
for (uint32_t i = 0; i < config.registers_count; i += config.registers_per_row()) {
const Point offset{
left, static_cast<int>((i / config.registers_per_row()) * row_height)};
const auto text = to_string_hex(i, config.legend_length());
const auto text = (r >= config.registers_count) ? spaces : to_string_hex(r, config.legend_length());
painter.draw_string(
pos + offset,
style().invert(),
@@ -193,15 +196,16 @@ void RegistersWidget::draw_values(
const Coord left,
Painter& painter) {
const auto pos = screen_pos();
const std::string spaces(config.value_length(), ' ');
for (uint32_t i = 0; i < config.registers_per_page; i++) {
uint32_t r = page_number * config.registers_per_page + i;
for (uint32_t i = 0; i < config.registers_count; i++) {
const Point offset = {
static_cast<int>(left + config.legend_width() + 8 + (i % config.registers_per_row()) * (config.value_width() + 8)),
static_cast<int>((i / config.registers_per_row()) * row_height)};
const auto value = reg_read(i);
const auto text = to_string_hex(value, config.value_length());
const auto text = (r >= config.registers_count) ? spaces : to_string_hex(reg_read(r), config.value_length());
painter.draw_string(
pos + offset,
style(),
@@ -213,7 +217,7 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
if (register_number < config.registers_count) {
switch (config.chip_type) {
case CT_PMEM:
return portapack::persistent_memory::pmem_data_word((page_number * config.registers_count + register_number) / 4) >> (register_number % 4 * 8);
return portapack::persistent_memory::pmem_data_word(register_number / 4) >> (register_number % 4 * 8);
case CT_RFFC5072:
return radio::debug::first_if::register_read(register_number);
case CT_MAX283X:
@@ -272,6 +276,7 @@ RegistersView::RegistersView(
button_done.on_select = [&nav](Button&) { nav.pop(); };
registers_widget.set_parent_rect({0, 48, 240, 192});
registers_widget.set_page(0);
text_title.set_parent_rect({(240 - static_cast<int>(title.size()) * 8) / 2, 16,
static_cast<int>(title.size()) * 8, 16});
@@ -296,6 +301,13 @@ void RegistersView::focus() {
button_done.focus();
}
bool RegistersView::on_encoder(const EncoderEvent delta) {
registers_widget.set_page(std::max(0ul, std::min(registers_widget.page_count() - 1, registers_widget.page() + delta)));
registers_widget.update();
return true;
}
/* ControlsSwitchesWidget ************************************************/
void ControlsSwitchesWidget::on_show() {
@@ -429,10 +441,10 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
const char* max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
const char* si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
add_items({
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 16}); }},
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 10}); }},
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 96, 8}); }},
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_bits()}); }},
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 31, 16}); }},
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
});
set_max_rows(2); // allow wider buttons
}
@@ -504,8 +516,8 @@ void DebugMemoryDumpView::focus() {
/* DebugPmemView *********************************************************/
DebugPmemView::DebugPmemView(NavigationView& nav)
: registers_widget(RegistersWidgetConfig{CT_PMEM, page_size, 8}) {
add_children({&text_page, &registers_widget, &text_checksum, &text_checksum2, &button_ok});
: registers_widget(RegistersWidgetConfig{CT_PMEM, PMEM_SIZE_BYTES, page_size, 8}) {
add_children({&registers_widget, &text_checksum, &text_checksum2, &button_ok});
registers_widget.set_parent_rect({0, 32, 240, 192});
@@ -532,7 +544,6 @@ void DebugPmemView::focus() {
}
void DebugPmemView::update() {
text_page.set(to_string_hex(registers_widget.page() * page_size, 2) + "+");
registers_widget.update();
}
+4 -4
View File
@@ -142,6 +142,7 @@ typedef enum {
struct RegistersWidgetConfig {
chip_type_t chip_type;
uint32_t registers_count;
uint32_t registers_per_page;
uint32_t register_bits;
constexpr size_t legend_length() const {
@@ -194,6 +195,7 @@ class RegistersWidget : public Widget {
void set_page(int32_t value) { page_number = value; }
uint32_t page(void) { return page_number; }
uint32_t page_count(void) { return (config.registers_count + config.registers_per_page - 1) / config.registers_per_page; }
private:
const RegistersWidgetConfig config;
@@ -208,8 +210,8 @@ class RegistersWidget : public Widget {
class RegistersView : public View {
public:
RegistersView(NavigationView& nav, const std::string& title, RegistersWidgetConfig&& config);
void focus();
void focus() override;
bool on_encoder(const EncoderEvent delta) override;
private:
Text text_title{};
@@ -364,8 +366,6 @@ class DebugPmemView : public View {
static constexpr uint8_t page_size{96}; // Must be multiply of 4 otherwise bit shifting for register view wont work properly
static constexpr uint8_t page_count{(portapack::memory::map::backup_ram.size() + page_size - 1) / page_size};
Text text_page{{16, 16, 208, 16}};
RegistersWidget registers_widget;
Text text_checksum{{16, 232, 208, 16}};
+2 -1
View File
@@ -412,7 +412,8 @@ void FrequencyEditView::refresh_ui() {
auto is_range = entry_.type == freqman_type::Range;
auto is_ham = entry_.type == freqman_type::HamRadio;
auto has_freq_b = is_range || is_ham;
auto is_repeater = entry_.type == freqman_type::Repeater;
auto has_freq_b = is_range || is_ham || is_repeater;
field_freq_b.set_style(has_freq_b ? &Styles::white : &Styles::grey);
field_step.set_style(is_range ? &Styles::white : &Styles::grey);
+2 -1
View File
@@ -220,7 +220,8 @@ class FrequencyEditView : public View {
{"Single", 0},
{"Range", 1},
{"HamRadio", 2},
{"Raw", 3},
{"Repeater", 3},
{"Raw", 4},
}};
FrequencyField field_freq_a{{13 * 8, 4 * 16}};
+277 -50
View File
@@ -22,13 +22,24 @@
*/
#include "ui_recon.hpp"
#include "ui_fileman.hpp"
#include "ui_freqman.hpp"
#include "capture_app.hpp"
#include "convert.hpp"
#include "file.hpp"
#include "file_reader.hpp"
#include "tone_key.hpp"
#include "replay_app.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "io_convert.hpp"
#include "oversample.hpp"
#include "baseband_api.hpp"
#include "metadata_file.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "utility.hpp"
#include "replay_thread.hpp"
using namespace portapack;
using namespace tonekey;
@@ -37,6 +48,30 @@ namespace fs = std::filesystem;
namespace ui {
void ReconView::reload_restart_recon() {
frequency_file_load();
if (frequency_list.size() > 0) {
if (fwd) {
button_dir.set_text("FW>");
} else {
button_dir.set_text("<RW");
}
recon_resume();
}
if (scanner_mode) {
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON");
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
}
}
void ReconView::check_update_ranges_from_current() {
if (frequency_list.size() && current_is_valid() && current_entry().type == freqman_type::Range) {
if (update_ranges && !manual_mode) {
@@ -67,7 +102,7 @@ void ReconView::set_loop_config(bool v) {
persistent_memory::set_recon_continuous(continuous);
}
void ReconView::recon_stop_recording() {
void ReconView::recon_stop_recording(bool exiting) {
if (is_recording) {
if (field_mode.selected_index_value() == SPEC_MODULATION)
button_audio_app.set_text("RAW");
@@ -75,13 +110,17 @@ void ReconView::recon_stop_recording() {
button_audio_app.set_text("AUDIO");
button_audio_app.set_style(&Styles::white);
record_view->stop();
button_config.set_style(&Styles::white); // disable config while recording as it's causing an IO error pop up at exit
button_config.set_style(&Styles::white);
is_recording = false;
// repeater mode
if (!exiting && persistent_memory::recon_repeat_recorded()) {
start_repeat();
}
}
}
void ReconView::clear_freqlist_for_ui_action() {
recon_stop_recording();
recon_stop_recording(false);
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
// flag to detect and reload frequency_list
@@ -111,6 +150,9 @@ void ReconView::update_description() {
case freqman_type::HamRadio:
description = "H: ";
break;
case freqman_type::Repeater:
description = "L: ";
break;
default:
description = "S: ";
}
@@ -182,6 +224,7 @@ void ReconView::load_persisted_settings() {
load_freqs = persistent_memory::recon_load_freqs();
load_ranges = persistent_memory::recon_load_ranges();
load_hamradios = persistent_memory::recon_load_hamradios();
load_repeaters = persistent_memory::recon_load_repeaters();
update_ranges = persistent_memory::recon_update_ranges_when_recon();
auto_record_locked = persistent_memory::recon_auto_record_locked();
}
@@ -265,9 +308,16 @@ void ReconView::focus() {
}
ReconView::~ReconView() {
recon_stop_recording();
if (recon_tx) {
replay_thread.reset();
}
recon_stop_recording(true);
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
transmitter_model.disable();
receiver_model.disable();
baseband::shutdown();
}
@@ -275,9 +325,20 @@ ReconView::~ReconView() {
ReconView::ReconView(NavigationView& nav)
: nav_{nav} {
chrono_start = chTimeNow();
tx_view.hidden(true);
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
u"AUTO_AUDIO", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
record_view->set_auto_trim(false);
record_view->hidden(true);
record_view->on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
add_children({&labels,
&field_lna,
&field_vga,
@@ -314,13 +375,9 @@ ReconView::ReconView(NavigationView& nav)
&button_restart,
&button_mic_app,
&button_remove,
record_view.get()});
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.get(),
&progressbar,
&tx_view});
def_step = 0;
load_persisted_settings();
@@ -492,7 +549,7 @@ ReconView::ReconView(NavigationView& nav)
};
button_manual_recon.on_select = [this](Button&) {
button_remove.set_text("DELETE");
button_remove.set_text("<DELETE>");
button_add.hidden(false);
scanner_mode = false;
manual_mode = true;
@@ -566,27 +623,7 @@ ReconView::ReconView(NavigationView& nav)
};
button_restart.on_select = [this](Button&) {
frequency_file_load();
if (frequency_list.size() > 0) {
if (fwd) {
button_dir.set_text("FW>");
} else {
button_dir.set_text("<RW");
}
recon_resume();
}
if (scanner_mode) {
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON");
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
}
reload_restart_recon();
};
button_add.on_select = [this](ButtonWithEncoder&) {
@@ -606,12 +643,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("REMOVE");
button_remove.set_text("<REMOVE>");
} else {
scanner_mode = true;
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_text("SCAN");
button_remove.set_text("DELETE");
button_remove.set_text("<DELETE>");
}
frequency_file_load();
if (autostart) {
@@ -704,6 +741,9 @@ ReconView::ReconView(NavigationView& nav)
change_mode(AM_MODULATION); // start on AM.
field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector
// tx progress bar
progressbar.hidden(true);
if (filedelete) {
delete_file(freq_file_path);
}
@@ -741,7 +781,8 @@ void ReconView::frequency_file_load() {
freqman_load_options options{
.load_freqs = load_freqs,
.load_ranges = load_ranges,
.load_hamradios = load_hamradios};
.load_hamradios = load_hamradios,
.load_repeaters = load_repeaters};
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
file_name.set_style(&Styles::red);
desc_cycle.set("...empty file...");
@@ -776,6 +817,12 @@ void ReconView::frequency_file_load() {
}
void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (recon_tx)
return;
if (is_repeat_active())
return;
chrono_end = chTimeNow();
systime_t time_interval = chrono_end - chrono_start;
chrono_start = chrono_end;
@@ -804,7 +851,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (status != 1) {
status = 1;
if (wait != 0) {
recon_stop_recording();
recon_stop_recording(false);
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
}
@@ -835,9 +882,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
// contents of a possible recon_start_recording(), but not yet since it's only called once
if (auto_record_locked && !is_recording) {
button_audio_app.set_style(&Styles::red);
if (field_mode.selected_index_value() == SPEC_MODULATION)
if (field_mode.selected_index_value() == SPEC_MODULATION) {
button_audio_app.set_text("RAW REC");
else
} else
button_audio_app.set_text("WAV REC");
record_view->start();
button_config.set_style(&Styles::light_grey); // disable config while recording as it's causing an IO error pop up at exit
@@ -956,6 +1003,26 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
}
}
}
} else if (current_entry().type == freqman_type::Repeater) {
// repeater is like single, we only listen on frequency_a and then jump to next entry
if ((fwd && stepper == 0) || stepper > 0) { // forward
current_index++;
entry_has_changed = true;
// looping
if ((uint32_t)current_index >= frequency_list.size()) {
has_looped = true;
current_index = 0;
}
} else if ((!fwd && stepper == 0) || stepper < 0) {
// reverse
current_index--;
entry_has_changed = true;
// if previous if under the list => go back from end
if (current_index < 0) {
has_looped = true;
current_index = frequency_list.size() - 1;
}
}
}
// set index to boundary if !continuous
if (has_looped && !continuous) {
@@ -984,6 +1051,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (entry_has_changed) {
timer = 0;
switch (current_entry().type) {
case freqman_type::Repeater:
case freqman_type::Single:
freq = current_entry().frequency_a;
break;
@@ -1017,7 +1085,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (stepper > 0) stepper--;
} // if( recon || stepper != 0 || index_stepper != 0 )
} // if (frequency_list.size() > 0 )
} /* on_statistic_updates */
} /* on_statistics_updates */
}
handle_retune();
recon_redraw();
@@ -1080,28 +1148,39 @@ void ReconView::on_stepper_delta(int32_t v) {
}
size_t ReconView::change_mode(freqman_index_t new_mod) {
if (recon_tx || is_repeat_active())
return 0;
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
recon_stop_recording();
remove_child(record_view.get());
record_view.reset();
if (new_mod == SPEC_MODULATION) {
recon_stop_recording(false);
if (record_view != nullptr) {
remove_child(record_view.get());
record_view.reset();
}
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"RECON_REPEAT.C16", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else if (new_mod == SPEC_MODULATION) {
audio::output::stop();
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW_", u"CAPTURES",
u"AUTO_RAW", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
u"AUTO_AUDIO", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
}
record_view->set_auto_trim(false);
add_child(record_view.get());
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [this](std::string message) {
nav_.display_modal("Error", message);
};
receiver_model.disable();
transmitter_model.disable();
baseband::shutdown();
size_t recording_sampling_rate = 0;
switch (new_mod) {
@@ -1144,7 +1223,6 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
// record_view determines the correct oversampling to apply and returns the actual sample rate.
auto actual_sampling_rate = record_view->set_sampling_rate(sampling_rate);
// The radio needs to know the effective sampling rate.
receiver_model.set_sampling_rate(actual_sampling_rate);
receiver_model.set_baseband_bandwidth(filter_bandwidth_for_sampling_rate(actual_sampling_rate));
@@ -1222,4 +1300,153 @@ void ReconView::handle_remove_current_item() {
update_description();
}
void ReconView::on_repeat_tx_progress(const uint32_t progress) {
progressbar.set_value(progress);
}
void ReconView::repeat_file_error(const std::filesystem::path& path, const std::string& message) {
nav_.display_modal("Error", "Error opening file \n" + path.string() + "\n" + message);
}
bool ReconView::is_repeat_active() const {
return replay_thread != nullptr;
}
void ReconView::start_repeat() {
// Prepare to send a file.
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
if (recon_tx == false) {
recon_tx = true;
if (record_view != nullptr) {
record_view->stop();
remove_child(record_view.get());
record_view.reset();
}
receiver_model.disable();
transmitter_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_replay);
size_t rawsize = 0;
{
File capture_file;
auto error = capture_file.open(rawfile);
if (error) {
repeat_file_error(rawfile, "Can't open file to send for size");
return;
}
rawsize = capture_file.size();
}
// Reset the transmit progress bar.
uint8_t sample_size = std::filesystem::capture_file_sample_size(rawfile);
progressbar.set_value(0);
progressbar.set_max(rawsize * sizeof(complex16_t) / sample_size);
progressbar.hidden(false);
auto metadata = read_metadata_file(rawmeta);
// If no metadata found, fallback to the TX frequency.
if (!metadata) {
metadata = {freq, 500'000};
repeat_file_error(rawmeta, "Can't open file to read meta, using default rx_freq,500'000");
}
// Update the sample rate in proc_replay baseband.
baseband::set_sample_rate(metadata->sample_rate,
get_oversample_rate(metadata->sample_rate));
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(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
// set TX to repeater TX freq if entry is Repeater and have a valid freq, else use recorded one
if (current_entry().type == freqman_type::Repeater && current_entry().frequency_b != 0) {
transmitter_model.set_target_frequency(current_entry().frequency_b);
} else {
transmitter_model.set_target_frequency(metadata->center_frequency);
}
// set TX powers and enable transmitter
transmitter_model.set_tx_gain(persistent_memory::recon_repeat_gain());
transmitter_model.set_rf_amp(persistent_memory::recon_repeat_amp());
transmitter_model.enable();
}
// clear replay thread and set reader
replay_thread.reset();
auto reader = std::make_unique<FileConvertReader>();
auto error = reader->open(rawfile);
if (error) {
repeat_file_error(rawfile, "Can't open file to send to thread");
return;
}
// wait for TX if needed (hackish, direct screen update since the UI will be blocked)
if (persistent_memory::recon_repeat_delay() > 0) {
uint8_t delay = persistent_memory::recon_repeat_delay();
Painter p;
while (delay > 0) {
std::string delay_message = "TX DELAY: " + to_string_dec_uint(delay) + "s";
// update display information
p.fill_rectangle({0, (SCREEN_H / 2) - 16, SCREEN_W, 64}, Color::light_grey());
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, Styles::red, delay_message);
// sleep 1 second
chThdSleepMilliseconds(1000);
// decre delay
if (delay > 0)
delay = delay - 1;
else
break;
}
}
// ReplayThread starts immediately on construction; must be set before creating.
repeat_ready_signal = true;
repeat_cur_rep++;
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
/* read_size */ repeat_read_size,
/* buffer_count */ repeat_buffer_count,
&repeat_ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
void ReconView::stop_repeat(const bool do_loop) {
repeat_ready_signal = false;
if (is_repeat_active()) {
replay_thread.reset();
transmitter_model.disable();
}
// repeat transmit if current number of repetitions (repeat_cur_rep) is < recon configured number of repetitions (recon_repeat_nb)
if (do_loop && repeat_cur_rep < persistent_memory::recon_repeat_nb()) {
start_repeat();
} else {
repeat_cur_rep = 0;
recon_tx = false;
reload_restart_recon();
progressbar.hidden(true);
set_dirty(); // fix progressbar no hiding
}
}
void ReconView::handle_repeat_thread_done(const uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
stop_repeat(true);
} else if (return_code == ReplayThread::READ_ERROR) {
stop_repeat(false);
repeat_file_error(u"/" + repeat_rec_path + u"/" + repeat_rec_file, "Can't open file to send.");
}
}
} /* namespace ui */
+73 -6
View File
@@ -41,10 +41,20 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_recon_settings.hpp"
#include "ui_transmitter.hpp"
#include "replay_thread.hpp"
#include "metadata_file.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp"
#include <string>
#include <memory>
namespace ui {
#define RECON_CFG_FILE "SETTINGS/recon.cfg"
#define RECON_CFG_FILE u"SETTINGS/recon.cfg"
enum class recon_mode : uint8_t {
Recon,
@@ -66,10 +76,17 @@ class ReconView : public View {
private:
NavigationView& nav_;
RxRadioState radio_state_{};
RxRadioState rx_radio_state_{};
TxRadioState tx_radio_state_{
0 /* frequency */,
1750000 /* bandwidth */,
500000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_recon"sv, app_settings::Mode::RX};
"rx_tx_recon"sv, app_settings::Mode::RX_TX};
;
void reload_restart_recon();
void check_update_ranges_from_current();
void set_loop_config(bool v);
void clear_freqlist_for_ui_action();
@@ -93,7 +110,7 @@ class ReconView : public View {
void load_persisted_settings();
bool recon_save_freq(const std::filesystem::path& path, size_t index, bool warn_if_exists);
// placeholder for possible void recon_start_recording();
void recon_stop_recording();
void recon_stop_recording(bool exiting);
// Returns true if 'current_index' is in bounds of frequency_list.
bool current_is_valid();
@@ -125,6 +142,7 @@ class ReconView : public View {
bool load_freqs{true};
bool load_ranges{true};
bool load_hamradios{true};
bool load_repeaters{true};
bool update_ranges{true};
bool fwd{true};
bool recon{true};
@@ -163,6 +181,25 @@ class ReconView : public View {
systime_t chrono_start{};
systime_t chrono_end{};
const std::filesystem::path repeat_rec_file = u"RECON_REPEAT.C16";
const std::filesystem::path repeat_rec_meta = u"RECON_REPEAT.TXT";
const std::filesystem::path repeat_rec_path = u"CAPTURES";
const size_t repeat_read_size{16384};
const size_t repeat_buffer_count{3};
int8_t repeat_cur_rep = 0;
int64_t repeat_sample_rate = 0;
static constexpr uint32_t repeat_bandwidth = 2500000;
void on_repeat_tx_progress(const uint32_t progress);
void start_repeat();
void stop_repeat(const bool do_loop);
bool is_repeat_active() const;
void handle_repeat_thread_done(const uint32_t return_code);
void repeat_file_error(const std::filesystem::path& path, const std::string& message);
std::filesystem::path repeat_file_path{};
std::unique_ptr<ReplayThread> replay_thread{};
bool repeat_ready_signal{false};
bool recon_tx{false};
// Persisted settings.
SettingsStore ui_settings{
"recon"sv,
@@ -354,17 +391,47 @@ class ReconView : public View {
{168, (35 * 8) - 4, 72, 28},
"<REMOVE>"};
ProgressBar progressbar{
{0 * 8, SCREEN_H / 2 - 16, SCREEN_W, 32}};
TransmitterView2 tx_view{
{11 * 8, 2 * 16},
/*short_ui*/ true};
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
handle_coded_squelch(message.value);
}};
MessageHandlerRegistration message_handler_stats{
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone,
[this](const Message* p) {
auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
handle_repeat_thread_done(message.return_code);
}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
[this](const Message* p) {
auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
repeat_ready_signal = true;
}
}};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* p) {
auto message = *reinterpret_cast<const TXProgressMessage*>(p);
on_repeat_tx_progress(message.progress);
}};
};
@@ -99,8 +99,14 @@ void ReconSetupViewMore::save() {
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
persistent_memory::set_recon_load_repeaters(checkbox_load_repeaters.value());
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
persistent_memory::set_recon_repeat_recorded(checkbox_repeat_recorded.value());
persistent_memory::set_recon_repeat_nb(field_repeat_nb.value());
persistent_memory::set_recon_repeat_amp(checkbox_repeat_amp.value());
persistent_memory::set_recon_repeat_gain(field_repeat_gain.value());
persistent_memory::set_recon_repeat_delay(field_repeat_delay.value());
};
void ReconSetupViewMain::focus() {
@@ -113,16 +119,55 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
hidden(true);
add_children({&checkbox_load_freqs,
&checkbox_load_repeaters,
&checkbox_load_ranges,
&checkbox_load_hamradios,
&checkbox_update_ranges_when_recon,
&checkbox_auto_record_locked});
&checkbox_auto_record_locked,
&checkbox_repeat_recorded,
&text_repeat_nb,
&field_repeat_nb,
&checkbox_repeat_amp,
&text_repeat_gain,
&field_repeat_gain,
&text_repeat_delay,
&field_repeat_delay});
// tx options have to be in yellow to inform the users that activating them will make the device transmit
checkbox_repeat_recorded.set_style(&Styles::yellow);
text_repeat_nb.set_style(&Styles::yellow);
field_repeat_nb.set_style(&Styles::yellow);
checkbox_repeat_amp.set_style(&Styles::yellow);
text_repeat_gain.set_style(&Styles::yellow);
field_repeat_gain.set_style(&Styles::yellow);
text_repeat_delay.set_style(&Styles::yellow);
field_repeat_delay.set_style(&Styles::yellow);
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
checkbox_load_repeaters.set_value(persistent_memory::recon_load_repeaters());
checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges());
checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios());
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
checkbox_repeat_recorded.set_value(persistent_memory::recon_repeat_recorded());
checkbox_repeat_amp.set_value(persistent_memory::recon_repeat_amp());
field_repeat_nb.set_value(persistent_memory::recon_repeat_nb());
field_repeat_gain.set_value(persistent_memory::recon_repeat_gain());
field_repeat_delay.set_value(persistent_memory::recon_repeat_delay());
// tx warning modal
checkbox_repeat_recorded.on_select = [this, &nav](Checkbox&, bool v) {
if (v) {
nav.display_modal(
"TX WARNING",
"This activate TX ability\nin Recon !",
YESNO,
[this, &nav](bool choice) {
if (!choice)
checkbox_repeat_recorded.set_value(choice);
});
}
};
};
void ReconSetupViewMore::focus() {
@@ -30,6 +30,7 @@
#include "ui_tabview.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "ui_styles.hpp"
// 1Mhz helper
#ifdef OneMHz
@@ -116,26 +117,82 @@ class ReconSetupViewMore : public View {
Checkbox checkbox_load_freqs{
{1 * 8, 12},
3,
"input: load freqs"};
"load freq",
true};
Checkbox checkbox_load_repeaters{
{14 * 8, 12},
3,
"load repeater",
true};
Checkbox checkbox_load_ranges{
{1 * 8, 42},
3,
"input: load ranges"};
"load range",
true};
Checkbox checkbox_load_hamradios{
{1 * 8, 72},
3,
"input: load hamradios"};
"load hamradio",
true};
Checkbox checkbox_update_ranges_when_recon{
{1 * 8, 102},
3,
"auto update m-ranges"};
Checkbox checkbox_auto_record_locked{
{1 * 8, 132},
3,
"record locked periods"};
Checkbox checkbox_repeat_recorded{
{1 * 8, 162},
3,
"repeater,"};
Text text_repeat_nb{
{14 * 8, 165, 3 * 8, 22},
"nb:"};
NumberField field_repeat_nb{
{17 * 8, 165},
2,
{1, 99},
1,
' ',
};
Checkbox checkbox_repeat_amp{
{1 * 8, 192},
3,
"AMP,"};
Text text_repeat_gain{
{9 * 8, 196, 5 * 8, 22},
"GAIN:"};
NumberField field_repeat_gain{
{14 * 8, 196},
2,
{0, 47},
1,
' ',
};
Text text_repeat_delay{
{16 * 8, 196, 8 * 8, 22},
", delay:"};
NumberField field_repeat_delay{
{24 * 8, 196},
3,
{0, 254},
1,
' ',
};
};
class ReconSetupView : public View {
+1
View File
@@ -564,6 +564,7 @@ void ScannerView::frequency_file_load(const fs::path& path) {
def_step_index = entry.step;
switch (entry.type) {
case freqman_type::Repeater:
case freqman_type::Single:
entries.push_back({entry.frequency_a, entry.description});
break;
+16 -39
View File
@@ -133,7 +133,6 @@ SetRadioView::SetRadioView(
&check_clkout,
&field_clkout_freq,
&labels_clkout_khz,
&value_freq_step,
&labels_bias,
&check_bias,
&disable_external_tcxo, // TODO: always show?
@@ -150,23 +149,10 @@ SetRadioView::SetRadioView(
});
}
std::string source_name("---");
switch (reference.source) {
case ClockManager::ReferenceSource::Xtal:
source_name = "HackRF";
break;
case ClockManager::ReferenceSource::PortaPack:
source_name = "PortaPack";
break;
case ClockManager::ReferenceSource::External:
source_name = "External";
break;
}
std::string source_name = clock_manager.get_source();
value_source.set(source_name);
value_source_frequency.set(
to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz");
value_source_frequency.set(clock_manager.get_freq());
// Make these Text controls look like Labels.
label_source.set_style(&Styles::light_grey);
@@ -185,29 +171,20 @@ SetRadioView::SetRadioView(
send_system_refresh();
};
field_clkout_freq.set_value(pmem::clkout_freq());
value_freq_step.set_style(&Styles::light_grey);
// Disallow CLKOUT freq change on hackrf_r9 due to dependencies on GP_CLKIN (same Si5351A clock);
// see comments in ClockManager::enable_clock_output()
if (hackrf_r9) {
if (pmem::clkout_freq() != 10000)
pmem::set_clkout_freq(10000);
field_clkout_freq.set_focusable(false);
}
field_clkout_freq.on_select = [this](NumberField&) {
freq_step_khz++;
if (freq_step_khz > 3) {
freq_step_khz = 0;
}
switch (freq_step_khz) {
case 0:
value_freq_step.set(" |");
break;
case 1:
value_freq_step.set(" | ");
break;
case 2:
value_freq_step.set(" | ");
break;
case 3:
value_freq_step.set("| ");
break;
}
field_clkout_freq.set_step(pow(10, freq_step_khz));
field_clkout_freq.set_value(pmem::clkout_freq());
field_clkout_freq.on_change = [this](SymField&) {
if (field_clkout_freq.to_integer() < 4) // Min. CLK out of Si5351A/B/C-B is 2.5khz , but in our application -intermediate freq 800Mhz-,Min working CLK=4khz.
field_clkout_freq.set_value(4);
if (field_clkout_freq.to_integer() > 60000)
field_clkout_freq.set_value(60000);
};
check_bias.set_value(get_antenna_bias());
@@ -248,7 +225,7 @@ void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
SetFrequencyCorrectionModel SetRadioView::form_collect() {
return {
.ppm = static_cast<int8_t>(field_ppm.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.to_integer()),
};
}
+2 -8
View File
@@ -163,20 +163,14 @@ class SetRadioView : public View {
13,
"Enable CLKOUT"};
NumberField field_clkout_freq{
SymField field_clkout_freq{
{20 * 8, 6 * 16},
5,
{10, 60000},
1000,
' '};
SymField::Type::Dec};
Labels labels_clkout_khz{
{{26 * 8, 6 * 16}, "kHz", Color::light_grey()}};
Text value_freq_step{
{21 * 8, (7 * 16), 4 * 8, 16},
"| "};
Labels labels_bias{
{{4 * 8 + 4, 8 * 16}, "CAUTION: Ensure that all", Color::red()},
{{5 * 8 + 0, 9 * 16}, "devices attached to the", Color::red()},
+46 -19
View File
@@ -217,6 +217,27 @@ ClockManager::Reference ClockManager::get_reference() const {
return reference;
}
std::string ClockManager::get_source() {
std::string source_name("---");
switch (reference.source) {
case ClockManager::ReferenceSource::Xtal:
source_name = "HackRF";
break;
case ClockManager::ReferenceSource::PortaPack:
source_name = "PortaPack";
break;
case ClockManager::ReferenceSource::External:
source_name = "External";
break;
}
return source_name;
}
std::string ClockManager::get_freq() {
return to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz";
}
static void portapack_tcxo_enable() {
portapack::io.reference_oscillator(true);
@@ -561,26 +582,32 @@ void ClockManager::stop_audio_pll() {
}
void ClockManager::enable_clock_output(bool enable) {
if (enable) {
clock_generator.enable_output(clock_generator_output_og_clkout);
if (portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
} else {
clock_generator.disable_output(clock_generator_output_og_clkout);
if (hackrf_r9) {
gpio_r9_clkout_en.output();
gpio_r9_clkout_en.write(enable);
// NOTE: RETURNING HERE IF HACKRF_R9 TO PREVENT CLK2 FROM BEING DISABLED OR FREQ MODIFIED SINCE CLK2 ON R9 IS
// USED FOR BOTH CLKOUT AND FOR THE MCU_CLOCK (== GP_CLKIN) WHICH OTHER LP43XX CLOCKS CURRENTLY RELY ON.
// FUTURE TBD: REMOVE OTHER LP43XX CLOCK DEPENDENCIES ON GP_CLKIN, THEN DELETE THE return LINE BELOW TO ALLOW
// CLKOUT FREQ CHANGES ON R9 BOARDS.
return;
}
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
auto clkout_select = hackrf_r9 ? clock_generator_output_r9_clkout : clock_generator_output_og_clkout;
if (enable)
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
else
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
if (enable) {
clock_generator.enable_output(clkout_select);
if (portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clkout_select, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clkout_select, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
auto si5351_clock_control_common = hackrf_r9 ? si5351a_clock_control_common : si5351c_clock_control_common;
const auto ref_pll = hackrf_r9 ? ClockControl::MultiSynthSource::PLLA : get_si5351c_reference_clock_generator_pll(reference.source);
clock_generator.set_clock_control(clkout_select, si5351_clock_control_common[clkout_select].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
} else {
clock_generator.disable_output(clkout_select);
clock_generator.set_clock_control(clkout_select, ClockControl::power_off());
}
}
+3
View File
@@ -22,6 +22,7 @@
#ifndef __CLOCK_MANAGER_H__
#define __CLOCK_MANAGER_H__
#include "string_format.hpp"
#include "ch.h"
#include "hal.h"
@@ -74,6 +75,8 @@ class ClockManager {
uint32_t get_frequency_monitor_measurement_in_hertz();
Reference get_reference() const;
std::string get_source();
std::string get_freq();
void enable_clock_output(bool enable);
File diff suppressed because it is too large Load Diff
+8 -3
View File
@@ -27,6 +27,8 @@
#ifndef __UI_BLESPAM_H__
#define __UI_BLESPAM_H__
#define BLESPMUSECONSOLE 1
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
@@ -111,9 +113,10 @@ class BLESpamView : public View {
LanguageHelper::currentMessages[LANG_START]};
Checkbox chk_randdev{{100, 16}, 10, "Rnd device", true};
#ifdef BLESPMUSECONSOLE
Console console{
{0, 70, 240, 220}};
#endif
OptionsField options_atkmode{
{0 * 8, 2 * 8},
10,
@@ -134,7 +137,7 @@ class BLESpamView : public View {
bool randomDev{true};
uint8_t channel_number = 37;
char mac[13] = "010203040507";
char mac[13] = "010203040407";
char advertisementData[63] = {"03032CFE06162CFED5A59E020AB4\0"};
PKT_TYPE pduType = {PKT_TYPE_DISCOVERY};
@@ -161,6 +164,8 @@ class BLESpamView : public View {
this->on_tx_progress(message.done);
}};
uint8_t packet[80];
// continuity
typedef enum {
@@ -227,7 +232,7 @@ class BLESpamView : public View {
static const uint16_t fastpairModels_count;
typedef struct {
uint32_t value;
const char* name; // could be moved too
// const char* name; // could be moved too
} fpUi32;
static const fpUi32 fastpairModels[];
+4 -3
View File
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_styles.hpp"
#include "ui_navigation.hpp"
@@ -56,8 +57,8 @@ class RangeView : public View {
const Style& style_info = Styles::grey;
Labels labels{
{{2 * 8, 8 * 8 + 4}, "Start", Color::light_grey()},
{{23 * 8, 8 * 8 + 4}, "Stop", Color::light_grey()},
{{2 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_START], Color::light_grey()},
{{23 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_STOP], Color::light_grey()},
{{12 * 8, 5 * 8 - 4}, "Center", Color::light_grey()},
{{12 * 8 + 4, 13 * 8}, "Width", Color::light_grey()}};
@@ -226,7 +227,7 @@ class JammerView : public View {
Button button_transmit{
{148, 216, 80, 80},
"START"};
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_retune{
Message::ID::Retune,
+19 -6
View File
@@ -185,6 +185,8 @@ bool operator==(const freqman_entry& lhs, const freqman_entry& rhs) {
} else if (lhs.type == freqman_type::HamRadio) {
equal = lhs.frequency_b == rhs.frequency_b &&
lhs.tone == rhs.tone;
} else if (lhs.type == freqman_type::Repeater) {
equal = lhs.frequency_b == rhs.frequency_b;
}
return equal;
@@ -248,6 +250,10 @@ std::string pretty_string(const freqman_entry& entry, size_t max_length) {
str = "R:" + to_string_rounded_freq(entry.frequency_a, 1) + "M,T:" +
to_string_rounded_freq(entry.frequency_b, 1) + "M: " + entry.description;
break;
case freqman_type::Repeater:
str = "L:" + to_string_rounded_freq(entry.frequency_a, 1) + "M,T:" +
to_string_rounded_freq(entry.frequency_b, 1) + "M: " + entry.description;
break;
case freqman_type::Raw:
str = entry.description;
break;
@@ -292,6 +298,10 @@ std::string to_freqman_string(const freqman_entry& entry) {
if (is_valid(entry.tone))
append_field("c", tonekey::tone_key_value_string(entry.tone));
break;
case freqman_type::Repeater:
append_field("l", to_string_dec_uint(entry.frequency_a));
append_field("t", to_string_dec_uint(entry.frequency_b));
break;
case freqman_type::Raw:
return entry.description;
default:
@@ -372,16 +382,18 @@ bool parse_freqman_entry(std::string_view str, freqman_entry& entry) {
parse_int(value, entry.frequency_a);
} else if (key == "m") {
entry.modulation = find_by_name(freqman_modulations, value);
} else if (key == "r") {
} else if (key == "r") { // HamRadio relay receive freq
entry.type = freqman_type::HamRadio;
parse_int(value, entry.frequency_a);
} else if (key == "l") { // Portapack Repeater mode listen freq. Used as a single freq if Repeater mode isn't active
entry.type = freqman_type::Repeater;
parse_int(value, entry.frequency_a);
} else if (key == "s") {
entry.step = find_by_name(freqman_steps_short, value);
} else if (key == "t") {
} else if (key == "t") { // Tx freq: scanned as a single freq in HamRadio mode, used as TX freq in Repeater mode and ignored by the scanner
parse_int(value, entry.frequency_b);
}
}
return is_valid(entry);
}
@@ -400,7 +412,8 @@ bool parse_freqman_file(const fs::path& path, freqman_db& db, freqman_load_optio
if (entry.type == freqman_type::Unknown ||
(entry.type == freqman_type::Single && !options.load_freqs) ||
(entry.type == freqman_type::Range && !options.load_ranges) ||
(entry.type == freqman_type::HamRadio && !options.load_hamradios)) {
(entry.type == freqman_type::HamRadio && !options.load_hamradios) ||
(entry.type == freqman_type::Repeater && !options.load_repeaters)) {
continue;
}
@@ -433,8 +446,8 @@ bool is_valid(const freqman_entry& entry) {
if (entry.frequency_a == 0)
return false;
// Frequency B must be set for type Range or Ham Radio
if (entry.type == freqman_type::Range || entry.type == freqman_type::HamRadio) {
// Frequency B must be set for type Range or HamRadio or Repeater
if (entry.type == freqman_type::Range || entry.type == freqman_type::HamRadio || entry.type == freqman_type::Repeater) {
if (entry.frequency_b == 0)
return false;
}
+3 -1
View File
@@ -56,6 +56,7 @@ enum class freqman_type : uint8_t {
Single, // f=
Range, // a=,b=
HamRadio, // r=,t=
Repeater, // l=,t=
Raw, // line content in description
Unknown,
};
@@ -167,6 +168,7 @@ struct freqman_load_options {
bool load_freqs{true};
bool load_ranges{true};
bool load_hamradios{true};
bool load_repeaters{true};
};
using freqman_entry_ptr = std::unique_ptr<freqman_entry>;
@@ -277,4 +279,4 @@ class FreqmanDB {
bool read_raw_{true};
};
#endif /* __FREQMAN_DB_H__ */
#endif /* __FREQMAN_DB_H__ */
+305
View File
@@ -0,0 +1,305 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
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.
*/
/**
* @file shell.c
* @brief Simple CLI shell code.
*
* @addtogroup SHELL
* @{
*/
#include <string.h>
#include <cstring>
#include <ctype.h>
#include "ch.h"
#include "hal.h"
#include "shell.hpp"
#include "chprintf.h"
#include "portapack.hpp"
/**
* @brief Shell termination event source.
*/
EventSource shell_terminated;
static char* _strtok(char* str, const char* delim, char** saveptr) {
char* token;
if (str)
*saveptr = str;
token = *saveptr;
if (!token)
return NULL;
token += strspn(token, delim);
*saveptr = strpbrk(token, delim);
if (*saveptr)
*(*saveptr)++ = '\0';
return *token ? token : NULL;
}
static void usage(BaseSequentialStream* chp, char* p) {
chprintf(chp, "Usage: %s\r\n", p);
}
static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
while (scp->sc_name != NULL) {
chprintf(chp, "%s ", scp->sc_name);
scp++;
}
}
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argv;
if (argc > 0) {
usage(chp, const_cast<char*>("info"));
return;
}
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
#ifdef CH_COMPILER_NAME
chprintf(chp, "Compiler: %s\r\n", CH_COMPILER_NAME);
#endif
chprintf(chp, "Architecture: %s\r\n", CH_ARCHITECTURE_NAME);
#ifdef CH_CORE_VARIANT_NAME
chprintf(chp, "Core Variant: %s\r\n", CH_CORE_VARIANT_NAME);
#endif
#ifdef CH_PORT_INFO
chprintf(chp, "Port Info: %s\r\n", CH_PORT_INFO);
#endif
#ifdef PLATFORM_NAME
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
#endif
#ifdef BOARD_NAME
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
#endif
#ifdef VERSION_STRING
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
#endif
chprintf(chp, "HackRF Board Rev: %s\r\n", hackrf_r9 ? "R9" : "R1-R8");
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
#ifdef __DATE__
#ifdef __TIME__
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
#endif
#endif
}
static void cmd_systime(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argv;
if (argc > 0) {
usage(chp, const_cast<char*>("systime"));
return;
}
chprintf(chp, "%lu\r\n", (unsigned long)chTimeNow());
}
/**
* @brief Array of the default commands.
*/
static ShellCommand local_commands[] = {
{"info", cmd_info},
{"systime", cmd_systime},
{NULL, NULL}};
static bool_t cmdexec(const ShellCommand* scp, BaseSequentialStream* chp, char* name, int argc, char* argv[]) {
while (scp->sc_name != NULL) {
if (strcmp(scp->sc_name, name) == 0) {
scp->sc_function(chp, argc, argv);
return FALSE;
}
scp++;
}
return TRUE;
}
/**
* @brief Shell thread function.
*
* @param[in] p pointer to a @p BaseSequentialStream object
* @return Termination reason.
* @retval RDY_OK terminated by command.
* @retval RDY_RESET terminated by reset condition on the I/O channel.
*
* @notapi
*/
static msg_t shell_thread(void* p) {
int n;
BaseSequentialStream* chp = ((ShellConfig*)p)->sc_channel;
const ShellCommand* scp = ((ShellConfig*)p)->sc_commands;
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
char* args[SHELL_MAX_ARGUMENTS + 1];
chRegSetThreadName("shell");
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
while (TRUE) {
chprintf(chp, "ch> ");
if (shellGetLine(chp, line, sizeof(line))) {
chprintf(chp, "\r\nlogout");
break;
}
lp = _strtok(line, " \t", &tokp);
cmd = lp;
n = 0;
while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
if (n >= SHELL_MAX_ARGUMENTS) {
chprintf(chp, "too many arguments\r\n");
cmd = NULL;
break;
}
args[n++] = lp;
}
args[n] = NULL;
if (cmd != NULL) {
if (strcmp(cmd, "exit") == 0) {
if (n > 0) {
usage(chp, const_cast<char*>("exit"));
continue;
}
break;
} else if (strcmp(cmd, "help") == 0) {
if (n > 0) {
usage(chp, const_cast<char*>("help"));
continue;
}
chprintf(chp, "Commands: help exit ");
list_commands(chp, local_commands);
if (scp != NULL)
list_commands(chp, scp);
chprintf(chp, "\r\n");
} else if (cmdexec(local_commands, chp, cmd, n, args) &&
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
chprintf(chp, "%s", cmd);
chprintf(chp, " ?\r\n");
}
}
}
shellExit(RDY_OK);
/* Never executed, silencing a warning.*/
return 0;
}
/**
* @brief Shell manager initialization.
*
* @api
*/
void shellInit(void) {
chEvtInit(&shell_terminated);
}
/**
* @brief Terminates the shell.
* @note Must be invoked from the command handlers.
* @note Does not return.
*
* @param[in] msg shell exit code
*
* @api
*/
void shellExit(msg_t msg) {
/* Atomically broadcasting the event source and terminating the thread,
there is not a chSysUnlock() because the thread terminates upon return.*/
chSysLock();
chEvtBroadcastI(&shell_terminated);
chThdExitS(msg);
}
/**
* @brief Spawns a new shell.
* @pre @p CH_USE_HEAP and @p CH_USE_DYNAMIC must be enabled.
*
* @param[in] scp pointer to a @p ShellConfig object
* @param[in] size size of the shell working area to be allocated
* @param[in] prio priority level for the new shell
* @return A pointer to the shell thread.
* @retval NULL thread creation failed because memory allocation.
*
* @api
*/
#if CH_USE_HEAP && CH_USE_DYNAMIC
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio) {
return chThdCreateFromHeap(NULL, size, prio, shell_thread, (void*)scp);
}
#endif
/**
* @brief Create statically allocated shell thread.
*
* @param[in] scp pointer to a @p ShellConfig object
* @param[in] wsp pointer to a working area dedicated to the shell thread stack
* @param[in] size size of the shell working area
* @param[in] prio priority level for the new shell
* @return A pointer to the shell thread.
*
* @api
*/
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio) {
return chThdCreateStatic(wsp, size, prio, shell_thread, (void*)scp);
}
/**
* @brief Reads a whole line from the input channel.
*
* @param[in] chp pointer to a @p BaseSequentialStream object
* @param[in] line pointer to the line buffer
* @param[in] size buffer maximum length
* @return The operation status.
* @retval TRUE the channel was reset or CTRL-D pressed.
* @retval FALSE operation successful.
*
* @api
*/
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size) {
char* p = line;
while (TRUE) {
char c;
if (chSequentialStreamRead(chp, (uint8_t*)&c, 1) == 0)
return TRUE;
if (c == 4) {
chprintf(chp, "^D");
return TRUE;
}
if ((c == 8) || (c == 127)) {
if (p != line) {
chSequentialStreamPut(chp, c);
chSequentialStreamPut(chp, 0x20);
chSequentialStreamPut(chp, c);
p--;
}
continue;
}
if (c == '\r') {
chprintf(chp, "\r\n");
*p = 0;
return FALSE;
}
if (c < 0x20)
continue;
if (p < line + size - 1) {
chSequentialStreamPut(chp, c);
*p++ = (char)c;
}
}
}
/** @} */
+83
View File
@@ -0,0 +1,83 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
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.
*/
/**
* @file shell.h
* @brief Simple CLI shell header.
*
* @addtogroup SHELL
* @{
*/
#ifndef _SHELL_H_
#define _SHELL_H_
/**
* @brief Shell maximum input line length.
*/
#if !defined(SHELL_MAX_LINE_LENGTH) || defined(__DOXYGEN__)
#define SHELL_MAX_LINE_LENGTH 64
#endif
/**
* @brief Shell maximum arguments per command.
*/
#if !defined(SHELL_MAX_ARGUMENTS) || defined(__DOXYGEN__)
#define SHELL_MAX_ARGUMENTS 4
#endif
/**
* @brief Command handler function type.
*/
typedef void (*shellcmd_t)(BaseSequentialStream* chp, int argc, char* argv[]);
/**
* @brief Custom command entry type.
*/
typedef struct {
const char* sc_name; /**< @brief Command name. */
shellcmd_t sc_function; /**< @brief Command function. */
} ShellCommand;
/**
* @brief Shell descriptor type.
*/
typedef struct {
BaseSequentialStream* sc_channel; /**< @brief I/O channel associated
to the shell. */
const ShellCommand* sc_commands; /**< @brief Shell extra commands
table. */
} ShellConfig;
#if !defined(__DOXYGEN__)
extern EventSource shell_terminated;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void shellInit(void);
void shellExit(msg_t msg);
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio);
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio);
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size);
#ifdef __cplusplus
}
#endif
#endif /* _SHELL_H_ */
/** @} */
+13
View File
@@ -173,6 +173,9 @@ SystemStatusView::SystemStatusView(
pmem::load_persistent_settings_from_file();
}
// configure CLKOUT per pmem setting
portapack::clock_manager.enable_clock_output(pmem::clkout_enabled());
// force apply of selected sdcard speed override at UI startup
pmem::set_config_sdcard_high_speed_io(pmem::config_sdcard_high_speed_io(), false);
@@ -438,7 +441,17 @@ InformationView::InformationView(
&version,
&ltime});
#if GCC_VERSION_MISMATCH
static constexpr Style style_gcc_warning{
.font = font::fixed_8x16,
.background = {33, 33, 33},
.foreground = Color::yellow(),
};
version.set_style(&style_gcc_warning);
#else
version.set_style(&style_infobar);
#endif
ltime.set_style(&style_infobar);
refresh();
set_dirty();
+13 -2
View File
@@ -145,6 +145,15 @@ OversampleRate RecordView::get_oversample_rate(uint32_t sample_rate) {
// Setter for datetime and frequency filename
void RecordView::set_filename_date_frequency(bool set) {
filename_date_frequency = set;
if (set)
filename_as_is = false;
}
// Setter for leaving the filename untouched
void RecordView::set_filename_as_is(bool set) {
filename_as_is = set;
if (set)
filename_date_frequency = false;
}
bool RecordView::is_active() const {
@@ -186,9 +195,11 @@ void RecordView::start() {
base_path = filename_stem_pattern.string() + "_" + date_time + "_" +
trim(to_string_freq(receiver_model.target_frequency())) + "Hz";
base_path = folder / base_path;
} else {
} else if (filename_as_is) {
base_path = filename_stem_pattern.string();
base_path = folder / base_path;
} else
base_path = next_filename_matching_pattern(folder / filename_stem_pattern);
}
if (base_path.empty()) {
return;
+2
View File
@@ -73,6 +73,7 @@ class RecordView : public View {
bool is_active() const;
void set_filename_date_frequency(bool set);
void set_filename_as_is(bool set);
private:
void toggle();
@@ -91,6 +92,7 @@ class RecordView : public View {
// Time Stamp
bool filename_date_frequency = false;
bool filename_as_is = false;
rtc::RTC datetime{};
const std::filesystem::path filename_stem_pattern;
+325 -8
View File
@@ -27,6 +27,15 @@
#include "png_writer.hpp"
#include "irq_controls.hpp"
#include "portapack.hpp"
#include "portapack_hal.hpp"
#include "hackrf_gpio.hpp"
#include "jtag_target_gpio.hpp"
#include "cpld_max5.hpp"
#include "portapack_cpld_data.hpp"
#include "crc.hpp"
#include "hackrf_cpld_data.hpp"
#include "usb_serial_io.h"
#include "ff.h"
#include "chprintf.h"
@@ -36,6 +45,7 @@
#include <codecvt>
#include <cstring>
#include <locale>
#include <libopencm3/lpc43xx/wwdt.h>
#define SHELL_WA_SIZE THD_WA_SIZE(1024 * 3)
#define palOutputPad(port, pad) (LPC_GPIO->DIR[(port)] |= 1 << (pad))
@@ -418,21 +428,18 @@ static void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]) {
int size = (int)strtol(argv[0], NULL, 10);
uint8_t buffer[16];
uint8_t buffer[62];
do {
File::Size bytes_to_read = size > 16 ? 16 : size;
File::Size bytes_to_read = size > 62 ? 62 : size;
auto bytes_read = shell_file->read(buffer, bytes_to_read);
if (bytes_read.is_error()) {
chprintf(chp, "error %d\r\n", bytes_read.error());
return;
}
for (size_t i = 0; i < bytes_read.value(); i++)
chprintf(chp, "%02X", buffer[i]);
chprintf(chp, "\r\n");
std::string res = to_string_hex_array(buffer, bytes_read.value());
res += "\r\n";
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)res.c_str(), res.size());
if (bytes_to_read != bytes_read.value())
return;
@@ -479,6 +486,314 @@ static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
chprintf(chp, "ok\r\n");
}
static void cpld_info(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: cpld_info <device>\r\n"
" supported modes:\r\n"
" cpld_info hackrf\r\n"
" cpld_info portapack\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
if (strncmp(argv[0], "hackrf", 5) == 0) {
jtag::GPIOTarget jtag_target_hackrf_cpld{
hackrf::one::gpio_cpld_tck,
hackrf::one::gpio_cpld_tms,
hackrf::one::gpio_cpld_tdi,
hackrf::one::gpio_cpld_tdo,
};
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
{
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
hackrf_cpld.prepare_read_eeprom();
for (const auto& block : hackrf::one::cpld::verify_blocks) {
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
auto bit = *i;
crc.process_bit(bit);
}
}
hackrf_cpld.finalize_read_eeprom();
chprintf(chp, "CPLD eeprom firmware checksum: 0x%08X\r\n", crc.checksum());
}
{
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
hackrf_cpld.prepare_read_sram();
for (const auto& block : hackrf::one::cpld::verify_blocks) {
auto from_device = hackrf_cpld.read_block_sram(block);
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
auto bit = *i;
crc.process_bit(bit);
}
}
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
chprintf(chp, "CPLD sram firmware checksum: 0x%08X\r\n", crc.checksum());
}
} else if (strncmp(argv[0], "portapack", 5) == 0) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo};
jtag::JTAG jtag{target};
portapack::cpld::CPLD cpld{jtag};
cpld.reset();
cpld.run_test_idle();
uint32_t idcode = cpld.get_idcode();
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
if (idcode == 0x20A50DD) {
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
cpld.reset();
cpld.run_test_idle();
cpld.sample();
cpld.bypass();
cpld.enable();
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
cpld.prepare_read(0x0000);
for (size_t i = 0; i < 3328; i++) {
uint16_t data = cpld.read();
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
cpld.prepare_read(0x0001);
for (size_t i = 0; i < 512; i++) {
uint16_t data = cpld.read();
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
m4_request_shutdown();
chThdSleepMilliseconds(1000);
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
WWDT_TC = 100000 & 0xFFFFFF;
WWDT_FEED_SEQUENCE;
} else if (idcode == 0x00025610) {
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
if (cpld.AGM_enter_maintenance_mode() == false) {
return;
}
cpld.AGM_enter_read_mode();
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
for (size_t i = 0; i < 2048; i++) {
uint32_t data = cpld.AGM_read(i);
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
cpld.AGM_exit_maintenance_mode();
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
m4_request_shutdown();
chThdSleepMilliseconds(1000);
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
WWDT_TC = 100000 & 0xFFFFFF;
WWDT_FEED_SEQUENCE;
} else {
chprintf(chp, "CPLD Model: unknown\r\n");
}
} else {
chprintf(chp, usage);
}
}
static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage =
"usage: cpld_read <device> <target>\r\n"
" device can be: hackrf, portapack\r\n"
" target can be: sram (hackrf only), eeprom\r\n";
if (argc != 2) {
chprintf(chp, usage);
return;
}
if (strncmp(argv[0], "hackrf", 5) == 0) {
if (strncmp(argv[1], "eeprom", 5) == 0) {
jtag::GPIOTarget jtag_target_hackrf_cpld{
hackrf::one::gpio_cpld_tck,
hackrf::one::gpio_cpld_tms,
hackrf::one::gpio_cpld_tdi,
hackrf::one::gpio_cpld_tdo,
};
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
hackrf_cpld.prepare_read_eeprom();
for (const auto& block : hackrf::one::cpld::verify_blocks) {
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
chprintf(chp, "bank %04X: ", block.id);
uint32_t n = 6;
uint8_t byte = 0;
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
auto bit = *i;
byte |= bit << (7 - (n % 8));
if (n % 8 == 7) {
chprintf(chp, "%02X ", byte);
byte = 0;
}
n++;
}
chprintf(chp, "\r\n");
}
hackrf_cpld.finalize_read_eeprom();
}
else if (strncmp(argv[1], "sram", 5) == 0) {
jtag::GPIOTarget jtag_target_hackrf_cpld{
hackrf::one::gpio_cpld_tck,
hackrf::one::gpio_cpld_tms,
hackrf::one::gpio_cpld_tdi,
hackrf::one::gpio_cpld_tdo,
};
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
hackrf_cpld.prepare_read_sram();
for (const auto& block : hackrf::one::cpld::verify_blocks) {
auto from_device = hackrf_cpld.read_block_sram(block);
chprintf(chp, "bank %04X: ", block.id);
uint32_t n = 6;
uint8_t byte = 0;
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
auto bit = *i;
byte |= bit << (7 - (n % 8));
if (n % 8 == 7) {
chprintf(chp, "%02X ", byte);
byte = 0;
}
n++;
}
chprintf(chp, "\r\n");
}
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
}
} else if (strncmp(argv[0], "portapack", 5) == 0) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo};
jtag::JTAG jtag{target};
portapack::cpld::CPLD cpld{jtag};
cpld.reset();
cpld.run_test_idle();
uint32_t idcode = cpld.get_idcode();
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
if (idcode == 0x20A50DD) {
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
cpld.reset();
cpld.run_test_idle();
cpld.sample();
cpld.bypass();
cpld.enable();
cpld.prepare_read(0x0000);
for (size_t i = 0; i < 3328; i++) {
uint16_t data = cpld.read();
chprintf(chp, "%d: 0x%04X\r\n", i, data);
}
cpld.prepare_read(0x0001);
for (size_t i = 0; i < 512; i++) {
uint16_t data = cpld.read();
chprintf(chp, "%d: 0x%04X\r\n", i, data);
}
m4_request_shutdown();
chThdSleepMilliseconds(1000);
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
WWDT_TC = 100000 & 0xFFFFFF;
WWDT_FEED_SEQUENCE;
} else if (idcode == 0x00025610) {
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
if (cpld.AGM_enter_maintenance_mode() == false) {
return;
}
cpld.AGM_enter_read_mode();
for (size_t i = 0; i < 2048; i++) {
uint32_t data = cpld.AGM_read(i);
if (i % 4 == 0)
chprintf(chp, "%5d: ", i * 4);
chprintf(chp, "0x%08X", data);
if (i % 4 == 3)
chprintf(chp, "\r\n");
else
chprintf(chp, " ");
}
cpld.AGM_exit_maintenance_mode();
m4_request_shutdown();
chThdSleepMilliseconds(1000);
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
WWDT_TC = 100000 & 0xFFFFFF;
WWDT_FEED_SEQUENCE;
} else {
chprintf(chp, "CPLD Model: unknown\r\n");
}
} else {
chprintf(chp, usage);
}
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
{"dfu", cmd_dfu},
@@ -499,6 +814,8 @@ static const ShellCommand commands[] = {
{"read", cmd_sd_read},
{"write", cmd_sd_write},
{"filesize", cmd_sd_filesize},
{"cpld_info", cpld_info},
{"cpld_read", cmd_cpld_read},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
+69 -2
View File
@@ -147,9 +147,12 @@ void CPLD::sector_select(const uint16_t id) {
}
bool CPLD::idcode_ok() {
return (get_idcode() == idcode);
}
uint32_t CPLD::get_idcode() {
shift_ir(instruction_t::IDCODE);
const auto idcode_read = jtag.shift_dr(idcode_length, 0);
return (idcode_read == idcode);
return jtag.shift_dr(idcode_length, 0);
}
std::array<uint16_t, 5> CPLD::read_silicon_id() {
@@ -208,6 +211,16 @@ void CPLD::program_block(
}
}
void CPLD::prepare_read(uint16_t block) {
sector_select(block);
shift_ir(instruction_t::ISC_READ);
jtag.runtest_tck(93); // 5us
}
uint32_t CPLD::read() {
return jtag.shift_dr(16, 0xffff) & 0xfbff;
}
bool CPLD::verify_block(
const uint16_t id,
const uint16_t* const data,
@@ -265,5 +278,59 @@ bool CPLD::is_blank() {
return block_0_blank && block_1_blank;
}
bool CPLD::AGM_enter_maintenance_mode() {
shift_ir(instruction_t::AGM_STAGE_1);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_STAGE_2);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_STAGE_1);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_SET_REGISTER);
jtag.runtest_tck(100);
jtag.shift_dr(8, 0x0);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_PROGRAM);
jtag.runtest_tck(100);
jtag.shift_dr(32, 0x203f0044uL, 0x80000000);
shift_ir(instruction_t::IDCODE);
jtag.runtest_tck(100);
auto idcode = jtag.shift_dr(idcode_length, 0);
return idcode == 0x00025610;
}
void CPLD::AGM_exit_maintenance_mode() {
shift_ir(instruction_t::AGM_RESET);
jtag.runtest_tck(100);
}
void CPLD::AGM_enter_read_mode() {
shift_ir(instruction_t::AGM_SET_REGISTER);
jtag.runtest_tck(100);
jtag.shift_dr(8, 0xf0);
jtag.runtest_tck(100);
shift_ir(instruction_t::AGM_READ);
jtag.runtest_tck(100);
}
uint32_t CPLD::AGM_encode_address(uint32_t address, uint32_t trailer) {
uint32_t p = trailer;
for (size_t i = 0; i < 18; i++) {
auto address_bit = (address >> i) & 0x01;
p |= address_bit << (31 - i);
}
return p;
}
uint32_t CPLD::AGM_read(uint32_t address) {
auto encoded_address = AGM_encode_address(address * 4, 0xC0);
return jtag.shift_dr(32, encoded_address, 0x0);
}
} /* namespace max5 */
} /* namespace cpld */
+17
View File
@@ -60,6 +60,7 @@ class CPLD {
}
bool idcode_ok();
uint32_t get_idcode();
void enable();
@@ -90,6 +91,15 @@ class CPLD {
std::pair<bool, uint8_t> boundary_scan();
void prepare_read(uint16_t block);
uint32_t read();
bool AGM_enter_maintenance_mode();
void AGM_exit_maintenance_mode();
void AGM_enter_read_mode();
uint32_t AGM_encode_address(uint32_t address, uint32_t trailer);
uint32_t AGM_read(uint32_t address);
private:
using idcode_t = uint32_t;
static constexpr size_t idcode_length = 32;
@@ -115,6 +125,13 @@ class CPLD {
ISC_ADDRESS_SHIFT = 0b1000000011, // 0x203
ISC_READ = 0b1000000101, // 0x205
ISC_NOOP = 0b1000010000, // 0x210
AGM_RESET = 0x3f7,
AGM_STAGE_1 = 0x3f8,
AGM_STAGE_2 = 0x3f9,
AGM_PROGRAM = 0x3fa,
AGM_SET_REGISTER = 0x3fc,
AGM_READ = 0x3fd,
AGM_ERASE = 0x3fe,
};
void shift_ir(const instruction_t instruction) {
+75 -11
View File
@@ -19,6 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
#include "ch.h"
#include "cpld_xilinx.hpp"
namespace cpld {
@@ -47,6 +48,25 @@ void XC2C64A::write_sram(const verify_blocks_t& blocks) {
}
bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
prepare_read_sram();
const jtag::tap::bits_t empty_row{block_length};
auto error = false;
for (const auto& block : blocks) {
tap.shift({&block.id, block_id_length}, true);
tap.state(state_t::run_test_idle);
tap.state(state_t::shift_dr);
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, nullptr);
}
finalize_read_sram(blocks[0].id);
return !error;
}
void XC2C64A::prepare_read_sram() {
tap.set_repeat(0);
tap.set_end_ir(state_t::run_test_idle);
tap.set_end_dr(state_t::run_test_idle);
@@ -61,17 +81,25 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
tap.state(state_t::shift_dr);
tap.shift(empty_row, false);
}
auto error = false;
for (const auto& block : blocks) {
tap.shift({&block.id, block_id_length}, true);
tap.state(state_t::run_test_idle);
std::array<bool, 274> XC2C64A::read_block_sram(verify_block_t block) {
tap.shift({&block.id, block_id_length}, true);
tap.state(state_t::run_test_idle);
tap.state(state_t::shift_dr);
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false);
}
// Redundant operation to finish the row.
tap.shift({&blocks[0].id, block_id_length}, true);
tap.state(state_t::shift_dr);
const jtag::tap::bits_t empty_row{block_length};
std::vector<bool> from_device;
tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, &from_device);
std::array<bool, block_length> ret;
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
return ret;
}
void XC2C64A::finalize_read_sram(block_id_t id) {
tap.shift({&id, block_id_length}, true);
tap.state(state_t::run_test_idle);
tap.set_end_dr(state_t::run_test_idle);
@@ -79,8 +107,6 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
bypass();
tap.state(state_t::test_logic_reset);
return !error;
}
bool XC2C64A::verify_eeprom(const verify_blocks_t& blocks) {
@@ -133,6 +159,44 @@ void XC2C64A::init_from_eeprom() {
tap.state(state_t::test_logic_reset);
}
void XC2C64A::prepare_read_eeprom() {
tap.set_repeat(0);
tap.set_end_ir(state_t::run_test_idle);
tap.set_end_dr(state_t::run_test_idle);
reset();
bypass();
enable();
shift_ir(instruction_t::ISC_READ);
}
std::array<bool, 274> XC2C64A::read_block_eeprom(block_id_t id) {
const jtag::tap::bits_t empty_row{block_length};
tap.set_end_dr(state_t::pause_dr);
tap.shift_dr({&id, block_id_length});
tap.set_end_ir(state_t::run_test_idle);
tap.wait(state_t::pause_dr, state_t::pause_dr, 20);
tap.set_end_ir(state_t::run_test_idle);
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
std::vector<bool> from_device = tap.shift_dr_read(empty_row);
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
std::array<bool, block_length> ret;
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
return ret;
}
void XC2C64A::finalize_read_eeprom() {
disable();
bypass();
tap.state(state_t::test_logic_reset);
}
bool XC2C64A::shift_ir(const instruction_t instruction) {
const ir_t ir_buffer = toUType(instruction);
const jtag::tap::bits_t bits{&ir_buffer, ir_length};
+7
View File
@@ -68,6 +68,13 @@ class XC2C64A {
bool verify_eeprom(const verify_blocks_t& blocks);
void init_from_eeprom();
void prepare_read_eeprom();
void prepare_read_sram();
std::array<bool, block_length> read_block_eeprom(block_id_t id);
std::array<bool, block_length> read_block_sram(verify_block_t block);
void finalize_read_eeprom();
void finalize_read_sram(block_id_t id);
private:
static constexpr size_t idcode_length = 32;
using idcode_t = uint32_t;
+1
View File
@@ -68,6 +68,7 @@ constexpr size_t clock_generator_output_og_mcu_clkin = 7;
constexpr size_t clock_generator_output_r9_if = 0;
constexpr size_t clock_generator_output_r9_sgpio = 1;
constexpr size_t clock_generator_output_r9_clkout = 2;
constexpr size_t clock_generator_output_r9_mcu_clkin = 2;
/* ADC0 */
+11
View File
@@ -37,4 +37,15 @@ uint32_t JTAG::shift(const size_t count, uint32_t value) {
return value;
}
uint32_t JTAG::shift_header(const size_t count, uint32_t value) {
for (size_t i = 0; i < count; i++) {
const auto tdo = target.clock(
0,
value & 1);
value >>= 1;
value |= tdo << (count - 1);
}
return value;
}
} /* namespace jtag */
+19
View File
@@ -89,10 +89,29 @@ class JTAG {
return result;
}
uint32_t shift_dr(const size_t count, const uint32_t address, const uint32_t value) {
/* Run-Test/Idle -> Select-DR-Scan */
target.clock(1, 0);
/* Scan -> Capture -> Shift */
target.clock(0, 0);
target.clock(0, 0);
shift_header(count, address);
const auto result = shift(count, value);
/* Exit1 -> Update */
target.clock(1, 0);
/* Update -> Run-Test/Idle */
target.clock(0, 0);
return result;
}
private:
Target& target;
uint32_t shift(const size_t count, uint32_t value);
uint32_t shift_header(const size_t count, uint32_t value);
};
} /* namespace jtag */
+18 -2
View File
@@ -191,6 +191,10 @@ bool TAPMachine::shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected, c
return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test);
}
std::vector<bool> TAPMachine::shift_dr_read(const bits_t& tdi_value) {
return shift_data_read(tdi_value, state_t::shift_dr, _end_dr, _run_test);
}
void TAPMachine::state(const state_t state) {
if (state == state_t::test_logic_reset) {
for (int i = 0; i < 5; i++) {
@@ -227,7 +231,7 @@ void TAPMachine::shift_start(const state_t state) {
advance_to_state(state);
}
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms) {
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device) {
if (tdo_expected.length() != tdo_mask.length()) {
return false;
}
@@ -239,6 +243,10 @@ bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits
for (uint32_t i = 0; i < tdi.length(); i++) {
const auto tms = end_tms & (i == (tdi.length() - 1));
const auto tdo = clock(tms, tdi[i]);
if (from_device != nullptr)
from_device->push_back(tdo);
if (tdo_expected && tdo_mask) {
tdo_error |= (tdo & tdo_mask[i]) != (tdo_expected[i] & tdo_mask[i]);
}
@@ -258,7 +266,15 @@ void TAPMachine::shift_end(const state_t end_state, const uint32_t end_delay) {
bool TAPMachine::shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay) {
shift_start(state);
const auto result = shift(tdi, tdo_expected, tdo_mask, true);
const auto result = shift(tdi, tdo_expected, tdo_mask, true, nullptr);
shift_end(end_state, end_delay);
return result;
}
std::vector<bool> TAPMachine::shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay) {
shift_start(state);
std::vector<bool> result;
shift(tdi, {}, {}, true, &result);
shift_end(end_state, end_delay);
return result;
}
+5 -2
View File
@@ -26,6 +26,7 @@
#include <cstdint>
#include <cstddef>
#include <vector>
namespace jtag {
namespace tap {
@@ -113,13 +114,14 @@ class TAPMachine {
void set_end_dr(const state_t state);
bool shift(const bits_t& tdi, const bool end_tms) {
return shift(tdi, {}, {}, end_tms);
return shift(tdi, {}, {}, end_tms, nullptr);
}
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms);
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device);
bool shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
bool shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
std::vector<bool> shift_dr_read(const bits_t& tdi_value);
void state(const state_t state);
@@ -142,6 +144,7 @@ class TAPMachine {
void shift_end(const state_t end_state, const uint32_t end_delay);
bool shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay);
std::vector<bool> shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay);
};
} /* namespace tap */
@@ -77,7 +77,7 @@ constexpr modem_repeat_range_t modem_repeat_range{1, 99};
constexpr int32_t modem_repeat_reset_value{5};
using clkout_freq_range_t = range_t<uint32_t>;
constexpr clkout_freq_range_t clkout_freq_range{10, 60000};
constexpr clkout_freq_range_t clkout_freq_range{4, 60000}; // Min. CLK out of Si5351A/B/C-B is 2.5khz , but in our application -intermediate freq 800Mhz-,Min working CLK=4khz.
constexpr uint16_t clkout_freq_reset_value{10000};
enum data_structure_version_enum : uint32_t {
@@ -195,6 +195,9 @@ struct data_t {
// Recon App
uint64_t recon_config;
int8_t recon_repeat_nb;
int8_t recon_repeat_gain;
uint8_t recon_repeat_delay;
// enable or disable converter
bool converter;
@@ -257,7 +260,11 @@ struct data_t {
tone_mix(tone_mix_reset_value),
hardware_config(0),
recon_config(0),
recon_repeat_nb(0),
recon_repeat_gain(0),
recon_repeat_delay(0),
converter(false),
updown_converter(false),
@@ -284,11 +291,11 @@ struct data_t {
struct backup_ram_t {
private:
volatile uint32_t regfile[63];
volatile uint32_t regfile[PMEM_SIZE_WORDS - 1];
volatile uint32_t check_value;
static void copy(const backup_ram_t& src, backup_ram_t& dst) {
for (size_t i = 0; i < 63; i++) {
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
dst.regfile[i] = src.regfile[i];
}
dst.check_value = src.check_value;
@@ -297,7 +304,7 @@ struct backup_ram_t {
static void copy_from_data_t(const data_t& src, backup_ram_t& dst) {
const uint32_t* const src_words = (uint32_t*)&src;
const size_t word_count = (sizeof(data_t) + 3) / 4;
for (size_t i = 0; i < 63; i++) {
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
if (i < word_count) {
dst.regfile[i] = src_words[i];
} else {
@@ -308,7 +315,7 @@ struct backup_ram_t {
uint32_t compute_check_value() {
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
for (size_t i = 0; i < 63; i++) {
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
const auto word = regfile[i];
crc.process_byte((word >> 0) & 0xff);
crc.process_byte((word >> 8) & 0xff);
@@ -391,10 +398,16 @@ void defaults() {
set_recon_continuous(true);
set_recon_clear_output(false);
set_recon_load_freqs(true);
set_recon_load_repeaters(true);
set_recon_load_ranges(true);
set_recon_update_ranges_when_recon(true);
set_recon_load_hamradios(true);
set_recon_match_mode(0);
set_recon_repeat_recorded(false);
set_recon_repeat_amp(false);
set_recon_repeat_gain(35);
set_recon_repeat_nb(3);
set_recon_repeat_delay(1);
set_config_sdcard_high_speed_io(false, true);
}
@@ -747,6 +760,24 @@ bool recon_match_mode() {
bool recon_auto_record_locked() {
return (data->recon_config & 0x00400000UL) ? true : false;
}
bool recon_repeat_recorded() {
return (data->recon_config & 0x00200000UL) ? true : false;
}
int8_t recon_repeat_nb() {
return data->recon_repeat_nb;
}
int8_t recon_repeat_gain() {
return data->recon_repeat_gain;
}
uint8_t recon_repeat_delay() {
return data->recon_repeat_delay;
}
bool recon_repeat_amp() {
return (data->recon_config & 0x00100000UL) ? true : false;
}
bool recon_load_repeaters() {
return (data->recon_config & 0x00080000UL) ? true : false;
}
void set_recon_autosave_freqs(const bool v) {
data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31);
@@ -778,6 +809,24 @@ void set_recon_match_mode(const bool v) {
void set_recon_auto_record_locked(const bool v) {
data->recon_config = (data->recon_config & ~0x00400000UL) | (v << 22);
}
void set_recon_repeat_recorded(const bool v) {
data->recon_config = (data->recon_config & ~0x00200000UL) | (v << 21);
}
void set_recon_repeat_nb(const int8_t v) {
data->recon_repeat_nb = v;
}
void set_recon_repeat_gain(const int8_t v) {
data->recon_repeat_gain = v;
}
void set_recon_repeat_delay(const uint8_t v) {
data->recon_repeat_delay = v;
}
void set_recon_repeat_amp(const bool v) {
data->recon_config = (data->recon_config & ~0x00100000UL) | (v << 20);
}
void set_recon_load_repeaters(const bool v) {
data->recon_config = (data->recon_config & ~0x00080000UL) | (v << 19);
}
/* UI Config 2 */
bool ui_hide_speaker() {
@@ -988,6 +1037,9 @@ bool debug_dump() {
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix));
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config));
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(data->recon_repeat_nb, 16));
pmem_dump_file.write_line("recon_repeat_gain:" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("recon_repeat_delay:" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter));
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction));
@@ -39,6 +39,9 @@
// persistant memory from/to sdcard flag file
#define PMEM_SETTING_FILE u"/SETTINGS/pmem_settings"
#define PMEM_SIZE_BYTES 256 // total amount of pmem space in bytes, including checksum
#define PMEM_SIZE_WORDS (PMEM_SIZE_BYTES / 4)
using namespace modems;
using namespace serializer;
@@ -242,11 +245,17 @@ bool recon_autostart_recon();
bool recon_continuous();
bool recon_clear_output();
bool recon_load_freqs();
bool recon_load_repeaters();
bool recon_load_ranges();
bool recon_update_ranges_when_recon();
bool recon_auto_record_locked();
bool recon_repeat_recorded();
int8_t recon_repeat_nb();
int8_t recon_repeat_gain();
bool recon_repeat_amp();
bool recon_load_hamradios();
bool recon_match_mode();
uint8_t recon_repeat_delay();
void set_recon_autosave_freqs(const bool v);
void set_recon_autostart_recon(const bool v);
void set_recon_continuous(const bool v);
@@ -255,8 +264,14 @@ void set_recon_load_freqs(const bool v);
void set_recon_load_ranges(const bool v);
void set_recon_update_ranges_when_recon(const bool v);
void set_recon_auto_record_locked(const bool v);
void set_recon_repeat_recorded(const bool v);
void set_recon_repeat_nb(const int8_t v);
void set_recon_repeat_gain(const int8_t v);
void set_recon_repeat_amp(const bool v);
void set_recon_load_hamradios(const bool v);
void set_recon_load_repeaters(const bool v);
void set_recon_match_mode(const bool v);
void set_recon_repeat_delay(const uint8_t v);
/* UI Config 2 */
bool ui_hide_speaker();
@@ -59,6 +59,17 @@ TEST_CASE("It can parse basic ham radio freq entry.") {
CHECK_EQ(e.type, freqman_type::HamRadio);
}
TEST_CASE("It can parse repeater entry.") {
freqman_entry e;
REQUIRE(
parse_freqman_entry(
"l=123000000,t=123000500,d=This is the description.", e));
CHECK_EQ(e.frequency_a, 123'000'000);
CHECK_EQ(e.frequency_b, 123'000'500);
CHECK_EQ(e.description, "This is the description.");
CHECK_EQ(e.type, freqman_type::Repeater);
}
TEST_CASE("It can parse modulation") {
freqman_entry e;
REQUIRE(
@@ -195,6 +206,16 @@ TEST_CASE("It can serialize basic HamRadio entry") {
CHECK(str == "r=123456000,t=423456000,d=Foobar");
}
TEST_CASE("It can serialize basic Repeater entry") {
auto str = to_freqman_string(freqman_entry{
.frequency_a = 123'456'000,
.frequency_b = 423'456'000,
.description = "Foobar",
.type = freqman_type::Repeater,
});
CHECK(str == "l=123456000,t=423456000,d=Foobar");
}
// New tables for a future PR.
/*
TEST_CASE("It can parse modulation") {