mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-07-15 23:28:30 +00:00
Merge pull request #135 from euquiq/radiosonde-vaisala-rs41-decoding
Radiosonde-app-Vaisala-rs41-decoding
This commit is contained in:
commit
40785e8094
@ -1,6 +1,6 @@
|
|||||||
# PortaPack Mayhem
|
# PortaPack Mayhem
|
||||||
|
|
||||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://app.buddy.works/eried/portapack/pipelines/pipeline/252276) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3) [](https://www.bountysource.com/teams/portapack-mayhem/issues)
|
[](https://travis-ci.com/eried/portapack-mayhem) [](https://app.buddy.works/eried/portapack/pipelines/pipeline/252276) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3) [](https://www.bountysource.com/teams/portapack-mayhem/issues)
|
||||||
|
|
||||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
|
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ SondeView::SondeView(NavigationView& nav) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
field_frequency.set_value(target_frequency_);
|
field_frequency.set_value(target_frequency_);
|
||||||
field_frequency.set_step(10000);
|
field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning
|
||||||
field_frequency.on_change = [this](rf::Frequency f) {
|
field_frequency.on_change = [this](rf::Frequency f) {
|
||||||
set_target_frequency(f);
|
set_target_frequency(f);
|
||||||
field_frequency.set_value(f);
|
field_frequency.set_value(f);
|
||||||
@ -86,12 +86,12 @@ SondeView::SondeView(NavigationView& nav) {
|
|||||||
|
|
||||||
button_see_map.on_select = [this, &nav](Button&) {
|
button_see_map.on_select = [this, &nav](Button&) {
|
||||||
nav.push<GeoMapView>(
|
nav.push<GeoMapView>(
|
||||||
"",
|
sonde_id,
|
||||||
altitude,
|
gps_info.alt,
|
||||||
GeoPos::alt_unit::METERS,
|
GeoPos::alt_unit::METERS,
|
||||||
latitude,
|
gps_info.lat,
|
||||||
longitude,
|
gps_info.lon,
|
||||||
0);
|
999); //set a dummy heading out of range to draw a cross...probably not ideal?
|
||||||
};
|
};
|
||||||
|
|
||||||
logger = std::make_unique<SondeLogger>();
|
logger = std::make_unique<SondeLogger>();
|
||||||
@ -113,16 +113,15 @@ void SondeView::on_packet(const sonde::Packet& packet) {
|
|||||||
//const auto hex_formatted = packet.symbols_formatted();
|
//const auto hex_formatted = packet.symbols_formatted();
|
||||||
|
|
||||||
text_signature.set(packet.type_string());
|
text_signature.set(packet.type_string());
|
||||||
text_serial.set(packet.serial_number());
|
sonde_id = packet.serial_number(); //used also as tag on the geomap
|
||||||
|
text_serial.set(sonde_id);
|
||||||
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V");
|
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V");
|
||||||
|
|
||||||
altitude = packet.GPS_altitude();
|
gps_info = packet.get_GPS_data();
|
||||||
latitude = packet.GPS_latitude();
|
|
||||||
longitude = packet.GPS_longitude();
|
|
||||||
|
|
||||||
geopos.set_altitude(altitude);
|
geopos.set_altitude(gps_info.alt);
|
||||||
geopos.set_lat(latitude);
|
geopos.set_lat(gps_info.lat);
|
||||||
geopos.set_lon(longitude);
|
geopos.set_lon(gps_info.lon);
|
||||||
|
|
||||||
if (logger && logging) {
|
if (logger && logging) {
|
||||||
logger->on_packet(packet);
|
logger->on_packet(packet);
|
||||||
|
@ -65,11 +65,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<SondeLogger> logger { };
|
std::unique_ptr<SondeLogger> logger { };
|
||||||
uint32_t target_frequency_ { 402000000 };
|
uint32_t target_frequency_ { 402700000 };
|
||||||
bool logging { false };
|
bool logging { false };
|
||||||
int32_t altitude { 0 };
|
sonde::GPS_data gps_info;
|
||||||
float latitude { 0 };
|
std::string sonde_id;
|
||||||
float longitude { 0 };
|
|
||||||
|
|
||||||
Labels labels {
|
Labels labels {
|
||||||
{ { 0 * 8, 2 * 16 }, "Signature:", Color::light_grey() },
|
{ { 0 * 8, 2 * 16 }, "Signature:", Color::light_grey() },
|
||||||
|
@ -445,7 +445,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
|||||||
{ "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push<AnalogTvView>(); } },
|
{ "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push<AnalogTvView>(); } },
|
||||||
{ "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
|
{ "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
|
||||||
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
|
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
|
||||||
{ "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
|
{ "Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
|
||||||
{ "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push<TPMSAppView>(); } },
|
{ "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push<TPMSAppView>(); } },
|
||||||
/*{ "APRS", ui::Color::dark_grey(), &bitmap_icon_aprs, [&nav](){ nav.push<NotImplementedView>(); } },
|
/*{ "APRS", ui::Color::dark_grey(), &bitmap_icon_aprs, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||||
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
|
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||||
|
@ -140,7 +140,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_fsk_4800_Vaisala {
|
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_fsk_4800_Vaisala {
|
||||||
{ 0b00001000011011010101001110001000, 32, 1 },
|
{ 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet)
|
||||||
|
//{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes.
|
||||||
{ },
|
{ },
|
||||||
{ 320 * 8 },
|
{ 320 * 8 },
|
||||||
[this](const baseband::Packet& packet) {
|
[this](const baseband::Packet& packet) {
|
||||||
|
@ -22,9 +22,25 @@
|
|||||||
|
|
||||||
#include "sonde_packet.hpp"
|
#include "sonde_packet.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
//#include <complex>
|
||||||
|
|
||||||
namespace sonde {
|
namespace sonde {
|
||||||
|
|
||||||
|
//Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c
|
||||||
|
#define MASK_LEN 64
|
||||||
|
#define pos_FrameNb 0x37 //0x03B // 2 byte
|
||||||
|
#define pos_SondeID 0x39 //0x03D // 8 byte
|
||||||
|
#define pos_Voltage 0x041 //0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v
|
||||||
|
#define pos_CalData 0x04E //0x052 // 1 byte, counter 0x00..0x32
|
||||||
|
#define pos_GPSweek 0x091 //0x095 // 2 byte
|
||||||
|
#define pos_GPSTOW 0x093 //0x097 // 4 byte
|
||||||
|
#define pos_GPSecefX 0x110 //0x114 // 4 byte
|
||||||
|
#define pos_GPSecefY 0x114 //0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop)
|
||||||
|
#define pos_GPSecefZ 0x118 //0x11C // 4 byte (same as Y)
|
||||||
|
|
||||||
|
#define PI 3.1415926535897932384626433832795 //3.1416 //(3.1415926535897932384626433832795)
|
||||||
|
|
||||||
Packet::Packet(
|
Packet::Packet(
|
||||||
const baseband::Packet& packet,
|
const baseband::Packet& packet,
|
||||||
const Type type
|
const Type type
|
||||||
@ -60,37 +76,65 @@ Packet::Type Packet::type() const {
|
|||||||
return type_;
|
return type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*uint8_t Packet::vaisala_descramble(const uint32_t pos) {
|
//euquiq here:
|
||||||
return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63];
|
//RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame")
|
||||||
};*/
|
//The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp)
|
||||||
|
|
||||||
uint32_t Packet::GPS_altitude() const {
|
|
||||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
uint8_t Packet::vaisala_descramble(const uint32_t pos) const {
|
||||||
return (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
|
//return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63];
|
||||||
else if (type_ == Type::Vaisala_RS41_SG) {
|
// packet_[i]; its a bit; packet_.size the total (should be 2560 bits)
|
||||||
/*uint32_t altitude_ecef = 0;
|
uint8_t value = 0;
|
||||||
for (uint32_t i = 0; i < 4; i++)
|
for (uint8_t i = 0; i < 8; i++)
|
||||||
altitude_ecef = (altitude_ecef << 8) + vaisala_descramble(0x11C + i);*/
|
value = (value << 1) | packet_[(pos * 8) + (7 -i)]; //get the byte from the bits collection
|
||||||
// TODO: and a bunch of maths (see ecef2elli() from RS1729)
|
|
||||||
return 0;
|
//packetReader reader { packet_ }; //This works just as above.
|
||||||
} else
|
//value = reader.read(pos * 8,8);
|
||||||
return 0; // Unknown
|
//shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature
|
||||||
|
uint32_t mask_pos = pos + 4;
|
||||||
|
value = value ^ vaisala_mask[mask_pos % MASK_LEN]; //descramble with the xor pseudorandom table
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS_data Packet::get_GPS_data() const {
|
||||||
|
GPS_data result;
|
||||||
|
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) {
|
||||||
|
|
||||||
|
result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
|
||||||
|
result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||||
|
result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
|
||||||
|
|
||||||
|
} else if (type_ == Type::Vaisala_RS41_SG) {
|
||||||
|
|
||||||
|
uint8_t XYZ_bytes[4];
|
||||||
|
int32_t XYZ; // 32bit
|
||||||
|
double_t X[3];
|
||||||
|
for (int32_t k = 0; k < 3; k++) { //Get X,Y,Z ECEF position from GPS
|
||||||
|
for (int32_t i = 0; i < 4; i++) //each one is 4 bytes (32 bits)
|
||||||
|
XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4*k) + i);
|
||||||
|
memcpy(&XYZ, XYZ_bytes, 4);
|
||||||
|
X[k] = XYZ / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Packet::GPS_latitude() const {
|
double_t a = 6378137.0;
|
||||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
double_t b = 6356752.31424518;
|
||||||
return reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
|
double_t e = sqrt( (a*a - b*b) / (a*a) );
|
||||||
//else if (type_ == Type::Vaisala_RS41_SG)
|
double_t ee = sqrt( (a*a - b*b) / (b*b) );
|
||||||
// return vaisala_descramble();
|
|
||||||
else
|
|
||||||
return 0; // Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
float Packet::GPS_longitude() const {
|
double_t lam = atan2( X[1] , X[0] );
|
||||||
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
|
double_t p = sqrt( X[0]*X[0] + X[1]*X[1] );
|
||||||
return reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
|
double_t t = atan2( X[2]*a , p*b );
|
||||||
else
|
double_t phi = atan2( X[2] + ee*ee * b * sin(t)*sin(t)*sin(t) ,
|
||||||
return 0; // Unknown
|
p - e*e * a * cos(t)*cos(t)*cos(t) );
|
||||||
|
|
||||||
|
double_t R = a / sqrt( 1 - e*e*sin(phi)*sin(phi) );
|
||||||
|
|
||||||
|
result.alt = p / cos(phi) - R;
|
||||||
|
result.lat = phi*180/PI;
|
||||||
|
result.lon = lam*180/PI;
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Packet::battery_voltage() const {
|
uint32_t Packet::battery_voltage() const {
|
||||||
@ -98,9 +142,14 @@ uint32_t Packet::battery_voltage() const {
|
|||||||
return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150;
|
return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150;
|
||||||
else if (type_ == Type::Meteomodem_M2K2)
|
else if (type_ == Type::Meteomodem_M2K2)
|
||||||
return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8
|
return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8
|
||||||
else
|
else if (type_ == Type::Vaisala_RS41_SG) {
|
||||||
|
uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; //byte 69 = voltage * 10 (check if this value needs to be multiplied)
|
||||||
|
return voltage;
|
||||||
|
}
|
||||||
|
else {
|
||||||
return 0; // Unknown
|
return 0; // Unknown
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string Packet::type_string() const {
|
std::string Packet::type_string() const {
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
@ -127,13 +176,34 @@ std::string Packet::serial_number() const {
|
|||||||
to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) +
|
to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) +
|
||||||
to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0');
|
to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0');
|
||||||
|
|
||||||
|
} else if(type() == Type::Vaisala_RS41_SG) {
|
||||||
|
std::string serial_id = "";
|
||||||
|
uint8_t achar;
|
||||||
|
for (uint8_t i=0; i<8; i++) { //euquiq: Serial ID is 8 bytes long, each byte a char
|
||||||
|
achar = vaisala_descramble(pos_SondeID + i);
|
||||||
|
if (achar < 32 || achar > 126) return "?"; //Maybe there are ids with less than 8 bytes and this is not OK.
|
||||||
|
serial_id += (char)achar;
|
||||||
|
}
|
||||||
|
return serial_id;
|
||||||
} else
|
} else
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
FormattedSymbols Packet::symbols_formatted() const {
|
FormattedSymbols Packet::symbols_formatted() const {
|
||||||
|
if (type() == Type::Vaisala_RS41_SG) { //Euquiq: now we distinguish different types
|
||||||
|
uint32_t bytes = packet_.size() / 8; //Need the byte amount, which if full, it SHOULD be 320 size() should return 2560
|
||||||
|
std::string hex_data;
|
||||||
|
std::string hex_error;
|
||||||
|
hex_data.reserve(bytes * 2); //2 hexa chars per byte
|
||||||
|
hex_error.reserve(1);
|
||||||
|
for (uint32_t i=0; i < bytes; i++) //log will show the packet starting on the last 4 bytes from signature 93DF1A60
|
||||||
|
hex_data += to_string_hex(vaisala_descramble(i),2);
|
||||||
|
return { hex_data, hex_error };
|
||||||
|
|
||||||
|
} else {
|
||||||
return format_symbols(decoder_);
|
return format_symbols(decoder_);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Packet::crc_ok() const {
|
bool Packet::crc_ok() const {
|
||||||
switch(type()) {
|
switch(type()) {
|
||||||
|
@ -32,6 +32,12 @@
|
|||||||
|
|
||||||
namespace sonde {
|
namespace sonde {
|
||||||
|
|
||||||
|
struct GPS_data {
|
||||||
|
uint32_t alt { 0 };
|
||||||
|
float lat { 0 };
|
||||||
|
float lon { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
class Packet {
|
class Packet {
|
||||||
public:
|
public:
|
||||||
enum class Type : uint32_t {
|
enum class Type : uint32_t {
|
||||||
@ -56,9 +62,7 @@ public:
|
|||||||
std::string serial_number() const;
|
std::string serial_number() const;
|
||||||
uint32_t battery_voltage() const;
|
uint32_t battery_voltage() const;
|
||||||
|
|
||||||
uint32_t GPS_altitude() const;
|
GPS_data get_GPS_data() const;
|
||||||
float GPS_latitude() const;
|
|
||||||
float GPS_longitude() const;
|
|
||||||
|
|
||||||
FormattedSymbols symbols_formatted() const;
|
FormattedSymbols symbols_formatted() const;
|
||||||
|
|
||||||
@ -76,13 +80,16 @@ private:
|
|||||||
0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1
|
0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1
|
||||||
};
|
};
|
||||||
|
|
||||||
//uint8_t vaisala_descramble(const uint32_t pos);
|
GPS_data ecef_to_gps() const;
|
||||||
|
|
||||||
|
uint8_t vaisala_descramble(uint32_t pos) const;
|
||||||
|
|
||||||
const baseband::Packet packet_;
|
const baseband::Packet packet_;
|
||||||
const BiphaseMDecoder decoder_;
|
const BiphaseMDecoder decoder_;
|
||||||
const FieldReader<BiphaseMDecoder, BitRemapNone> reader_bi_m;
|
const FieldReader<BiphaseMDecoder, BitRemapNone> reader_bi_m;
|
||||||
Type type_;
|
Type type_;
|
||||||
|
|
||||||
|
using packetReader = FieldReader<baseband::Packet, BitRemapByteReverse>; //baseband::Packet instead of BiphaseMDecoder
|
||||||
bool crc_ok_M10() const;
|
bool crc_ok_M10() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user