mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-14 02:29:29 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05146638fc | |||
| 765e3be55b | |||
| 9211975868 | |||
| 96a60e82bc | |||
| 5d9428e568 | |||
| 2fdd531fe7 | |||
| f13756d4c7 | |||
| ae6724b359 | |||
| e11c28cf30 | |||
| b93fbad207 | |||
| 2bedb5f09f | |||
| 19eb6b44d5 | |||
| 048359fb0e | |||
| ae75540d35 | |||
| 9ef58b7763 | |||
| 032168234a | |||
| b53b5c70d3 | |||
| ee53016c6e | |||
| 2bf1116c6a |
@@ -54,7 +54,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -16,9 +16,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check these:
|
||||
|
||||
[<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="512">](https://grabify.link/C0J6ZR)
|
||||
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="512">](https://grabify.link/9UZGEW)
|
||||
|
||||
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="254">](https://grabify.link/9UZGEW)
|
||||
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="254">](https://grabify.link/C0J6ZR)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
@@ -30,7 +30,7 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
|
||||
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/HTDXG5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
|
||||
|
||||
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of their [PortaPack H2](https://grabify.link/LG0OUY), you can support them too by ordering.
|
||||
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of one of their [PortaPack H2 bundle](https://grabify.link/T328XL) or [only metal case kits](https://grabify.link/LG0OUY), you can support them too by ordering.
|
||||
|
||||
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ set(CPPSRC
|
||||
${COMMON}/utility.cpp
|
||||
${COMMON}/wm8731.cpp
|
||||
${COMMON}/ads1110.cpp
|
||||
${COMMON}/max17055.cpp
|
||||
${COMMON}/battery.cpp
|
||||
${COMMON}/performance_counter.cpp
|
||||
${COMMON}/bmpfile.cpp
|
||||
@@ -508,6 +509,7 @@ include_directories(. ${INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS} "-L${CMAKE_CURRENT_LIST_DIR}/external")
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,--print-memory-usage)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.bin
|
||||
|
||||
@@ -24,14 +24,13 @@
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include "battery.hpp"
|
||||
#include <cstring>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool BattinfoView::needRun = true;
|
||||
void BattinfoView::focus() {
|
||||
button_exit.focus();
|
||||
}
|
||||
@@ -53,24 +52,25 @@ void BattinfoView::update_result() {
|
||||
return;
|
||||
}
|
||||
bool uichg = false;
|
||||
battery::BatteryManagement::getBatteryInfo(percent, voltage, current, isCharging);
|
||||
uint8_t valid_mask = 0;
|
||||
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current);
|
||||
// update text fields
|
||||
if (percent <= 100)
|
||||
if (percent <= 100 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE)
|
||||
text_percent.set(to_string_dec_uint(percent) + " %");
|
||||
else
|
||||
text_percent.set("UNKNOWN");
|
||||
if (voltage > 1) {
|
||||
if (voltage > 1 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE) {
|
||||
text_voltage.set(to_string_decimal(voltage / 1000.0, 3) + " V");
|
||||
} else {
|
||||
text_voltage.set("UNKNOWN");
|
||||
}
|
||||
if (current != 0) {
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CURRENT) == battery::BatteryManagement::BATT_VALID_CURRENT) {
|
||||
if (labels_opt.hidden()) uichg = true;
|
||||
labels_opt.hidden(false);
|
||||
text_current.hidden(false);
|
||||
text_charge.hidden(false);
|
||||
text_current.set(to_string_dec_int(current) + " mA");
|
||||
text_charge.set(isCharging ? "charge" : "discharge");
|
||||
text_charge.set(current >= 0 ? "Charging" : "Discharging");
|
||||
labels_opt.hidden(false);
|
||||
} else {
|
||||
if (!labels_opt.hidden()) uichg = true;
|
||||
@@ -80,7 +80,7 @@ void BattinfoView::update_result() {
|
||||
}
|
||||
if (uichg) set_dirty();
|
||||
// to update status bar too, send message in behalf of batt manager
|
||||
BatteryStateMessage msg{percent, isCharging, voltage};
|
||||
BatteryStateMessage msg{valid_mask, percent, current >= 0, voltage};
|
||||
EventDispatcher::send_message(msg);
|
||||
}
|
||||
|
||||
@@ -99,13 +99,12 @@ BattinfoView::BattinfoView(NavigationView& nav)
|
||||
};
|
||||
|
||||
update_result();
|
||||
needRun = true;
|
||||
thread = chThdCreateFromHeap(NULL, 512, NORMALPRIO + 10, BattinfoView::static_fn, this);
|
||||
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
|
||||
}
|
||||
|
||||
msg_t BattinfoView::static_fn(void* arg) {
|
||||
auto obj = static_cast<BattinfoView*>(arg);
|
||||
while (needRun) {
|
||||
while (!chThdShouldTerminate()) {
|
||||
chThdSleepMilliseconds(16);
|
||||
obj->on_timer();
|
||||
}
|
||||
@@ -113,7 +112,6 @@ msg_t BattinfoView::static_fn(void* arg) {
|
||||
}
|
||||
|
||||
BattinfoView::~BattinfoView() {
|
||||
needRun = false;
|
||||
if (thread) {
|
||||
chThdTerminate(thread);
|
||||
chThdWait(thread);
|
||||
|
||||
@@ -50,7 +50,6 @@ class BattinfoView : public View {
|
||||
uint8_t percent = 0;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
bool isCharging = false;
|
||||
|
||||
Labels labels{
|
||||
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
|
||||
@@ -77,7 +76,6 @@ class BattinfoView : public View {
|
||||
{72, 17 * 16, 96, 32},
|
||||
"Back"};
|
||||
static msg_t static_fn(void* arg);
|
||||
static bool needRun;
|
||||
Thread* thread{nullptr};
|
||||
};
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
|
||||
return radio::debug::second_if::register_read(register_number);
|
||||
case CT_SI5351:
|
||||
return portapack::clock_generator.read_register(register_number);
|
||||
case CT_BATTERY:
|
||||
return battery::BatteryManagement::read_register(register_number);
|
||||
case CT_AUDIO:
|
||||
return audio::debug::reg_read(register_number);
|
||||
}
|
||||
@@ -246,6 +248,9 @@ void RegistersWidget::reg_write(const uint32_t register_number, const uint32_t v
|
||||
case CT_SI5351:
|
||||
portapack::clock_generator.write_register(register_number, value);
|
||||
break;
|
||||
case CT_BATTERY:
|
||||
battery::BatteryManagement::write_register(register_number, value);
|
||||
break;
|
||||
case CT_AUDIO:
|
||||
audio::debug::reg_write(register_number, value);
|
||||
break;
|
||||
@@ -459,6 +464,10 @@ void DebugPeripheralsMenuView::on_populate() {
|
||||
{si5351x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
|
||||
{audio::debug::codec_name(), Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
|
||||
});
|
||||
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
|
||||
add_item(
|
||||
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_BATTERY, 256, 16, 16}); }});
|
||||
}
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -496,10 +505,10 @@ void DebugMenuView::on_populate() {
|
||||
{"Peripherals", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Pers. Memory", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugPmemView>(); }},
|
||||
//{ "Radio State", ui::Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ nav_.push<NotImplementedView>(); } },
|
||||
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
|
||||
{"SD Card", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_sdcard, [this]() { nav_.push<SDCardDebugView>(); }},
|
||||
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
|
||||
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
|
||||
});
|
||||
|
||||
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
|
||||
|
||||
@@ -139,6 +139,7 @@ typedef enum {
|
||||
CT_MAX283X,
|
||||
CT_SI5351,
|
||||
CT_AUDIO,
|
||||
CT_BATTERY,
|
||||
} chip_type_t;
|
||||
|
||||
struct RegistersWidgetConfig {
|
||||
|
||||
@@ -269,16 +269,24 @@ void MicTXView::set_rxbw_options(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void MicTXView::set_rxbw_defaults(bool use_app_settings) {
|
||||
void MicTXView::set_rxbw_defaults(bool use_app_settings) { // Initially in that function we set up rxbw, but now also txbw.
|
||||
if (use_app_settings) {
|
||||
field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
|
||||
field_rxbw.set_by_value(rxbw_index);
|
||||
} else if (mic_mod_index == MIC_MOD_NFM) {
|
||||
field_bw.set_value(10); // NFM TX bw 10k, RX bw 16k (2) default
|
||||
field_rxbw.set_by_value(2);
|
||||
field_bw.set_value(10); // NFM TX bw 10k, RX bw 16k (index 2) default
|
||||
field_bw.set_range(1, 60); // In NFM , FM , we are limitting index modulation range (0.08 ..5) ; (Ex max dev 60khz/12k = 5)
|
||||
field_bw.set_step(1);
|
||||
field_rxbw.set_by_value(2); // 16k from the three options (8k5,11k,16k)
|
||||
} else if (mic_mod_index == MIC_MOD_WFM) {
|
||||
field_bw.set_value(75); // WFM TX bw 75K, RX bw 200k (0) default
|
||||
field_bw.set_value(75); // WFM TX bw 75K, RX bw 200k (index 0) default
|
||||
field_bw.set_range(1, 150); // In our case Mod. Index range (1,67 ...12,5) ; 150k/12k=12,5
|
||||
field_bw.set_step(1);
|
||||
field_rxbw.set_by_value(0);
|
||||
} else if ((mic_mod_index == MIC_MOD_USB) | (mic_mod_index == MIC_MOD_LSB)) {
|
||||
field_bw.set_value(3); // In SSB by default let's limit TX_BW to 3kHz.
|
||||
field_bw.set_range(2, 3); // User TXBW GUI range to modify that SSB TX_BW to limit SSB radiated spectrum.
|
||||
field_bw.set_step(1);
|
||||
}
|
||||
// field_bw is hidden in other modulation cases
|
||||
}
|
||||
@@ -482,7 +490,11 @@ MicTXView::MicTXView(
|
||||
field_bw.hidden(false);
|
||||
options_tone_key.hidden(false);
|
||||
} else {
|
||||
field_bw.hidden(true);
|
||||
if ((mic_mod_index == MIC_MOD_USB) || (mic_mod_index == MIC_MOD_LSB)) {
|
||||
field_bw.hidden(false);
|
||||
} else {
|
||||
field_bw.hidden(true);
|
||||
}
|
||||
options_tone_key.set_selected_index(0);
|
||||
options_tone_key.hidden(true);
|
||||
}
|
||||
|
||||
@@ -148,10 +148,10 @@ class MicTXView : public View {
|
||||
|
||||
Labels labels_both{
|
||||
{{3 * 8, 1 * 8}, "MIC-GAIN:", Theme::getInstance()->fg_light->foreground},
|
||||
{{3 * 8, 3 * 8}, "F:", Theme::getInstance()->fg_light->foreground},
|
||||
{{15 * 8, 3 * 8}, "FM TXBW: kHz", Theme::getInstance()->fg_light->foreground}, // to be more symetric and consistent to the below FM RXBW
|
||||
{{18 * 8, (5 * 8)}, "Mode:", Theme::getInstance()->fg_light->foreground}, // now, no need to handle GAIN, Amp here It is handled by ui_transmitter.cpp
|
||||
{{4 * 8, 10 * 8}, "LVL:", Theme::getInstance()->fg_light->foreground}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Theme::getInstance()->fg_light->foreground },
|
||||
{{3 * 8, 3 * 8}, "F: MHz", Theme::getInstance()->fg_light->foreground},
|
||||
{{18 * 8, 3 * 8}, "TXBW: kHz", Theme::getInstance()->fg_light->foreground}, // to be more symetric and consistent to the below FM RXBW
|
||||
{{18 * 8, (5 * 8)}, "Mode:", Theme::getInstance()->fg_light->foreground}, // now, no need to handle GAIN, Amp here It is handled by ui_transmitter.cpp
|
||||
{{4 * 8, 10 * 8}, "LVL:", Theme::getInstance()->fg_light->foreground}, // we delete { {11 * 8, 5 * 8 }, "Amp:", Theme::getInstance()->fg_light->foreground },
|
||||
{{12 * 8, 10 * 8}, "ATT:", Theme::getInstance()->fg_light->foreground},
|
||||
{{20 * 8, 10 * 8}, "DEC:", Theme::getInstance()->fg_light->foreground},
|
||||
{{3 * 8, (13 * 8) - 5}, "TONE KEY:", Theme::getInstance()->fg_light->foreground},
|
||||
|
||||
@@ -873,6 +873,8 @@ class SetThemeView : public View {
|
||||
{"Default - Grey", 0},
|
||||
{"Yellow", 1},
|
||||
{"Aqua", 2},
|
||||
{"Green", 3},
|
||||
{"Red", 4},
|
||||
}};
|
||||
|
||||
Checkbox checkbox_menuset{
|
||||
|
||||
@@ -32,12 +32,15 @@ using namespace ui;
|
||||
namespace ui {
|
||||
|
||||
void SubGhzDRecentEntryDetailView::update_data() {
|
||||
// process protocol data
|
||||
parseProtocol();
|
||||
// set text elements
|
||||
text_type.set(SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry_.sensorType));
|
||||
text_id.set("0x" + to_string_hex(entry_.serial));
|
||||
|
||||
text_id.set("0x" + to_string_hex(serial));
|
||||
if (entry_.bits > 0) console.writeln("Bits: " + to_string_dec_uint(entry_.bits));
|
||||
if (entry_.btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(entry_.btn));
|
||||
if (entry_.cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(entry_.cnt));
|
||||
if (btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(btn));
|
||||
if (cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(cnt));
|
||||
|
||||
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
|
||||
}
|
||||
@@ -103,7 +106,7 @@ void SubGhzDView::on_tick_second() {
|
||||
}
|
||||
|
||||
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
|
||||
SubGhzDRecentEntry key{data->sensorType, data->serial, data->bits, data->data, data->btn, data->cnt};
|
||||
SubGhzDRecentEntry key{data->sensorType, data->data, data->bits};
|
||||
auto matching_recent = find(recent, key.key());
|
||||
if (matching_recent != std::end(recent)) {
|
||||
// Found within. Move to front of list, increment counter.
|
||||
@@ -203,6 +206,13 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
|
||||
return "Star Line";
|
||||
case FPS_X10:
|
||||
return "X10";
|
||||
case FPS_LEGRAND:
|
||||
return "Legrand";
|
||||
case FPS_SOMIFY_KEYTIS:
|
||||
return "Somify Keytis";
|
||||
case FPS_SOMIFY_TELIS:
|
||||
return "Somify Telis";
|
||||
|
||||
case FPS_Invalid:
|
||||
default:
|
||||
return "Unknown";
|
||||
@@ -224,7 +234,7 @@ void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
|
||||
line.reserve(30);
|
||||
|
||||
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
|
||||
line = line + " " + to_string_hex(entry.serial);
|
||||
line = line + " " + to_string_hex(entry.data << 32);
|
||||
if (line.length() < 19) {
|
||||
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
|
||||
} else {
|
||||
@@ -239,4 +249,469 @@ void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
}
|
||||
|
||||
// decoder helper functions
|
||||
|
||||
void atomo_decrypt(uint8_t* buff) {
|
||||
buff[0] = (buff[0] ^ 5) & 0x7F;
|
||||
uint8_t tmpB = (-buff[0]) & 0x7F;
|
||||
|
||||
uint8_t bitCnt = 8;
|
||||
while (bitCnt < 59) {
|
||||
if ((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) {
|
||||
tmpB = ((tmpB << 1) & 0xFF) | 1;
|
||||
} else {
|
||||
tmpB = (tmpB << 1) & 0xFF;
|
||||
}
|
||||
|
||||
if (tmpB & 0x80) {
|
||||
buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7));
|
||||
}
|
||||
|
||||
bitCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t came_twee_magic_numbers_xor[15] = {
|
||||
0x0E0E0E00,
|
||||
0x1D1D1D11,
|
||||
0x2C2C2C22,
|
||||
0x3B3B3B33,
|
||||
0x4A4A4A44,
|
||||
0x59595955,
|
||||
0x68686866,
|
||||
0x77777777,
|
||||
0x86868688,
|
||||
0x95959599,
|
||||
0xA4A4A4AA,
|
||||
0xB3B3B3BB,
|
||||
0xC2C2C2CC,
|
||||
0xD1D1D1DD,
|
||||
0xE0E0E0EE,
|
||||
};
|
||||
|
||||
// to save some byte of fw space, these will be inline. unreadeable? yes. needs a tons of free space? certanly. so sorry for this.
|
||||
|
||||
void SubGhzDRecentEntryDetailView::parseProtocol() {
|
||||
btn = SD_NO_BTN;
|
||||
cnt = SD_NO_CNT;
|
||||
serial = 0;
|
||||
|
||||
if (entry_.sensorType == FPS_Invalid) return;
|
||||
|
||||
if (entry_.sensorType == FPS_BETT) {
|
||||
return; // needs dip pattern output.
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_AIRFORCE || entry_.sensorType == FPS_PRASTEL || entry_.sensorType == FPS_CAME) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_CAMEATOMO) {
|
||||
entry_.data ^= 0xFFFFFFFFFFFFFFFF;
|
||||
entry_.data <<= 4;
|
||||
uint8_t pack[8] = {};
|
||||
pack[0] = (entry_.data >> 56);
|
||||
pack[1] = ((entry_.data >> 48) & 0xFF);
|
||||
pack[2] = ((entry_.data >> 40) & 0xFF);
|
||||
pack[3] = ((entry_.data >> 32) & 0xFF);
|
||||
pack[4] = ((entry_.data >> 24) & 0xFF);
|
||||
pack[5] = ((entry_.data >> 16) & 0xFF);
|
||||
pack[6] = ((entry_.data >> 8) & 0xFF);
|
||||
pack[7] = (entry_.data & 0xFF);
|
||||
|
||||
atomo_decrypt(pack);
|
||||
|
||||
// cnt_2 = pack[0];
|
||||
cnt = (uint16_t)pack[1] << 8 | pack[2];
|
||||
serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6];
|
||||
|
||||
uint8_t btn_decode = (pack[7] >> 4);
|
||||
if (btn_decode == 0x0) {
|
||||
btn = 0x1;
|
||||
} else if (btn_decode == 0x2) {
|
||||
btn = 0x2;
|
||||
} else if (btn_decode == 0x4) {
|
||||
btn = 0x3;
|
||||
} else if (btn_decode == 0x6) {
|
||||
btn = 0x4;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_CAMETWEE) {
|
||||
uint8_t cnt_parcel = (uint8_t)(entry_.data & 0xF);
|
||||
uint32_t data = (uint32_t)(entry_.data & 0x0FFFFFFFF);
|
||||
|
||||
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
|
||||
serial = data;
|
||||
data /= 4;
|
||||
btn = (data >> 4) & 0x0F;
|
||||
data >>= 16;
|
||||
data = (uint16_t)FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 16);
|
||||
cnt = data >> 6;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_CHAMBCODE) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_CLEMSA) {
|
||||
serial = (entry_.data >> 2) & 0xFFFF;
|
||||
btn = (entry_.data & 0x03);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_DOITRAND) {
|
||||
cnt = (entry_.data >> 24) | ((entry_.data >> 15) & 0x1);
|
||||
btn = ((entry_.data >> 18) & 0x3);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_DOOYA) {
|
||||
serial = (entry_.data >> 16);
|
||||
if ((entry_.data >> 12) & 0x0F) {
|
||||
cnt = (entry_.data >> 8) & 0x0F;
|
||||
} else {
|
||||
cnt = 0xff;
|
||||
}
|
||||
btn = entry_.data & 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_FAAC) { // stripped down a lot.
|
||||
uint32_t code_fix = entry_.data >> 32;
|
||||
uint32_t code_hop = entry_.data & 0xFFFFFFFF;
|
||||
// uint32_t decrypt = 0;
|
||||
// uint64_t man;
|
||||
|
||||
uint8_t data_tmp = 0;
|
||||
uint8_t data_prg[8];
|
||||
data_prg[0] = (code_hop & 0xFF);
|
||||
data_prg[1] = ((code_hop >> 8) & 0xFF);
|
||||
data_prg[2] = ((code_hop >> 16) & 0xFF);
|
||||
data_prg[3] = (code_hop >> 24);
|
||||
data_prg[4] = (code_fix & 0xFF);
|
||||
data_prg[5] = ((code_fix >> 8) & 0xFF);
|
||||
data_prg[6] = ((code_fix >> 16) & 0xFF);
|
||||
data_prg[7] = (code_fix >> 24);
|
||||
|
||||
if (((data_prg[7] == 0x52) && (data_prg[6] == 0x0F) && (data_prg[0] == 0x00))) {
|
||||
// ProgMode ON
|
||||
for (uint8_t i = data_prg[1] & 0xF; i != 0; i--) {
|
||||
data_tmp = data_prg[2];
|
||||
|
||||
data_prg[2] = data_prg[2] >> 1 | (data_prg[3] & 1) << 7;
|
||||
data_prg[3] = data_prg[3] >> 1 | (data_prg[4] & 1) << 7;
|
||||
data_prg[4] = data_prg[4] >> 1 | (data_prg[5] & 1) << 7;
|
||||
data_prg[5] = data_prg[5] >> 1 | (data_tmp & 1) << 7;
|
||||
}
|
||||
data_prg[2] ^= data_prg[1];
|
||||
data_prg[3] ^= data_prg[1];
|
||||
data_prg[4] ^= data_prg[1];
|
||||
data_prg[5] ^= data_prg[1];
|
||||
seed = data_prg[5] << 24 | data_prg[4] << 16 | data_prg[3] << 8 | data_prg[2];
|
||||
// uint32_t dec_prg_1 = data_prg[7] << 24 | data_prg[6] << 16 | data_prg[5] << 8 | data_prg[4];
|
||||
// uint32_t dec_prg_2 = data_prg[3] << 24 | data_prg[2] << 16 | data_prg[1] << 8 | data_prg[0];
|
||||
// entry_.data_2 = (uint64_t)dec_prg_1 << 32 | dec_prg_2;
|
||||
cnt = data_prg[1];
|
||||
} else {
|
||||
if (code_fix != 0x0) {
|
||||
serial = code_fix >> 4;
|
||||
btn = code_fix & 0xF;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_GATETX) {
|
||||
uint32_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
|
||||
serial = (code_found_reverse & 0xFF) << 12 | ((code_found_reverse >> 8) & 0xFF) << 4 | ((code_found_reverse >> 20) & 0x0F);
|
||||
btn = ((code_found_reverse >> 16) & 0x0F);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_HOLTEK) {
|
||||
if ((entry_.data & 0xF000000000) == 0x5000000000) {
|
||||
serial = FProtoGeneral::subghz_protocol_blocks_reverse_key((entry_.data >> 16) & 0xFFFFF, 20);
|
||||
uint16_t btn_ = entry_.data & 0xFFFF;
|
||||
if ((btn_ & 0xf) != 0xA) {
|
||||
btn = 0x1 << 4 | (btn_ & 0xF);
|
||||
} else if (((btn_ >> 4) & 0xF) != 0xA) {
|
||||
btn = 0x2 << 4 | ((btn_ >> 4) & 0xF);
|
||||
} else if (((btn_ >> 8) & 0xF) != 0xA) {
|
||||
btn = 0x3 << 4 | ((btn_ >> 8) & 0xF);
|
||||
} else if (((btn_ >> 12) & 0xF) != 0xA) {
|
||||
btn = 0x4 << 4 | ((btn_ >> 12) & 0xF);
|
||||
} else {
|
||||
btn = 0;
|
||||
}
|
||||
} else {
|
||||
serial = 0;
|
||||
btn = 0;
|
||||
cnt = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_HOLTEKHT12X) {
|
||||
btn = entry_.data & 0x0F;
|
||||
cnt = (entry_.data >> 4) & 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_HONEYWELL) {
|
||||
serial = (entry_.data >> 24) & 0xFFFFF;
|
||||
btn = (entry_.data >> 16) & 0xFF; // not exactly button, but can contain btn data too.
|
||||
cnt = (entry_.data >> 44) & 0xF;
|
||||
/*
|
||||
uint8_t contact = (entry_.databtn & 0x80) >> 7;
|
||||
uint8_t tamper = (entry_.databtn & 0x40) >> 6;
|
||||
uint8_t reed = (entry_.databtn & 0x20) >> 5;
|
||||
uint8_t alarm = (entry_.databtn & 0x10) >> 4;
|
||||
uint8_t battery_low = (entry_.databtn & 0x08) >> 3;
|
||||
uint8_t heartbeat = (entry_.databtn & 0x04) >> 2;
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_HONEYWELLWDB) {
|
||||
serial = (entry_.data >> 28) & 0xFFFFF;
|
||||
// enabled, when we'll have extra fields and free fw space
|
||||
/* switch ((entry_.data >> 20) & 0x3) {
|
||||
case 0x02:
|
||||
device_type = "Doorbell";
|
||||
break;
|
||||
case 0x01:
|
||||
device_type = "PIR-Motion";
|
||||
break;
|
||||
default:
|
||||
device_type = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((entry_.data >> 16) & 0x3) {
|
||||
case 0x00:
|
||||
alert = "Normal";
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
alert = "High";
|
||||
break;
|
||||
case 0x03:
|
||||
alert = "Full";
|
||||
break;
|
||||
default:
|
||||
alert = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
secret_knock = (uint8_t)((entry_.data >> 4) & 0x1);
|
||||
relay = (uint8_t)((entry_.data >> 3) & 0x1);
|
||||
lowbat = (uint8_t)((entry_.data >> 1) & 0x1);*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_HORMANN) {
|
||||
btn = (entry_.data >> 8) & 0xF;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if (entry_.sensorType == FPS_HORMANNBISECURE) { //fm not implemented
|
||||
serial = 0;
|
||||
|
||||
for (uint8_t i = 1; i < 5; i++) {
|
||||
serial = serial << 8 | ((uint8_t*)(&entry_.data))[i];
|
||||
}
|
||||
} */
|
||||
|
||||
if (entry_.sensorType == FPS_IDO) {
|
||||
uint64_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
|
||||
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
|
||||
serial = code_fix & 0xFFFFF;
|
||||
btn = (code_fix >> 20) & 0x0F;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_INTERTECHNOV3) {
|
||||
if (entry_.bits == 32) {
|
||||
serial = (entry_.data >> 6) & 0x3FFFFFF;
|
||||
if ((entry_.data >> 5) & 0x1) {
|
||||
cnt = 1 << 5;
|
||||
} else {
|
||||
cnt = (~entry_.data & 0xF);
|
||||
}
|
||||
btn = (entry_.data >> 4) & 0x1;
|
||||
} else if (entry_.bits == 36) {
|
||||
serial = (entry_.data >> 10) & 0x3FFFFFF;
|
||||
if ((entry_.data >> 9) & 0x1) {
|
||||
cnt = 1 << 5;
|
||||
} else {
|
||||
cnt = (~(entry_.data >> 4) & 0xF);
|
||||
}
|
||||
btn = (entry_.data) & 0xF;
|
||||
} else {
|
||||
serial = 0;
|
||||
cnt = 0;
|
||||
btn = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_KEELOQ) {
|
||||
// too many sub protocol versions, skipping. maybe in future when we'll have much more fw space
|
||||
return;
|
||||
}
|
||||
|
||||
/* fm not implemented
|
||||
if (entry_.sensorType == FPS_KIA) {
|
||||
serial = (uint32_t)((entry_.data >> 12) & 0x0FFFFFFF);
|
||||
btn = (entry_.data >> 8) & 0x0F;
|
||||
cnt = (entry_.data >> 40) & 0xFFFF;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (entry_.sensorType == FPS_KINGGATESSTYLO4K) {
|
||||
uint64_t fix = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, 53);
|
||||
btn = (fix >> 17) & 0x0F;
|
||||
serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_LEGRAND) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_LINEAR || entry_.sensorType == FPS_LINEARDELTA3) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_MAGELLAN) {
|
||||
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data >> 8, 24);
|
||||
serial = data_rev & 0xFFFF;
|
||||
btn = (data_rev >> 16) & 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_MARANTEC) {
|
||||
btn = (entry_.data >> 16) & 0xF;
|
||||
serial = ((entry_.data >> 12) & 0xFFFFFF00) | ((entry_.data >> 8) & 0xFF);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_MASTERCODE) {
|
||||
serial = (entry_.data >> 4) & 0xFFFF;
|
||||
btn = (entry_.data >> 2 & 0x03);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_MEGACODE) {
|
||||
if ((entry_.data >> 23) == 1) {
|
||||
serial = (entry_.data >> 3) & 0xFFFF;
|
||||
btn = entry_.data & 0b111;
|
||||
cnt = (entry_.data >> 19) & 0b1111;
|
||||
} else {
|
||||
serial = 0;
|
||||
btn = 0;
|
||||
cnt = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_NERORADIO) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_NERO_SKETCH) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_NICEFLO || entry_.sensorType == FPS_NICEFLORS) {
|
||||
return; // nothing, and can't
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_PHOENIXV2) {
|
||||
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits + 4);
|
||||
serial = data_rev & 0xFFFFFFFF;
|
||||
cnt = (data_rev >> 40) & 0xFFFF;
|
||||
btn = (data_rev >> 32) & 0xF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_POWERSMART) {
|
||||
btn = ((entry_.data >> 54) & 0x02) | ((entry_.data >> 40) & 0x1);
|
||||
serial = ((entry_.data >> 33) & 0x3FFF00) | ((entry_.data >> 32) & 0xFF);
|
||||
cnt = ((entry_.data >> 49) & 0x3F);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_PRINCETON) {
|
||||
serial = entry_.data >> 4;
|
||||
btn = entry_.data & 0xF;
|
||||
return;
|
||||
}
|
||||
if (entry_.sensorType == FPS_SECPLUSV1) {
|
||||
uint32_t fixed = (entry_.data >> 32) & 0xFFFFFFFF;
|
||||
cnt = entry_.data & 0xFFFFFFFF;
|
||||
btn = fixed % 3;
|
||||
// uint8_t id0 = (fixed / 3) % 3;
|
||||
uint8_t id1 = (fixed / 9) % 3;
|
||||
// uint16_t pin = 0;
|
||||
if (id1 == 0) {
|
||||
// (fixed // 3**3) % (3**7) 3^3=27 3^73=72187
|
||||
|
||||
serial = (fixed / 27) % 2187;
|
||||
// pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683
|
||||
// pin = (fixed / 59049) % 19683;
|
||||
|
||||
/* if (pin <= 9999) {
|
||||
furi_string_cat_printf(output, " pin:%d", pin);
|
||||
} else if (pin <= 11029) {
|
||||
furi_string_cat_printf(output, " pin:enter");
|
||||
} */
|
||||
} else {
|
||||
// id = fixed / 27;
|
||||
serial = fixed / 27;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_SECPLUSV2) {
|
||||
return; // fw space saver
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_SMC5326) {
|
||||
return; // dip pattern output needed. skipping
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_STARLINE) {
|
||||
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
|
||||
uint32_t key_fix = key >> 32;
|
||||
serial = key_fix & 0x00FFFFFF;
|
||||
btn = key_fix >> 24;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_X10) {
|
||||
serial = (entry_.data & 0xF0000000) >> (24 + 4);
|
||||
btn = (((entry_.data & 0x07000000) >> 24) | ((entry_.data & 0xF800) >> 8));
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_SOMIFY_KEYTIS) {
|
||||
uint64_t dataa = entry_.data ^ (entry_.data >> 8);
|
||||
btn = (dataa >> 48) & 0xF;
|
||||
cnt = (dataa >> 24) & 0xFFFF;
|
||||
serial = dataa & 0xFFFFFF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry_.sensorType == FPS_SOMIFY_TELIS) {
|
||||
uint64_t dataa = entry_.data ^ (entry_.data >> 8);
|
||||
btn = (dataa >> 44) & 0xF; // ctrl
|
||||
cnt = (dataa >> 24) & 0xFFFF; // rolling code
|
||||
serial = dataa & 0xFFFFFF; // address}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace ui
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef __UI_SUBGHZD_H__
|
||||
#define __UI_SUBGHZD_H__
|
||||
|
||||
#define SD_NO_SERIAL 0xFFFFFFFF
|
||||
#define SD_NO_BTN 0xFF
|
||||
#define SD_NO_CNT 0xFF
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
@@ -33,6 +37,7 @@
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "../baseband/fprotos/subghztypes.hpp"
|
||||
#include "../baseband/fprotos/fprotogeneral.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
@@ -42,29 +47,20 @@ struct SubGhzDRecentEntry {
|
||||
using Key = uint64_t;
|
||||
static constexpr Key invalid_key = 0x0fffffff;
|
||||
uint8_t sensorType = FPS_Invalid;
|
||||
uint8_t btn = SD_NO_BTN;
|
||||
uint32_t serial = SD_NO_SERIAL;
|
||||
uint16_t bits = 0;
|
||||
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
|
||||
uint32_t cnt = SD_NO_CNT;
|
||||
uint64_t data = 0;
|
||||
SubGhzDRecentEntry() {}
|
||||
SubGhzDRecentEntry(
|
||||
uint8_t sensorType,
|
||||
uint32_t serial,
|
||||
uint16_t bits = 0,
|
||||
uint64_t data = 0,
|
||||
uint8_t btn = SD_NO_BTN,
|
||||
uint32_t cnt = SD_NO_CNT)
|
||||
uint16_t bits = 0)
|
||||
: sensorType{sensorType},
|
||||
btn{btn},
|
||||
serial{serial},
|
||||
bits{bits},
|
||||
cnt{cnt},
|
||||
data{data} {
|
||||
}
|
||||
Key key() const {
|
||||
return (data ^ ((static_cast<uint64_t>(serial) << 32) | (static_cast<uint64_t>(sensorType) & 0xFF) << 0));
|
||||
return (data ^ ((static_cast<uint64_t>(sensorType) & 0xFF) << 0));
|
||||
}
|
||||
void inc_age(int delta) {
|
||||
if (UINT16_MAX - delta > age) age += delta;
|
||||
@@ -149,6 +145,12 @@ class SubGhzDRecentEntryDetailView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
SubGhzDRecentEntry entry_{};
|
||||
|
||||
uint32_t serial = 0;
|
||||
uint8_t btn = SD_NO_BTN;
|
||||
uint32_t cnt = SD_NO_CNT;
|
||||
uint32_t seed = 0;
|
||||
|
||||
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
|
||||
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
|
||||
|
||||
@@ -164,6 +166,8 @@ class SubGhzDRecentEntryDetailView : public View {
|
||||
Button button_done{
|
||||
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
|
||||
"Done"};
|
||||
|
||||
void parseProtocol();
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -203,7 +203,12 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
|
||||
return "Wendox W6726";
|
||||
case FPW_Acurite986:
|
||||
return "Acurite986";
|
||||
|
||||
case FPW_KEDSUM:
|
||||
return "Kedsum";
|
||||
case FPW_Acurite5in1:
|
||||
return "Acurite5in1";
|
||||
case FPW_EmosE601x:
|
||||
return "EmosE601x";
|
||||
case FPW_Invalid:
|
||||
default:
|
||||
return "Unknown";
|
||||
|
||||
@@ -327,6 +327,52 @@ void BLESpamView::createNameSpamPacket() {
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
char BLESpamView::randomNameChar() {
|
||||
int randVal = rand() % 62; // 26 uppercase + 26 lowercase + 10 digits
|
||||
if (randVal < 26) {
|
||||
return 'A' + randVal; // Uppercase letters
|
||||
} else if (randVal < 52) {
|
||||
return 'a' + (randVal - 26); // Lowercase letters
|
||||
} else {
|
||||
return '0' + (randVal - 52); // Digits
|
||||
}
|
||||
}
|
||||
|
||||
void BLESpamView::createNameRandomPacket() {
|
||||
char name[] = " ";
|
||||
|
||||
uint8_t name_len = strlen(name);
|
||||
for (uint8_t i = 0; i < name_len; ++i) {
|
||||
name[i] = randomNameChar();
|
||||
}
|
||||
|
||||
uint8_t size = 12 + name_len;
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x01; // AD Type (Flags)
|
||||
packet[i++] = 0x06; // Flags
|
||||
|
||||
packet[i++] = name_len + 1; // Size
|
||||
packet[i++] = 0x09; // AD Type (Complete Local Name)
|
||||
memcpy(&packet[i], name, name_len); // Device Name
|
||||
i += name_len;
|
||||
|
||||
packet[i++] = 3; // Size
|
||||
packet[i++] = 0x02; // AD Type (Incomplete Service UUID List)
|
||||
packet[i++] = 0x12; // Service UUID (Human Interface Device)
|
||||
packet[i++] = 0x18; // ...
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x0A; // AD Type (Tx Power Level)
|
||||
packet[i++] = 0x00; // 0dBm
|
||||
|
||||
// size, packet
|
||||
std::string res = to_string_hex_array(packet, size);
|
||||
memset(advertisementData, 0, sizeof(advertisementData));
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createIosPacket(bool crash = false) {
|
||||
uint8_t ios_packet_sizes[18] = {0, 0, 0, 0, 0, 24, 0, 31, 0, 12, 0, 0, 20, 0, 12, 11, 11, 17};
|
||||
ContinuityType type;
|
||||
@@ -550,6 +596,7 @@ void BLESpamView::createAnyPacket(bool safe) {
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_NAMERANDOM,
|
||||
ATK_IOS_CRASH};
|
||||
ATK_TYPE attackType = type[rand() % (COUNT_OF(type) - (1 ? safe : 0))];
|
||||
createPacket(attackType);
|
||||
@@ -572,6 +619,9 @@ void BLESpamView::createPacket(ATK_TYPE attackType) {
|
||||
case ATK_NAMESPAM:
|
||||
createNameSpamPacket();
|
||||
break;
|
||||
case ATK_NAMERANDOM:
|
||||
createNameRandomPacket();
|
||||
break;
|
||||
case ATK_ALL_SAFE:
|
||||
createAnyPacket(true);
|
||||
break;
|
||||
|
||||
+6
-2
@@ -51,6 +51,7 @@ enum ATK_TYPE {
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_NAMERANDOM,
|
||||
ATK_ALL_SAFE,
|
||||
ATK_ALL
|
||||
};
|
||||
@@ -129,8 +130,9 @@ class BLESpamView : public View {
|
||||
{"Windows", 3},
|
||||
{"Samsung", 4},
|
||||
{"NameSpam", 5},
|
||||
{"All-Safe", 6},
|
||||
{"All", 7}}};
|
||||
{"NameRandom", 6},
|
||||
{"All-Safe", 7},
|
||||
{"All", 8}}};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
@@ -155,6 +157,7 @@ class BLESpamView : public View {
|
||||
void createSamsungPacket();
|
||||
void createWindowsPacket();
|
||||
void createNameSpamPacket();
|
||||
void createNameRandomPacket();
|
||||
void createAnyPacket(bool safe);
|
||||
void createPacket(ATK_TYPE attackType);
|
||||
void changePacket(bool forced);
|
||||
@@ -163,6 +166,7 @@ class BLESpamView : public View {
|
||||
uint64_t get_freq_by_channel_number(uint8_t channel_number);
|
||||
void randomizeMac();
|
||||
void randomChn();
|
||||
char randomNameChar();
|
||||
|
||||
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len);
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ set(EXTCPPSRC
|
||||
#tpmsrx
|
||||
external/tpmsrx/main.cpp
|
||||
external/tpmsrx/tpms_app.cpp
|
||||
|
||||
#protoview
|
||||
external/protoview/main.cpp
|
||||
external/protoview/ui_protoview.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -102,4 +106,5 @@ set(EXTAPPLIST
|
||||
audio_test
|
||||
wardrivemap
|
||||
tpmsrx
|
||||
protoview
|
||||
)
|
||||
|
||||
+9
@@ -42,6 +42,7 @@ MEMORY
|
||||
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
|
||||
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
|
||||
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
|
||||
ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -160,4 +161,12 @@ SECTIONS
|
||||
*(*ui*external_app*tpmsrx*);
|
||||
} > ram_external_app_tpmsrx
|
||||
|
||||
|
||||
.external_app_protoview : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_protoview.application_information));
|
||||
*(*ui*external_app*protoview*);
|
||||
} > ram_external_app_protoview
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_protoview.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::protoview {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<ProtoView>();
|
||||
}
|
||||
} // namespace ui::external_app::protoview
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_protoview.application_information"), used)) application_information_t _application_information_protoview = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::protoview::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "ProtoView",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xF8,
|
||||
0x87,
|
||||
0x08,
|
||||
0x84,
|
||||
0x0F,
|
||||
0xFC,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xF3,
|
||||
0xE0,
|
||||
0x92,
|
||||
0xA0,
|
||||
0x9E,
|
||||
0xBF,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xFC,
|
||||
0xF3,
|
||||
0x04,
|
||||
0x12,
|
||||
0x07,
|
||||
0x1E,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_protoview */ {'P', 'P', 'V', 'W'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_protoview.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::protoview {
|
||||
|
||||
void ProtoView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
ProtoView::ProtoView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({&rssi,
|
||||
&field_rf_amp,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_volume,
|
||||
&field_frequency,
|
||||
&labels,
|
||||
&options_zoom,
|
||||
&button_reset,
|
||||
&waveform,
|
||||
&waveform2,
|
||||
&waveform3,
|
||||
&waveform4});
|
||||
|
||||
field_frequency.set_step(100);
|
||||
options_zoom.on_change = [this](size_t, int32_t v) {
|
||||
zoom = v;
|
||||
draw();
|
||||
draw2();
|
||||
};
|
||||
button_reset.on_select = [this](Button&) {
|
||||
reset();
|
||||
};
|
||||
baseband::set_subghzd_config(0, receiver_model.sampling_rate());
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void ProtoView::reset() {
|
||||
cnt = 0;
|
||||
for (uint16_t i = 0; i < MAXSIGNALBUFFER; i++) time_buffer[i] = 0;
|
||||
needCntReset = false;
|
||||
draw();
|
||||
draw2();
|
||||
}
|
||||
|
||||
void ProtoView::on_timer() {
|
||||
timercnt++;
|
||||
if ((timercnt % 90) == 0) {
|
||||
if (datacnt == 0) {
|
||||
needCntReset = true;
|
||||
}
|
||||
datacnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoView::draw2() {
|
||||
if (drawcnt < MAXDRAWCNTPERWF) {
|
||||
waveform.set_length(drawcnt);
|
||||
waveform2.set_length(0);
|
||||
waveform3.set_length(0);
|
||||
waveform4.set_length(0);
|
||||
} else if (drawcnt < MAXDRAWCNTPERWF * 2) {
|
||||
waveform.set_length(MAXDRAWCNTPERWF);
|
||||
waveform2.set_length(drawcnt - MAXDRAWCNTPERWF);
|
||||
waveform3.set_length(0);
|
||||
waveform4.set_length(0);
|
||||
} else if (drawcnt < MAXDRAWCNTPERWF * 3) {
|
||||
waveform.set_length(MAXDRAWCNTPERWF);
|
||||
waveform2.set_length(MAXDRAWCNTPERWF);
|
||||
waveform3.set_length(drawcnt - MAXDRAWCNTPERWF * 2);
|
||||
waveform4.set_length(0);
|
||||
} else {
|
||||
waveform.set_length(MAXDRAWCNTPERWF);
|
||||
waveform2.set_length(MAXDRAWCNTPERWF);
|
||||
waveform3.set_length(MAXDRAWCNTPERWF);
|
||||
waveform4.set_length(drawcnt - MAXDRAWCNTPERWF * 3);
|
||||
}
|
||||
waveform.set_dirty();
|
||||
waveform2.set_dirty();
|
||||
waveform3.set_dirty();
|
||||
waveform4.set_dirty();
|
||||
}
|
||||
|
||||
void ProtoView::draw() {
|
||||
uint32_t remain = 0;
|
||||
int32_t lmax = 0;
|
||||
bool lmaxstate = false;
|
||||
bool state = false;
|
||||
drawcnt = 0;
|
||||
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
|
||||
|
||||
for (uint16_t i = 0; i < MAXSIGNALBUFFER; ++i) {
|
||||
state = time_buffer[i] >= 0;
|
||||
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
|
||||
int32_t timesize = timeabs / zoom;
|
||||
if (timesize == 0) {
|
||||
remain += timeabs;
|
||||
if (lmax < timeabs) {
|
||||
lmax = timeabs;
|
||||
lmaxstate = state;
|
||||
}
|
||||
if (remain / zoom > 0) {
|
||||
timesize = remain / zoom;
|
||||
state = lmaxstate;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
remain = 0;
|
||||
lmax = 0;
|
||||
for (int32_t ii = 0; ii < timesize; ++ii) {
|
||||
waveform_buffer[drawcnt++] = state;
|
||||
if (drawcnt >= MAXDRAWCNT) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoView::add_time(int32_t time) {
|
||||
if (cnt >= MAXSIGNALBUFFER) cnt = 0;
|
||||
time_buffer[cnt++] = time;
|
||||
}
|
||||
|
||||
void ProtoView::on_data(const ProtoViewDataMessage* message) {
|
||||
// filter out invalid ones.
|
||||
uint16_t start = 0;
|
||||
uint16_t stop = 0;
|
||||
bool has_valid = false;
|
||||
for (uint16_t i = 0; i <= message->maxptr; ++i) {
|
||||
if (message->times[i] >= 30000 || message->times[i] <= -30000) {
|
||||
if (!has_valid) {
|
||||
start = i;
|
||||
}
|
||||
} else {
|
||||
has_valid = true;
|
||||
stop = i;
|
||||
}
|
||||
}
|
||||
if (!has_valid) return; // no valid data arrived
|
||||
// if (needCntReset) reset(); //todo implement auto reset
|
||||
|
||||
datacnt++;
|
||||
|
||||
// valid data, redraw
|
||||
for (uint16_t i = start; i <= stop; i++) {
|
||||
add_time(message->times[i]);
|
||||
}
|
||||
|
||||
draw();
|
||||
draw2();
|
||||
}
|
||||
|
||||
ProtoView::~ProtoView() {
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::protoview
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_PROTOVIEW_H__
|
||||
#define __UI_PROTOVIEW_H__
|
||||
|
||||
#define MAXSIGNALBUFFER 400
|
||||
#define MAXDRAWCNT 600
|
||||
#define MAXDRAWCNTPERWF 150
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::protoview {
|
||||
|
||||
class ProtoView : public View {
|
||||
public:
|
||||
ProtoView(NavigationView& nav);
|
||||
~ProtoView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "ProtoView"; };
|
||||
|
||||
private:
|
||||
int16_t waveform_buffer[MAXDRAWCNT];
|
||||
int32_t time_buffer[MAXSIGNALBUFFER];
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_protoview", app_settings::Mode::RX};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{13 * 8, 0 * 16}};
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, 0 * 16}};
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 0 * 16}};
|
||||
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
nav_};
|
||||
Labels labels{
|
||||
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
OptionsField options_zoom{
|
||||
{7 * 8, 1 * 16},
|
||||
4,
|
||||
{{"1", 1},
|
||||
{"2", 2},
|
||||
{"5", 5},
|
||||
{"15", 15},
|
||||
{"30", 30},
|
||||
{"50", 50},
|
||||
{"100", 100},
|
||||
{"200", 200},
|
||||
{"500", 500},
|
||||
{"1000", 1000}}};
|
||||
|
||||
Button button_reset{
|
||||
{screen_width - 12 * 8, 1 * 16, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_RESET]};
|
||||
|
||||
Waveform waveform{
|
||||
{0, 5 * 8, 240, 50},
|
||||
waveform_buffer,
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Theme::getInstance()->fg_yellow->foreground};
|
||||
|
||||
Waveform waveform2{
|
||||
{0, 5 * 8 + 55, 240, 50},
|
||||
&waveform_buffer[MAXDRAWCNTPERWF],
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Theme::getInstance()->fg_yellow->foreground};
|
||||
|
||||
Waveform waveform3{
|
||||
{0, 5 * 8 + 110, 240, 50},
|
||||
&waveform_buffer[MAXDRAWCNTPERWF * 2],
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Theme::getInstance()->fg_yellow->foreground};
|
||||
|
||||
Waveform waveform4{
|
||||
{0, 5 * 8 + 165, 240, 50},
|
||||
&waveform_buffer[MAXDRAWCNTPERWF * 3],
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
Theme::getInstance()->fg_yellow->foreground};
|
||||
|
||||
bool needCntReset = false;
|
||||
|
||||
int16_t zoom = 1; // one value in ms
|
||||
|
||||
uint16_t cnt = 0; // pointer to next element
|
||||
uint16_t drawcnt = 0; // pointer to draw next element
|
||||
|
||||
uint16_t timercnt = 0; // screen refresh count
|
||||
uint16_t datacnt = 0; // how many data i got. these are for track if there is no data, so need a cnt reset
|
||||
|
||||
void add_time(int32_t time);
|
||||
void on_timer();
|
||||
void on_data(const ProtoViewDataMessage* message);
|
||||
void draw();
|
||||
void draw2();
|
||||
void reset();
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::ProtoViewData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const ProtoViewDataMessage*>(p);
|
||||
this->on_data(message);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->on_timer();
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::protoview
|
||||
|
||||
#endif /*__UI_PROTOVIEW_H__*/
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "usb_serial.hpp"
|
||||
|
||||
#include "ads1110.hpp"
|
||||
#include "max17055.hpp"
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
|
||||
@@ -19,6 +19,12 @@ void Theme::SetTheme(ThemeId theme) {
|
||||
case Aqua:
|
||||
current = new ThemeAqua();
|
||||
break;
|
||||
case Green:
|
||||
current = new ThemeGreen();
|
||||
break;
|
||||
case Red:
|
||||
current = new ThemeRed();
|
||||
break;
|
||||
case DefaultGrey:
|
||||
default:
|
||||
current = new ThemeDefault();
|
||||
@@ -453,4 +459,270 @@ ThemeDefault::ThemeDefault() {
|
||||
bg_table_header = new Color{0, 0, 255};
|
||||
}
|
||||
|
||||
ThemeGreen::ThemeGreen() {
|
||||
bg_lightest = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 245, 29},
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
bg_lightest_small = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 245, 29},
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
bg_light = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 212, 25},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_medium = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 143, 17},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 99, 12},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_darker = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 79, 9},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
bg_darkest = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_darkest_small = new Style{
|
||||
.font = font::fixed_5x8,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
bg_important_small = new Style{
|
||||
.font = ui::font::fixed_5x8,
|
||||
.background = ui::Color::yellow(),
|
||||
.foreground = {0, 33, 4},
|
||||
};
|
||||
|
||||
error_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
warning_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
ok_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
|
||||
fg_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = {0, 99, 12},
|
||||
};
|
||||
fg_medium = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = {0, 143, 17},
|
||||
};
|
||||
fg_light = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::light_grey(),
|
||||
};
|
||||
|
||||
fg_red = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
fg_green = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
fg_yellow = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
fg_orange = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
fg_blue = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::blue(),
|
||||
};
|
||||
fg_cyan = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::cyan(),
|
||||
};
|
||||
fg_darkcyan = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::dark_cyan(),
|
||||
};
|
||||
fg_magenta = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {0, 33, 4},
|
||||
.foreground = Color::magenta(),
|
||||
};
|
||||
|
||||
option_active = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::orange(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
status_active = new Color{0, 255, 0}; // green, the status bar icons when active
|
||||
|
||||
bg_table_header = new Color{0, 205, 30};
|
||||
}
|
||||
|
||||
ThemeRed::ThemeRed() {
|
||||
bg_lightest = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {245, 29, 0},
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
bg_lightest_small = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {245, 29, 0},
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
bg_light = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {212, 25, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_medium = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {143, 17, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {99, 12, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_darker = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {79, 9, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
bg_darkest = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
bg_darkest_small = new Style{
|
||||
.font = font::fixed_5x8,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
bg_important_small = new Style{
|
||||
.font = ui::font::fixed_5x8,
|
||||
.background = ui::Color::yellow(),
|
||||
.foreground = {33, 4, 0},
|
||||
};
|
||||
|
||||
error_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
warning_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
ok_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
|
||||
fg_dark = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = {99, 12, 0},
|
||||
};
|
||||
fg_medium = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = {143, 17, 0},
|
||||
};
|
||||
fg_light = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::light_grey(),
|
||||
};
|
||||
|
||||
fg_red = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
fg_green = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
fg_yellow = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
fg_orange = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
fg_blue = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::blue(),
|
||||
};
|
||||
fg_cyan = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::cyan(),
|
||||
};
|
||||
fg_darkcyan = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::dark_cyan(),
|
||||
};
|
||||
fg_magenta = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 4, 0},
|
||||
.foreground = Color::magenta(),
|
||||
};
|
||||
|
||||
option_active = new Style{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::orange(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
status_active = new Color{0, 255, 0}; // green, the status bar icons when active
|
||||
|
||||
bg_table_header = new Color{205, 30, 0};
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
@@ -83,12 +83,24 @@ class ThemeAqua : public ThemeTemplate {
|
||||
ThemeAqua();
|
||||
};
|
||||
|
||||
class ThemeGreen : public ThemeTemplate {
|
||||
public:
|
||||
ThemeGreen();
|
||||
};
|
||||
|
||||
class ThemeRed : public ThemeTemplate {
|
||||
public:
|
||||
ThemeRed();
|
||||
};
|
||||
|
||||
class Theme {
|
||||
public:
|
||||
enum ThemeId {
|
||||
DefaultGrey = 0,
|
||||
Yellow = 1,
|
||||
Aqua = 2,
|
||||
Green = 3,
|
||||
Red = 4,
|
||||
MAX
|
||||
};
|
||||
static ThemeTemplate* getInstance();
|
||||
|
||||
@@ -385,11 +385,16 @@ void SystemStatusView::on_battery_details() {
|
||||
}
|
||||
|
||||
void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
|
||||
if (!batt_was_inited) {
|
||||
batt_was_inited = true;
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
battery_text.set_battery(msg->percent, msg->on_charger);
|
||||
battery_text.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
|
||||
}
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
battery_icon.set_battery(msg->percent, msg->on_charger);
|
||||
battery_icon.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -410,14 +415,15 @@ void SystemStatusView::refresh() {
|
||||
|
||||
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
batt_was_inited = true;
|
||||
uint8_t percent = battery::BatteryManagement::getPercent();
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
status_icons.add(&battery_icon);
|
||||
battery_text.set_battery(percent, false); // got an on select, that may pop up the details of the battery.
|
||||
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false); // got an on select, that may pop up the details of the battery.
|
||||
};
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
status_icons.add(&battery_text);
|
||||
battery_text.set_battery(percent, false);
|
||||
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +194,8 @@ class SystemStatusView : public View {
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "";
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
bool batt_was_inited = false; // if the battery was off on tart, but later turned on.
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop{
|
||||
|
||||
@@ -49,6 +49,7 @@ void USBSerial::on_channel_opened() {
|
||||
}
|
||||
|
||||
void USBSerial::on_channel_closed() {
|
||||
reset_transfer_queues();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ CH_IRQ_HANDLER(USB0_IRQHandler) {
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
if (status & USB0_USBSTS_D_SLI) {
|
||||
on_channel_closed();
|
||||
}
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
@@ -131,11 +135,7 @@ usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint,
|
||||
}
|
||||
usb_request_status_t usb_set_control_line_state_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) {
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
// if (endpoint->setup.value == 3) {
|
||||
on_channel_opened();
|
||||
//} else {
|
||||
// on_channel_closed();
|
||||
//}
|
||||
|
||||
usb_transfer_schedule_ack(endpoint->in);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ void init_host_to_device() {
|
||||
thread_usb_event = chThdSelf();
|
||||
}
|
||||
|
||||
void reset_transfer_queues() {
|
||||
while (usb_bulk_buffer_queue.empty() == false)
|
||||
usb_bulk_buffer_queue.pop();
|
||||
|
||||
while (usb_bulk_buffer_spare.empty() == false)
|
||||
usb_bulk_buffer_spare.pop();
|
||||
}
|
||||
|
||||
void schedule_host_to_device_transfer() {
|
||||
if (usb_bulk_buffer_queue.size() >= 8)
|
||||
return;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define USB_BULK_BUFFER_SIZE 64
|
||||
|
||||
void init_host_to_device();
|
||||
void reset_transfer_queues();
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
|
||||
void schedule_host_to_device_transfer();
|
||||
void complete_host_to_device_transfer();
|
||||
|
||||
@@ -278,7 +278,9 @@ macro(DeclareTargets chunk_tag name)
|
||||
include_directories(. ${INCDIR} ${MODE_INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,--print-memory-usage)
|
||||
|
||||
if(add_to_firmware)
|
||||
|
||||
@@ -646,6 +648,15 @@ set(MODE_CPPSRC
|
||||
DeclareTargets(PABP audio_beep)
|
||||
|
||||
|
||||
### ProtoView
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_protoview.cpp
|
||||
)
|
||||
DeclareTargets(PPVW protoview)
|
||||
|
||||
|
||||
|
||||
### TPMS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
|
||||
@@ -27,29 +27,34 @@ namespace dsp {
|
||||
HilbertTransform::HilbertTransform() {
|
||||
n = 0;
|
||||
|
||||
sos_input.configure(half_band_lpf_config);
|
||||
sos_i.configure(half_band_lpf_config);
|
||||
sos_q.configure(half_band_lpf_config);
|
||||
}
|
||||
|
||||
void HilbertTransform::execute(float in, float& out_i, float& out_q) {
|
||||
// Synthesized Hilbert Transform, it is implemented based on 1/2 band LPF and later freq shift fs/4, achieving a H.T_BW of transmitted = fs/2 ;
|
||||
// Half_band LPF means a LP filter with f_cut_off = fs/4; Half band = Half max band = 1/2 * fs_max = 1/2 x f_Nyquist = 1/2 * fs/2 = fs/4
|
||||
float a = 0, b = 0;
|
||||
|
||||
float in_filtered = sos_input.execute(in) * 1.0f; // Anti-aliasing LPF at fs/4 mic audio filter front-end.
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
a = in;
|
||||
a = in_filtered;
|
||||
b = 0;
|
||||
break;
|
||||
case 1:
|
||||
a = 0;
|
||||
b = -in;
|
||||
b = -in_filtered;
|
||||
break;
|
||||
case 2:
|
||||
a = -in;
|
||||
a = -in_filtered;
|
||||
b = 0;
|
||||
break;
|
||||
case 3:
|
||||
a = 0;
|
||||
b = in;
|
||||
b = in_filtered;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class HilbertTransform {
|
||||
|
||||
private:
|
||||
uint8_t n = 0;
|
||||
SOSFilter<5> sos_input = {};
|
||||
SOSFilter<5> sos_i = {};
|
||||
SOSFilter<5> sos_q = {};
|
||||
};
|
||||
|
||||
@@ -81,6 +81,20 @@ SSB::SSB()
|
||||
mode = Mode::LSB;
|
||||
}
|
||||
|
||||
void SSB::set_fs_div_factor(float new_bw_ssb) {
|
||||
switch ((int)new_bw_ssb / 1000) {
|
||||
case 2:
|
||||
fs_div_factor = 192; // TXBW_ssb = 2khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (4khz=fs/2) ==> (8k=fs) Hilbert_fs = 1.536.000/8000= 192
|
||||
break;
|
||||
case 3:
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (6khz=fs/2) ==> (12k=fs) Hilbert_fs = 1.536.000/12000= 128
|
||||
break;
|
||||
default:
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (6khz=fs/2) ==> (12k=fs) Hilbert_fs = 1.536.000/12000= 128
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider) {
|
||||
// unused
|
||||
(void)configured_in;
|
||||
@@ -93,9 +107,10 @@ void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& co
|
||||
int8_t re = 0, im = 0;
|
||||
|
||||
for (size_t counter = 0; counter < buffer.count; counter++) {
|
||||
if (counter % 128 == 0) {
|
||||
if (counter % fs_div_factor == 0) { // Ex. TX bw_ssb 3khz =fs/4 Hilbert Transform sample rate, 128 = 1.536.000 hz / 12.000 hz (fs H.T.)
|
||||
float i = 0.0, q = 0.0;
|
||||
|
||||
// over = 1.536.000/24khz = 64 . (Mic audio has fixed SR in audio_p buffer[] = 24khz), but in tx mode , we are running Transceiver fs @tx = 1.536.000 Hz.
|
||||
sample = audio.p[counter / over] >> audio_shift_bits_s16_AM_DSB_SSB; // originally fixed >> 2, now >>2 for AK, 0,1,2,3 for WM (boost off)
|
||||
sample *= audio_gain; // Apply GAIN Scale factor to the audio TX modulation.
|
||||
|
||||
|
||||
@@ -79,9 +79,12 @@ class SSB : public Modulator {
|
||||
SSB();
|
||||
|
||||
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer, bool& configured_in, uint32_t& new_beep_index, uint32_t& new_beep_timer, TXProgressMessage& new_txprogress_message, AudioLevelReportMessage& new_level_message, uint32_t& new_power_acc_count, uint32_t& new_divider);
|
||||
void set_fs_div_factor(float new_bw_ssb);
|
||||
|
||||
private:
|
||||
dsp::HilbertTransform hilbert;
|
||||
float new_bw_ssb{3.0};
|
||||
int fs_div_factor{128};
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
@@ -53,8 +53,6 @@ class FProtoSubGhzDCame : public FProtoSubGhzDBase {
|
||||
parser_step = CameDecoderStepFoundStartBit;
|
||||
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == AIRFORCE_COUNT_BIT) ||
|
||||
(decode_count_bit == PRASTEL_COUNT_BIT) || (decode_count_bit == CAME_24_COUNT_BIT)) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// if flippa hacky, i hacky
|
||||
|
||||
@@ -45,36 +45,6 @@ class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
data ^= 0xFFFFFFFFFFFFFFFF;
|
||||
data <<= 4;
|
||||
|
||||
uint8_t pack[8] = {};
|
||||
pack[0] = (data >> 56);
|
||||
pack[1] = ((data >> 48) & 0xFF);
|
||||
pack[2] = ((data >> 40) & 0xFF);
|
||||
pack[3] = ((data >> 32) & 0xFF);
|
||||
pack[4] = ((data >> 24) & 0xFF);
|
||||
pack[5] = ((data >> 16) & 0xFF);
|
||||
pack[6] = ((data >> 8) & 0xFF);
|
||||
pack[7] = (data & 0xFF);
|
||||
|
||||
atomo_decrypt(pack);
|
||||
|
||||
cnt = (uint16_t)pack[1] << 8 | pack[2];
|
||||
serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6];
|
||||
|
||||
uint8_t btn_decode = (pack[7] >> 4);
|
||||
if (btn_decode == 0x0) {
|
||||
btn = 0x1;
|
||||
} else if (btn_decode == 0x2) {
|
||||
btn = 0x2;
|
||||
} else if (btn_decode == 0x4) {
|
||||
btn = 0x3;
|
||||
} else if (btn_decode == 0x6) {
|
||||
btn = 0x4;
|
||||
}
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -45,7 +45,6 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
subghz_protocol_came_twee_remote_controller();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
@@ -79,69 +78,6 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
|
||||
|
||||
protected:
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
|
||||
void subghz_protocol_came_twee_remote_controller() {
|
||||
/* Came Twee 54 bit, rolling code 15 parcels with
|
||||
* a decreasing counter from 0xE to 0x0
|
||||
* with originally coded dip switches on the console 10 bit code
|
||||
*
|
||||
* 0x003FFF72E04A6FEE
|
||||
* 0x003FFF72D17B5EDD
|
||||
* 0x003FFF72C2684DCC
|
||||
* 0x003FFF72B3193CBB
|
||||
* 0x003FFF72A40E2BAA
|
||||
* 0x003FFF72953F1A99
|
||||
* 0x003FFF72862C0988
|
||||
* 0x003FFF7277DDF877
|
||||
* 0x003FFF7268C2E766
|
||||
* 0x003FFF7259F3D655
|
||||
* 0x003FFF724AE0C544
|
||||
* 0x003FFF723B91B433
|
||||
* 0x003FFF722C86A322
|
||||
* 0x003FFF721DB79211
|
||||
* 0x003FFF720EA48100
|
||||
*
|
||||
* decryption
|
||||
* the last 32 bits, do XOR by the desired number, divide the result by 4,
|
||||
* convert the first 16 bits of the resulting 32-bit number to bin and do
|
||||
* bit-by-bit mirroring, adding up to 10 bits
|
||||
*
|
||||
* Example
|
||||
* Step 1. 0x003FFF721DB79211 => 0x1DB79211
|
||||
* Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00
|
||||
* Step 4. 0x00AA8F00 / 4 => 0x002AA3C0
|
||||
* Step 5. 0x002AA3C0 => 0x002A
|
||||
* Step 6. 0x002A bin => b101010
|
||||
* Step 7. b101010 => b0101010000
|
||||
* Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off
|
||||
*/
|
||||
|
||||
uint8_t cnt_parcel = (uint8_t)(data & 0xF);
|
||||
serial = (uint32_t)(data & 0x0FFFFFFFF);
|
||||
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
|
||||
data /= 4;
|
||||
btn = (data >> 4) & 0x0F;
|
||||
data >>= 16;
|
||||
data = (uint16_t)FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 16);
|
||||
cnt = data >> 6;
|
||||
}
|
||||
inline static const uint32_t came_twee_magic_numbers_xor[15] = {
|
||||
0x0E0E0E00,
|
||||
0x1D1D1D11,
|
||||
0x2C2C2C22,
|
||||
0x3B3B3B33,
|
||||
0x4A4A4A44,
|
||||
0x59595955,
|
||||
0x68686866,
|
||||
0x77777777,
|
||||
0x86868688,
|
||||
0x95959599,
|
||||
0xA4A4A4AA,
|
||||
0xB3B3B3BB,
|
||||
0xC2C2C2CC,
|
||||
0xD1D1D1DD,
|
||||
0xE0E0E0EE,
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -57,8 +57,6 @@ class FProtoSubGhzDChambCode : public FProtoSubGhzDBase {
|
||||
if (!level) { // save interval
|
||||
if (duration > te_short * 5) {
|
||||
if (decode_count_bit >= min_count_bit_for_found) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
if (subghz_protocol_decoder_chamb_code_check_mask_and_parse()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
@@ -64,11 +64,6 @@ class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller
|
||||
serial = (data >> 2) & 0xFFFF;
|
||||
btn = (data & 0x03);
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
parser_step = ClemsaDecoderStepSaveDuration;
|
||||
|
||||
@@ -46,10 +46,6 @@ class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller
|
||||
cnt = (data >> 24) | ((data >> 15) & 0x1);
|
||||
btn = ((data >> 18) & 0x3);
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -74,16 +74,6 @@ class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller:
|
||||
serial = (data >> 16);
|
||||
if ((data >> 12) & 0x0F) {
|
||||
cnt = (data >> 8) & 0x0F;
|
||||
} else {
|
||||
cnt = 0xFF;
|
||||
}
|
||||
btn = data & 0xFF;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -46,12 +46,6 @@ class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller
|
||||
uint32_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
|
||||
serial = (code_found_reverse & 0xFF) << 12 | ((code_found_reverse >> 8) & 0xFF) << 4 | ((code_found_reverse >> 20) & 0x0F);
|
||||
btn = ((code_found_reverse >> 16) & 0x0F);
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -51,22 +51,6 @@ class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
|
||||
if ((decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller
|
||||
serial = FProtoGeneral::subghz_protocol_blocks_reverse_key((data >> 16) & 0xFFFFF, 20);
|
||||
uint16_t btn = data & 0xFFFF;
|
||||
if ((btn & 0xf) != 0xA) {
|
||||
btn = 0x1 << 4 | (btn & 0xF);
|
||||
} else if (((btn >> 4) & 0xF) != 0xA) {
|
||||
btn = 0x2 << 4 | ((btn >> 4) & 0xF);
|
||||
} else if (((btn >> 8) & 0xF) != 0xA) {
|
||||
btn = 0x3 << 4 | ((btn >> 8) & 0xF);
|
||||
} else if (((btn >> 12) & 0xF) != 0xA) {
|
||||
btn = 0x4 << 4 | ((btn >> 12) & 0xF);
|
||||
} else {
|
||||
btn = 0;
|
||||
}
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@ class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
|
||||
if (data != decode_data) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
btn = data & 0x0F;
|
||||
cnt = (data >> 4) & 0xFF;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
|
||||
// the data is good. process it.
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit; // maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
|
||||
serial = (decode_data >> 24) & 0xFFFFF;
|
||||
btn = (decode_data >> 16) & 0xFF; // not exactly button, but can contain btn data too.
|
||||
if (callback) callback(this);
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
|
||||
@@ -49,8 +49,6 @@ class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
btn = (data >> 4) & 0xF;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
#ifndef __FPROTO_HORMANNBISECURE_H__
|
||||
#define __FPROTO_HORMANNBISECURE_H__
|
||||
|
||||
#include "subghzdbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
HormannBiSecurDecoderStepReset = 0,
|
||||
HormannBiSecurDecoderStepFoundPreambleAlternatingShort,
|
||||
HormannBiSecurDecoderStepFoundPreambleHighVeryLong,
|
||||
HormannBiSecurDecoderStepFoundPreambleAlternatingLong,
|
||||
HormannBiSecurDecoderStepFoundData,
|
||||
} HormannBiSecurDecoderStep;
|
||||
|
||||
class FProtoSubGhzDHormannBiSecure : public FProtoSubGhzDBase {
|
||||
public:
|
||||
FProtoSubGhzDHormannBiSecure() {
|
||||
sensorType = FPS_HORMANN;
|
||||
te_short = 208;
|
||||
te_long = 416;
|
||||
te_delta = 104;
|
||||
min_count_bit_for_found = 176;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_reset() {
|
||||
parser_step = HormannBiSecurDecoderStepReset;
|
||||
data = 0;
|
||||
for (uint8_t i = 0; i < 22; ++i) dataa[i] = 0;
|
||||
data_count_bit = 0;
|
||||
manchester_saved_state = ManchesterStateStart1;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
switch (parser_step) {
|
||||
case HormannBiSecurDecoderStepReset:
|
||||
if (!level && DURATION_DIFF(duration, duration_short + duration_half_short) < te_delta) {
|
||||
parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingShort;
|
||||
}
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleAlternatingShort:
|
||||
if (DURATION_DIFF(duration, duration_short) < te_delta) {
|
||||
// stay on the same step, the pattern repeats around 21 times
|
||||
break;
|
||||
}
|
||||
|
||||
if (level && DURATION_DIFF(duration, duration_long * 4) < te_delta) {
|
||||
parser_step = HormannBiSecurDecoderStepFoundPreambleHighVeryLong;
|
||||
break;
|
||||
}
|
||||
|
||||
parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleHighVeryLong:
|
||||
if (!level && DURATION_DIFF(duration, duration_long) < te_delta) {
|
||||
sync_cnt = 3;
|
||||
parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingLong;
|
||||
break;
|
||||
}
|
||||
|
||||
parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleAlternatingLong:
|
||||
if (level == (sync_cnt-- & 1) &&
|
||||
DURATION_DIFF(duration, duration_long) < te_delta) {
|
||||
if (!sync_cnt) {
|
||||
FProtoGeneral::manchester_advance_alt(manchester_saved_state, event, &manchester_saved_state, NULL);
|
||||
parser_step = HormannBiSecurDecoderStepFoundData;
|
||||
}
|
||||
|
||||
// stay on the same step, or advance to the next if enough transitions are found
|
||||
break;
|
||||
}
|
||||
|
||||
parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundData:
|
||||
if (DURATION_DIFF(duration, duration_short) < te_delta ||
|
||||
(
|
||||
// the last bit can be arbitrary long, but it is parsed as a short
|
||||
data_count_bit == min_count_bit_for_found - 1 &&
|
||||
duration > duration_short)) {
|
||||
event = !level ? ManchesterEventShortHigh : ManchesterEventShortLow;
|
||||
}
|
||||
|
||||
if (DURATION_DIFF(duration, duration_long) < te_delta) {
|
||||
event = !level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
||||
}
|
||||
|
||||
if (event == ManchesterEventReset) {
|
||||
subghz_protocol_decoder_hormann_bisecur_reset();
|
||||
} else {
|
||||
bool new_level;
|
||||
|
||||
if (manchester_advance_alt(instance->manchester_saved_state, event, &instance->manchester_saved_state, &new_level)) {
|
||||
subghz_protocol_decoder_hormann_bisecur_add_bit(instance, new_level);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_add_bit(bool level) {
|
||||
if (data_count_bit >= min_count_bit_for_found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level) {
|
||||
uint8_t byte_index = data_count_bit / 8;
|
||||
uint8_t bit_index = data_count_bit % 8;
|
||||
dataa[byte_index] |= 1 << (7 - bit_index);
|
||||
}
|
||||
data_count_bit++;
|
||||
if (data_count_bit >= min_count_bit_for_found) {
|
||||
if (callback) {
|
||||
callback(this);
|
||||
} else {
|
||||
subghz_protocol_decoder_hormann_bisecur_reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
uint8_t dataa[22] = {0};
|
||||
uint8_t sync_cnt = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -46,12 +46,6 @@ class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
uint64_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
|
||||
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
|
||||
|
||||
serial = code_fix & 0xFFFFF;
|
||||
btn = (code_fix >> 20) & 0x0F;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -68,7 +68,6 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
|
||||
(decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
remote_controller();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
@@ -120,29 +119,6 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
|
||||
}
|
||||
|
||||
protected:
|
||||
void remote_controller() {
|
||||
if (data_count_bit == min_count_bit_for_found) {
|
||||
serial = (data >> 6) & 0x3FFFFFF;
|
||||
if ((data >> 5) & 0x1) {
|
||||
cnt = 1 << 5;
|
||||
} else {
|
||||
cnt = (~data & 0xF);
|
||||
}
|
||||
btn = (data >> 4) & 0x1;
|
||||
} else if (data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {
|
||||
serial = (data >> 10) & 0x3FFFFFF;
|
||||
if ((data >> 9) & 0x1) {
|
||||
cnt = 1 << 5;
|
||||
} else {
|
||||
cnt = (~(data >> 4) & 0xF);
|
||||
}
|
||||
btn = data & 0xF;
|
||||
} else {
|
||||
serial = 0;
|
||||
cnt = 0;
|
||||
btn = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,12 +60,6 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
|
||||
if (data != decode_data) {
|
||||
data = decode_data;
|
||||
data_count_bit = min_count_bit_for_found;
|
||||
// controller
|
||||
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
|
||||
uint32_t key_fix = key >> 32;
|
||||
// uint32_t key_hop = key & 0x00000000ffffffff; //unused
|
||||
serial = key_fix & 0x0FFFFFFF;
|
||||
btn = key_fix >> 28;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -61,13 +61,8 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit ==
|
||||
min_count_bit_for_found) {
|
||||
data = data_2;
|
||||
data_2 = decode_data;
|
||||
data_2 = decode_data; // TODO DATA2
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
uint64_t fix = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 53);
|
||||
|
||||
btn = (fix >> 17) & 0x0F;
|
||||
serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
|
||||
if (callback) callback(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
#ifndef __FPROTO_LEGRAND_H__
|
||||
#define __FPROTO_LEGRAND_H__
|
||||
|
||||
#include "subghzdbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
LegrandDecoderStepReset = 0,
|
||||
LegrandDecoderStepFirstBit,
|
||||
LegrandDecoderStepSaveDuration,
|
||||
LegrandDecoderStepCheckDuration,
|
||||
} LegrandDecoderStep;
|
||||
|
||||
class FProtoSubGhzDLegrand : public FProtoSubGhzDBase {
|
||||
public:
|
||||
FProtoSubGhzDLegrand() {
|
||||
sensorType = FPS_LEGRAND;
|
||||
te_short = 375;
|
||||
te_long = 1125;
|
||||
te_delta = 150;
|
||||
min_count_bit_for_found = 18;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case LegrandDecoderStepReset:
|
||||
if (!level && DURATION_DIFF(duration, te_short * 16) < te_delta * 8) {
|
||||
parser_step = LegrandDecoderStepFirstBit;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
te = 0;
|
||||
}
|
||||
break;
|
||||
case LegrandDecoderStepFirstBit:
|
||||
if (level) {
|
||||
if (DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
te += duration * 4; // long low that is part of sync, then short high
|
||||
}
|
||||
|
||||
if (DURATION_DIFF(duration, te_long) < te_delta * 3) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
te += duration / 3 * 4; // short low that is part of sync, then long high
|
||||
}
|
||||
|
||||
if (decode_count_bit > 0) {
|
||||
// advance to the next step if either short or long is found
|
||||
parser_step = LegrandDecoderStepSaveDuration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parser_step = LegrandDecoderStepReset;
|
||||
break;
|
||||
case LegrandDecoderStepSaveDuration:
|
||||
if (!level) {
|
||||
te_last = duration;
|
||||
te += duration;
|
||||
parser_step = LegrandDecoderStepCheckDuration;
|
||||
break;
|
||||
}
|
||||
|
||||
parser_step = LegrandDecoderStepReset;
|
||||
break;
|
||||
case LegrandDecoderStepCheckDuration:
|
||||
if (level) {
|
||||
uint8_t found = 0;
|
||||
|
||||
if (DURATION_DIFF(te_last, te_long) < te_delta * 3 && DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
found = 1;
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
}
|
||||
|
||||
if (DURATION_DIFF(te_last, te_short) < te_delta && DURATION_DIFF(duration, te_long) < te_delta * 3) {
|
||||
found = 1;
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
}
|
||||
|
||||
if (found) {
|
||||
te += duration;
|
||||
|
||||
if (decode_count_bit <
|
||||
min_count_bit_for_found) {
|
||||
parser_step = LegrandDecoderStepSaveDuration;
|
||||
break;
|
||||
}
|
||||
|
||||
// enough bits for a packet found, save it only if there was a previous packet
|
||||
// with the same data
|
||||
if (data && (data != decode_data)) {
|
||||
te /= decode_count_bit * 4;
|
||||
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) {
|
||||
callback(this);
|
||||
}
|
||||
}
|
||||
// fallthrough to reset, the next bit is expected to be a sync
|
||||
// it also takes care of resetting the decoder state
|
||||
}
|
||||
}
|
||||
parser_step = LegrandDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -54,11 +54,8 @@ class FProtoSubGhzDLinear : public FProtoSubGhzDBase {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
}
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -50,12 +50,8 @@ class FProtoSubGhzDLinearDelta3 : public FProtoSubGhzDBase {
|
||||
}
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
if ((data == decode_data) && data) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
parser_step = LinearD3DecoderStepSaveDuration;
|
||||
|
||||
@@ -92,11 +92,6 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
|
||||
subghz_protocol_magellan_check_crc()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
// controller
|
||||
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 8, 24);
|
||||
serial = data_rev & 0xFFFF;
|
||||
btn = (data_rev >> 16) & 0xFF;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -45,9 +45,6 @@ class FProtoSubGhzDMarantec : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
btn = (data >> 16) & 0xF;
|
||||
serial = ((data >> 12) & 0xFFFFFF00) | ((data >> 8) & 0xFF);
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 1;
|
||||
|
||||
@@ -63,9 +63,7 @@ class FProtoSubGhzDMastercode : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
serial = (data >> 4) & 0xFFFF;
|
||||
btn = (data >> 2 & 0x03);
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
parser_step = MastercodeDecoderStepSaveDuration;
|
||||
|
||||
@@ -50,16 +50,7 @@ class FProtoSubGhzDMegacode : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
if ((data >> 23) == 1) {
|
||||
serial = (data >> 3) & 0xFFFF;
|
||||
btn = data & 0b111;
|
||||
cnt = (data >> 19) & 0b1111;
|
||||
} else {
|
||||
serial = 0;
|
||||
btn = 0;
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -47,9 +47,6 @@ class FProtoSubGhzDNiceflo : public FProtoSubGhzDBase {
|
||||
if (duration >= (te_short * 4)) {
|
||||
parser_step = NiceFloDecoderStepFoundStartBit;
|
||||
if (decode_count_bit >= min_count_bit_for_found) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
if (callback) callback(this);
|
||||
|
||||
@@ -58,10 +58,7 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
|
||||
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == NICE_ONE_COUNT_BIT)) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller-
|
||||
cnt = SD_NO_CNT;
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -47,12 +47,6 @@ class FProtoSubGhzDPhoenixV2 : public FProtoSubGhzDBase {
|
||||
min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit + 4);
|
||||
serial = data_rev & 0xFFFFFFFF;
|
||||
cnt = (data_rev >> 40) & 0xFFFF;
|
||||
btn = (data_rev >> 32) & 0xF;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -52,11 +52,6 @@ class FProtoSubGhzDPowerSmart : public FProtoSubGhzDBase {
|
||||
data = decode_data;
|
||||
data_count_bit = min_count_bit_for_found;
|
||||
|
||||
// controller
|
||||
btn = ((data >> 54) & 0x02) | ((data >> 40) & 0x1);
|
||||
serial = ((data >> 33) & 0x3FFF00) | ((data >> 32) & 0xFF);
|
||||
cnt = ((data >> 49) & 0x3F);
|
||||
|
||||
if (callback) callback(this);
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
|
||||
@@ -44,9 +44,6 @@ class FProtoSubGhzDPrinceton : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller
|
||||
serial = data >> 4;
|
||||
btn = data & 0xF;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
#ifndef __FPROTO_SOMIFYKEYTIS_H__
|
||||
#define __FPROTO_SOMIFYKEYTIS_H__
|
||||
|
||||
#include "subghzdbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
SomfyKeytisDecoderStepReset = 0,
|
||||
SomfyKeytisDecoderStepCheckPreambula,
|
||||
SomfyKeytisDecoderStepFoundPreambula,
|
||||
SomfyKeytisDecoderStepStartDecode,
|
||||
SomfyKeytisDecoderStepDecoderData,
|
||||
} SomfyKeytisDecoderStep;
|
||||
|
||||
class FProtoSubGhzDSomifyKeytis : public FProtoSubGhzDBase {
|
||||
public:
|
||||
FProtoSubGhzDSomifyKeytis() {
|
||||
sensorType = FPS_SOMIFY_KEYTIS;
|
||||
te_short = 640;
|
||||
te_long = 1280;
|
||||
te_delta = 250;
|
||||
min_count_bit_for_found = 80;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
switch (parser_step) {
|
||||
case SomfyKeytisDecoderStepReset:
|
||||
if ((level) && DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
|
||||
parser_step = SomfyKeytisDecoderStepFoundPreambula;
|
||||
header_count++;
|
||||
}
|
||||
break;
|
||||
case SomfyKeytisDecoderStepFoundPreambula:
|
||||
if ((!level) && (DURATION_DIFF(duration, te_short * 4) < te_delta * 4)) {
|
||||
parser_step = SomfyKeytisDecoderStepCheckPreambula;
|
||||
} else {
|
||||
header_count = 0;
|
||||
parser_step = SomfyKeytisDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case SomfyKeytisDecoderStepCheckPreambula:
|
||||
if (level) {
|
||||
if (DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
|
||||
parser_step = SomfyKeytisDecoderStepFoundPreambula;
|
||||
header_count++;
|
||||
} else if (
|
||||
(header_count > 1) && (DURATION_DIFF(duration, te_short * 7) < te_delta * 4)) {
|
||||
parser_step = SomfyKeytisDecoderStepDecoderData;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
// press_duration_counter = 0;
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SomfyKeytisDecoderStepDecoderData:
|
||||
if (!level) {
|
||||
if (DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) < te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if (
|
||||
duration >= (te_long + te_delta)) {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
// check crc
|
||||
uint64_t data_tmp = data ^ (data >> 8);
|
||||
if (((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
|
||||
parser_step = SomfyKeytisDecoderStepReset;
|
||||
} else {
|
||||
parser_step = SomfyKeytisDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if (DURATION_DIFF(duration, te_short) <
|
||||
te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) <
|
||||
te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
parser_step = SomfyKeytisDecoderStepReset;
|
||||
}
|
||||
}
|
||||
if (event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
|
||||
|
||||
if (data_ok) {
|
||||
if (decode_count_bit < 56) {
|
||||
decode_data = (decode_data << 1) | data;
|
||||
} else {
|
||||
// press_duration_counter = (press_duration_counter << 1) | data;
|
||||
}
|
||||
|
||||
decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) {
|
||||
uint8_t crc = 0;
|
||||
data &= 0xFFF0FFFFFFFFFF;
|
||||
for (uint8_t i = 0; i < 56; i += 8) {
|
||||
crc = crc ^ data >> i ^ (data >> (i + 4));
|
||||
}
|
||||
return crc & 0xf;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
|
||||
#ifndef __FPROTO_SOMIFYTELIS_H__
|
||||
#define __FPROTO_SOMIFYTELIS_H__
|
||||
|
||||
#include "subghzdbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
SomfyTelisDecoderStepReset = 0,
|
||||
SomfyTelisDecoderStepCheckPreambula,
|
||||
SomfyTelisDecoderStepFoundPreambula,
|
||||
SomfyTelisDecoderStepStartDecode,
|
||||
SomfyTelisDecoderStepDecoderData,
|
||||
} SomfyTelisDecoderStep;
|
||||
|
||||
class FProtoSubGhzDSomifyTelis : public FProtoSubGhzDBase {
|
||||
public:
|
||||
FProtoSubGhzDSomifyTelis() {
|
||||
sensorType = FPS_SOMIFY_TELIS;
|
||||
te_short = 640;
|
||||
te_long = 1280;
|
||||
te_delta = 250;
|
||||
min_count_bit_for_found = 56;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
switch (parser_step) {
|
||||
case SomfyTelisDecoderStepReset:
|
||||
if ((level) && DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
|
||||
parser_step = SomfyTelisDecoderStepFoundPreambula;
|
||||
header_count++;
|
||||
}
|
||||
break;
|
||||
case SomfyTelisDecoderStepFoundPreambula:
|
||||
if ((!level) && (DURATION_DIFF(duration, te_short * 4) < te_delta * 4)) {
|
||||
parser_step = SomfyTelisDecoderStepCheckPreambula;
|
||||
} else {
|
||||
header_count = 0;
|
||||
parser_step = SomfyTelisDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case SomfyTelisDecoderStepCheckPreambula:
|
||||
if (level) {
|
||||
if (DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
|
||||
parser_step = SomfyTelisDecoderStepFoundPreambula;
|
||||
header_count++;
|
||||
} else if (
|
||||
(header_count > 1) &&
|
||||
(DURATION_DIFF(duration, te_short * 7) < te_delta * 4)) {
|
||||
parser_step = SomfyTelisDecoderStepDecoderData;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
header_count = 0;
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SomfyTelisDecoderStepDecoderData:
|
||||
if (!level) {
|
||||
if (DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) < te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if (
|
||||
duration >= (te_long + te_delta)) {
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
// check crc
|
||||
uint64_t data_tmp = decode_data ^ (decode_data >> 8);
|
||||
if (((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
|
||||
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
|
||||
parser_step = SomfyTelisDecoderStepReset;
|
||||
} else {
|
||||
parser_step = SomfyTelisDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if (DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) < te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
parser_step = SomfyTelisDecoderStepReset;
|
||||
}
|
||||
}
|
||||
if (event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
|
||||
|
||||
if (data_ok) {
|
||||
decode_data = (decode_data << 1) | data;
|
||||
decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) {
|
||||
uint8_t crc = 0;
|
||||
data &= 0xFFF0FFFFFFFFFF;
|
||||
for (uint8_t i = 0; i < 56; i += 8) {
|
||||
crc = crc ^ data >> i ^ (data >> (i + 4));
|
||||
}
|
||||
return crc & 0xf;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,10 +25,7 @@ class FProtoSubGhzDBase {
|
||||
|
||||
// General data holder, these will be passed
|
||||
uint8_t sensorType = FPS_Invalid;
|
||||
uint8_t btn = SD_NO_BTN;
|
||||
uint16_t data_count_bit = 0;
|
||||
uint32_t cnt = SD_NO_CNT;
|
||||
uint32_t serial = SD_NO_SERIAL;
|
||||
uint64_t data = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -47,6 +47,10 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
#include "s-smc5326.hpp"
|
||||
#include "s-star_line.hpp"
|
||||
#include "s-x10.hpp"
|
||||
// #include "s-hormannbisecure.hpp" //fm
|
||||
#include "s-legrand.hpp"
|
||||
#include "s-somify_keytis.hpp"
|
||||
#include "s-somify_telis.hpp"
|
||||
|
||||
// GENIE FROM PR
|
||||
|
||||
@@ -94,11 +98,12 @@ class SubGhzDProtos : public FProtoListGeneral {
|
||||
protos[FPS_SECPLUSV1] = new FProtoSubGhzDSecPlusV1();
|
||||
protos[FPS_SECPLUSV2] = new FProtoSubGhzDSecPlusV2();
|
||||
protos[FPS_SMC5326] = new FProtoSubGhzDSmc5326();
|
||||
// somify keytis skipped
|
||||
// somify telis skipped
|
||||
protos[FPS_SOMIFY_KEYTIS] = new FProtoSubGhzDSomifyKeytis();
|
||||
protos[FPS_SOMIFY_TELIS] = new FProtoSubGhzDSomifyTelis();
|
||||
protos[FPS_STARLINE] = new FProtoSubGhzDStarLine();
|
||||
protos[FPS_X10] = new FProtoSubGhzDX10();
|
||||
// genie skipped
|
||||
// protos[FPS_HORMANNBISECURE] = new FProtoSubGhzDHormannBiSecure(); //fm
|
||||
protos[FPS_LEGRAND] = new FProtoSubGhzDLegrand();
|
||||
|
||||
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
|
||||
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
|
||||
@@ -115,7 +120,7 @@ class SubGhzDProtos : public FProtoListGeneral {
|
||||
};
|
||||
|
||||
static void callbackTarget(FProtoSubGhzDBase* instance) {
|
||||
SubGhzDDataMessage packet_message{instance->sensorType, instance->btn, instance->data_count_bit, instance->serial, instance->data, instance->cnt};
|
||||
SubGhzDDataMessage packet_message{instance->sensorType, instance->data_count_bit, instance->data};
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,6 @@ Also it must have a switch-case element in the getSubGhzDSensorTypeName() functi
|
||||
#define FPM_AM 0
|
||||
#define FPM_FM 1
|
||||
|
||||
#define SD_NO_SERIAL 0xFFFFFFFF
|
||||
#define SD_NO_BTN 0xFF
|
||||
#define SD_NO_CNT 0xFF
|
||||
|
||||
enum FPROTO_SUBGHZD_SENSOR : uint8_t {
|
||||
FPS_Invalid = 0,
|
||||
FPS_PRINCETON,
|
||||
@@ -35,10 +31,12 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
|
||||
FPS_HONEYWELL,
|
||||
FPS_HONEYWELLWDB,
|
||||
FPS_HORMANN,
|
||||
// FPS_HORMANNBISECURE,
|
||||
FPS_IDO,
|
||||
FPS_INTERTECHNOV3,
|
||||
FPS_KEELOQ,
|
||||
FPS_KINGGATESSTYLO4K,
|
||||
FPS_LEGRAND,
|
||||
FPS_LINEAR,
|
||||
FPS_LINEARDELTA3,
|
||||
FPS_MAGELLAN,
|
||||
@@ -56,7 +54,8 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
|
||||
FPS_SMC5326,
|
||||
FPS_STARLINE,
|
||||
FPS_X10,
|
||||
|
||||
FPS_SOMIFY_KEYTIS,
|
||||
FPS_SOMIFY_TELIS,
|
||||
FPS_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -148,7 +148,6 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase {
|
||||
humidity = (data >> 24) & 0x7F;
|
||||
uint16_t temp_raw = ((data >> 9) & 0xF80) | ((data >> 8) & 0x7F);
|
||||
temp = ((float)(temp_raw)-1000) / 10.0f;
|
||||
btn = WS_NO_BTN;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
#ifndef __FPROTO_Acurite_5in1_H__
|
||||
#define __FPROTO_Acurite_5in1_H__
|
||||
|
||||
#include "weatherbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
Acurite_5n1DecoderStepReset = 0,
|
||||
Acurite_5n1DecoderStepCheckPreambule,
|
||||
Acurite_5n1DecoderStepSaveDuration,
|
||||
Acurite_5n1DecoderStepCheckDuration,
|
||||
} Acurite_5n1DecoderStep;
|
||||
|
||||
class FProtoWeatherAcurite5in1 : public FProtoWeatherBase {
|
||||
public:
|
||||
FProtoWeatherAcurite5in1() {
|
||||
sensorType = FPW_Acurite5in1;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case Acurite_5n1DecoderStepReset:
|
||||
if ((level) && (DURATION_DIFF(duration, te_short * 3) < te_delta * 2)) {
|
||||
parser_step = Acurite_5n1DecoderStepCheckPreambule;
|
||||
te_last = duration;
|
||||
header_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case Acurite_5n1DecoderStepCheckPreambule:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
} else {
|
||||
if ((DURATION_DIFF(te_last, te_short * 3) < te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, te_short * 3) < te_delta * 2)) {
|
||||
// Found preambule
|
||||
header_count++;
|
||||
} else if ((header_count > 2) && (header_count < 5)) {
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta)) {
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = Acurite_5n1DecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_long) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = Acurite_5n1DecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Acurite_5n1DecoderStepSaveDuration:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
parser_step = Acurite_5n1DecoderStepCheckDuration;
|
||||
} else {
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case Acurite_5n1DecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if (duration >= ((uint32_t)te_short * 5)) {
|
||||
if ((decode_count_bit == min_count_bit_for_found) &&
|
||||
ws_protocol_acurite_5n1_check_crc() &&
|
||||
ws_protocol_acurite_5n1_check_message_type()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
ws_protocol_acurite_5n1_remote_controller();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
break;
|
||||
} else if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = Acurite_5n1DecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_long) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = Acurite_5n1DecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
parser_step = Acurite_5n1DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 200;
|
||||
uint32_t te_long = 400;
|
||||
uint32_t te_delta = 90;
|
||||
uint32_t min_count_bit_for_found = 64;
|
||||
|
||||
bool ws_protocol_acurite_5n1_check_message_type() {
|
||||
if (((decode_data >> 40) & 0x3F) == 0x38) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ws_protocol_acurite_5n1_check_crc() {
|
||||
uint8_t msg[] = {
|
||||
(uint8_t)(decode_data >> 56),
|
||||
(uint8_t)(decode_data >> 48),
|
||||
(uint8_t)(decode_data >> 40),
|
||||
(uint8_t)(decode_data >> 32),
|
||||
(uint8_t)(decode_data >> 24),
|
||||
(uint8_t)(decode_data >> 16),
|
||||
(uint8_t)(decode_data >> 8)};
|
||||
|
||||
if ((FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 7) ==
|
||||
(uint8_t)(decode_data & 0xFF)) &&
|
||||
(!FProtoGeneral::subghz_protocol_blocks_parity_bytes(&msg[2], 5))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ws_protocol_acurite_5n1_remote_controller() {
|
||||
uint8_t channell[] = {3, 0, 2, 1};
|
||||
uint8_t channel_raw = ((data >> 62) & 0x03);
|
||||
channel = channell[channel_raw];
|
||||
id = (data >> 48) & 0x3FFF;
|
||||
battery_low = !((data >> 46) & 1);
|
||||
humidity = (data >> 8) & 0x7F;
|
||||
uint16_t temp_raw = ((data >> (24 - 7)) & 0x780) | ((data >> 16) & 0x7F);
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)(temp_raw)-400) / 10.0f);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -92,7 +92,6 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase {
|
||||
} else {
|
||||
temp = (float)((~(data >> 8) & 0x07FF) + 1) / -10.0f;
|
||||
}
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
bool ws_protocol_acurite_606tx_check() {
|
||||
|
||||
@@ -39,16 +39,11 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
|
||||
case Acurite_609TXCDecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if (DURATION_DIFF(te_last, te_short) <
|
||||
te_delta) {
|
||||
if ((DURATION_DIFF(duration, te_short) <
|
||||
te_delta) ||
|
||||
(duration > te_long * 3)) {
|
||||
if (DURATION_DIFF(te_last, te_short) < te_delta) {
|
||||
if ((DURATION_DIFF(duration, te_short) < te_delta) || (duration > te_long * 3)) {
|
||||
// Found syncPostfix
|
||||
parser_step = Acurite_609TXCDecoderStepReset;
|
||||
if ((decode_count_bit ==
|
||||
min_count_bit_for_found) &&
|
||||
ws_protocol_acurite_609txc_check()) {
|
||||
if ((decode_count_bit == min_count_bit_for_found) && ws_protocol_acurite_609txc_check()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
ws_protocol_acurite_609txc_remote_controller();
|
||||
@@ -56,14 +51,10 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) <
|
||||
te_delta * 2) {
|
||||
} else if (DURATION_DIFF(duration, te_long) < te_delta * 2) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = Acurite_609TXCDecoderStepSaveDuration;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long * 2) <
|
||||
te_delta * 4) {
|
||||
} else if (DURATION_DIFF(duration, te_long * 2) < te_delta * 4) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = Acurite_609TXCDecoderStepSaveDuration;
|
||||
} else {
|
||||
@@ -81,7 +72,7 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 500;
|
||||
uint32_t te_long = 2000;
|
||||
uint32_t te_long = 1000;
|
||||
uint32_t te_delta = 150;
|
||||
uint32_t min_count_bit_for_found = 32;
|
||||
|
||||
@@ -97,7 +88,6 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
(int16_t)(((data >> 12) & 0xf000) | ((data >> 16) << 4));
|
||||
temp = (temp_raw >> 4) * 0.1f;
|
||||
humidity = (data >> 8) & 0xff;
|
||||
btn = WS_NO_BTN;
|
||||
}
|
||||
bool ws_protocol_acurite_609txc_check() {
|
||||
if (!decode_data) return false;
|
||||
|
||||
@@ -118,7 +118,6 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
|
||||
temp = -(temp & 0x7F);
|
||||
}
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)temp);
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
|
||||
uint32_t te_long = 1000;
|
||||
uint32_t te_delta = 120;
|
||||
uint32_t min_count_bit_for_found = 48;
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
bool ws_protocol_ambient_weather_check_crc() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(decode_data >> 40),
|
||||
@@ -81,7 +81,6 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
|
||||
channel = ((data >> 28) & 0x07) + 1;
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
|
||||
humidity = (data >> 8) & 0xFF;
|
||||
btn = WS_NO_BTN;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class FProtoWeatherAuriolAhfl : public FProtoWeatherBase {
|
||||
void ws_protocol_auriol_ahfl_remote_controller() {
|
||||
id = data >> 34;
|
||||
battery_low = (data >> 33) & 1;
|
||||
btn = (data >> 32) & 1;
|
||||
// btn = (data >> 32) & 1;
|
||||
channel = ((data >> 30) & 0x3) + 1;
|
||||
if (!((data >> 29) & 1)) {
|
||||
temp = (float)((data >> 18) & 0x07FF) / 10.0f;
|
||||
|
||||
@@ -91,7 +91,6 @@ class FProtoWeatherAuriolTh : public FProtoWeatherBase {
|
||||
id = (data >> 31) & 0xFF;
|
||||
battery_low = ((data >> 30) & 1);
|
||||
channel = ((data >> 25) & 0x03) + 1;
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 23) & 1)) {
|
||||
temp = (float)((data >> 13) & 0x07FF) / 10.0f;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
#ifndef __FPROTO_EmosE601x_H__
|
||||
#define __FPROTO_EmosE601x_H__
|
||||
|
||||
#include "weatherbase.hpp"
|
||||
|
||||
#define EmosE601xMAGIC_HEADER 0xaaa583
|
||||
|
||||
typedef enum {
|
||||
EmosE601xDecoderStepReset = 0,
|
||||
EmosE601xDecoderStepCheckPreamble,
|
||||
EmosE601xDecoderStepSaveDuration,
|
||||
EmosE601xDecoderStepCheckDuration,
|
||||
} EmosE601xDecoderStep;
|
||||
|
||||
class FProtoWeatherEmosE601x : public FProtoWeatherBase {
|
||||
public:
|
||||
FProtoWeatherEmosE601x() {
|
||||
sensorType = FPW_EmosE601x;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case EmosE601xDecoderStepReset:
|
||||
if ((level) && (DURATION_DIFF(duration, te_short * 7) <
|
||||
te_delta * 2)) {
|
||||
parser_step = EmosE601xDecoderStepCheckPreamble;
|
||||
te_last = duration;
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepCheckPreamble:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
} else {
|
||||
if ((DURATION_DIFF(te_last, te_short * 7) <
|
||||
te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, te_short) <
|
||||
te_delta)) {
|
||||
// Found preamble
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepSaveDuration:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
parser_step = EmosE601xDecoderStepCheckDuration;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta)) {
|
||||
subghz_protocol_blocks_add_to_128_bit(0, &upper_decode_data);
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_long) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
subghz_protocol_blocks_add_to_128_bit(1, &upper_decode_data);
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Bail out if the header doesn't match */
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
if (decode_data != EmosE601xMAGIC_HEADER) {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_count_bit == 120) {
|
||||
data_count_bit = decode_count_bit;
|
||||
if (ws_protocol_emose601x_check()) {
|
||||
ws_protocol_emose601x_extract_data();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 260;
|
||||
uint32_t te_long = 800;
|
||||
uint32_t te_delta = 100;
|
||||
uint32_t min_count_bit_for_found = 24;
|
||||
|
||||
uint64_t upper_decode_data = 0;
|
||||
void subghz_protocol_blocks_add_to_128_bit(uint8_t bit, uint64_t* head_64_bit) {
|
||||
if (++decode_count_bit > 64) {
|
||||
(*head_64_bit) = ((*head_64_bit) << 1) | (decode_data >> 63);
|
||||
}
|
||||
decode_data = decode_data << 1 | bit;
|
||||
}
|
||||
bool ws_protocol_emose601x_check() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(upper_decode_data >> 48),
|
||||
static_cast<uint8_t>(upper_decode_data >> 40),
|
||||
static_cast<uint8_t>(upper_decode_data >> 32),
|
||||
static_cast<uint8_t>(upper_decode_data >> 24),
|
||||
static_cast<uint8_t>(upper_decode_data >> 16),
|
||||
static_cast<uint8_t>(upper_decode_data >> 8),
|
||||
static_cast<uint8_t>(upper_decode_data),
|
||||
static_cast<uint8_t>(decode_data >> 56),
|
||||
static_cast<uint8_t>(decode_data >> 48),
|
||||
static_cast<uint8_t>(decode_data >> 40),
|
||||
static_cast<uint8_t>(decode_data >> 32),
|
||||
static_cast<uint8_t>(decode_data >> 24),
|
||||
static_cast<uint8_t>(decode_data >> 16)};
|
||||
|
||||
uint8_t sum = FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 13);
|
||||
return (sum == ((decode_data >> 8) & 0xff));
|
||||
}
|
||||
|
||||
void ws_protocol_emose601x_extract_data() {
|
||||
id = (upper_decode_data >> 24) & 0xff;
|
||||
battery_low = (decode_data >> 10) & 1;
|
||||
int16_t temp = (decode_data >> 40) & 0xfff;
|
||||
/* Handle signed data */
|
||||
if (temp & 0x800) {
|
||||
temp |= 0xf000;
|
||||
}
|
||||
temp = (float)temp / 10.0;
|
||||
humidity = (decode_data >> 32) & 0xff;
|
||||
channel = (decode_data >> 52) & 0x03;
|
||||
data = (decode_data >> 16);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -91,7 +91,7 @@ class FProtoWeatherGTWT02 : public FProtoWeatherBase {
|
||||
void ws_protocol_gt_wt_02_remote_controller() {
|
||||
id = (data >> 29) & 0xFF;
|
||||
battery_low = (data >> 28) & 1;
|
||||
btn = (data >> 27) & 1;
|
||||
// btn = (data >> 27) & 1;
|
||||
channel = ((data >> 25) & 0x3) + 1;
|
||||
|
||||
if (!((data >> 24) & 1)) {
|
||||
|
||||
@@ -124,7 +124,7 @@ class FProtoWeatherGTWT03 : public FProtoWeatherBase {
|
||||
}
|
||||
|
||||
battery_low = (data >> 24) & 1;
|
||||
btn = (data >> 23) & 1;
|
||||
// (data >> 23) & 1;
|
||||
channel = ((data >> 21) & 0x03) + 1;
|
||||
|
||||
if (!((data >> 20) & 1)) {
|
||||
|
||||
@@ -125,7 +125,6 @@ class FProtoWeatherInfactory : public FProtoWeatherBase {
|
||||
void ws_protocol_infactory_remote_controller() {
|
||||
id = data >> 32;
|
||||
battery_low = (data >> 26) & 1;
|
||||
btn = WS_NO_BTN;
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
|
||||
humidity = (((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH
|
||||
channel = data & 0x03;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
#ifndef __FPROTO_Kedsum_H__
|
||||
#define __FPROTO_Kedsum_H__
|
||||
|
||||
#include "weatherbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
KedsumTHDecoderStepReset = 0,
|
||||
KedsumTHDecoderStepCheckPreambule,
|
||||
KedsumTHDecoderStepSaveDuration,
|
||||
KedsumTHDecoderStepCheckDuration,
|
||||
} KedsumTHDecoderStep;
|
||||
|
||||
class FProtoWeatherKedsum : public FProtoWeatherBase {
|
||||
public:
|
||||
FProtoWeatherKedsum() {
|
||||
sensorType = FPW_KEDSUM;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case KedsumTHDecoderStepReset:
|
||||
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
parser_step = KedsumTHDecoderStepCheckPreambule;
|
||||
te_last = duration;
|
||||
header_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepCheckPreambule:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
} else {
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 4) < te_delta * 4)) {
|
||||
// Found preambule
|
||||
header_count++;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(duration < (te_long * 2 + te_delta * 2))) {
|
||||
// Found syncPrefix
|
||||
if (header_count > 0) {
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 2) < te_delta * 4)) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepSaveDuration:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
parser_step = KedsumTHDecoderStepCheckDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if (DURATION_DIFF(duration, te_long * 4) < te_delta * 4) {
|
||||
// Found syncPostfix
|
||||
if ((decode_count_bit ==
|
||||
min_count_bit_for_found) &&
|
||||
ws_protocol_kedsum_th_check_crc()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
ws_protocol_kedsum_th_remote_controller();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
break;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) <
|
||||
te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 2) <
|
||||
te_delta * 4)) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 500;
|
||||
uint32_t te_long = 2000;
|
||||
uint32_t te_delta = 150;
|
||||
uint32_t min_count_bit_for_found = 42;
|
||||
|
||||
bool ws_protocol_kedsum_th_check_crc() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(decode_data >> 32),
|
||||
static_cast<uint8_t>(decode_data >> 24),
|
||||
static_cast<uint8_t>(decode_data >> 16),
|
||||
static_cast<uint8_t>(decode_data >> 8),
|
||||
static_cast<uint8_t>(decode_data)};
|
||||
|
||||
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x03, 0); // CRC-4 poly 0x3 init 0x0 xor last 4 bits
|
||||
crc ^= msg[4] >> 4; // last nibble is only XORed
|
||||
return (crc == (msg[4] & 0x0F));
|
||||
}
|
||||
|
||||
void ws_protocol_kedsum_th_remote_controller() {
|
||||
id = data >> 32;
|
||||
if ((data >> 30) & 0x3) {
|
||||
battery_low = 0;
|
||||
} else {
|
||||
battery_low = 1;
|
||||
}
|
||||
channel = ((data >> 28) & 0x3) + 1;
|
||||
uint16_t temp_raw = ((data >> 16) & 0x0f) << 8 |
|
||||
((data >> 20) & 0x0f) << 4 | ((data >> 24) & 0x0f);
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)temp_raw - 900.0f) / 10.0f);
|
||||
humidity = ((data >> 8) & 0x0f) << 4 | ((data >> 12) & 0x0f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -137,7 +137,6 @@ class FProtoWeatherLaCrosseTx : public FProtoWeatherBase {
|
||||
// furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type.");
|
||||
}
|
||||
|
||||
btn = WS_NO_BTN;
|
||||
battery_low = WS_NO_BATT;
|
||||
channel = WS_NO_CHANNEL;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class FProtoWeatherLaCrosseTx141thbv2 : public FProtoWeatherBase {
|
||||
}
|
||||
id = data >> 32;
|
||||
battery_low = (data >> 31) & 1;
|
||||
btn = (data >> 30) & 1;
|
||||
// btn = (data >> 30) & 1;
|
||||
channel = ((data >> 28) & 0x03) + 1;
|
||||
temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
|
||||
humidity = (data >> 8) & 0xFF;
|
||||
|
||||
@@ -104,7 +104,6 @@ class FProtoWeatherNexusTH : public FProtoWeatherBase {
|
||||
id = (data >> 28) & 0xFF;
|
||||
battery_low = !((data >> 27) & 1);
|
||||
channel = ((data >> 24) & 0x03) + 1;
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 23) & 1)) {
|
||||
temp = (float)((data >> 12) & 0x07FF) / 10.0f;
|
||||
} else {
|
||||
|
||||
@@ -160,7 +160,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase {
|
||||
bool prev_bit = 0;
|
||||
uint8_t var_bits{0};
|
||||
uint32_t var_data{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
void ws_protocol_decoder_oregon2_reset() {
|
||||
parser_step = Oregon2DecoderStepReset;
|
||||
decode_data = 0UL;
|
||||
|
||||
@@ -117,7 +117,7 @@ class FProtoWeatherOregon3 : public FProtoWeatherBase {
|
||||
bool prev_bit = false;
|
||||
uint8_t var_bits{0};
|
||||
uint64_t var_data{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) {
|
||||
bool is_long = false;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
|
||||
uint32_t min_count_bit_for_found = 32;
|
||||
|
||||
uint8_t first_bit{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
bool ws_protocol_oregon_v1_check() {
|
||||
if (!decode_data) return false;
|
||||
uint64_t data = FProtoGeneral::subghz_protocol_blocks_reverse_key(decode_data, 32);
|
||||
@@ -159,7 +159,6 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
|
||||
temp = -temp_raw;
|
||||
}
|
||||
battery_low = !((data2 >> 23) & 1ULL);
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,7 +96,7 @@ class FProtoWeatherThermoProTx4 : public FProtoWeatherBase {
|
||||
void ws_protocol_thermopro_tx4_remote_controller() {
|
||||
id = (data >> 25) & 0xFF;
|
||||
battery_low = (data >> 24) & 1;
|
||||
btn = (data >> 23) & 1;
|
||||
// btn = (data >> 23) & 1;
|
||||
channel = ((data >> 21) & 0x03) + 1;
|
||||
if (!((data >> 20) & 1)) {
|
||||
temp = (float)((data >> 9) & 0x07FF) / 10.0f;
|
||||
|
||||
@@ -120,7 +120,6 @@ class FProtoWeatherTX8300 : public FProtoWeatherBase {
|
||||
}
|
||||
void ws_protocol_tx_8300_remote_controller() {
|
||||
humidity = (((data >> 28) & 0x0F) * 10) + ((data >> 24) & 0x0F);
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 22) & 0x03))
|
||||
battery_low = 0;
|
||||
else
|
||||
|
||||
@@ -147,8 +147,6 @@ class FProtoWeatherWendoxW6726 : public FProtoWeatherBase {
|
||||
} else if (temp > 70.0f) {
|
||||
temp = 70.0f;
|
||||
}
|
||||
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,12 +24,11 @@ class FProtoWeatherBase {
|
||||
void setCallback(SubGhzProtocolDecoderBaseRxCallback cb) { callback = cb; } // this is called when there is a hit.
|
||||
|
||||
uint8_t getSensorType() { return sensorType; }
|
||||
uint32_t getSensorId() { return id; }
|
||||
float getTemp() { return temp; }
|
||||
uint8_t getHumidity() { return humidity; }
|
||||
uint8_t getBattLow() { return battery_low; }
|
||||
uint8_t getChannel() { return channel; }
|
||||
uint8_t getButton() { return btn; }
|
||||
uint32_t getSensorId() { return id; }
|
||||
float getTemp() { return temp; }
|
||||
|
||||
protected:
|
||||
// Helper functions to keep it as compatible with flipper as we can, so adding new protos will be easy.
|
||||
@@ -38,25 +37,27 @@ class FProtoWeatherBase {
|
||||
decode_count_bit++;
|
||||
}
|
||||
|
||||
// General weather data holder
|
||||
// needs to be in this chaotic order, to save flash!
|
||||
// General weather data holder
|
||||
uint8_t sensorType = FPW_Invalid;
|
||||
uint32_t id = WS_NO_ID;
|
||||
float temp = WS_NO_TEMPERATURE;
|
||||
uint8_t humidity = WS_NO_HUMIDITY;
|
||||
uint8_t battery_low = WS_NO_BATT;
|
||||
uint8_t channel = WS_NO_CHANNEL;
|
||||
uint8_t btn = WS_NO_BTN;
|
||||
|
||||
// inner logic stuff, also for flipper compatibility.
|
||||
SubGhzProtocolDecoderBaseRxCallback callback = NULL;
|
||||
uint16_t header_count = 0;
|
||||
// inner logic stuff
|
||||
uint8_t parser_step = 0;
|
||||
// inner logic stuff, also for flipper compatibility.
|
||||
uint16_t header_count = 0;
|
||||
// General weather data holder
|
||||
float temp = WS_NO_TEMPERATURE;
|
||||
uint32_t id = WS_NO_ID;
|
||||
// inner logic stuff,
|
||||
|
||||
uint32_t te_last = 0;
|
||||
uint64_t data = 0;
|
||||
uint32_t data_count_bit = 0;
|
||||
uint64_t decode_data = 0;
|
||||
uint32_t decode_count_bit = 0;
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
uint64_t decode_data = 0;
|
||||
uint64_t data = 0;
|
||||
SubGhzProtocolDecoderBaseRxCallback callback = NULL;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,6 +25,10 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
#include "w-tx8300.hpp"
|
||||
#include "w-wendox-w6726.hpp"
|
||||
#include "w-acurite986.hpp"
|
||||
#include "w-kedsum.hpp"
|
||||
#include "w-acurite5in1.hpp"
|
||||
#include "w-emose601x.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@@ -34,49 +38,63 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
|
||||
class WeatherProtos : public FProtoListGeneral {
|
||||
public:
|
||||
WeatherProtos(const WeatherProtos&) { WeatherProtos(); }; // won't use, but makes compiler happy
|
||||
WeatherProtos& operator=(const WeatherProtos&) { return *this; } // won't use, but makes compiler happy
|
||||
WeatherProtos() {
|
||||
// add protos
|
||||
protos.push_back(std::make_unique<FProtoWeatherNexusTH>()); // 1
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite592TXR>()); // 2
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite606TX>()); // 3
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite609TX>()); // 4
|
||||
protos.push_back(std::make_unique<FProtoWeatherAmbient>()); // 5
|
||||
protos.push_back(std::make_unique<FProtoWeatherAuriolAhfl>()); // 6
|
||||
protos.push_back(std::make_unique<FProtoWeatherAuriolTh>()); // 7
|
||||
protos.push_back(std::make_unique<FProtoWeatherGTWT02>()); // 8
|
||||
protos.push_back(std::make_unique<FProtoWeatherGTWT03>()); // 9
|
||||
protos.push_back(std::make_unique<FProtoWeatherInfactory>()); // 10
|
||||
protos.push_back(std::make_unique<FProtoWeatherLaCrosseTx>()); // 11
|
||||
protos.push_back(std::make_unique<FProtoWeatherLaCrosseTx141thbv2>()); // 12
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregon2>()); // 13
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregon3>()); // 14
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregonV1>()); // 15
|
||||
protos.push_back(std::make_unique<FProtoWeatherThermoProTx4>()); // 16
|
||||
protos.push_back(std::make_unique<FProtoWeatherTX8300>()); // 17
|
||||
protos.push_back(std::make_unique<FProtoWeatherWendoxW6726>()); // 18
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite986>()); // 19
|
||||
protos[FPW_NexusTH] = new FProtoWeatherNexusTH();
|
||||
protos[FPW_Acurite592TXR] = new FProtoWeatherAcurite592TXR();
|
||||
protos[FPW_Acurite606TX] = new FProtoWeatherAcurite606TX();
|
||||
protos[FPW_Acurite609TX] = new FProtoWeatherAcurite609TX();
|
||||
protos[FPW_Ambient] = new FProtoWeatherAmbient();
|
||||
protos[FPW_AuriolAhfl] = new FProtoWeatherAuriolAhfl();
|
||||
protos[FPW_AuriolTH] = new FProtoWeatherAuriolTh();
|
||||
protos[FPW_GTWT02] = new FProtoWeatherGTWT02();
|
||||
protos[FPW_GTWT03] = new FProtoWeatherGTWT03();
|
||||
protos[FPW_INFACTORY] = new FProtoWeatherInfactory();
|
||||
protos[FPW_LACROSSETX] = new FProtoWeatherLaCrosseTx();
|
||||
protos[FPW_LACROSSETX141thbv2] = new FProtoWeatherLaCrosseTx141thbv2();
|
||||
protos[FPW_OREGON2] = new FProtoWeatherOregon2();
|
||||
protos[FPW_OREGON3] = new FProtoWeatherOregon3();
|
||||
protos[FPW_OREGONv1] = new FProtoWeatherOregonV1();
|
||||
protos[FPW_THERMOPROTX4] = new FProtoWeatherThermoProTx4();
|
||||
protos[FPW_TX_8300] = new FProtoWeatherTX8300();
|
||||
protos[FPW_WENDOX_W6726] = new FProtoWeatherWendoxW6726();
|
||||
protos[FPW_Acurite986] = new FProtoWeatherAcurite986();
|
||||
protos[FPW_KEDSUM] = new FProtoWeatherKedsum();
|
||||
protos[FPW_Acurite5in1] = new FProtoWeatherAcurite5in1();
|
||||
protos[FPW_EmosE601x] = new FProtoWeatherEmosE601x();
|
||||
|
||||
// set callback for them
|
||||
for (const auto& obj : protos) {
|
||||
obj->setCallback(callbackTarget);
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
protos[i]->setCallback(callbackTarget);
|
||||
}
|
||||
}
|
||||
|
||||
~WeatherProtos() { // not needed for current operation logic, but a bit more elegant :)
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
if (protos[i] != NULL) {
|
||||
free(protos[i]);
|
||||
protos[i] = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void callbackTarget(FProtoWeatherBase* instance) {
|
||||
WeatherDataMessage packet_message{instance->getSensorType(), instance->getSensorId(),
|
||||
instance->getTemp(), instance->getHumidity(), instance->getBattLow(),
|
||||
instance->getChannel(), instance->getButton()};
|
||||
instance->getChannel()};
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
for (const auto& obj : protos) {
|
||||
obj->feed(level, duration);
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
if (protos[i] != NULL) protos[i]->feed(level, duration);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<FProtoWeatherBase>> protos{};
|
||||
FProtoWeatherBase* protos[FPW_COUNT] = {NULL};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@ Also it must have a switch-case element in the getWeatherSensorTypeName() functi
|
||||
#define WS_NO_BATT 0xFF
|
||||
#define WS_NO_HUMIDITY 0xFF
|
||||
#define WS_NO_CHANNEL 0xFF
|
||||
#define WS_NO_BTN 0xFF
|
||||
// #define WS_NO_BTN 0xFF
|
||||
#define WS_NO_TEMPERATURE -273.0f
|
||||
|
||||
enum FPROTO_WEATHER_SENSOR {
|
||||
@@ -34,7 +34,11 @@ enum FPROTO_WEATHER_SENSOR {
|
||||
FPW_THERMOPROTX4 = 16,
|
||||
FPW_TX_8300 = 17,
|
||||
FPW_WENDOX_W6726 = 18,
|
||||
FPW_Acurite986 = 19
|
||||
FPW_Acurite986 = 19,
|
||||
FPW_KEDSUM = 20,
|
||||
FPW_Acurite5in1 = 21,
|
||||
FPW_EmosE601x = 22,
|
||||
FPW_COUNT // this must be the last
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -113,13 +113,27 @@ void MicTXProcessor::on_message(const Message* const msg) {
|
||||
}
|
||||
|
||||
if (usb_enabled) {
|
||||
modulator = new dsp::modulate::SSB();
|
||||
modulator->set_mode(dsp::modulate::Mode::USB);
|
||||
dsp::modulate::SSB* ssb = new dsp::modulate::SSB();
|
||||
|
||||
// Config fs_div_factor private var inside DSP modulate.cpp
|
||||
ssb->set_fs_div_factor(config_message.deviation_hz); // we use same FM var deviation_hz , to set up also SSB BW
|
||||
ssb->set_mode(dsp::modulate::Mode::USB);
|
||||
modulator = ssb;
|
||||
|
||||
// modulator = new dsp::modulate::SSB(); // Keeping previous code as ref., when not passing deviation_hz parameter.
|
||||
// modulator->set_mode(dsp::modulate::Mode::USB);
|
||||
}
|
||||
|
||||
if (lsb_enabled) {
|
||||
modulator = new dsp::modulate::SSB();
|
||||
modulator->set_mode(dsp::modulate::Mode::LSB);
|
||||
dsp::modulate::SSB* ssb = new dsp::modulate::SSB();
|
||||
|
||||
// Config fs_div_factor private var inside DSP modulate.cpp
|
||||
ssb->set_fs_div_factor(config_message.deviation_hz); // we use same FM var deviation_hz , to set up also SSB BW
|
||||
ssb->set_mode(dsp::modulate::Mode::LSB);
|
||||
modulator = ssb;
|
||||
|
||||
// modulator = new dsp::modulate::SSB(); // Keeping previous code as ref., when not passing deviation_hz parameter.
|
||||
// modulator->set_mode(dsp::modulate::Mode::LSB);
|
||||
}
|
||||
if (am_enabled) {
|
||||
modulator = new dsp::modulate::AM();
|
||||
@@ -130,7 +144,7 @@ void MicTXProcessor::on_message(const Message* const msg) {
|
||||
modulator->set_mode(dsp::modulate::Mode::DSB);
|
||||
}
|
||||
|
||||
modulator->set_over(baseband_fs / 24000); // Keep no change.
|
||||
modulator->set_over(baseband_fs / 24000); // "over" is calculated based on relationship fs_transceiver / fs_audio_mic_capture, to be used in dsp_modulate.cpp
|
||||
|
||||
am_enabled = config_message.am_enabled;
|
||||
usb_enabled = config_message.usb_enabled;
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_protoview.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
void ProtoViewProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
// SR = 4Mhz , and we are decimating by /8 in total , decim1_out clock 4Mhz /8= 500khz samples/sec.
|
||||
// buffer has 2048 complex i8 I,Q signed samples
|
||||
// decim0 out: 2048/4 = 512 complex i16 I,Q signed samples
|
||||
// decim1 out: 512/2 = 256 complex i16 I,Q signed samples
|
||||
// Regarding Filters, we are re-using existing FIR filters, @4Mhz, FIR decim1 ilter, BW =+-220Khz (at -3dB's). BW = 440kHZ.
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
int16_t im = decim_1_out.p[i].imag();
|
||||
uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im);
|
||||
|
||||
mag = (mag >> 12); // Decim samples are calculated with saturated gain . (we could also reduce that sat. param at configure time)
|
||||
|
||||
bool meashl = (mag > threshold);
|
||||
tm += mag;
|
||||
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
|
||||
{
|
||||
currentDuration += nsPerDecSamp;
|
||||
} else { // called on change, so send the last duration and dir.
|
||||
message.times[message.timeptr++] = currentHiLow ? (int32_t)(currentDuration / 1000) : -1 * (int32_t)(currentDuration / 1000);
|
||||
if (message.timeptr > message.maxptr) {
|
||||
shared_memory.application_queue.push(message);
|
||||
message.timeptr = 0;
|
||||
}
|
||||
currentDuration = nsPerDecSamp;
|
||||
currentHiLow = meashl;
|
||||
}
|
||||
}
|
||||
|
||||
cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor.
|
||||
if (cnt > 90'000) {
|
||||
threshold = (tm / cnt) / 2;
|
||||
cnt = 0;
|
||||
tm = 0;
|
||||
if (threshold < 50) threshold = 50;
|
||||
if (threshold > 1700) threshold = 1700;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::on_message(const Message* const message) {
|
||||
switch (message->id) {
|
||||
case Message::ID::SubGhzFPRxConfigure:
|
||||
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::AudioBeep:
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
|
||||
baseband_fs = message.sampling_rate;
|
||||
baseband_thread.set_sampling_rate(baseband_fs);
|
||||
nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // Scaled it due to less array buffer sampes due to /8 decimation. 250 nseg (4Mhz) * 8
|
||||
|
||||
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused
|
||||
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused
|
||||
|
||||
decim_0.configure(taps_200k_wfm_decim_0.taps);
|
||||
decim_1.configure(taps_200k_wfm_decim_1.taps);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<ProtoViewProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/*
|
||||
Creator: @htotoo
|
||||
*/
|
||||
|
||||
#ifndef __PROC_PROTOVIEW_H__
|
||||
#define __PROC_PROTOVIEW_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
#include "message.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
|
||||
class ProtoViewProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
size_t baseband_fs = 0; // will be set later by configure message.
|
||||
uint32_t nsPerDecSamp = 0;
|
||||
|
||||
/* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */
|
||||
std::array<complex16_t, 512> dst{}; // decim0 /4 , 2048/4 = 512 complex I,Q
|
||||
const buffer_c16_t dst_buffer{
|
||||
dst.data(),
|
||||
dst.size()};
|
||||
|
||||
/* Decimates */
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
|
||||
|
||||
uint32_t currentDuration = 0;
|
||||
uint32_t threshold = 0x0630; // will overwrite after the first iteration
|
||||
|
||||
bool currentHiLow = false;
|
||||
bool configured{false};
|
||||
|
||||
// for threshold
|
||||
uint32_t cnt = 0;
|
||||
uint32_t tm = 0;
|
||||
|
||||
void configure(const SubGhzFPRxConfigureMessage& message);
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
ProtoViewDataMessage message = {};
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive};
|
||||
RSSIThread rssi_thread{};
|
||||
};
|
||||
|
||||
#endif /*__PROC_PROTOVIEW_H__*/
|
||||
@@ -35,6 +35,7 @@ void SubGhzDProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
|
||||
@@ -36,6 +36,7 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
|
||||
@@ -58,8 +58,6 @@ class WeatherProcessor : public BasebandProcessor {
|
||||
bool currentHiLow = false;
|
||||
bool configured{false};
|
||||
|
||||
uint8_t modulation = 255; // 0 AM, 1 FM 255 = Not set
|
||||
uint8_t protoMode = 255; // 0 weather, 1 subghzd, 255 = Not set
|
||||
// for threshold
|
||||
uint32_t cnt = 0;
|
||||
uint32_t tm = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user