常规更新

This commit is contained in:
2026-06-26 17:56:28 +08:00
parent c66cb28604
commit e9c68c5e8e
5 changed files with 684 additions and 96 deletions
+1
View File
@@ -1 +1,2 @@
/readsb/
/old/
-1
View File
@@ -435,7 +435,6 @@ D_BDS(60) // 航向和速度报告
// Commb MS Meteorological services
D_BDS(44) // 气象例行空气报告
D_BDS(45) // 气象灾害报告
// TODO: 1111
#define CE(cond, msg) \
if (cond) { \
static Log_Type log_type({}, {{"POS", LOG_POS}}); \
+673 -90
View File
@@ -1,4 +1,5 @@
#include "./Common.h"
#include "Core/Statistics/Frequency_Limit.h"
#include "SSR/CPR/CPR.h"
#undef max
#undef min
@@ -1957,118 +1958,700 @@ std::string infer(const std::string& msg_bin, bool mrar) {
});
return result.empty() ? "" : std::string(result);
}
std::vector<std::string> infer_old(const std::string& msg, bool mrar) {
auto df = SSR::decode_df(msg);
if (allzeros(msg)) {
return {};
static bool bds_bit(const std::string& msg, int bit) {
return msg[bit - 1] == '1';
}
static unsigned long long bds_bits(const std::string& msg, int first, int last) {
return bin2<unsigned long long>(msg.substr(first - 1, last - first + 1));
}
static int bds10_score(const std::string& msg) {
if (bds_bits(msg, 1, 8) != 0x10) {
return 0;
}
// For ADS-B / Mode-S extended squitter
if (df == Downlink_Format::Extended_Squitter_17) {
auto tc = type_code(msg);
if (!tc) {
return {};
if (bds_bits(msg, 10, 14) != 0) {
return 0;
}
return 56;
}
static int bds17_score(const std::string& msg) {
if (bds_bits(msg, 25, 56) != 0) {
return 0;
}
int score = 0;
if (bds_bit(msg, 7)) {
score += 1;
}
else {
score -= 2;
}
if (bds_bit(msg, 10)) {
score -= 2;
}
if (bds_bit(msg, 11)) {
score -= 2;
}
if (bds_bit(msg, 12)) {
score -= 2;
}
if (bds_bit(msg, 13)) {
score -= 2;
}
if (bds_bit(msg, 14)) {
score -= 2;
}
if (bds_bit(msg, 20)) {
score -= 2;
}
if (bds_bit(msg, 21)) {
score -= 2;
}
if (bds_bit(msg, 22)) {
score -= 2;
}
if (bds_bit(msg, 1) && bds_bit(msg, 2) && bds_bit(msg, 3) && bds_bit(msg, 4) && bds_bit(msg, 5)) {
score += 5;
if (bds_bit(msg, 6)) {
score += 1;
}
if (tc >= 1 && tc <= 4) return {"08"}; // identification and category
if (tc >= 5 && tc <= 8) return {"06"}; // surface movement
if (tc >= 9 && tc <= 18) return {"05"}; // airborne position, baro-alt
if (tc == 19) return {"09"}; // airborne velocity
if (tc >= 20 && tc <= 22) return {"05"}; // airborne position, gnss-alt
if (tc == 28) return {"61"}; // aircraft status
if (tc == 29) return {"62"}; // target state and status
if (tc == 31) return {"65"}; // operational status
}
// For Comm-B replies
bool IS10 = is10(msg);
bool IS17 = is17(msg);
bool IS20 = is20(msg);
bool IS30 = is30(msg);
bool IS40 = is40(msg);
bool IS50 = is50(msg);
bool IS60 = is60(msg);
bool IS44 = is44(msg);
bool IS45 = is45(msg);
std::vector<std::string> bds_result;
if (mrar) {
std::array<std::string, 9> allbds = {
"10", "17", "20", "30", "40",
"44", "45", "50", "60"
};
bool mask[] = {IS10, IS17, IS20, IS30, IS40, IS44, IS45, IS50, IS60};
for (size_t i = 0; i < 9; ++i) {
if (mask[i]) {
bds_result.push_back(allbds[i]);
}
else if (!bds_bit(msg, 1) && !bds_bit(msg, 2) && !bds_bit(msg, 3) && !bds_bit(msg, 4) && !bds_bit(msg, 5) && !bds_bit(msg, 6)) {
score += 1;
}
else {
score -= 12;
}
if (bds_bit(msg, 16) && bds_bit(msg, 24)) {
score += 2;
if (bds_bit(msg, 9)) {
score += 1;
}
}
else if (!bds_bit(msg, 16) && !bds_bit(msg, 24) && !bds_bit(msg, 9)) {
score += 1;
}
else {
score -= 6;
}
return score;
}
static int bds20_score(const std::string& msg) {
if (bds_bits(msg, 1, 8) != 0x20) {
return 0;
}
const std::string chars = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?";
int score = 8;
for (int i = 0; i < 8; ++i) {
char ch = chars[bds_bits(msg, 9 + i * 6, 14 + i * 6)];
if ((ch >= 'A' && ch <= 'Z') || (ch >= '-' && ch <= '9') || ch == ' ' || ch == '@') {
score += 6;
}
else {
return 0;
}
}
return score;
}
static int bds30_score(const std::string& msg) {
if (bds_bits(msg, 1, 8) != 0x30) {
return 0;
}
return 56;
}
static int bds40_score(const std::string& msg) {
unsigned mcp_valid = bds_bit(msg, 1);
unsigned mcp_raw = bds_bits(msg, 2, 13);
unsigned fms_valid = bds_bit(msg, 14);
unsigned fms_raw = bds_bits(msg, 15, 26);
unsigned baro_valid = bds_bit(msg, 27);
unsigned baro_raw = bds_bits(msg, 28, 39);
unsigned reserved_1 = bds_bits(msg, 40, 47);
unsigned mode_valid = bds_bit(msg, 48);
unsigned mode_raw = bds_bits(msg, 49, 51);
unsigned reserved_2 = bds_bits(msg, 52, 53);
unsigned source_valid = bds_bit(msg, 54);
unsigned source_raw = bds_bits(msg, 55, 56);
if (!mcp_valid && !fms_valid && !baro_valid && !mode_valid && !source_valid) {
return 0;
}
int score = 0;
unsigned mcp_alt = 0;
if (mcp_valid && mcp_raw != 0) {
mcp_alt = mcp_raw * 16;
if (mcp_alt >= 1000 && mcp_alt <= 50000) {
score += 13;
}
else {
return 0;
}
}
else if (!mcp_valid && mcp_raw == 0) {
score += 1;
}
else {
return 0;
}
unsigned fms_alt = 0;
if (fms_valid && fms_raw != 0) {
fms_alt = fms_raw * 16;
if (fms_alt >= 1000 && fms_alt <= 50000) {
score += 13;
}
else {
return 0;
}
}
else if (!fms_valid && fms_raw == 0) {
score += 1;
}
else {
return 0;
}
double baro_setting = 0;
if (baro_valid && baro_raw != 0) {
baro_setting = 800 + baro_raw * 0.1;
if (baro_setting >= 900 && baro_setting <= 1100) {
score += 13;
}
else {
return 0;
}
}
else if (!baro_valid && baro_raw == 0) {
score += 1;
}
else {
return 0;
}
if (reserved_1 != 0) {
return 0;
}
if (mode_valid) {
score += 4;
}
else if (!mode_valid && mode_raw == 0) {
score += 1;
}
else {
return 0;
}
if (reserved_2 != 0) {
return 0;
}
if (source_valid) {
score += 3;
}
else if (!source_valid && source_raw == 0) {
score += 1;
}
else {
return 0;
}
if (mcp_valid && fms_valid && mcp_alt != fms_alt) {
score -= 4;
}
if (mcp_valid) {
unsigned remainder = mcp_alt % 500;
if (!(remainder < 16 || remainder > 484)) {
score -= 4;
}
}
if (fms_valid) {
unsigned remainder = fms_alt % 500;
if (!(remainder < 16 || remainder > 484)) {
score -= 4;
}
}
return score;
}
static int bds50_score(const std::string& msg) {
unsigned roll_valid = bds_bit(msg, 1);
unsigned roll_sign = bds_bit(msg, 2);
unsigned roll_raw = bds_bits(msg, 3, 11);
unsigned track_valid = bds_bit(msg, 12);
unsigned track_sign = bds_bit(msg, 13);
unsigned track_raw = bds_bits(msg, 14, 23);
unsigned gs_valid = bds_bit(msg, 24);
unsigned gs_raw = bds_bits(msg, 25, 34);
unsigned track_rate_valid = bds_bit(msg, 35);
unsigned track_rate_sign = bds_bit(msg, 36);
unsigned track_rate_raw = bds_bits(msg, 37, 45);
unsigned tas_valid = bds_bit(msg, 46);
unsigned tas_raw = bds_bits(msg, 47, 56);
if (!roll_valid || !track_valid || !gs_valid || !tas_valid) {
return 0;
}
int score = 0;
double roll = 0;
if (roll_valid) {
roll = roll_raw * 45.0 / 256.0;
if (roll_sign) {
roll -= 90.0;
}
if (roll >= -40 && roll < 40) {
score += 11;
}
else {
return 0;
}
}
else if (!roll_valid && roll_raw == 0 && !roll_sign) {
score += 1;
}
else {
return 0;
}
if (track_valid) {
score += 12;
}
else if (!track_valid && track_raw == 0 && !track_sign) {
score += 1;
}
else {
return 0;
}
unsigned gs = 0;
if (gs_valid && gs_raw != 0) {
gs = gs_raw * 2;
if (gs >= 50 && gs <= 700) {
score += 11;
}
else {
return 0;
}
}
else if (!gs_valid && gs_raw == 0) {
score += 1;
}
else {
return 0;
}
double track_rate = 0;
if (track_rate_valid) {
track_rate = track_rate_raw * 8.0 / 256.0;
if (track_rate_sign) {
track_rate -= 16;
}
if (track_rate >= -10.0 && track_rate <= 10.0) {
score += 11;
}
else {
return 0;
}
}
else if (!track_rate_valid && track_rate_raw == 0 && !track_rate_sign) {
score += 1;
}
else {
return 0;
}
unsigned tas = 0;
if (tas_valid && tas_raw != 0) {
tas = tas_raw * 2;
if (tas >= 50 && tas <= 700) {
score += 11;
}
else {
return 0;
}
}
else if (!tas_valid && tas_raw == 0) {
score += 1;
}
else {
return 0;
}
if (gs_valid && tas_valid) {
int delta = abs((int)gs_valid - (int)tas_valid);
if (delta > 150) {
score -= 6;
}
}
if (roll_valid && tas_valid && tas > 0 && track_rate_valid) {
double turn_rate = 68625 * tan(roll * PI / 180.0) / (tas * 20 * PI);
double delta = fabs(turn_rate - track_rate);
if (delta > 2.0) {
score -= 6;
}
}
return score;
}
static int bds60_score(const std::string& msg) {
unsigned heading_valid = bds_bit(msg, 1);
unsigned heading_sign = bds_bit(msg, 2);
unsigned heading_raw = bds_bits(msg, 3, 12);
unsigned ias_valid = bds_bit(msg, 13);
unsigned ias_raw = bds_bits(msg, 14, 23);
unsigned mach_valid = bds_bit(msg, 24);
unsigned mach_raw = bds_bits(msg, 25, 34);
unsigned baro_rate_valid = bds_bit(msg, 35);
unsigned baro_rate_sign = bds_bit(msg, 36);
unsigned baro_rate_raw = bds_bits(msg, 37, 45);
unsigned inertial_rate_valid = bds_bit(msg, 46);
unsigned inertial_rate_sign = bds_bit(msg, 47);
unsigned inertial_rate_raw = bds_bits(msg, 48, 56);
if (!heading_valid || !ias_valid || !mach_valid || (!baro_rate_valid && !inertial_rate_valid)) {
return 0;
}
int score = 0;
if (heading_valid) {
score += 12;
}
else if (!heading_valid && heading_raw == 0 && !heading_sign) {
score += 1;
}
else {
return 0;
}
if (ias_valid && ias_raw != 0) {
if (ias_raw >= 50 && ias_raw <= 700) {
score += 11;
}
else {
return 0;
}
}
else if (!ias_valid && ias_raw == 0) {
score += 1;
}
else {
return 0;
}
double mach = 0;
if (mach_valid && mach_raw != 0) {
mach = mach_raw * 2.048 / 512;
if (mach >= 0.1 && mach <= 0.9) {
score += 11;
}
else {
return 0;
}
}
else if (!mach_valid && mach_raw == 0) {
score += 1;
}
else {
return 0;
}
int baro_rate = 0;
if (baro_rate_valid) {
baro_rate = baro_rate_raw * 32;
if (baro_rate_sign) {
baro_rate -= 16384;
}
if (baro_rate >= -6000 && baro_rate <= 6000) {
score += 11;
}
else {
return 0;
}
}
else if (!baro_rate_valid && baro_rate_raw == 0) {
score += 1;
}
else {
return 0;
}
int inertial_rate = 0;
if (inertial_rate_valid) {
inertial_rate = inertial_rate_raw * 32;
if (inertial_rate_sign) {
inertial_rate -= 16384;
}
if (inertial_rate >= -6000 && inertial_rate <= 6000) {
score += 11;
}
else {
return 0;
}
}
else if (!inertial_rate_valid && inertial_rate_raw == 0) {
score += 1;
}
else {
return 0;
}
if (baro_rate_valid && inertial_rate_valid) {
int delta = abs(baro_rate - inertial_rate);
if (delta > 2000) {
score -= 12;
}
}
return score;
}
static int bds44_score(const std::string& msg) {
unsigned source = bds_bits(msg, 1, 4);
unsigned wind_valid = bds_bit(msg, 5);
unsigned wind_speed_raw = bds_bits(msg, 6, 14);
unsigned wind_direction_raw = bds_bits(msg, 15, 23);
unsigned temperature_sign = bds_bit(msg, 24);
unsigned static_air_temperature_raw = bds_bits(msg, 25, 34);
unsigned pressure_valid = bds_bit(msg, 35);
unsigned static_pressure_raw = bds_bits(msg, 36, 46);
unsigned turbulence_valid = bds_bit(msg, 47);
unsigned turbulence_raw = bds_bits(msg, 48, 49);
unsigned humidity_valid = bds_bit(msg, 50);
unsigned humidity_raw = bds_bits(msg, 51, 56);
int score = 0;
if (source <= 6) {
score += 4;
}
else {
return 0;
}
if (wind_valid) {
if (wind_speed_raw <= 511) {
score += 9;
}
else {
return 0;
}
double wind_direction = wind_direction_raw * 180.0 / 256.0;
if (wind_direction >= 0 && wind_direction <= 360) {
score += 9;
}
else {
return 0;
}
}
else {
std::array<std::string, 7> allbds = {
"10", "17", "20", "30",
"40", "50", "60"
};
bool mask[] = {IS10, IS17, IS20, IS30, IS40, IS50, IS60};
for (size_t i = 0; i < 7; ++i) {
if (mask[i]) {
bds_result.push_back(allbds[i]);
}
score += 2;
}
double temperature = temperature_sign ? (static_air_temperature_raw - pow(2, 10)) * 0.25 : static_air_temperature_raw * 0.25;
if (temperature >= -128 && temperature <= 128) {
score += 10;
}
else {
return 0;
}
if (pressure_valid) {
if (static_pressure_raw <= 2048) {
score += 11;
}
else {
return 0;
}
}
return bds_result;
else {
score += 1;
}
if (turbulence_valid) {
if (turbulence_raw <= 3) {
score += 2;
}
else {
return 0;
}
}
else {
score += 1;
}
if (humidity_valid) {
double humidity = humidity_raw * (100.0 / 64);
if (humidity >= 0 && humidity <= 100) {
score += 6;
}
else {
return 0;
}
}
else {
score += 1;
}
return score;
}
// std::vector<std::string> infer2(const std::string& msg, bool mrar) {
// auto df = SSR::decode_df(msg);
// if (allzeros(msg)) {
// return {};
// }
// if (df == Downlink_Format::Extended_Squitter_17) {
// auto tc = type_code(msg);
// if (!tc) {
// return {};
// }
// if (tc >= 1 && tc <= 4) return {"08"}; // identification and category
// if (tc >= 5 && tc <= 8) return {"06"}; // surface movement
// if (tc >= 9 && tc <= 18) return {"05"}; // airborne position, baro-alt
// if (tc == 19) return {"09"}; // airborne velocity
// if (tc >= 20 && tc <= 22) return {"05"}; // airborne position, gnss-alt
// if (tc == 28) return {"61"}; // aircraft status
// if (tc == 29) return {"62"}; // target state and status
// if (tc == 31) return {"65"}; // operational status
// }
// if (df != Downlink_Format::Comm_B_Altitude_Reply_20 && df != Downlink_Format::Comm_B_Identity_Reply_21) {
// static Frequency_Limit mt;
// if (mt.test()) {
// std::cout << std::format("非20 21 异常舍弃 hex: {} bin:{} \n", bin2hex(msg), msg);
// }
// return {};
// }
// // if (bin2<int>(msg.substr(8, 5)) != 0 || bin2<int>(msg.substr(13, 6)) != 0) {
// // static Frequency_Limit mt;
// // // if (mt.test()) {
// // std::cout << std::format("异常舍弃 hex: {} bin:{} \n", bin2hex(msg), msg);
// // // }
// // return {};
// // }
// std::string data = databin(msg);
// if (allzeros(data)) {
// static Frequency_Limit mt;
// if (mt.test()) {
// std::cout << std::format("bds解码出了allzeros hex: {} bin:{} \n", bin2hex(msg), msg);
// }
// return {};
// }
// struct Candidate {
// const char *name;
// int (*score)(const std::string&);
// };
// std::vector<Candidate> candidates = {
// {"10", bds10_score},
// {"20", bds20_score},
// {"30", bds30_score},
// {"17", bds17_score},
// {"40", bds40_score},
// {"50", bds50_score},
// {"60", bds60_score}
// };
// if (true) {
// candidates.push_back({"44", bds44_score});
// }
// int best_score = 0;
// const char *best_name = nullptr;
// bool ambiguous = false;
// for (const auto& candidate : candidates) {
// int score = candidate.score(data);
// if (score > best_score) {
// best_score = score;
// best_name = candidate.name;
// ambiguous = false;
// }
// else if (score == best_score && score > 0) {
// ambiguous = true;
// }
// }
// if (!best_name || ambiguous) {
// static Frequency_Limit mt;
// if (mt.test()) {
// std::cout << std::format("bds解码出了ambiguous hex: {} bin:{} \n", bin2hex(msg), msg);
// }
// return {};
// }
// static Frequency_Limit_Multi mt(0.001);
// if (mt.test(best_name)) {
// std::cout << std::format("bds解码出了{} \n", best_name);
// }
// return {best_name};
// }
std::vector<std::string> infer2(const std::string& msg, bool mrar) {
auto df = SSR::decode_df(msg);
if (allzeros(msg)) {
return {};
}
// For ADS-B / Mode-S extended squitter
if (df == Downlink_Format::Extended_Squitter_17) {
auto tc = type_code(msg);
if (!tc) {
return {};
}
if (tc >= 1 && tc <= 4) return {"08"}; // identification and category
if (tc >= 5 && tc <= 8) return {"06"}; // surface movement
if (tc >= 9 && tc <= 18) return {"05"}; // airborne position, baro-alt
if (tc == 19) return {"09"}; // airborne velocity
if (tc >= 20 && tc <= 22) return {"05"}; // airborne position, gnss-alt
if (tc == 28) return {"61"}; // aircraft status
if (tc == 29) return {"62"}; // target state and status
if (tc == 31) return {"65"}; // operational status
}
// For Comm-B replies
bool IS10 = is10(msg);
bool IS17 = is17(msg);
bool IS20 = is20(msg);
bool IS30 = is30(msg);
bool IS40 = is40(msg);
bool IS50 = is50(msg);
bool IS60 = is60(msg);
bool IS44 = is44(msg);
bool IS45 = is45(msg);
std::vector<std::string> bds_result;
if (mrar) {
std::array<std::string, 9> allbds = {
"10", "17", "20", "30", "40",
"44", "45", "50", "60"
};
bool mask[] = {IS10, IS17, IS20, IS30, IS40, IS44, IS45, IS50, IS60};
for (size_t i = 0; i < 9; ++i) {
if (mask[i]) {
bds_result.push_back(allbds[i]);
}
if (tc >= 1 && tc <= 4) {
return {"08"};
}
if (tc >= 5 && tc <= 8) {
return {"06"};
}
if (tc >= 9 && tc <= 18) {
return {"05"};
}
if (tc == 19) {
return {"09"};
}
if (tc >= 20 && tc <= 22) {
return {"05"};
}
if (tc == 28) {
return {"61"};
}
if (tc == 29) {
return {"62"};
}
if (tc == 31) {
return {"65"};
}
}
else {
std::array<std::string, 7> allbds = {
"10", "17", "20", "30",
"40", "50", "60"
};
bool mask[] = {IS10, IS17, IS20, IS30, IS40, IS50, IS60};
for (size_t i = 0; i < 7; ++i) {
if (mask[i]) {
bds_result.push_back(allbds[i]);
}
if (df != Downlink_Format::Comm_B_Altitude_Reply_20 && df != Downlink_Format::Comm_B_Identity_Reply_21) {
static Frequency_Limit mt;
if (mt.test()) {
std::cout << std::format("非20 21 异常舍弃 hex: {} bin:{} df{}\n", bin2hex(msg), msg, Psc::to_string(df));
}
return {};
}
if (bin2<int>(msg.substr(8, 5)) != 0 || bin2<int>(msg.substr(13, 6)) != 0) {
static Frequency_Limit mt;
if (mt.test()) {
std::cout << std::format("原因是 DR/UM 非 0 时,这条 DF20/DF21 下行应答里带了额外的通信控制状态 {} \n", msg);
}
}
return bds_result;
std::string data = databin(msg);
if (allzeros(data)) {
static Frequency_Limit mt;
if (mt.test()) {
std::cout << std::format("bds解码出了allzeros hex: {} bin:{}\n", bin2hex(msg), msg);
}
return {};
}
struct Candidate {
const char *name;
int (*score)(const std::string&);
};
std::vector<Candidate> candidates = {
{"10", bds10_score},
{"20", bds20_score},
{"30", bds30_score},
{"17", bds17_score},
{"40", bds40_score},
{"50", bds50_score},
{"60", bds60_score}
};
if (true) {
candidates.push_back({"44", bds44_score});
}
int best_score = 0;
const char *best_name = nullptr;
std::vector<const char*> best_names;
for (const auto& candidate : candidates) {
int score = candidate.score(data);
if (score > best_score) {
best_score = score;
best_name = candidate.name;
best_names.clear();
best_names.emplace_back(candidate.name);
}
else if (score == best_score && score > 0) {
best_names.emplace_back(candidate.name);
}
}
if (!best_name || best_names.size() > 1) {
static Frequency_Limit mt;
if (mt.test()) {
std::ostringstream oss;
for (auto name : best_names) {
if (oss.tellp() > 0) {
oss << ",";
}
oss << name;
}
std::cout << std::format("bds解码出了ambiguous score:{} best:[{}] hex:{} bin:{}\n", best_score, oss.str(), bin2hex(msg), msg);
}
return {};
}
static Frequency_Limit_Multi mt(0.001);
if (mt.test(best_name)) {
std::cout << std::format("bds解码出了{} score:{}\n", best_name, best_score);
}
return {best_name};
}
}
#ifdef _USE_GTEST
#include <gtest/gtest.h>
#endif
+3 -4
View File
@@ -9,8 +9,7 @@ _attach_source_dir("SSR" "${CMAKE_CURRENT_LIST_DIR}/test")
_depend(SSR ${Core})
_create(dll readsb2)
_attach_include_dir(readsb2 "${CMAKE_CURRENT_LIST_DIR}")
_attach_source_dir(readsb2 "${CMAKE_CURRENT_LIST_DIR}/readsb2")
#_create(dll readsb2)
#_attach_include_dir(readsb2 "${CMAKE_CURRENT_LIST_DIR}")
#_attach_source_dir(readsb2 "${CMAKE_CURRENT_LIST_DIR}/readsb2")
+7 -1
View File
@@ -27,7 +27,13 @@ TEST_F(BdsTest, TestBdsInfer) {
// std::cout << infer("A0001838201584F23468207CDFA5").value();
}
TEST_F(BdsTest, Infer2LargeBds17ReservedBitsDoesNotThrow) {
std::string msg = "10100000000000000000000000000000";
msg += "00010000000000110000101010000000111101010000000000000000";
msg += "000000000000000000000000";
EXPECT_NO_THROW(infer2(msg, true));
EXPECT_NO_THROW(infer2("1010000000000000000011110011100100010000000000110000101010000000111101010000000000000000001110000101001101010111", true));
}
TEST_F(BdsTest, TestBdsIs50Or60) {
auto result = is50or60(hex2bin("A0001838201584F23468207CDFA5"), 0, 0, 0);
EXPECT_EQ(result, ""); // Check if the optional is empty