Separate weather and sgd, bc of baseband size limit

This commit is contained in:
HTotoo
2023-12-09 22:01:15 +01:00
parent 28d89fbb88
commit 76cd0e1771
13 changed files with 341 additions and 27 deletions
+2
View File
@@ -140,6 +140,8 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "Airforce";
case FPS_CAMEATOMO:
return "Came Atomo";
case FPS_CAMETWEE:
return "Came Twee";
case FPS_Invalid:
default:
return "Unknown";
@@ -96,7 +96,7 @@ WeatherView::WeatherView(NavigationView& nav)
&button_clear_list,
&recent_entries_view});
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
baseband::run_image(portapack::spi_flash::image_tag_weather);
button_clear_list.on_select = [this](Button&) {
recent.clear();
+2 -2
View File
@@ -320,12 +320,12 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
}
void set_weather() {
const SubGhzFPRxConfigureMessage message{0, 0};
const SubGhzFPRxConfigureMessage message{0};
send_message(&message);
}
void set_subghzd(uint8_t modulation = 0) {
const SubGhzFPRxConfigureMessage message{1, modulation};
const SubGhzFPRxConfigureMessage message{modulation};
send_message(&message);
}
+9 -2
View File
@@ -550,12 +550,19 @@ set(MODE_CPPSRC
)
DeclareTargets(PWFM wfm_audio)
### SubGhz Decoders + Weather Stations
### SubGhz Decoders
set(MODE_CPPSRC
proc_subghzd.cpp
)
DeclareTargets(PWTH subghzd)
DeclareTargets(PSGD subghzd)
### Weather Stations
set(MODE_CPPSRC
proc_weather.cpp
)
DeclareTargets(PWTH weather)
### Flash Utility
+151
View File
@@ -0,0 +1,151 @@
#ifndef __FPROTO_CAMETWEE_H__
#define __FPROTO_CAMETWEE_H__
#include "subghzdbase.hpp"
typedef enum {
CameTweeDecoderStepReset = 0,
CameTweeDecoderStepDecoderData,
} CameTweeDecoderStep;
class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCameTwee() {
sensorType = FPS_CAMETWEE;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case CameTweeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 51) < te_delta * 20)) {
// Found header CAME
parser_step = CameTweeDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case CameTweeDecoderStepDecoderData:
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 >= ((uint32_t)te_long * 2 + te_delta)) {
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;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = CameTweeDecoderStepReset;
}
} 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 = CameTweeDecoderStepReset;
}
}
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:
uint32_t te_short = 500;
uint32_t te_long = 1000;
uint32_t te_delta = 250;
uint32_t min_count_bit_for_found = 54;
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);
uint32_t data = (uint32_t)(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;
}
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
@@ -15,6 +15,7 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include "s-bett.hpp"
#include "s-came.hpp"
#include "s-came_atomo.hpp"
#include "s-came_twee.hpp"
#ifndef __FPROTO_PROTOLISTSGZ_H__
#define __FPROTO_PROTOLISTSGZ_H__
@@ -28,6 +29,7 @@ class SubGhzDProtos : public FProtoListGeneral {
protos.push_back(std::make_unique<FProtoSubGhzDBett>()); // 3
protos.push_back(std::make_unique<FProtoSubGhzDCame>()); // 4, 5, 6
protos.push_back(std::make_unique<FProtoSubGhzDCameAtomo>()); // 7
protos.push_back(std::make_unique<FProtoSubGhzDCameTwee>()); // 8
// set callback for them
for (const auto& obj : protos) {
@@ -26,6 +26,7 @@ enum FPROTO_SUBGHZD_SENSOR {
FPS_PRASTEL = 5,
FPS_AIRFORCE = 6,
FPS_CAMEATOMO = 7,
FPS_CAMETWEE = 8,
};
#endif
+4 -14
View File
@@ -24,7 +24,7 @@
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
void WeatherProcessor::execute(const buffer_c8_t& buffer) {
void SubGhzDProcessor::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.
@@ -65,28 +65,18 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) {
}
}
void WeatherProcessor::on_message(const Message* const message) {
void SubGhzDProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::WeatherRxConfigure)
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
}
void WeatherProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
void SubGhzDProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
// 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);
if (protoMode != message.protoMode) {
// change it.
FProtoListGeneral* tmp = protoList;
protoList = NULL;
protoMode = message.protoMode;
if (tmp) free(tmp); // takes some time
if (protoMode == 0) protoList = new WeatherProtos();
if (protoMode == 1) protoList = new SubGhzDProtos();
}
modulation = message.modulation; // NIY
if (protoList != NULL) {
@@ -97,7 +87,7 @@ void WeatherProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
}
int main() {
EventDispatcher event_dispatcher{std::make_unique<WeatherProcessor>()};
EventDispatcher event_dispatcher{std::make_unique<SubGhzDProcessor>()};
event_dispatcher.run();
return 0;
}
+2 -3
View File
@@ -32,10 +32,9 @@
#include "message.hpp"
#include "dsp_decimate.hpp"
#include "fprotos/weatherprotos.hpp"
#include "fprotos/subghzdprotos.hpp"
class WeatherProcessor : public BasebandProcessor {
class SubGhzDProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
@@ -65,7 +64,7 @@ class WeatherProcessor : public BasebandProcessor {
uint32_t cnt = 0;
uint32_t tm = 0;
FProtoListGeneral* protoList = NULL; // holds all the protocols we can parse
FProtoListGeneral* protoList = new SubGhzDProtos(); // holds all the protocols we can parse
void configure(const SubGhzFPRxConfigureMessage& message);
/* NB: Threads should be the last members in the class definition. */
+88
View File
@@ -0,0 +1,88 @@
/*
* 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_weather.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
void WeatherProcessor::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)
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 < 10'000'000) // allow pass 'end' signal
{
if (currentDuration < UINT32_MAX) currentDuration += nsPerDecSamp;
} else { // called on change, so send the last duration and dir.
if (protoList) protoList->feed(currentHiLow, currentDuration / 1000);
currentDuration = nsPerDecSamp;
currentHiLow = meashl;
}
}
cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor.
if (cnt > 30'000) {
threshold = (tm / cnt) / 2;
cnt = 0;
tm = 0;
if (threshold < 50) threshold = 50;
if (threshold > 1700) threshold = 1700;
}
}
void WeatherProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::WeatherRxConfigure)
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
}
void WeatherProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
// 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
(void)message; // unused
decim_0.configure(taps_200k_wfm_decim_0.taps);
decim_1.configure(taps_200k_wfm_decim_1.taps);
configured = true;
}
int main() {
EventDispatcher event_dispatcher{std::make_unique<WeatherProcessor>()};
event_dispatcher.run();
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 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_WEATHER_H__
#define __PROC_WEATHER_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "message.hpp"
#include "dsp_decimate.hpp"
#include "fprotos/weatherprotos.hpp"
class WeatherProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4'000'000; // it works, I think we need to write that master clock in the baseband_threat , even later we decimate it.
static constexpr uint32_t nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // 10 exp9/baseband_fs * 8
/* 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};
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;
FProtoListGeneral* protoList = new WeatherProtos(); // holds all the protocols we can parse
void configure(const SubGhzFPRxConfigureMessage& 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_WEATHER_H__*/
+2 -4
View File
@@ -1241,11 +1241,9 @@ class SpectrumPainterBufferConfigureResponseMessage : public Message {
class SubGhzFPRxConfigureMessage : public Message {
public:
constexpr SubGhzFPRxConfigureMessage(uint8_t protoMode = 0, uint8_t modulation = 0)
: Message{ID::WeatherRxConfigure}, protoMode{protoMode}, modulation{modulation} {
// todoh give some more info
constexpr SubGhzFPRxConfigureMessage(uint8_t modulation = 0)
: Message{ID::WeatherRxConfigure}, modulation{modulation} {
}
uint8_t protoMode = 0; // 0 weather, 1 subhgzd
uint8_t modulation = 0; // 0 am, 1 fm
};
+2 -1
View File
@@ -112,7 +112,8 @@ constexpr image_tag_t image_tag_tones{'P', 'T', 'O', 'N'};
constexpr image_tag_t image_tag_flash_utility{'P', 'F', 'U', 'T'};
constexpr image_tag_t image_tag_usb_sd{'P', 'U', 'S', 'B'};
constexpr image_tag_t image_tag_subghzd{'P', 'W', 'T', 'H'};
constexpr image_tag_t image_tag_weather{'P', 'W', 'T', 'H'};
constexpr image_tag_t image_tag_subghzd{'P', 'S', 'G', 'D'};
constexpr image_tag_t image_tag_noop{'P', 'N', 'O', 'P'};