diff --git a/SSR/Aircraft_Info.cpp b/SSR/Aircraft_Info.cpp index a751ef2..ea2dce3 100644 --- a/SSR/Aircraft_Info.cpp +++ b/SSR/Aircraft_Info.cpp @@ -1,3 +1,4 @@ + #include "Aircraft_Info.h" #include "Core/Statistics/Frequency_Limit.h" #include "convert/SBS.h" @@ -19,592 +20,619 @@ using namespace Psc; to_string(df), {}, nullptr, mode_s_msg); \ return false; \ } -namespace SSR { -Parse_Call_Back parse_call_back = [](SSR::Parse_Call_Back_Type type, - Aircraft_Info *info, - std::string_view field_name, - std::vector bds_list, void *, - SSR::P_S mode_s_msg) { - if (type == Parse_Call_Back_Type::Normal_Block) return; - auto& msg_hex = mode_s_msg->msg_hex; - auto& msg = mode_s_msg->msg_bin; - auto& df = mode_s_msg->df; - auto& icao = mode_s_msg->icao; - std::string bds_list_str; - for (size_t i = 0; i < bds_list.size(); i++) { - bds_list_str += bds_list[i] + "_"; - } - if (!bds_list_str.empty()) { - bds_list_str.pop_back(); - } - Log_Type lt({}, - { - {"Parse_Call_Back_Type", to_string(type)}, - {"bds_list_str", bds_list_str}, - {"ICAO", icao}, - {"DF", to_string(df)}, - {"signal_level", std::to_string((int)mode_s_msg->signal_level)}, - {"mlat", mode_s_msg->mlat_timestamp.to_string()}, - {"msg", msg_hex} - }); - if (type == Parse_Call_Back_Type::Length_Error) { - mode_s_logger->error( - to_string(df) + " length error", {}, - "should:" + std::to_string(mode_s_msg->msg_hex.size()) + - " msg_hex_size:" + std::to_string(msg_hex.size()) + - " icao:" + icao + "; msg_hex:" + msg_hex); - } - mode_s_logger->error("", lt, ""); -}; -Parse_Pos_Call_Back parse_pos_call_back = [](Aircraft_Info *info, - P_S mode_s_msg, bool surface) { - if (surface) { - mode_s_logger->debug("surface/地面轨迹", {}, - info->surface_pos_track_list.to_string()); - } - else { - mode_s_logger->debug("air/轨迹", {}, info->air_pos_track_list.to_string()); - } -}; -Parse_OK_Json_Call_Back parse_ok_json = [](Aircraft_Info *aircraft_info, - P_S mode_s_msg, Psc::JSON msg_json) { - auto& icao = mode_s_msg->icao; - auto& msg_hex = mode_s_msg->msg_hex; - auto& df = mode_s_msg->df; - Log_Type lt({}, - { - {"ICAO", icao}, - {"DF", std::to_string(static_cast(df))}, - {"signal_level", std::to_string((int)mode_s_msg->signal_level)}, - {"mlat", mode_s_msg->mlat_timestamp.to_string()}, - {"msg", msg_hex} - }); - mode_s_logger->debug("mode_s/json", lt, msg_json.to_json_string()); -}; -void set_logger(BaseLogger *logger) { - delete mode_s_logger; - mode_s_logger = logger; -} -bool check_new_prase_pos(ADS_B_T::CPR_Data& t, std::optional& tp, - const std::optional& last, - SSR::P_S mode_s_msg) { - if (tp == std::nullopt) return false; - if (last == std::nullopt) return true; - return true; -} -constexpr double s_ns = 1000000000.0; -// cpr回调 -CPR_CB cpr_cb = [](CPR_Error_Type t, std::string_view log, P_S msg) { - if (mode_s_logger) mode_s_logger->debug("CPR/" + to_string(t), {}, log); -}; -bool parse_absb_17(SSR::Data_Source_Interface *src, SSR::P_S mode_s_msg, - JSON& msg_json, - std::optional base_station_pos, - CPR::D max_speed_m_s, CPR::Time air_pos_time_out, - CPR::Time surface_pos_time_out) { - bool new_create; - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (!a) { - a = src->create_aircraft(mode_s_msg->icao); - } - Aircraft_Info& info = *a; - info.refresh_pos(false); - std::string& msg_bin = mode_s_msg->msg_bin; - MLAT_timestamp *mlat_timestamp = &mode_s_msg->mlat_timestamp; - Parse(base2, ADS_B_base) - int tc = type_code(msg_bin); - if (tc >= 1 && tc <= 4) { - Parse(t, BDS08_id) - } - else if (tc >= 5 && tc <= 8) { - Parse_no_json(t, BDS06_surface_position) - info.refresh_pos(true); - t.base_station_pos = base_station_pos; - auto tp = t.parse_message(mode_s_msg, mode_s_msg->time().abs_sec(), - max_speed_m_s, surface_pos_time_out, cpr_cb); - if (tp.has_value()) { - auto t2 = t.odd_msg; - Position_Info p(tp.value(), info.altitude_meter().value_or(0)); - info.surface_pos_track_list.push(p); - info.mlat = false; - parse_pos_call_back(&info, mode_s_msg, true); - msg_json.append_list(t.toJson().children); - } - } - else if ((tc >= 9 && tc <= 18) || (tc >= 20 && tc <= 22)) { - Parse_no_json(t, BDS05_airborne_position) - info.refresh_pos(true); - if (info.absb_version == 0 || info.absb_version == 1) { - Parse(t2, BDS05_airborne_Position_v01) - } - else if (info.absb_version == 2) { - Parse(t2, BDS05_airborne_Position_v2) - } - using namespace std::chrono; - auto tp = t.parse_message(mode_s_msg, mode_s_msg->time().abs_sec(), - max_speed_m_s, air_pos_time_out, cpr_cb); - if (tp.has_value()) { - auto alt = t.get_alt_meter(); - Position_Info p(tp.value(), alt); - info.air_pos_track_list.push(p); - info.mlat = false; - parse_pos_call_back(&info, mode_s_msg, false); - msg_json.append_list(t.toJson().children); - } - } - else if (tc == 19) { - // 速度 - Parse(t, BDS09_Base) - int subType = get_bin < 37, - 3 > (msg_bin); - if (subType == 1 || subType == 2) { - Parse(s12, ground_speed) +namespace SSR { + Parse_Call_Back parse_call_back = [](SSR::Parse_Call_Back_Type type, + Aircraft_Info *info, + std::string_view field_name, + std::vector bds_list, void *, + SSR::P_S mode_s_msg) { + if (type == Parse_Call_Back_Type::Normal_Block) return; + auto &msg_hex = mode_s_msg->msg_hex; + auto &msg = mode_s_msg->msg_bin; + auto &df = mode_s_msg->df; + auto &icao = mode_s_msg->icao; + std::string bds_list_str; + for (size_t i = 0; i < bds_list.size(); i++) { + bds_list_str += bds_list[i] + "_"; } - else if (subType == 3 || subType == 4) { - Parse(s34, air_speed) + if (!bds_list_str.empty()) { + bds_list_str.pop_back(); } - else { + Log_Type lt({}, + { + {"Parse_Call_Back_Type", to_string(type)}, + {"bds_list_str", bds_list_str}, + {"ICAO", icao}, + {"DF", to_string(df)}, + {"signal_level", std::to_string((int) mode_s_msg->signal_level)}, + {"mlat", mode_s_msg->mlat_timestamp.to_string()}, + {"msg", msg_hex} + }); + if (type == Parse_Call_Back_Type::Length_Error) { + mode_s_logger->error( + to_string(df) + " length error", {}, + "should:" + std::to_string(mode_s_msg->msg_hex.size()) + + " msg_hex_size:" + std::to_string(msg_hex.size()) + + " icao:" + icao + "; msg_hex:" + msg_hex); + } + mode_s_logger->error("", lt, ""); + }; + Parse_Pos_Call_Back parse_pos_call_back = [](Aircraft_Info *info, + P_S mode_s_msg, bool surface) { + if (surface) { + mode_s_logger->debug("surface/地面轨迹", {}, + info->surface_pos_track_list.to_string()); + } else { + mode_s_logger->debug("air/轨迹", {}, info->air_pos_track_list.to_string()); + } + }; + Parse_OK_Json_Call_Back parse_ok_json = [](Aircraft_Info *aircraft_info, + P_S mode_s_msg, Psc::JSON msg_json) { + auto &icao = mode_s_msg->icao; + auto &msg_hex = mode_s_msg->msg_hex; + auto &df = mode_s_msg->df; + Log_Type lt({}, + { + {"ICAO", icao}, + {"DF", std::to_string(static_cast(df))}, + {"signal_level", std::to_string((int) mode_s_msg->signal_level)}, + {"mlat", mode_s_msg->mlat_timestamp.to_string()}, + {"msg", msg_hex} + }); + mode_s_logger->debug("mode_s/json", lt, msg_json.to_json_string()); + }; + + void set_logger(BaseLogger *logger) { + delete mode_s_logger; + mode_s_logger = logger; + } + + bool check_new_prase_pos(ADS_B_T::CPR_Data &t, std::optional &tp, + const std::optional &last, + SSR::P_S mode_s_msg) { + if (tp == std::nullopt) return false; + if (last == std::nullopt) return true; + return true; + } + + constexpr double s_ns = 1000000000.0; + // cpr回调 + CPR_CB cpr_cb = [](CPR_Error_Type t, std::string_view log, P_S msg) { + if (mode_s_logger) mode_s_logger->debug("CPR/" + to_string(t), {}, log); + }; + + bool parse_absb_17(SSR::Data_Source_Interface *src, SSR::P_S mode_s_msg, + JSON &msg_json, + std::optional base_station_pos, + CPR::D max_speed_m_s, CPR::Time air_pos_time_out, + CPR::Time surface_pos_time_out) { + bool new_create; + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + if (!a) { + a = src->create_aircraft(mode_s_msg->icao); + } + Aircraft_Info &info = *a; + info.refresh_pos(false); + std::string &msg_bin = mode_s_msg->msg_bin; + MLAT_timestamp *mlat_timestamp = &mode_s_msg->mlat_timestamp; + Parse(base2, ADS_B_base) + int tc = type_code(msg_bin); + if (tc >= 1 && tc <= 4) { + Parse(t, BDS08_id) + } else if (tc >= 5 && tc <= 8) { + Parse_no_json(t, BDS06_surface_position) + info.refresh_pos(true); + t.base_station_pos = base_station_pos; + auto tp = t.parse_message(mode_s_msg, mode_s_msg->time().abs_sec(), + max_speed_m_s, surface_pos_time_out, cpr_cb); + if (tp.has_value()) { + auto t2 = t.odd_msg; + Position_Info p(tp.value(), info.altitude_meter().value_or(0)); + info.surface_pos_track_list.push(p); + info.mlat = false; + parse_pos_call_back(&info, mode_s_msg, true); + msg_json.append_list(t.toJson().children); + } + } else if ((tc >= 9 && tc <= 18) || (tc >= 20 && tc <= 22)) { + Parse_no_json(t, BDS05_airborne_position) + info.refresh_pos(true); + if (info.absb_version == 0 || info.absb_version == 1) { + Parse(t2, BDS05_airborne_Position_v01) + } else if (info.absb_version == 2) { + Parse(t2, BDS05_airborne_Position_v2) + } + using namespace std::chrono; + auto tp = t.parse_message(mode_s_msg, mode_s_msg->time().abs_sec(), + max_speed_m_s, air_pos_time_out, cpr_cb); + if (tp.has_value()) { + auto alt = t.get_alt_meter(); + Position_Info p(tp.value(), alt); + info.air_pos_track_list.push(p); + info.mlat = false; + parse_pos_call_back(&info, mode_s_msg, false); + msg_json.append_list(t.toJson().children); + } + } else if (tc == 19) { + // 速度 + Parse(t, BDS09_Base) + int subType = get_bin<37, + + 3>(msg_bin); + if (subType == 1 || subType == 2) { + Parse(s12, ground_speed) + } else if (subType == 3 || subType == 4) { + Parse(s34, air_speed) + } else { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::Unknown_tc, nullptr, + VAR_STR_1(subType) + "超出范围 tc == 19 error! ", {}, + nullptr, mode_s_msg); + } + if (info.absb_version == 0) { + Parse(v0, BDS09_v0) + } else if (info.absb_version == 1 || info.absb_version == 2) { + Parse(v12, BDS09_v12) + } + } else if (tc == 28) { + Parse(t, BDS61) + int st = t.get_sub_type(); + if (st == 1) { + Parse(st1, BDS61_mode_a) + } + if (st == 2) { + Parse(st2, BDS61_mode_TCAS_ACAS_RA_Broadcast) + } + } else if (tc == 29) { + Parse(t, BDS62) + auto st = t.get_sub_type(); + if (st == 0) { + Parse(tv1, BDS62_ver1) + } + if (st == 1) { + Parse(tv2, BDS62_ver2) + } + } else if (tc == 31) { + Parse(t, BDS65_Base) + int st = get_bin<37, + + 3>(msg_bin); + int ver = t.get_ADS_B_version(); + info.absb_version = ver; + if (st == 0) { + Parse(t2, BDS65_Airborne) + } else if (st == 1) { + Parse(t2, BDS65_Surface) + } + if (ver == 1) { + Parse(t2, BDS65_v1) + } else if (ver == 2) { + Parse(t2, BDS65_v2) + } + } else { if (parse_call_back) parse_call_back(Parse_Call_Back_Type::Unknown_tc, nullptr, - VAR_STR_1(subType) + "超出范围 tc == 19 error! ", {}, - nullptr, mode_s_msg); - } - if (info.absb_version == 0) { - Parse(v0, BDS09_v0) - } - else if (info.absb_version == 1 || info.absb_version == 2) { - Parse(v12, BDS09_v12) - } - } - else if (tc == 28) { - Parse(t, BDS61) - int st = t.get_sub_type(); - if (st == 1) { - Parse(st1, BDS61_mode_a) - } - if (st == 2) { - Parse(st2, BDS61_mode_TCAS_ACAS_RA_Broadcast) - } - } - else if (tc == 29) { - Parse(t, BDS62) - auto st = t.get_sub_type(); - if (st == 0) { - Parse(tv1, BDS62_ver1) - } - if (st == 1) { - Parse(tv2, BDS62_ver2) - } - } - else if (tc == 31) { - Parse(t, BDS65_Base) - int st = get_bin < 37, - - 3 > (msg_bin); - int ver = t.get_ADS_B_version(); - info.absb_version = ver; - if (st == 0) { - Parse(t2, BDS65_Airborne) - } - else if (st == 1) { - Parse(t2, BDS65_Surface) - } - if (ver == 1) { - Parse(t2, BDS65_v1) - } - else if (ver == 2) { - Parse(t2, BDS65_v2) - } - } - else { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::Unknown_tc, nullptr, - "commb bds not one", {}, nullptr, mode_s_msg); - } - return true; -} -bool parse_Message_commb(SSR::Data_Source_Interface *src, - std::string_view BDS, SSR::P_S mode_s_msg, - JSON& msg_json) { - std::string& msg = mode_s_msg->msg_bin; - Downlink_Format& df = mode_s_msg->df; - bool new_create; - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (!a) { - a = src->create_aircraft(mode_s_msg->icao); - } - Aircraft_Info& info = *a; - Parse(rb, Reply_Base_builder) - if (df == - Downlink_Format::Comm_B_Altitude_Reply_20) { - Parse(alt, alt) - } - if (df == Downlink_Format::Comm_B_Identity_Reply_21) { - Parse(id, id) - } - if (BDS == "10") { - Parse(t, BDS10) - } - else if (BDS == "17") { - Parse(t, BDS17) - } - else if (BDS == "20") { - Parse(t, BDS20) - } - else if (BDS == "30") { - Parse(t, BDS30) - ACAS_SubType st = t.parse_sum(msg); - msg_json.append({"ACAS_SubType st", to_string(st)}); - switch (st) { - case ACAS_SubType::NO_RA: break; - case ACAS_SubType::One_threat: { - Parse(t2, one) - break; - } - case ACAS_SubType::Mult_below_above: - case ACAS_SubType::Mult_same_direction: { - Parse(t2, muti) - break; - } - } - Threat_Type_Indicator ti = t.get_threat_Type_Indicator(); - msg_json.append({"Threat_Type_Indicator", to_string(ti)}); - switch (ti) { - case Threat_Type_Indicator::No_Identity_Data: break; - case Threat_Type_Indicator::Mode_S_Transponder_Address: { - Parse(t2, transponder_address) - break; - } - case Threat_Type_Indicator::Altitude_Range_Bearing: { - Parse(t2, Altitude_Range_Bearing) - break; - } - } - } - else if (BDS == "40") { - Parse(t, BDS40) - } - else if (BDS == "50") { - Parse(t, BDS50) - } - else if (BDS == "60") { - Parse(t, BDS60) - } - else if (BDS == "44") { - Parse(t, BDS44) - } - else if (BDS == "45") { - Parse(t, BDS45) - } - return true; -} -bool parse_mode_s_bin(Data_Source_Interface *src, P_S mode_s_msg, - std::optional base_station_pos, - CPR::D max_speed_m_s, CPR::Time air_pos_time_out, - CPR::Time surface_pos_time_out) { - auto& msg_hex = mode_s_msg->msg_hex; - auto& msg = mode_s_msg->msg_bin; - auto& df = mode_s_msg->df; - auto& icao = mode_s_msg->icao; - JSON msg_json = JSON::object(); - bool ok = true; - if (df == Downlink_Format::Extended_Squitter_17) { - checkLength(28) - if (!with_PI::check_crc(msg, icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, - "parse_absb_17 crc_error", {}, nullptr, mode_s_msg); - return false; - } - ok = parse_absb_17(src, mode_s_msg, msg_json, base_station_pos, - max_speed_m_s, air_pos_time_out, surface_pos_time_out); - } - else if (df == Downlink_Format::Comm_B_Altitude_Reply_20 || - df == Downlink_Format::Comm_B_Identity_Reply_21) { - checkLength(28) - if (!src->get_aircraft(icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, - nullptr, mode_s_msg); - return false; - } - std::vector bds_list = infer2(msg, true); - if (bds_list.empty()) { - msg_json.append({"bds", "bds_empty"}); - std::string bds_list_str; - for (auto& bds : bds_list) { - bds_list_str += "_" + bds; - } - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::Cannot_Choose_BDS, nullptr, - "commb bds zero", {}, nullptr, mode_s_msg); - return false; - } - if (bds_list.size() > 1) { - std::string that; - for (std::string_view bds : bds_list) { - that.append(std::string(bds) + "_"); - } - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::More_than_one_BDS, nullptr, "commb bds not one", {}, nullptr, mode_s_msg); } - std::string BDS = bds_list[0]; - if (!with_AP::check_crc(msg, icao, BDS)) { - msg_json.append({"crc", "invalid crc"}); - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, "commb_crc", - {}, nullptr, mode_s_msg); - return false; - } - ok = parse_Message_commb(src, BDS, mode_s_msg, msg_json); + return true; } - else if (df == Downlink_Format::Surveillance_Altitude_Reply_4 || - df == Downlink_Format::Surveillance_Identity_Reply_5) { - checkLength(14) - if (!src->get_aircraft(icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, - nullptr, mode_s_msg); - return false; - } - if (!with_AP::check_crc(msg, icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, - "Surveillance crc_error", {}, nullptr, mode_s_msg); - return false; - } + + bool parse_Message_commb(SSR::Data_Source_Interface *src, + std::string_view BDS, SSR::P_S mode_s_msg, + JSON &msg_json) { + std::string &msg = mode_s_msg->msg_bin; + Downlink_Format &df = mode_s_msg->df; + bool new_create; std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - Aircraft_Info& info = *a; + if (!a) { + a = src->create_aircraft(mode_s_msg->icao); + } + Aircraft_Info &info = *a; Parse(rb, Reply_Base_builder) - if (df == Downlink_Format:: - Surveillance_Altitude_Reply_4) { + if (df == + Downlink_Format::Comm_B_Altitude_Reply_20) { Parse(alt, alt) } - if (df == Downlink_Format::Surveillance_Identity_Reply_5) { + if (df == Downlink_Format::Comm_B_Identity_Reply_21) { Parse(id, id) } - } - else if (df == Downlink_Format::All_Call_Reply_11) { - checkLength(14) - if (!src->get_aircraft(icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, - nullptr, mode_s_msg); - return false; - } - // if (!with_PI::check_crc(msg, icao, "")) { - // if (error_call_back) error_call_back(info, "All_Call_Reply - // crc_error", mode_s_msg); return false; - // } - // https://mode-s.org/1090mhz/content/mode-s/1-basics.html - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (a) { - Aircraft_Info& info = *a; - Parse(all_call_reply, all_call_reply) - } - } - else if (df == Downlink_Format::Short_Air_Air_Surveillance_0) { - checkLength(14) - if (!src->get_aircraft(icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, - nullptr, mode_s_msg); - return false; - } - if (!with_AP::check_crc(msg, icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, - "Short_Air_Air_Surveillance crc_error", {}, nullptr, - mode_s_msg); - return false; - } - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (a) { - Aircraft_Info& info = *a; - Parse(s_acas, short_ACAS) - } - } - else if (df == Downlink_Format::Long_Air_Air_Surveillance_16) { - checkLength(28) - if (!src->get_aircraft(icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, - nullptr, mode_s_msg); - return false; - } - if (!with_AP::check_crc(msg, icao)) { - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, - "Long_Air_Air_Surveillance crc_error", {}, nullptr, - mode_s_msg); - return false; - } - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (a) { - Aircraft_Info& info = *a; - Parse(s_acas, - long_ACAS) - if (ACAS_T::Parser_VDS_3_0_Coordination_Reply::is(msg)) { - Parse(base, VDS_3_0_Base) - if (ACAS_T::Parser_VDS_3_0_ARA1::is(msg)) { - Parse(base1, VDS_3_0_1) + if (BDS == "10") { + Parse(t, BDS10) + } else if (BDS == "17") { + Parse(t, BDS17) + } else if (BDS == "20") { + Parse(t, BDS20) + } else if (BDS == "30") { + Parse(t, BDS30) + ACAS_SubType st = t.parse_sum(msg); + msg_json.append({"ACAS_SubType st", to_string(st)}); + switch (st) { + case ACAS_SubType::NO_RA: break; + case ACAS_SubType::One_threat: { + Parse(t2, one) + break; } - if (ACAS_T::Parser_VDS_3_0_ARA2::is(msg)) { - Parse(base2, VDS_3_0_2) + case ACAS_SubType::Mult_below_above: + case ACAS_SubType::Mult_same_direction: { + Parse(t2, muti) + break; } } + Threat_Type_Indicator ti = t.get_threat_Type_Indicator(); + msg_json.append({"Threat_Type_Indicator", to_string(ti)}); + switch (ti) { + case Threat_Type_Indicator::No_Identity_Data: break; + case Threat_Type_Indicator::Mode_S_Transponder_Address: { + Parse(t2, transponder_address) + break; + } + case Threat_Type_Indicator::Altitude_Range_Bearing: { + Parse(t2, Altitude_Range_Bearing) + break; + } + } + } else if (BDS == "40") { + Parse(t, BDS40) + } else if (BDS == "50") { + Parse(t, BDS50) + } else if (BDS == "60") { + Parse(t, BDS60) + } else if (BDS == "44") { + Parse(t, BDS44) + } else if (BDS == "45") { + Parse(t, BDS45) + } + return true; + } + + bool parse_mode_s_bin(Data_Source_Interface *src, P_S mode_s_msg, + std::optional base_station_pos, + CPR::D max_speed_m_s, CPR::Time air_pos_time_out, + CPR::Time surface_pos_time_out) { + auto &msg_hex = mode_s_msg->msg_hex; + auto &msg = mode_s_msg->msg_bin; + auto &df = mode_s_msg->df; + auto &icao = mode_s_msg->icao; + JSON msg_json = JSON::object(); + bool ok = true; + if (df == Downlink_Format::Extended_Squitter_17) { + checkLength(28) + if (!with_PI::check_crc(msg, icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, + "parse_absb_17 crc_error", {}, nullptr, mode_s_msg); + return false; + } + ok = parse_absb_17(src, mode_s_msg, msg_json, base_station_pos, + max_speed_m_s, air_pos_time_out, surface_pos_time_out); + } else if (df == Downlink_Format::Comm_B_Altitude_Reply_20 || + df == Downlink_Format::Comm_B_Identity_Reply_21) { + checkLength(28) + if (!src->get_aircraft(icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, + nullptr, mode_s_msg); + return false; + } + std::vector bds_list = infer2(msg, true); + if (bds_list.empty()) { + msg_json.append({"bds", "bds_empty"}); + std::string bds_list_str; + for (auto &bds: bds_list) { + bds_list_str += "_" + bds; + } + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::Cannot_Choose_BDS, nullptr, + "commb bds zero", {}, nullptr, mode_s_msg); + return false; + } + if (bds_list.size() > 1) { + std::string that; + for (std::string_view bds: bds_list) { + that.append(std::string(bds) + "_"); + } + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::More_than_one_BDS, nullptr, + "commb bds not one", {}, nullptr, mode_s_msg); + } + std::string BDS = bds_list[0]; + if (!with_AP::check_crc(msg, icao, BDS)) { + msg_json.append({"crc", "invalid crc"}); + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, "commb_crc", + {}, nullptr, mode_s_msg); + return false; + } + ok = parse_Message_commb(src, BDS, mode_s_msg, msg_json); + } else if (df == Downlink_Format::Surveillance_Altitude_Reply_4 || + df == Downlink_Format::Surveillance_Identity_Reply_5) { + checkLength(14) + if (!src->get_aircraft(icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, + nullptr, mode_s_msg); + return false; + } + if (!with_AP::check_crc(msg, icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, + "Surveillance crc_error", {}, nullptr, mode_s_msg); + return false; + } + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + Aircraft_Info &info = *a; + Parse(rb, Reply_Base_builder) + if (df == Downlink_Format:: + Surveillance_Altitude_Reply_4) { + Parse(alt, alt) + } + if (df == Downlink_Format::Surveillance_Identity_Reply_5) { + Parse(id, id) + } + } else if (df == Downlink_Format::All_Call_Reply_11) { + checkLength(14) + if (!src->get_aircraft(icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, + nullptr, mode_s_msg); + return false; + } + // if (!with_PI::check_crc(msg, icao, "")) { + // if (error_call_back) error_call_back(info, "All_Call_Reply + // crc_error", mode_s_msg); return false; + // } + // https://mode-s.org/1090mhz/content/mode-s/1-basics.html + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + if (a) { + Aircraft_Info &info = *a; + Parse(all_call_reply, all_call_reply) + } + } else if (df == Downlink_Format::Short_Air_Air_Surveillance_0) { + checkLength(14) + if (!src->get_aircraft(icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, + nullptr, mode_s_msg); + return false; + } + if (!with_AP::check_crc(msg, icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, + "Short_Air_Air_Surveillance crc_error", {}, nullptr, + mode_s_msg); + return false; + } + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + if (a) { + Aircraft_Info &info = *a; + Parse(s_acas, short_ACAS) + } + } else if (df == Downlink_Format::Long_Air_Air_Surveillance_16) { + checkLength(28) + if (!src->get_aircraft(icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::ICAO_Error, nullptr, "", {}, + nullptr, mode_s_msg); + return false; + } + if (!with_AP::check_crc(msg, icao)) { + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::CRC_Error, nullptr, + "Long_Air_Air_Surveillance crc_error", {}, nullptr, + mode_s_msg); + return false; + } + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + if (a) { + Aircraft_Info &info = *a; + Parse(s_acas, + long_ACAS) + if (ACAS_T::Parser_VDS_3_0_Coordination_Reply::is(msg)) { + Parse(base, VDS_3_0_Base) + if (ACAS_T::Parser_VDS_3_0_ARA1::is(msg)) { + Parse(base1, VDS_3_0_1) + } + if (ACAS_T::Parser_VDS_3_0_ARA2::is(msg)) { + Parse(base2, VDS_3_0_2) + } + } + } + } else { + // 没找到能解的df + if (parse_call_back) + parse_call_back(Parse_Call_Back_Type::Unknown_DF, nullptr, + "unknown df error", {}, nullptr, mode_s_msg); + return false; + } + std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); + if (a) { + ++a->times; + } + parse_ok_json(a.get(), mode_s_msg, msg_json); + return ok; + } + + bool Monitor_Mode_ACS_Message_Num = false; + + + struct Frequency_State { + std::atomic_int64_t cur_num{}; + }; + + Msg::Msg(const std::shared_ptr &source, std::string_view packet) : source(source), + packet(packet) { + type = static_cast(packet[1]); + static Frequency_Limit_Multi_T fl; + auto t = source->get_key() + "_" + Psc::to_string(type); + auto state = fl.get_state(t); + if (state->cur_num > 100) { + if (fl.test_ex(t)) { + std::cerr << t << " 构造 Msg msg_num " << ++state->cur_num << std::endl; + } } } - else { - // 没找到能解的df - if (parse_call_back) - parse_call_back(Parse_Call_Back_Type::Unknown_DF, nullptr, - "unknown df error", {}, nullptr, mode_s_msg); - return false; - } - std::shared_ptr a = src->get_aircraft(mode_s_msg->icao); - if (a) { - ++a->times; - } - parse_ok_json(a.get(), mode_s_msg, msg_json); - return ok; -} -bool Monitor_Mode_ACS_Message_Num = false; -struct Live_Report { - explicit Live_Report(std::string_view name) : name(name) {} - void add() { - ++live_num; - if (l.test()) { - report(); - return; + + Msg::~Msg() { + auto t = source->get_key() + "_" + Psc::to_string(type); + static Frequency_Limit_Multi_T fl; + auto state = fl.get_state(t); + if (state->cur_num > 100) { + if (fl.test_ex(t)) { + std::cerr << t << " 析构 Msg msg_num " << --state->cur_num << std::endl; + } } } - void remove() { - --live_num; - if (l.test()) { - report(); - return; + + + struct Live_Report { + explicit Live_Report(std::string_view name) : name(name) { + } + + void add() { + ++live_num; + if (l.test()) { + report(); + return; + } + } + + void remove() { + --live_num; + if (l.test()) { + report(); + return; + } + // if (live_num < 10) { + // report(); + // } + } + + void report() { + std::ostringstream oss; + oss << "[" << name << "]: " << total++; + oss << " current_num==" << live_num << std::endl; + std::cout << oss.str() << std::flush; + } + + protected: + std::string name = "null"; + Frequency_Limit l; + std::atomic live_num{}; // 存活的对象数量 + std::atomic total{}; + }; + + Live_Report ac_mode("ac"); + Live_Report s_mode("s"); + Live_Report plane("plane"); + // 以字节为单位 + // 1 + 1 + 6 + 1 + 2/7/14 + // 1a + type time_stamp signal_level data + Mode_Msg::Mode_Msg(const std::shared_ptr &source, + std::string_view packet) : Msg(source, packet) { + mlat_timestamp = MLAT_timestamp(packet.substr(2, 6)); + signal_level = packet[8]; + std::string memory(packet.substr(9)); + msg_hex = mem2hex(memory); + msg_bin = hex2bin(msg_hex); + if (mlat_timestamp.daysec == 0 && mlat_timestamp.nanosec == 0) { + std::time_t tp = std::time(nullptr); + mlat_timestamp.daysec = tp % (3600 * 24); } - // if (live_num < 10) { - // report(); - // } } - void report() { - std::ostringstream oss; - oss << "[" << name << "]: " << total++; - oss << " current_num==" << live_num << std::endl; - std::cout << oss.str() << std::flush; + + Mode_Msg::~Mode_Msg() { } -protected: - std::string name = "null"; - Frequency_Limit l; - std::atomic live_num{}; // 存活的对象数量 - std::atomic total{}; -}; -Live_Report ac_mode("ac"); -Live_Report s_mode("s"); -Live_Report plane("plane"); -// 以字节为单位 -// 1 + 1 + 6 + 1 + 2/7/14 -// 1a + type time_stamp signal_level data -Mode_Msg::Mode_Msg(const std::shared_ptr& source, - std::string_view packet) : Msg(source, packet) { - mlat_timestamp = MLAT_timestamp(packet.substr(2, 6)); - signal_level = packet[8]; - std::string memory(packet.substr(9)); - msg_hex = mem2hex(memory); - msg_bin = hex2bin(msg_hex); - if (mlat_timestamp.daysec == 0 && mlat_timestamp.nanosec == 0) { - std::time_t tp = std::time(nullptr); - mlat_timestamp.daysec = tp % (3600 * 24); - } -} -Mode_Msg::~Mode_Msg() {} -Mode_AC_Msg::Mode_AC_Msg(const std::shared_ptr& source, - std::string_view packet) : Mode_Msg(source, packet) { - if (packet.size() != AC_len) { - std::string log = "0x31 Mode_S_Msg 长度错误!" + std::string(packet); - std::cout << log << std::endl; - mode_s_logger->error("", {}, log); - Psc::fail_fast(); - } - if (Monitor_Mode_ACS_Message_Num) { - ac_mode.add(); - } -} -Mode_AC_Msg::~Mode_AC_Msg() { - if (Monitor_Mode_ACS_Message_Num) { - ac_mode.remove(); - } -} -Mode_S_Msg::Mode_S_Msg(const std::shared_ptr& source, - std::string_view packet) : Mode_Msg(source, packet) { - if (type == S7) { - if (packet.size() != S7_len) { - std::string log = "0x32 Mode_S_Msg 长度错误!" + std::string(packet); + + Mode_AC_Msg::Mode_AC_Msg(const std::shared_ptr &source, + std::string_view packet) : Mode_Msg(source, packet) { + if (packet.size() != AC_len) { + std::string log = "0x31 Mode_S_Msg 长度错误!" + std::string(packet); std::cout << log << std::endl; mode_s_logger->error("", {}, log); Psc::fail_fast(); } - } - if (type == S14) { - if (packet.size() != S14_len) { - std::string log = "0x33 Mode_S_Msg 长度错误!" + std::string(packet); - std::cout << log << std::endl; - mode_s_logger->error("", {}, log); - Psc::fail_fast(); + if (Monitor_Mode_ACS_Message_Num) { + ac_mode.add(); } } - df = decode_df(hex2bin(msg_hex.substr(0, 2))); - icao = SSR::decode_icao(msg_bin); - if (Monitor_Mode_ACS_Message_Num) { - s_mode.add(); + + Mode_AC_Msg::~Mode_AC_Msg() { + if (Monitor_Mode_ACS_Message_Num) { + ac_mode.remove(); + } } -} -Mode_S_Msg::~Mode_S_Msg() { - if (Monitor_Mode_ACS_Message_Num) { - s_mode.remove(); + + Mode_S_Msg::Mode_S_Msg(const std::shared_ptr &source, + std::string_view packet) : Mode_Msg(source, packet) { + if (type == S7) { + if (packet.size() != S7_len) { + std::string log = "0x32 Mode_S_Msg 长度错误!" + std::string(packet); + std::cout << log << std::endl; + mode_s_logger->error("", {}, log); + Psc::fail_fast(); + } + } + if (type == S14) { + if (packet.size() != S14_len) { + std::string log = "0x33 Mode_S_Msg 长度错误!" + std::string(packet); + std::cout << log << std::endl; + mode_s_logger->error("", {}, log); + Psc::fail_fast(); + } + } + df = decode_df(hex2bin(msg_hex.substr(0, 2))); + icao = SSR::decode_icao(msg_bin); + if (Monitor_Mode_ACS_Message_Num) { + s_mode.add(); + } } -} -Psc::JSON POS_List::get_last_array_json(std::uint64_t last_seq) { - std::lock_guard g(mtx.mtx); - Psc::JSON ret = Psc::JSON::object(); - Psc::JSON arr = Psc::JSON::array(); - std::uint64_t cur_seq = seq; - if (last_seq >= cur_seq) { + + Mode_S_Msg::~Mode_S_Msg() { + if (Monitor_Mode_ACS_Message_Num) { + s_mode.remove(); + } + } + + Psc::JSON POS_List::get_last_array_json(std::uint64_t last_seq) { + std::lock_guard g(mtx.mtx); + Psc::JSON ret = Psc::JSON::object(); + Psc::JSON arr = Psc::JSON::array(); + std::uint64_t cur_seq = seq; + if (last_seq >= cur_seq) { + ret.append({"sta_seq", last_seq}); + ret.append({"end_seq", cur_seq}); + ret.append({"size", 0}); + ret.append({"list", arr}); + return ret; + } + std::uint64_t delta = cur_seq - last_seq; + std::uint64_t available = std::min(delta, count); + for (std::uint64_t i = available; i > 0; --i) { + std::size_t idx = (head + max_size - i) % max_size; + auto cur = buf[idx].to_json(); + cur.append({"idx", idx}); + arr.append(cur); + } ret.append({"sta_seq", last_seq}); ret.append({"end_seq", cur_seq}); - ret.append({"size", 0}); + ret.append({"size", arr.children.size()}); ret.append({"list", arr}); return ret; } - std::uint64_t delta = cur_seq - last_seq; - std::uint64_t available = std::min(delta, count); - for (std::uint64_t i = available; i > 0; --i) { - std::size_t idx = (head + max_size - i) % max_size; - auto cur = buf[idx].to_json(); - cur.append({"idx", idx}); - arr.append(cur); + + std::string POS_List::to_string() { + return get_last_array_json(0).to_json_string(); } - ret.append({"sta_seq", last_seq}); - ret.append({"end_seq", cur_seq}); - ret.append({"size", arr.children.size()}); - ret.append({"list", arr}); - return ret; -} -std::string POS_List::to_string() { - return get_last_array_json(0).to_json_string(); -} -Aircraft_Info::Aircraft_Info(std::string_view icao) : icao(std::move(icao)) { - if (Monitor_Mode_ACS_Message_Num) { - plane.add(); + + Aircraft_Info::Aircraft_Info(std::string_view icao) : icao(std::move(icao)) { + if (Monitor_Mode_ACS_Message_Num) { + plane.add(); + } + } + + Aircraft_Info::~Aircraft_Info() { + if (Monitor_Mode_ACS_Message_Num) { + plane.remove(); + } } } -Aircraft_Info::~Aircraft_Info() { - if (Monitor_Mode_ACS_Message_Num) { - plane.remove(); - } -} -} diff --git a/SSR/Msg.h b/SSR/Msg.h index 12d464a..057860e 100644 --- a/SSR/Msg.h +++ b/SSR/Msg.h @@ -7,260 +7,317 @@ #include #include #define STRINGIFY(x) #x + // 硬件接收来的 二进制消息族 namespace SSR { -class Data_Source_Interface; -static std::atomic_int64_t msg_num = 0; -extern bool Monitor_Mode_ACS_Message_Num; -class Msg { -public: - enum T : unsigned char { - AC = 0x31, // 49 - S7 = 0x32, // 50 - S14 = 0x33, // 51 - Radarcape_status = 0x34, // 52 - HULC_Status = 0x48, // 72 - UNKNOWN = 0xFF - }; - enum Len { - AC_len = 9 + 2, - S7_len = 9 + 7, - S14_len = 9 + 14, - Radarcape_status_len = 2 + 3, - HULC_len = 28, - }; - std::shared_ptr source{}; - T type = UNKNOWN; - std::string packet; // 收到的原始消息 - explicit Msg(const std::shared_ptr& source, - std::string_view packet) : source(source), packet(packet) { - type = static_cast(packet[1]); - // std::cerr << "Msg msg_num "<< ++msg_num << std::endl; - } - virtual Psc::JSON to_Json() { - Psc::JSON ret = Psc::JSON::object(); - ret.append({"type", Psc::to_string((T)(type))}); - return ret; - } - virtual ~Msg() = default; - virtual std::string to_string() { - return VAR_STR_2(type, packet); - } -}; -struct Play_Back_Time_Point { - std::uint64_t day_num{}; - std::uint64_t day_sec{}; // 0..86399 - struct HMS { - std::uint32_t hour; - std::uint32_t minute; - std::uint32_t second; - std::string to_string() const { - std::ostringstream oss; - oss << std::setw(2) << std::setfill('0') << hour << ':' << std::setw(2) - << std::setfill('0') << minute << ':' << std::setw(2) - << std::setfill('0') << second; - return oss.str(); - } - }; - HMS hms() noexcept { - HMS hms{}; - hms.hour = static_cast(day_sec / 3600); - day_sec %= 3600; - hms.minute = static_cast(day_sec / 60); - hms.second = static_cast(day_sec % 60); - return hms; - } - std::string to_string() { - return "Play_Back_Time_Point[" + Psc::to_string(day_num) + "," + - hms().to_string() + "]"; - } - static constexpr std::uint64_t SECS_PER_DAY = 86400ULL; - // 归一化:保证 day_sec < 86400 - void normalize() noexcept { - if (day_sec >= SECS_PER_DAY) { - day_num += day_sec / SECS_PER_DAY; - day_sec %= SECS_PER_DAY; - } - } - bool operator<(const Play_Back_Time_Point& other) const noexcept { - if (other == *this) return false; - if (day_num != other.day_num) return day_num < other.day_num; - return day_sec < other.day_sec; - } - bool operator==(const Play_Back_Time_Point& other) const noexcept { - return day_num == other.day_num && day_sec == other.day_sec; - } - bool operator>(const Play_Back_Time_Point& other) const noexcept { - if (other == *this) return false; - return other < *this; - } - std::uint64_t abs_sec() const noexcept { - return day_num * SECS_PER_DAY + day_sec; - } - void add_sec(std::uint64_t sec) noexcept { - day_sec += sec; - normalize(); - } -}; -class Mode_Msg : public Msg { -public: - std::uint64_t day_num = 0; // 自己维护的字段 - MLAT_timestamp mlat_timestamp; - std::string msg_hex; - std::string msg_bin; - char signal_level{}; - explicit Mode_Msg(const std::shared_ptr& source, - std::string_view packet); - Psc::JSON to_Json() override { - Psc::JSON ret = Msg::to_Json(); - Ret_J(msg_hex) - Ret_J(signal_level) - ret.append({"mlat_timestamp", mlat_timestamp.to_string()}); - Ret_J(day_num) - return ret; - } - ~Mode_Msg() override; - Play_Back_Time_Point time() { - return {day_num, mlat_timestamp.daysec}; - } - std::string to_string() override { - auto& time = mlat_timestamp; - return VAR_STR_3(signal_level, time, msg_hex); - } -}; -class Mode_AC_Msg : public Mode_Msg { -public: - explicit Mode_AC_Msg(const std::shared_ptr& source, - std::string_view packet); - ~Mode_AC_Msg() override; -}; -// 1 + 1 + 6 + 1 + 2/7/14 -// 1a + type time_stamp signal_level data -// 用于数据发送 -class Mode_S_Msg : public Mode_Msg { -public: - Downlink_Format df = Downlink_Format::Unknown; - std::string icao; - Psc::JSON to_Json() override { - Psc::JSON ret = Mode_Msg::to_Json(); - ret.append({"df", Psc::to_string(df)}); - ret.append({"icao", icao}); - return ret; - } - // 1a 31 19 b2dd1c7af671 3220 - explicit Mode_S_Msg(const std::shared_ptr& source, - std::string_view packet); - ~Mode_S_Msg() override; - std::string to_string() override { - auto& time = mlat_timestamp; - return VAR_STR_5(df, icao, signal_level, time, msg_hex); - } -}; -class NoneCopyLock { -public: - std::mutex mtx; // 互斥量,不能被复制 - NoneCopyLock() = default; - NoneCopyLock(const NoneCopyLock& other) = delete; - NoneCopyLock& operator=(const NoneCopyLock& other) { - return *this; - } -}; -struct Position_3D : CPR::Position { - CPR::D alt; - Position_3D(CPR::D lat, CPR::D lon, CPR::D alt) : CPR::Position(lat, lon), alt(alt) {} -}; -// 这个返回值的意义在于 解码失败后 返回值能反应出是 位置解码失败 -// 还是奇偶报文解码位置失败 -class Pos_Ret { -public: - CPR::Position pos; - std::shared_ptr that; - using Extra = std::variant, CPR::Position>; - Extra extra; - Pos_Ret(CPR::Position p, std::shared_ptr t, - std::shared_ptr o) : pos(p), that(std::move(t)), extra(std::move(o)) {} - Pos_Ret(CPR::Position p, std::shared_ptr t, CPR::Position old) : pos(p), that(std::move(t)), extra(old) {} -}; -// 展示出最详细的信息 -struct Position_Info : Position_3D { - std::int64_t sys_day_ns{}; - std::uint64_t utc{}; - std::uint64_t odd_even_diff_ns{}; - Position_Info() : Position_3D(0.0, 0.0, 0.0) {} - Pos_Ret::Extra extra; - std::shared_ptr that; - Position_Info(const Pos_Ret& r, CPR::D alt) : Position_3D(r.pos.lat, r.pos.lon, alt), that(r.that), extra(r.extra) { - using namespace std::chrono; - utc = static_cast( - duration_cast(system_clock::now().time_since_epoch()).count()); - } - Psc::JSON to_json() { - Psc::JSON ret = Psc::JSON::object(); - Ret_J(lat) - Ret_J(lon) - Ret_J(alt) - Ret_J(utc) - Ret_J(that->mlat_timestamp) - Ret_J(that->msg_hex) - Ret_J(that->signal_level) - if (auto p = std::get_if< - std::shared_ptr>( - &extra)) { - std::shared_ptr other = *p; - Ret_J(other->mlat_timestamp) - Ret_J(other->msg_hex) - Ret_J(other->signal_level) + class Data_Source_Interface; + + extern bool Monitor_Mode_ACS_Message_Num; + + class Msg { + public: + enum T : unsigned char { + AC = 0x31, // 49 + S7 = 0x32, // 50 + S14 = 0x33, // 51 + Radarcape_status = 0x34, // 52 + HULC_Status = 0x48, // 72 + UNKNOWN = 0xFF + }; + + enum Len { + AC_len = 9 + 2, + S7_len = 9 + 7, + S14_len = 9 + 14, + Radarcape_status_len = 2 + 3, + HULC_len = 28, + }; + + std::shared_ptr source{}; + T type = UNKNOWN; + std::string packet; // 收到的原始消息 + explicit Msg(const std::shared_ptr &source, std::string_view packet); + + virtual Psc::JSON to_Json() { + Psc::JSON ret = Psc::JSON::object(); + ret.append({"type", Psc::to_string((T) (type))}); return ret; } - if (auto p = std::get_if(&extra)) { - CPR::Position old = *p; - Ret_J(old) + + virtual ~Msg(); + + virtual std::string to_string() { + return VAR_STR_2(type, packet); + } + }; + + struct Play_Back_Time_Point { + std::uint64_t day_num{}; + std::uint64_t day_sec{}; // 0..86399 + struct HMS { + std::uint32_t hour; + std::uint32_t minute; + std::uint32_t second; + + std::string to_string() const { + std::ostringstream oss; + oss << std::setw(2) << std::setfill('0') << hour << ':' << std::setw(2) + << std::setfill('0') << minute << ':' << std::setw(2) + << std::setfill('0') << second; + return oss.str(); + } + }; + + HMS hms() noexcept { + HMS hms{}; + hms.hour = static_cast(day_sec / 3600); + day_sec %= 3600; + hms.minute = static_cast(day_sec / 60); + hms.second = static_cast(day_sec % 60); + return hms; + } + + std::string to_string() { + return "Play_Back_Time_Point[" + Psc::to_string(day_num) + "," + + hms().to_string() + "]"; + } + + static constexpr std::uint64_t SECS_PER_DAY = 86400ULL; + // 归一化:保证 day_sec < 86400 + void normalize() noexcept { + if (day_sec >= SECS_PER_DAY) { + day_num += day_sec / SECS_PER_DAY; + day_sec %= SECS_PER_DAY; + } + } + + bool operator<(const Play_Back_Time_Point &other) const noexcept { + if (other == *this) return false; + if (day_num != other.day_num) return day_num < other.day_num; + return day_sec < other.day_sec; + } + + bool operator==(const Play_Back_Time_Point &other) const noexcept { + return day_num == other.day_num && day_sec == other.day_sec; + } + + bool operator>(const Play_Back_Time_Point &other) const noexcept { + if (other == *this) return false; + return other < *this; + } + + std::uint64_t abs_sec() const noexcept { + return day_num * SECS_PER_DAY + day_sec; + } + + void add_sec(std::uint64_t sec) noexcept { + day_sec += sec; + normalize(); + } + }; + + class Mode_Msg : public Msg { + public: + std::uint64_t day_num = 0; // 自己维护的字段 + MLAT_timestamp mlat_timestamp; + std::string msg_hex; + std::string msg_bin; + char signal_level{}; + + explicit Mode_Msg(const std::shared_ptr &source, + std::string_view packet); + + Psc::JSON to_Json() override { + Psc::JSON ret = Msg::to_Json(); + Ret_J(msg_hex) + Ret_J(signal_level) + ret.append({"mlat_timestamp", mlat_timestamp.to_string()}); + Ret_J(day_num) return ret; } - return ret; - } -}; -struct POS_List { - NoneCopyLock mtx; - size_t max_size; - POS_List() {} - void reset() { - std::lock_guard g(mtx.mtx); - seq = 0; - head = 0; - count = 0; - } - void init(size_t _max_size) { - this->max_size = _max_size; - buf.resize(_max_size); - } - size_t size() { - std::lock_guard g(mtx.mtx); - return count; - } - bool empty() { - std::lock_guard g(mtx.mtx); - return count == 0; - } - std::optional last() { - std::lock_guard g(mtx.mtx); - if (count == 0) return std::nullopt; - std::size_t idx = (head + max_size - 1) % max_size; - return buf[idx]; - } - void push(Position_Info new_position) { - std::lock_guard g(mtx.mtx); - buf[head] = std::move(new_position); - head = (head + 1) % max_size; - if (count < max_size) ++count; - ++seq; - } - Psc::JSON get_last_array_json(std::uint64_t last_seq); - std::string to_string(); -protected: - std::uint64_t seq = 0; - std::uint64_t head = 0; - std::uint64_t count = 0; - std::vector buf; -}; -} // namespace SSR \ No newline at end of file + + ~Mode_Msg() override; + + Play_Back_Time_Point time() { + return {day_num, mlat_timestamp.daysec}; + } + + std::string to_string() override { + auto &time = mlat_timestamp; + return VAR_STR_3(signal_level, time, msg_hex); + } + }; + + class Mode_AC_Msg : public Mode_Msg { + public: + explicit Mode_AC_Msg(const std::shared_ptr &source, + std::string_view packet); + + ~Mode_AC_Msg() override; + }; + + // 1 + 1 + 6 + 1 + 2/7/14 + // 1a + type time_stamp signal_level data + // 用于数据发送 + class Mode_S_Msg : public Mode_Msg { + public: + Downlink_Format df = Downlink_Format::Unknown; + std::string icao; + + Psc::JSON to_Json() override { + Psc::JSON ret = Mode_Msg::to_Json(); + ret.append({"df", Psc::to_string(df)}); + ret.append({"icao", icao}); + return ret; + } + + // 1a 31 19 b2dd1c7af671 3220 + explicit Mode_S_Msg(const std::shared_ptr &source, + std::string_view packet); + + ~Mode_S_Msg() override; + + std::string to_string() override { + auto &time = mlat_timestamp; + return VAR_STR_5(df, icao, signal_level, time, msg_hex); + } + }; + + class NoneCopyLock { + public: + std::mutex mtx; // 互斥量,不能被复制 + NoneCopyLock() = default; + + NoneCopyLock(const NoneCopyLock &other) = delete; + + NoneCopyLock &operator=(const NoneCopyLock &other) { + return *this; + } + }; + + struct Position_3D : CPR::Position { + CPR::D alt; + + Position_3D(CPR::D lat, CPR::D lon, CPR::D alt) : CPR::Position(lat, lon), alt(alt) { + } + }; + + // 这个返回值的意义在于 解码失败后 返回值能反应出是 位置解码失败 + // 还是奇偶报文解码位置失败 + class Pos_Ret { + public: + CPR::Position pos; + std::shared_ptr that; + using Extra = std::variant, CPR::Position>; + Extra extra; + + Pos_Ret(CPR::Position p, std::shared_ptr t, + std::shared_ptr o) : pos(p), that(std::move(t)), extra(std::move(o)) { + } + + Pos_Ret(CPR::Position p, std::shared_ptr t, CPR::Position old) : pos(p), that(std::move(t)), + extra(old) { + } + }; + + // 展示出最详细的信息 + struct Position_Info : Position_3D { + std::int64_t sys_day_ns{}; + std::uint64_t utc{}; + std::uint64_t odd_even_diff_ns{}; + + Position_Info() : Position_3D(0.0, 0.0, 0.0) { + } + + Pos_Ret::Extra extra; + std::shared_ptr that; + + Position_Info(const Pos_Ret &r, CPR::D alt) : Position_3D(r.pos.lat, r.pos.lon, alt), that(r.that), + extra(r.extra) { + using namespace std::chrono; + utc = static_cast( + duration_cast(system_clock::now().time_since_epoch()).count()); + } + + Psc::JSON to_json() { + Psc::JSON ret = Psc::JSON::object(); + Ret_J(lat) + Ret_J(lon) + Ret_J(alt) + Ret_J(utc) + Ret_J(that->mlat_timestamp) + Ret_J(that->msg_hex) + Ret_J(that->signal_level) + if (auto p = std::get_if< + std::shared_ptr >( + &extra)) { + std::shared_ptr other = *p; + Ret_J(other->mlat_timestamp) + Ret_J(other->msg_hex) + Ret_J(other->signal_level) + return ret; + } + if (auto p = std::get_if(&extra)) { + CPR::Position old = *p; + Ret_J(old) + return ret; + } + return ret; + } + }; + + struct POS_List { + NoneCopyLock mtx; + size_t max_size; + + POS_List() { + } + + void reset() { + std::lock_guard g(mtx.mtx); + seq = 0; + head = 0; + count = 0; + } + + void init(size_t _max_size) { + this->max_size = _max_size; + buf.resize(_max_size); + } + + size_t size() { + std::lock_guard g(mtx.mtx); + return count; + } + + bool empty() { + std::lock_guard g(mtx.mtx); + return count == 0; + } + + std::optional last() { + std::lock_guard g(mtx.mtx); + if (count == 0) return std::nullopt; + std::size_t idx = (head + max_size - 1) % max_size; + return buf[idx]; + } + + void push(Position_Info new_position) { + std::lock_guard g(mtx.mtx); + buf[head] = std::move(new_position); + head = (head + 1) % max_size; + if (count < max_size) ++count; + ++seq; + } + + Psc::JSON get_last_array_json(std::uint64_t last_seq); + + std::string to_string(); + + protected: + std::uint64_t seq = 0; + std::uint64_t head = 0; + std::uint64_t count = 0; + std::vector buf; + }; +} // namespace SSR diff --git a/SSR/py_Mode_S/bds.cpp b/SSR/py_Mode_S/bds.cpp index f500584..012b85c 100644 --- a/SSR/py_Mode_S/bds.cpp +++ b/SSR/py_Mode_S/bds.cpp @@ -5,2652 +5,2665 @@ #undef max #undef min using namespace Psc; + namespace SSR { -static constexpr double cpr_divisor = 131072.0; -static constexpr double PI = 3.14159265358979323846; -/* ------------------------------------------- - BDS 0,5 - ADS-B TC=9-18 - Airborne position ------------------------------------------- -*/ -/** - * @brief 解码飞机高度。 - * - * @param msg 112位二进制字符串。 - * - * @return 高度,单位为英尺。 - */ -// df 17 tc 9-18 tc=20-22 Airborne position 报文 -// int altitude(const std::string& msg) { -// int tc = type_code(msg); -// CE(!tc || tc < 9 || tc == 19 || tc > 22, std::string(msg) + ": Not an airborne -// position message") std::string mb(msg.substr(32)); std::string altbin = -// mb.substr(8, 12); // 12 bits from index 8 to 20 if (tc < 19) -// { -// std::string altcode(altbin.substr(0, 6) + "0" + altbin.substr(6)); -// int alt = altitude13_ft(altcode); -// if (alt != -999999) -// { -// return alt; -// } -// CE(true, "alt" + std::to_string(alt) + ": Not an airborne position -// message") return -1; -// } -// return static_cast(bin2(altbin) * 3.28084); // convert to -// feet -// } -// ------------------------------------------ -// BDS 0,6 -// ADS-B TC=5-8 -// Surface movement -// ------------------------------------------ -// Ground_Speed surface_velocity(const std::string& msg, Ground_Speed_Type st) { -// double we_speed = 0.000001; -// int we_speed_code = bin2(msg.substr(46, 10)); -// if (we_speed_code != 0) { -// int sign = (msg.at(45) == '0') ? 1 : -1 ; -// if (st == Ground_Speed_Type::Ground_Normal) { -// we_speed = we_speed_code - 1; -// } else { -// we_speed = 4 * (we_speed_code - 1); -// } -// we_speed *= sign; -// } -// double sn_speed = 0.000001; -// int sn_speed_code = bin2(msg.substr(57, 10)); -// if (sn_speed_code != 0) { -// int sign = (msg.at(56) == '0') ? 1 : -1 ; -// if (st == Ground_Speed_Type::Ground_Normal) { -// sn_speed = sn_speed_code - 1; -// } else { -// sn_speed = 4 * (sn_speed_code - 1); -// } -// sn_speed *= sign; -// } -// double heading_rad = atan2(sn_speed, we_speed); // 弧度 -// double heading_deg = heading_rad * 180.0 / M_PI; // 转换为度数 -// if (heading_deg < 0) { -// heading_deg += 360.0; -// } -// double speed = sqrt(we_speed * we_speed + sn_speed * sn_speed); -// Ground_Speed ret; -// ret.angle = heading_deg; -// ret.speed = speed; -// return ret; -// } -Ground_Speed surface_velocity(std::string_view msg, Ground_Speed_Type st) { - double we_speed = 0.000001; - int we_speed_code = bin2(msg.substr(46, 10)); - if (we_speed_code != 0) { - int sign = (msg.at(45) == '0') ? 1 : -1; - if (st == Ground_Speed_Type::Ground_Normal) { - we_speed = we_speed_code - 1; - } - else { - we_speed = 4 * (we_speed_code - 1); - } - we_speed *= sign; - } - double sn_speed = 0.000001; - int sn_speed_code = bin2(msg.substr(57, 10)); - if (sn_speed_code != 0) { - int sign = (msg.at(56) == '0') ? 1 : -1; - if (st == Ground_Speed_Type::Ground_Normal) { - sn_speed = sn_speed_code - 1; - } - else { - sn_speed = 4 * (sn_speed_code - 1); - } - sn_speed *= sign; - } - // 以正北为起点,计算方向 - double heading_rad = atan2(we_speed, sn_speed); // 弧度 - double heading_deg = heading_rad * 180.0 / M_PI; // 转换为度数 - if (heading_deg < 0) { - heading_deg += 360.0; - } - double speed = sqrt(we_speed * we_speed + sn_speed * sn_speed); - Ground_Speed ret; - ret.angle = heading_deg; - ret.speed = speed; - return ret; -} -double decode_ground_track_7(std::string_view code) { - auto encodedSpeed = bin2(code); - return encodedSpeed / 360.0 * 128.0; -} -double decode_movement_7(std::string_view code) { - int encodedSpeed = bin2(code); - double decodedSpeed = -1; // -1 表示速度不可用 - if (encodedSpeed == 0) { - decodedSpeed = -1; // 无速度信息 - } - else if (encodedSpeed == 1) { - decodedSpeed = 0.125; // 停止,速度为 0.125 kt - } - else if (encodedSpeed >= 2 && encodedSpeed <= 8) { - decodedSpeed = 0.125 * (encodedSpeed - 1); // 步长为 0.125 kt - } - else if (encodedSpeed >= 9 && encodedSpeed <= 12) { - decodedSpeed = 1.0 + 0.25 * (encodedSpeed - 8); // 步长为 0.25 kt - } - else if (encodedSpeed >= 13 && encodedSpeed <= 38) { - decodedSpeed = 2.0 + 0.5 * (encodedSpeed - 12); // 步长为 0.5 kt - } - else if (encodedSpeed >= 39 && encodedSpeed <= 93) { - decodedSpeed = 15.0 + (encodedSpeed - 38); // 步长为 1 kt - } - else if (encodedSpeed >= 94 && encodedSpeed <= 108) { - decodedSpeed = 70.0 + 2.0 * (encodedSpeed - 93); // 步长为 2 kt - } - else if (encodedSpeed >= 109 && encodedSpeed <= 123) { - decodedSpeed = 100.0 + 5.0 * (encodedSpeed - 108); // 步长为 5 kt - } - else if (encodedSpeed == 124) { - decodedSpeed = 175.0; // 或者某种其他大于 175 kt 的速度 - } - else if (encodedSpeed >= 125 && encodedSpeed <= 127) { - decodedSpeed = -1; // 保留值,不使用 - } - return decodedSpeed; -} -std::string encode_movement_7(double v) { - unsigned long long movement_code; - if (v < 0) { - // 负速度不合逻辑,这里处理成0 - movement_code = 0; - } - else if (v < 0.125) { - movement_code = 1; - } - else if (v < 1) { - movement_code = 2 + static_cast((v - 0.125) / 0.125); - } - else if (v < 2) { - movement_code = 9 + static_cast((v - 1) / 0.25); - } - else if (v < 15) { - movement_code = 13 + static_cast((v - 2) / 0.5); - } - else if (v < 70) { - movement_code = 39 + static_cast((v - 15) / 1); - } - else if (v < 100) { - movement_code = 94 + static_cast((v - 70) / 2); - } - else if (v < 175) { - movement_code = 109 + static_cast((v - 100) / 5); - } - else { - movement_code = 124; - } - return to_bin<7>(movement_code); -} -std::string encode_ground_track_7(double degree) { - return to_bin<7>(degree * 128.0 / 360.0); -} -// ------------------------------------------ -// BDS 0,8 -// ADS-B TC=1-4 -// Aircraft identification and category -// ------------------------------------------ -/** - * @brief 获取飞机类别编号。 - * - * @param msg 112位二进制字符串。 - * - * @return 飞机类别编号。 - */ -int category(std::string_view msg) { - int tc = type_code(msg); - CE(!tc || tc < 1 || tc > 4, bin2hex(msg) + ": Not an identification message") - std::string msgbin(msg); - std::string mebin(msgbin.substr(32, 55)); // 55 bits from index 32 to 87 - return bin2(mebin.substr(5, 3)); // 3 bits from index 5 to 8 -} -/** - * @brief 设置消息的分类代码。 - * - * 该函数将给定的分类代码 (CA) 设置到指定的 112 位二进制消息字符串中。 - * 分类代码由 3 位二进制组成,位于消息的第 38 到 40 位之间。 - * 如果分类代码不在 0 到 7 的范围内(即 CA 的有效值),函数将抛出异常。 - * - * @param msg 输入和输出的 112 位二进制字符串,代表 ADS-B - * 消息,该消息将在函数内部被修改以 包含新的分类代码。 - * @param ca 要设置的分类代码,范围应为 0 到 7。 - * - * @throw std::out_of_range 如果 CA 超出有效范围(0 到 7)。 - */ -void set_category(std::string& msg, int ca) { - if (ca < 0 || ca > 7) { - throw std::out_of_range("CA must be in the range of 0 to 7"); - } - msg.replace(37, 3, std::bitset<3>(ca).to_string()); -} -std::string get_call_sign(std::string_view msg_bin) { - // int tc = type_code(msg); - // CE(!tc || tc < 1 || tc > 4, + "call_sign : Not an identification message - // " + bin2hex(msg)) - const std::string chars = - "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"; - // std::string msgbin(msg); - // std::string csbin(msgbin.substr(40, 56)); // 56 bits from index 40 to 96 - std::string cs; - for (int i = 0; i < 8; ++i) { - cs += chars[bin2(msg_bin.substr(32 + 8 + i * 6, 6))]; // 6 bits each - } - // Clean string, remove spaces and marks, if any. - cs.erase(std::remove(cs.begin(), cs.end(), '#'), cs.end()); - return cs; -} -void set_call_sign(std::string& msg, std::string_view call_sign) { - msg.replace(40, 48, get_call_sign_code(call_sign)); -} -std::string get_call_sign_code(std::string_view call_sign) { - std::string cs(call_sign); - cs.resize(8, '#'); // 如果呼号不足8个字符,用 '#' 填充 - std::string csbin; - const std::string chars = - "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"; - for (char c : cs) { - auto pos = chars.find(c); - if (pos != std::string::npos) { - // 将字符的索引转换为6位二进制 - csbin += std::bitset<6>(pos).to_string(); - } - else { - throw std::runtime_error("Invalid character in call sign: " + - std::string(1, c)); - } - } - return csbin; -} -// ------------------------------------------ -// BDS 0,9 -// ADS-B TC=19 -// Aircraft Airborne velocity -// ------------------------------------------ -Air_Speed airborne_velocity(std::string_view msg) { - CE(type_code(msg) != 19, - std::string(msg) + ": Not an airborne velocity message, expecting TC=19") - std::string mb(msg.substr(32)); - auto subtype = static_cast(bin2(mb.substr(5, 3))); - Air_Speed result; - result.speed_type = "GS"; // default value - // std::cout << "14, == " << (bin2(mb.substr(14, 10)) == 0) << - // std::endl; std::cout << "25,10 == " << bin2(mb.substr(25, 10)) << - // std::endl; std::cout << "25,10 == " << (bin2(mb.substr(25, 10)) == 0) - // << std::endl; std::cout << "subtype = " << subtype << std::endl; - if (bin2(mb.substr(14, 10)) == 0 || bin2(mb.substr(25, 10)) == 0) { - // std::cout << "1111111111111111122222222222" << subtype << std::endl; - return result; // Return early with defaults - } - if (subtype == 1 || subtype == 2) { - // std::cout << "11111111111111111" << subtype << std::endl; - auto v_ew = bin2(mb.substr(14, 10)); - auto v_ns = bin2(mb.substr(25, 10)); - // std::cout << "v_ew = " << v_ew << std::endl; - // std::cout << "v_ns = " << v_ns << std::endl; - if (v_ew != 0 && v_ns != 0) { - int v_ew_sign = (mb[13] == '1') ? -1 : 1; - v_ew -= 1; // east-west velocity - if (subtype == 2) { - // Supersonic - v_ew *= 4; + static constexpr double cpr_divisor = 131072.0; + static constexpr double PI = 3.14159265358979323846; + /* + ------------------------------------------ + BDS 0,5 + ADS-B TC=9-18 + Airborne position + ------------------------------------------ + */ + /** + * @brief 解码飞机高度。 + * + * @param msg 112位二进制字符串。 + * + * @return 高度,单位为英尺。 + */ + // df 17 tc 9-18 tc=20-22 Airborne position 报文 + // int altitude(const std::string& msg) { + // int tc = type_code(msg); + // CE(!tc || tc < 9 || tc == 19 || tc > 22, std::string(msg) + ": Not an airborne + // position message") std::string mb(msg.substr(32)); std::string altbin = + // mb.substr(8, 12); // 12 bits from index 8 to 20 if (tc < 19) + // { + // std::string altcode(altbin.substr(0, 6) + "0" + altbin.substr(6)); + // int alt = altitude13_ft(altcode); + // if (alt != -999999) + // { + // return alt; + // } + // CE(true, "alt" + std::to_string(alt) + ": Not an airborne position + // message") return -1; + // } + // return static_cast(bin2(altbin) * 3.28084); // convert to + // feet + // } + // ------------------------------------------ + // BDS 0,6 + // ADS-B TC=5-8 + // Surface movement + // ------------------------------------------ + // Ground_Speed surface_velocity(const std::string& msg, Ground_Speed_Type st) { + // double we_speed = 0.000001; + // int we_speed_code = bin2(msg.substr(46, 10)); + // if (we_speed_code != 0) { + // int sign = (msg.at(45) == '0') ? 1 : -1 ; + // if (st == Ground_Speed_Type::Ground_Normal) { + // we_speed = we_speed_code - 1; + // } else { + // we_speed = 4 * (we_speed_code - 1); + // } + // we_speed *= sign; + // } + // double sn_speed = 0.000001; + // int sn_speed_code = bin2(msg.substr(57, 10)); + // if (sn_speed_code != 0) { + // int sign = (msg.at(56) == '0') ? 1 : -1 ; + // if (st == Ground_Speed_Type::Ground_Normal) { + // sn_speed = sn_speed_code - 1; + // } else { + // sn_speed = 4 * (sn_speed_code - 1); + // } + // sn_speed *= sign; + // } + // double heading_rad = atan2(sn_speed, we_speed); // 弧度 + // double heading_deg = heading_rad * 180.0 / M_PI; // 转换为度数 + // if (heading_deg < 0) { + // heading_deg += 360.0; + // } + // double speed = sqrt(we_speed * we_speed + sn_speed * sn_speed); + // Ground_Speed ret; + // ret.angle = heading_deg; + // ret.speed = speed; + // return ret; + // } + Ground_Speed surface_velocity(std::string_view msg, Ground_Speed_Type st) { + double we_speed = 0.000001; + int we_speed_code = bin2(msg.substr(46, 10)); + if (we_speed_code != 0) { + int sign = (msg.at(45) == '0') ? 1 : -1; + if (st == Ground_Speed_Type::Ground_Normal) { + we_speed = we_speed_code - 1; + } else { + we_speed = 4 * (we_speed_code - 1); } - long long v_ns_sign = (mb[24] == '1') ? -1 : 1; - v_ns -= 1; // north-south velocity - if (subtype == 2) { - // Supersonic - v_ns *= 4; + we_speed *= sign; + } + double sn_speed = 0.000001; + int sn_speed_code = bin2(msg.substr(57, 10)); + if (sn_speed_code != 0) { + int sign = (msg.at(56) == '0') ? 1 : -1; + if (st == Ground_Speed_Type::Ground_Normal) { + sn_speed = sn_speed_code - 1; + } else { + sn_speed = 4 * (sn_speed_code - 1); } - long long v_we = v_ew_sign * v_ew; - long long v_sn = v_ns_sign * v_ns; - // std::cout << "v_we == " << v_we << std::endl; - // std::cout << "v_sn == " << v_sn << std::endl; - result.speed = - static_cast(std::sqrt(v_sn * v_sn + v_we * v_we)); // unit in kts - double trk = std::atan2(v_we, v_sn); - // std::cout << trk << std::endl; - trk = (trk * 180.0 / M_PI); // convert to degrees - trk = (trk >= 0) ? trk : trk + 360; // no negative val - result.angle = trk; - result.direction_source = "TRUE_NORTH"; + sn_speed *= sign; } - } - else { - if (mb[13] == '1') { - result.angle = bin2(mb.substr(14, 10)) / 1024.0 * 360.0; + // 以正北为起点,计算方向 + double heading_rad = atan2(we_speed, sn_speed); // 弧度 + double heading_deg = heading_rad * 180.0 / M_PI; // 转换为度数 + if (heading_deg < 0) { + heading_deg += 360.0; } - int raw_spd = bin2(mb.substr(25, 10)); - if (raw_spd != 0) { - result.speed = raw_spd - 1; + double speed = sqrt(we_speed * we_speed + sn_speed * sn_speed); + Ground_Speed ret; + ret.angle = heading_deg; + ret.speed = speed; + return ret; + } + + double decode_ground_track_7(std::string_view code) { + auto encodedSpeed = bin2(code); + return encodedSpeed / 360.0 * 128.0; + } + + double decode_movement_7(std::string_view code) { + int encodedSpeed = bin2(code); + double decodedSpeed = -1; // -1 表示速度不可用 + if (encodedSpeed == 0) { + decodedSpeed = -1; // 无速度信息 + } else if (encodedSpeed == 1) { + decodedSpeed = 0.125; // 停止,速度为 0.125 kt + } else if (encodedSpeed >= 2 && encodedSpeed <= 8) { + decodedSpeed = 0.125 * (encodedSpeed - 1); // 步长为 0.125 kt + } else if (encodedSpeed >= 9 && encodedSpeed <= 12) { + decodedSpeed = 1.0 + 0.25 * (encodedSpeed - 8); // 步长为 0.25 kt + } else if (encodedSpeed >= 13 && encodedSpeed <= 38) { + decodedSpeed = 2.0 + 0.5 * (encodedSpeed - 12); // 步长为 0.5 kt + } else if (encodedSpeed >= 39 && encodedSpeed <= 93) { + decodedSpeed = 15.0 + (encodedSpeed - 38); // 步长为 1 kt + } else if (encodedSpeed >= 94 && encodedSpeed <= 108) { + decodedSpeed = 70.0 + 2.0 * (encodedSpeed - 93); // 步长为 2 kt + } else if (encodedSpeed >= 109 && encodedSpeed <= 123) { + decodedSpeed = 100.0 + 5.0 * (encodedSpeed - 108); // 步长为 5 kt + } else if (encodedSpeed == 124) { + decodedSpeed = 175.0; // 或者某种其他大于 175 kt 的速度 + } else if (encodedSpeed >= 125 && encodedSpeed <= 127) { + decodedSpeed = -1; // 保留值,不使用 } - if (subtype == 4 && raw_spd != 0) { - result.speed = (raw_spd - 1) * 4; + return decodedSpeed; + } + + std::string encode_movement_7(double v) { + unsigned long long movement_code; + if (v < 0) { + // 负速度不合逻辑,这里处理成0 + movement_code = 0; + } else if (v < 0.125) { + movement_code = 1; + } else if (v < 1) { + movement_code = 2 + static_cast((v - 0.125) / 0.125); + } else if (v < 2) { + movement_code = 9 + static_cast((v - 1) / 0.25); + } else if (v < 15) { + movement_code = 13 + static_cast((v - 2) / 0.5); + } else if (v < 70) { + movement_code = 39 + static_cast((v - 15) / 1); + } else if (v < 100) { + movement_code = 94 + static_cast((v - 70) / 2); + } else if (v < 175) { + movement_code = 109 + static_cast((v - 100) / 5); + } else { + movement_code = 124; } - result.speed_type = (mb[24] == '0') ? "IAS" : "TAS"; - result.direction_source = "MAGNETIC_NORTH"; + return to_bin<7>(movement_code); } - // Vertical rate processing - std::string vr_source = (mb[35] == '0') ? "GNSS" : "BARO"; - int vr_sign = (mb[36] == '1') ? -1 : 1; - int vr = bin2(mb.substr(37, 9)); // Assume 9 bits for vertical rate - // std::cout << "vr == " << vr << std::endl; - result.vertical_rate = - (vr == 0) ? 0 : vr_sign * (vr - 1) * 64; // 直接赋值为 0 - result.vertical_rate_source = vr_source; - return result; -} -/** - * @brief 解码GNSS与气压高度之间的差异。 - * - * @param msg 112位二进制字符串,TC=19。 - * - * @return 高度差,单位为英尺。如果返回负值,表示GNSS高度低于气压高度。 - */ -double altitude_diff(std::string_view msg) { - auto tc_opt = type_code(msg); - CE(!tc_opt || tc_opt != 19, - std::string(msg) + ": Not an airborne velocity message, expecting TC=19") - std::string msgbin(msg); - int sign = (msgbin[80] == '1') ? -1 : 1; - auto value = bin2(msgbin.substr(81, 7)); - CE(value == 0 || value == 127, "value error!") - return sign * (value - 1) * 25; // in ft -} -std::string vertical_rate_code_10(double vertical_rate) { - if (vertical_rate == 0) { - return "0000000000"; + + std::string encode_ground_track_7(double degree) { + return to_bin<7>(degree * 128.0 / 360.0); } - int sign_bit = vertical_rate > 0 ? 0 : 1; - vertical_rate = std::abs(vertical_rate); - int decimal_value = static_cast(std::round(vertical_rate / 64)) + 1; - if (decimal_value > 511) { - decimal_value = 511; // Maximum value for 9 bits is 511 + + // ------------------------------------------ + // BDS 0,8 + // ADS-B TC=1-4 + // Aircraft identification and category + // ------------------------------------------ + /** + * @brief 获取飞机类别编号。 + * + * @param msg 112位二进制字符串。 + * + * @return 飞机类别编号。 + */ + int category(std::string_view msg) { + int tc = type_code(msg); + CE(!tc || tc < 1 || tc > 4, bin2hex(msg) + ": Not an identification message") + std::string msgbin(msg); + std::string mebin(msgbin.substr(32, 55)); // 55 bits from index 32 to 87 + return bin2(mebin.substr(5, 3)); // 3 bits from index 5 to 8 } - std::bitset<9> vr_bits(decimal_value); - return std::bitset<1>(sign_bit).to_string() + vr_bits.to_string(); -} -std::string GNSS_barometric_altitudes_difference_8(double altitude_difference) { - if (altitude_difference == 0) { - return "000000000"; // All zeros indicate no information - } - int sign_bit = - (altitude_difference < 0) ? 1 : 0; // 1 for negative, 0 for positive - altitude_difference = std::abs(altitude_difference); - int decimal_value = static_cast(std::round(altitude_difference / 25)); - if (decimal_value > 127) { - decimal_value = 127; // Cap at the maximum value for 7 bits - } - std::bitset<7> magnitude_bits(decimal_value); - std::bitset<1> sign_bitset(sign_bit); - return sign_bitset.to_string() + magnitude_bits.to_string(); -} -// Helper function to generate 10-bit velocity block from velocity component -int generate_velocity_component(Ground_Speed_Type subType, - double velocity_component) { - // Special case: if velocity component is 0, return 0 (no velocity - // information) - if (velocity_component == 0) { - return 0; - } - // For supersonic, divide velocity by 4 - double velocity = subType == Ground_Speed_Type::Ground_Supersonic - ? velocity_component / 4.0 - : velocity_component; - // Round and ensure it's within the 10-bit range - int encoded_velocity = static_cast(std::round(std::abs(velocity))); - // Ensure the value fits in a 10-bit block (0-1023) - if (encoded_velocity > 1023) { - encoded_velocity = 1023; // Cap at maximum value for 10 bits - } - return encoded_velocity; -} -std::string encode_velocity_22(Ground_Speed_Type subType, double track_angle, - double ground_speed) { - double V_x = - ground_speed * std::cos(track_angle * PI / 180.0); // West-East component - double V_y = ground_speed * - std::sin(track_angle * PI / 180.0); // South-North component - // Step 2: Determine sign bits - int Sew = (V_x >= 0) ? 0 : 1; // 0 for East, 1 for West - int Sns = (V_y >= 0) ? 0 : 1; // 0 for North, 1 for South - // Step 3: Generate the 10-bit velocity blocks - int Vew = generate_velocity_component(subType, - V_x); // East-West component (10 bits) - int Vns = generate_velocity_component(subType, - V_y); // North-South component (10 bits) - // Step 4: Create the final encoded result as a binary string - std::bitset<10> Vew_bits(Vew); // 10-bit binary for East-West velocity - std::bitset<10> Vns_bits(Vns); // 10-bit binary for North-South velocity - // Convert sign bits and velocity bits into a string representation - std::string encoded_result = std::to_string(Sew) + "" + Vew_bits.to_string() + - "" + std::to_string(Sns) + "" + - Vns_bits.to_string(); - return encoded_result; -} -std::string encode_magnetic_heading_11(double heading) { - // Step 1: Set heading status (SH) - // Step 2: Encode the magnetic heading (HDG) - // Ensure the heading is within the range of 0 to 360 - if (heading < 0 || heading >= 360) { - std::cerr - << "Invalid heading value. It must be in the range of 0 to 360 degrees." - << std::endl; - return ""; - } - // Calculate the decimal value based on LSB (360/1024 degrees) - int encoded_heading = static_cast(std::round(heading * 1024 / 360)); - // Ensure the value is within 10-bit range (0-1023) - if (encoded_heading > 1023) { - std::cerr << "Encoded heading value out of range." << std::endl; - return ""; - } - // Convert the decimal value to 10-bit binary - std::bitset<10> HDG_bits(encoded_heading); - return "1" + HDG_bits.to_string(); -} -std::string encode_airspeed_10(Air_Speed_Code_Type subtype, double airspeed) { - // Step 2: Encode the airspeed (AS) - int encoded_airspeed = 0; - if (subtype == Air_Speed_Code_Type::Air_Normal) { - // Sub-type 1: Speed = Decimal value - 1 - encoded_airspeed = static_cast(std::round(airspeed)) + 1; - } - else if (subtype == Air_Speed_Code_Type::Air_Supersonic) { - // Sub-type 2: Speed = 4 x (Decimal value - 1) - encoded_airspeed = static_cast(std::round(airspeed / 4)) + 1; - } - else { - std::cerr << "Invalid subtype. Must be 1 (subsonic) or 2 (supersonic)." - << std::endl; - return ""; - } - // Ensure the encoded airspeed is within 10-bit range (0-1023) - if (encoded_airspeed < 0 || encoded_airspeed > 1023) { - std::cerr << "Encoded airspeed value out of range." << std::endl; - return ""; - } - return std::bitset<10>(encoded_airspeed).to_string(); -} -// ------------------------------------------ -// BDS 1,0 -// Data link capability report -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码1,0。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码1,0则返回 true,否则返回 false。 - */ -bool is10(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - // first 8 bits must be 0x10 - if (d.substr(0, 8) != "00010000") { - return false; - } - // bit 10 to 14 are reserved - if (bin2(d.substr(9, 5)) != 0) { - return false; - } - // overlay capability conflict - if (d[14] == '1' && bin2(d.substr(16, 7)) < 5) { - return false; - } - if (d[14] == '0' && bin2(d.substr(16, 7)) > 4) { - return false; - } - return true; -} -/** - * @brief 返回覆盖控制能力。 - * - * @param msg 112位二进制字符串。 - * - * @return 整数:指示应答机是否具备OVC能力。 - */ -int ovc10(std::string_view msg) { - std::string d = databin(msg); - return static_cast(d[14] - '0'); -} -// ------------------------------------------ -// BDS 1,7 -// Common usage GICB capability report -// ------------------------------------------ -/** - * @brief 返回覆盖控制能力。 - * - * @param msg 112位二进制字符串。 - * - * @return 整数:指示应答机是否具备OVC能力。 - */ -bool is17(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - if (!allzeros(d.substr(24, 32))) { - return false; - } - auto caps = cap17(msg); - // at least you can respond who you are - if (std::find(caps.begin(), caps.end(), "BDS20") == caps.end()) { - return false; - } - return true; -} -/** - * @brief 从BDS 1,7消息中提取能力。 - * - * @param msg 112位二进制字符串。 - * - * @return 支持的BDS代码列表。 - */ -std::vector cap17(std::string_view msg) { - std::vector allbds = { - "05", "06", "07", "08", "09", "0A", "20", "21", "40", "41", "42", "43", - "44", "45", "48", "50", "51", "52", "53", "54", "55", "56", "5F", "60" - }; - std::string d = databin(msg); - std::vector capacity; - for (size_t i = 0; i < 24; ++i) { - if (d[i] == '1') { - capacity.push_back("BDS" + allbds[i]); + + /** + * @brief 设置消息的分类代码。 + * + * 该函数将给定的分类代码 (CA) 设置到指定的 112 位二进制消息字符串中。 + * 分类代码由 3 位二进制组成,位于消息的第 38 到 40 位之间。 + * 如果分类代码不在 0 到 7 的范围内(即 CA 的有效值),函数将抛出异常。 + * + * @param msg 输入和输出的 112 位二进制字符串,代表 ADS-B + * 消息,该消息将在函数内部被修改以 包含新的分类代码。 + * @param ca 要设置的分类代码,范围应为 0 到 7。 + * + * @throw std::out_of_range 如果 CA 超出有效范围(0 到 7)。 + */ + void set_category(std::string &msg, int ca) { + if (ca < 0 || ca > 7) { + throw std::out_of_range("CA must be in the range of 0 to 7"); } + msg.replace(37, 3, std::bitset<3>(ca).to_string()); } - return capacity; -} -// ------------------------------------------ -// BDS 2,0 -// Aircraft identification -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码2,0。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码2,0则返回 true,否则返回 false。 - */ -bool is20(std::string_view msg) { - if (allzeros(msg)) { - return false; + + std::string get_call_sign(std::string_view msg_bin) { + // int tc = type_code(msg); + // CE(!tc || tc < 1 || tc > 4, + "call_sign : Not an identification message + // " + bin2hex(msg)) + const std::string chars = + "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"; + // std::string msgbin(msg); + // std::string csbin(msgbin.substr(40, 56)); // 56 bits from index 40 to 96 + std::string cs; + for (int i = 0; i < 8; ++i) { + cs += chars[bin2(msg_bin.substr(32 + 8 + i * 6, 6))]; // 6 bits each + } + // Clean string, remove spaces and marks, if any. + cs.erase(std::remove(cs.begin(), cs.end(), '#'), cs.end()); + return cs; } - std::string d = databin(msg); - if (d.substr(0, 8) != "00100000") { - return false; + + void set_call_sign(std::string &msg, std::string_view call_sign) { + msg.replace(40, 48, get_call_sign_code(call_sign)); } - // allow empty callsign - if (bin2(d.substr(8, 48)) == 0) { - return true; + + std::string get_call_sign_code(std::string_view call_sign) { + std::string cs(call_sign); + cs.resize(8, '#'); // 如果呼号不足8个字符,用 '#' 填充 + std::string csbin; + const std::string chars = + "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"; + for (char c: cs) { + auto pos = chars.find(c); + if (pos != std::string::npos) { + // 将字符的索引转换为6位二进制 + csbin += std::bitset<6>(pos).to_string(); + } else { + throw std::runtime_error("Invalid character in call sign: " + + std::string(1, c)); + } + } + return csbin; } - if (cs20(msg).find('#') != std::string::npos) { - return false; + + // ------------------------------------------ + // BDS 0,9 + // ADS-B TC=19 + // Aircraft Airborne velocity + // ------------------------------------------ + Air_Speed airborne_velocity(std::string_view msg) { + CE(type_code(msg) != 19, + std::string(msg) + ": Not an airborne velocity message, expecting TC=19") + std::string mb(msg.substr(32)); + auto subtype = static_cast(bin2(mb.substr(5, 3))); + Air_Speed result; + result.speed_type = "GS"; // default value + // std::cout << "14, == " << (bin2(mb.substr(14, 10)) == 0) << + // std::endl; std::cout << "25,10 == " << bin2(mb.substr(25, 10)) << + // std::endl; std::cout << "25,10 == " << (bin2(mb.substr(25, 10)) == 0) + // << std::endl; std::cout << "subtype = " << subtype << std::endl; + if (bin2(mb.substr(14, 10)) == 0 || bin2(mb.substr(25, 10)) == 0) { + // std::cout << "1111111111111111122222222222" << subtype << std::endl; + return result; // Return early with defaults + } + if (subtype == 1 || subtype == 2) { + // std::cout << "11111111111111111" << subtype << std::endl; + auto v_ew = bin2(mb.substr(14, 10)); + auto v_ns = bin2(mb.substr(25, 10)); + // std::cout << "v_ew = " << v_ew << std::endl; + // std::cout << "v_ns = " << v_ns << std::endl; + if (v_ew != 0 && v_ns != 0) { + int v_ew_sign = (mb[13] == '1') ? -1 : 1; + v_ew -= 1; // east-west velocity + if (subtype == 2) { + // Supersonic + v_ew *= 4; + } + long long v_ns_sign = (mb[24] == '1') ? -1 : 1; + v_ns -= 1; // north-south velocity + if (subtype == 2) { + // Supersonic + v_ns *= 4; + } + long long v_we = v_ew_sign * v_ew; + long long v_sn = v_ns_sign * v_ns; + // std::cout << "v_we == " << v_we << std::endl; + // std::cout << "v_sn == " << v_sn << std::endl; + result.speed = + static_cast(std::sqrt(v_sn * v_sn + v_we * v_we)); // unit in kts + double trk = std::atan2(v_we, v_sn); + // std::cout << trk << std::endl; + trk = (trk * 180.0 / M_PI); // convert to degrees + trk = (trk >= 0) ? trk : trk + 360; // no negative val + result.angle = trk; + result.direction_source = "TRUE_NORTH"; + } + } else { + if (mb[13] == '1') { + result.angle = bin2(mb.substr(14, 10)) / 1024.0 * 360.0; + } + int raw_spd = bin2(mb.substr(25, 10)); + if (raw_spd != 0) { + result.speed = raw_spd - 1; + } + if (subtype == 4 && raw_spd != 0) { + result.speed = (raw_spd - 1) * 4; + } + result.speed_type = (mb[24] == '0') ? "IAS" : "TAS"; + result.direction_source = "MAGNETIC_NORTH"; + } + // Vertical rate processing + std::string vr_source = (mb[35] == '0') ? "GNSS" : "BARO"; + int vr_sign = (mb[36] == '1') ? -1 : 1; + int vr = bin2(mb.substr(37, 9)); // Assume 9 bits for vertical rate + // std::cout << "vr == " << vr << std::endl; + result.vertical_rate = + (vr == 0) ? 0 : vr_sign * (vr - 1) * 64; // 直接赋值为 0 + result.vertical_rate_source = vr_source; + return result; } - return true; -} -/** - * @brief 获取飞机呼号。 - * - * @param msg 112位二进制字符串。 - * - * @return 呼号,最多 8 个字符。 - */ -std::string cs20(std::string_view msg) { - const std::string chars = - "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"; - std::string d = databin(msg); - std::string cs; - for (size_t i = 0; i < 8; ++i) { - cs += chars[bin2(d.substr(8 + i * 6, 6))]; + + /** + * @brief 解码GNSS与气压高度之间的差异。 + * + * @param msg 112位二进制字符串,TC=19。 + * + * @return 高度差,单位为英尺。如果返回负值,表示GNSS高度低于气压高度。 + */ + double altitude_diff(std::string_view msg) { + auto tc_opt = type_code(msg); + CE(!tc_opt || tc_opt != 19, + std::string(msg) + ": Not an airborne velocity message, expecting TC=19") + std::string msgbin(msg); + int sign = (msgbin[80] == '1') ? -1 : 1; + auto value = bin2(msgbin.substr(81, 7)); + CE(value == 0 || value == 127, "value error!") + return sign * (value - 1) * 25; // in ft } - return cs; -} -// ------------------------------------------ -// BDS 3,0 -// ACAS active resolution advisory -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码3,0。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码3,0则返回 true,否则返回 false。 - */ -bool is30(std::string_view msg) { - if (allzeros(msg)) { - return false; + + std::string vertical_rate_code_10(double vertical_rate) { + if (vertical_rate == 0) { + return "0000000000"; + } + int sign_bit = vertical_rate > 0 ? 0 : 1; + vertical_rate = std::abs(vertical_rate); + int decimal_value = static_cast(std::round(vertical_rate / 64)) + 1; + if (decimal_value > 511) { + decimal_value = 511; // Maximum value for 9 bits is 511 + } + std::bitset<9> vr_bits(decimal_value); + return std::bitset<1>(sign_bit).to_string() + vr_bits.to_string(); } - std::string d = databin(msg); - if (d.substr(0, 8) != "00110000") { - return false; + + std::string GNSS_barometric_altitudes_difference_8(double altitude_difference) { + if (altitude_difference == 0) { + return "000000000"; // All zeros indicate no information + } + int sign_bit = + (altitude_difference < 0) ? 1 : 0; // 1 for negative, 0 for positive + altitude_difference = std::abs(altitude_difference); + int decimal_value = static_cast(std::round(altitude_difference / 25)); + if (decimal_value > 127) { + decimal_value = 127; // Cap at the maximum value for 7 bits + } + std::bitset<7> magnitude_bits(decimal_value); + std::bitset<1> sign_bitset(sign_bit); + return sign_bitset.to_string() + magnitude_bits.to_string(); } - // threat type 3 not assigned - if (d.substr(28, 2) == "11") { - return false; + + // Helper function to generate 10-bit velocity block from velocity component + int generate_velocity_component(Ground_Speed_Type subType, + double velocity_component) { + // Special case: if velocity component is 0, return 0 (no velocity + // information) + if (velocity_component == 0) { + return 0; + } + // For supersonic, divide velocity by 4 + double velocity = subType == Ground_Speed_Type::Ground_Supersonic + ? velocity_component / 4.0 + : velocity_component; + // Round and ensure it's within the 10-bit range + int encoded_velocity = static_cast(std::round(std::abs(velocity))); + // Ensure the value fits in a 10-bit block (0-1023) + if (encoded_velocity > 1023) { + encoded_velocity = 1023; // Cap at maximum value for 10 bits + } + return encoded_velocity; } - // reserved for ACAS III, in far future - if (bin2(d.substr(15, 7)) >= 48) { - return false; + + std::string encode_velocity_22(Ground_Speed_Type subType, double track_angle, + double ground_speed) { + double V_x = + ground_speed * std::cos(track_angle * PI / 180.0); // West-East component + double V_y = ground_speed * + std::sin(track_angle * PI / 180.0); // South-North component + // Step 2: Determine sign bits + int Sew = (V_x >= 0) ? 0 : 1; // 0 for East, 1 for West + int Sns = (V_y >= 0) ? 0 : 1; // 0 for North, 1 for South + // Step 3: Generate the 10-bit velocity blocks + int Vew = generate_velocity_component(subType, + V_x); // East-West component (10 bits) + int Vns = generate_velocity_component(subType, + V_y); // North-South component (10 bits) + // Step 4: Create the final encoded result as a binary string + std::bitset<10> Vew_bits(Vew); // 10-bit binary for East-West velocity + std::bitset<10> Vns_bits(Vns); // 10-bit binary for North-South velocity + // Convert sign bits and velocity bits into a string representation + std::string encoded_result = std::to_string(Sew) + "" + Vew_bits.to_string() + + "" + std::to_string(Sns) + "" + + Vns_bits.to_string(); + return encoded_result; } - return true; -} -// ------------------------------------------ -// BDS 4,0 -// Selected vertical intention -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码4,0。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码4,0则返回 true,否则返回 false。 - */ -bool is40(std::string_view msg) { - if (allzeros(msg)) { - return false; + + std::string encode_magnetic_heading_11(double heading) { + // Step 1: Set heading status (SH) + // Step 2: Encode the magnetic heading (HDG) + // Ensure the heading is within the range of 0 to 360 + if (heading < 0 || heading >= 360) { + std::cerr + << "Invalid heading value. It must be in the range of 0 to 360 degrees." + << std::endl; + return ""; + } + // Calculate the decimal value based on LSB (360/1024 degrees) + int encoded_heading = static_cast(std::round(heading * 1024 / 360)); + // Ensure the value is within 10-bit range (0-1023) + if (encoded_heading > 1023) { + std::cerr << "Encoded heading value out of range." << std::endl; + return ""; + } + // Convert the decimal value to 10-bit binary + std::bitset<10> HDG_bits(encoded_heading); + return "1" + HDG_bits.to_string(); } - std::string d = databin(msg); - // status bit 1, 14, and 27 - if (wrongstatus(d, 1, 2, 13) || wrongstatus(d, 14, 15, 26) || - wrongstatus(d, 27, 28, 39) || wrongstatus(d, 48, 49, 51) || - wrongstatus(d, 54, 55, 56)) { - return false; + + std::string encode_airspeed_10(Air_Speed_Code_Type subtype, double airspeed) { + // Step 2: Encode the airspeed (AS) + int encoded_airspeed = 0; + if (subtype == Air_Speed_Code_Type::Air_Normal) { + // Sub-type 1: Speed = Decimal value - 1 + encoded_airspeed = static_cast(std::round(airspeed)) + 1; + } else if (subtype == Air_Speed_Code_Type::Air_Supersonic) { + // Sub-type 2: Speed = 4 x (Decimal value - 1) + encoded_airspeed = static_cast(std::round(airspeed / 4)) + 1; + } else { + std::cerr << "Invalid subtype. Must be 1 (subsonic) or 2 (supersonic)." + << std::endl; + return ""; + } + // Ensure the encoded airspeed is within 10-bit range (0-1023) + if (encoded_airspeed < 0 || encoded_airspeed > 1023) { + std::cerr << "Encoded airspeed value out of range." << std::endl; + return ""; + } + return std::bitset<10>(encoded_airspeed).to_string(); } - // bits 40-47 and 52-53 shall all be zero - if (bin2(d.substr(39, 8)) != 0 || bin2(d.substr(51, 2)) != 0) { - return false; - } - return true; -} -/** - * @brief 获取选定的高度,来自MCP/FCU。 - * - * @param msg 112位二进制字符串。 - * - * @return 高度,单位为英尺。 - */ -RET selalt40mcp(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return std::nullopt; - } - int alt = bin2(d.substr(1, 12)) * 16; // ft - return alt; -} -/** - * @brief 获取选定的高度,来自FMS。 - * - * @param msg 112位二进制字符串。 - * - * @return 高度,单位为英尺。 - */ -RET selalt40fms(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return std::nullopt; - } - int alt = bin2(d.substr(14, 12)) * 16; // ft - return alt; -} -/** - * @brief 获取气压设置。 - * - * @param msg 112位二进制字符串。 - * - * @return 气压,单位为毫巴(millibar)。 - */ -RET p40baro(std::string_view msg) { - std::string d = databin(msg); - if (d[26] == '0') { - return std::nullopt; - } - double p = bin2(d.substr(27, 12)) * 0.1 + 800; // millibar - return p; -} -RET alt40mcp(std::string_view msg) { - // Deprecated warning - return selalt40mcp(msg); -} -RET alt40fms(std::string_view msg) { - // Deprecated warning - return selalt40fms(msg); -} -// ------------------------------------------ -// BDS 4,4 -// Meteorological routine air report -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码4,4。 - * - * 气象常规航空报告。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码4,4则返回 true,否则返回 false。 - */ -bool is44(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - // status bit 5, 35, 47, 50 - if (wrongstatus(d, 5, 6, 23) || wrongstatus(d, 35, 36, 46) || - wrongstatus(d, 47, 48, 49) || wrongstatus(d, 50, 51, 56)) { - return false; - } - // Bits 1-4 indicate source, values > 4 reserved - if (bin2(d.substr(0, 4)) > 4) { - return false; - } - auto w = wind44(msg); - if (w.has_value()) { - auto wind = w.value(); - int vw = wind.wind_speed; - int dw = wind.direction; - if (vw > 250) { + + // ------------------------------------------ + // BDS 1,0 + // Data link capability report + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码1,0。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码1,0则返回 true,否则返回 false。 + */ + bool is10(std::string_view msg) { + if (allzeros(msg)) { return false; } + std::string d = databin(msg); + // first 8 bits must be 0x10 + if (d.substr(0, 8) != "00010000") { + return false; + } + // bit 10 to 14 are reserved + if (bin2(d.substr(9, 5)) != 0) { + return false; + } + // overlay capability conflict + if (d[14] == '1' && bin2(d.substr(16, 7)) < 5) { + return false; + } + if (d[14] == '0' && bin2(d.substr(16, 7)) > 4) { + return false; + } + return true; } - auto tem = temp44(msg); - if (std::min(tem.temperature, tem.temperature_alternative) > 60 || - std::max(tem.temperature, tem.temperature_alternative) < -80) { - return false; + + /** + * @brief 返回覆盖控制能力。 + * + * @param msg 112位二进制字符串。 + * + * @return 整数:指示应答机是否具备OVC能力。 + */ + int ovc10(std::string_view msg) { + std::string d = databin(msg); + return static_cast(d[14] - '0'); } - return true; -} -/** - * @brief 获取风速和风向。 - * - * @param msg 112位二进制字符串。 - * - * @return (int, float): 风速(节,kt)、风向(度)。 - */ -RET wind44(std::string_view msg) { - std::string d = databin(msg); - int status = static_cast(d[4] - '0'); - if (status == 0) { - return std::nullopt; + + // ------------------------------------------ + // BDS 1,7 + // Common usage GICB capability report + // ------------------------------------------ + /** + * @brief 返回覆盖控制能力。 + * + * @param msg 112位二进制字符串。 + * + * @return 整数:指示应答机是否具备OVC能力。 + */ + bool is17(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + if (!allzeros(d.substr(24, 32))) { + return false; + } + auto caps = cap17(msg); + // at least you can respond who you are + if (std::find(caps.begin(), caps.end(), "BDS20") == caps.end()) { + return false; + } + return true; } - int speed = bin2(d.substr(5, 9)); // knots - double direction = bin2(d.substr(14, 9)) * 180.0 / 256.0; // degrees - return Wind{speed, direction}; -} -/** - * @brief 获取静态空气温度。 - * - * @param msg 112位二进制字符串。 - * - * @return float, float: 温度和替代温度,单位为摄氏度(Celsius)。 - * 注意:返回两个值是由于ICAO 9871 (2008) 附录 A-67 - * 中似乎存在的不一致性错误。 - */ -Temperature temp44(std::string_view msg) { - std::string d = databin(msg); - int sign = static_cast(d[23] - '0'); - int value = bin2(d.substr(24, 10)); - if (sign) { - value -= 1024; + + /** + * @brief 从BDS 1,7消息中提取能力。 + * + * @param msg 112位二进制字符串。 + * + * @return 支持的BDS代码列表。 + */ + std::vector cap17(std::string_view msg) { + std::vector allbds = { + "05", "06", "07", "08", "09", "0A", "20", "21", "40", "41", "42", "43", + "44", "45", "48", "50", "51", "52", "53", "54", "55", "56", "5F", "60" + }; + std::string d = databin(msg); + std::vector capacity; + for (size_t i = 0; i < 24; ++i) { + if (d[i] == '1') { + capacity.push_back("BDS" + allbds[i]); + } + } + return capacity; } - double temp = value * 0.25; // Celsius - double temp_alternative = value * 0.125; // Celsius - return {temp, temp_alternative}; -} -/** - * @brief 获取静态压力。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 静态压力,单位为 hPa。 - */ -int p44(std::string_view msg) { - std::string d = databin(msg); - if (d[34] == '0') { - return -1; + + // ------------------------------------------ + // BDS 2,0 + // Aircraft identification + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码2,0。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码2,0则返回 true,否则返回 false。 + */ + bool is20(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + if (d.substr(0, 8) != "00100000") { + return false; + } + // allow empty callsign + if (bin2(d.substr(8, 48)) == 0) { + return true; + } + if (cs20(msg).find('#') != std::string::npos) { + return false; + } + return true; } - int p = bin2(d.substr(35, 11)); // hPa - return p; -} -/** - * @brief 获取湿度。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 湿度百分比,范围为 [0 - 100] %。 - */ -double hum44(std::string_view msg) { - std::string d = databin(msg); - if (d[49] == '0') { - return -1; + + /** + * @brief 获取飞机呼号。 + * + * @param msg 112位二进制字符串。 + * + * @return 呼号,最多 8 个字符。 + */ + std::string cs20(std::string_view msg) { + const std::string chars = + "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######"; + std::string d = databin(msg); + std::string cs; + for (size_t i = 0; i < 8; ++i) { + cs += chars[bin2(d.substr(8 + i * 6, 6))]; + } + return cs; } - double hm = bin2(d.substr(50, 6)) * 100.0 / 64.0; // % - return hm; -} -/** - * @brief 获取湍流等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 湍流等级。0 = 无湍流 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int turb44(std::string_view msg) { - std::string d = databin(msg); - if (d[46] == '0') { - return -1; + + // ------------------------------------------ + // BDS 3,0 + // ACAS active resolution advisory + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码3,0。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码3,0则返回 true,否则返回 false。 + */ + bool is30(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + if (d.substr(0, 8) != "00110000") { + return false; + } + // threat type 3 not assigned + if (d.substr(28, 2) == "11") { + return false; + } + // reserved for ACAS III, in far future + if (bin2(d.substr(15, 7)) >= 48) { + return false; + } + return true; } - int turb = bin2(d.substr(47, 2)); - return turb; -} -// ------------------------------------------ -// BDS 4,5 -// Meteorological hazard report -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码4,5。 - * - * 气象危害报告。 - * - * @param msg 112位二进制字符串。 - * - * @return 如果是BDS代码4,5则返回 true,否则返回 false。 - */ -bool is45(std::string_view msg) { - if (allzeros(msg)) { - return false; + + // ------------------------------------------ + // BDS 4,0 + // Selected vertical intention + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码4,0。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码4,0则返回 true,否则返回 false。 + */ + bool is40(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 1, 14, and 27 + if (wrongstatus(d, 1, 2, 13) || wrongstatus(d, 14, 15, 26) || + wrongstatus(d, 27, 28, 39) || wrongstatus(d, 48, 49, 51) || + wrongstatus(d, 54, 55, 56)) { + return false; + } + // bits 40-47 and 52-53 shall all be zero + if (bin2(d.substr(39, 8)) != 0 || bin2(d.substr(51, 2)) != 0) { + return false; + } + return true; } - std::string d = databin(msg); - // status bit 1, 4, 7, 10, 13, 16, 27, 39 - if (wrongstatus(d, 1, 2, 3) || wrongstatus(d, 4, 5, 6) || - wrongstatus(d, 7, 8, 9) || wrongstatus(d, 10, 11, 12) || - wrongstatus(d, 13, 14, 15) || wrongstatus(d, 16, 17, 26) || - wrongstatus(d, 27, 28, 38) || wrongstatus(d, 39, 40, 51)) { - return false; + + /** + * @brief 获取选定的高度,来自MCP/FCU。 + * + * @param msg 112位二进制字符串。 + * + * @return 高度,单位为英尺。 + */ + RET selalt40mcp(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return std::nullopt; + } + int alt = bin2(d.substr(1, 12)) * 16; // ft + return alt; } - // reserved - if (bin2(d.substr(51, 5)) != 0) { - return false; + + /** + * @brief 获取选定的高度,来自FMS。 + * + * @param msg 112位二进制字符串。 + * + * @return 高度,单位为英尺。 + */ + RET selalt40fms(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return std::nullopt; + } + int alt = bin2(d.substr(14, 12)) * 16; // ft + return alt; } - auto temp = temp45(msg); - if (temp && (temp > 60 || temp < -80)) { - return false; + + /** + * @brief 获取气压设置。 + * + * @param msg 112位二进制字符串。 + * + * @return 气压,单位为毫巴(millibar)。 + */ + RET p40baro(std::string_view msg) { + std::string d = databin(msg); + if (d[26] == '0') { + return std::nullopt; + } + double p = bin2(d.substr(27, 12)) * 0.1 + 800; // millibar + return p; } - return true; -} -/** - * @brief 获取湍流等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 湍流等级。0 = 无湍流 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int turb45(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return -1; + + RET alt40mcp(std::string_view msg) { + // Deprecated warning + return selalt40mcp(msg); } - int turb = bin2(d.substr(1, 2)); - return turb; -} -/** - * @brief 获取风切变等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 风切变等级。0 = 无风切变 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int ws45(std::string_view msg) { - std::string d = databin(msg); - if (d[3] == '0') { - return -1; + + RET alt40fms(std::string_view msg) { + // Deprecated warning + return selalt40fms(msg); } - int ws = bin2(d.substr(4, 2)); - return ws; -} -/** - * @brief 获取风切变等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 风切变等级。0 = 无风切变 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int mb45(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return -1; - } - int mb = bin2(d.substr(7, 2)); - return mb; -} -/** - * @brief 获取结冰等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 结冰等级。0 = 无结冰 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int ic45(std::string_view msg) { - std::string d = databin(msg); - if (d[9] == '0') { - return -1; - } - int ic = bin2(d.substr(10, 2)); - return ic; -} -/** - * @brief 获取尾流涡旋等级。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 尾流涡旋等级。0 = 无尾流涡旋 (NIL),1 = 轻度 (Light),2 = 中度 - * (Moderate),3 = 严重 (Severe)。 - */ -int wv45(std::string_view msg) { - std::string d = databin(msg); - if (d[12] == '0') { - return -1; - } - int ws = bin2(d.substr(13, 2)); - return ws; -} -/** - * @brief 获取静态空气温度。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 温度,单位为摄氏度(Celsius)。 - */ -double temp45(std::string_view msg) { - std::string d = databin(msg); - int sign = static_cast(d[16] - '0'); - int value = bin2(d.substr(17, 9)); - if (sign) { - value -= 512; - } - double temp = value * 0.25; // Celsius - return temp; -} -/** - * @brief 获取平均静态压力。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 静态压力,单位为 hPa。 - */ -int p45(std::string_view msg) { - std::string d = databin(msg); - if (d[26] == '0') { - return -1; - } - int p = bin2(d.substr(27, 11)); // hPa - return p; -} -/** - * @brief 获取无线电高度。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 无线电高度,单位为英尺(ft)。 - */ -int rh45(std::string_view msg) { - std::string d = databin(msg); - if (d[38] == '0') { - return -1; - } - int rh = bin2(d.substr(39, 12)) * 16; // ft - return rh; -} -// ------------------------------------------ -// BDS 5,0 -// Track and turn report -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码5,0(轨迹和转向报告)。 - * - * @param msg 112位二进制字符串。 - * - * @return bool: 如果是BDS代码5,0则返回 true,否则返回 false。 - */ -bool is50(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - // status bit 1, 12, 24, 35, 46 - if (wrongstatus(d, 1, 3, 11) || wrongstatus(d, 12, 13, 23) || - wrongstatus(d, 24, 25, 34) || wrongstatus(d, 35, 36, 45) || - wrongstatus(d, 46, 47, 56)) { - return false; - } - auto roll = roll50(msg); - if (std::abs(roll) > 50) { - return false; - } - auto gs = gs50(msg); - if (gs != -1 && gs > 600) { - return false; - } - auto tas = tas50(msg); - if (tas != -1 && tas > 500) { - return false; - } - if (gs != -1 && std::abs(tas - gs) > 200) { - return false; - } - return true; -} -/** - * @brief 获取滚转角度,来自BDS 5,0消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 角度(度)。 - * 负值表示左翼下沉,正值表示右翼下沉。 - */ -double roll50(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return -1; - } - int sign = static_cast(d[1] - '0'); // 1 -> left wing down - int value = bin2(d.substr(2, 9)); - if (sign) { - value -= 512; - } - double angle = value * 45 / 256.0; // degrees - return angle; -} -/** - * @brief 获取真航向角,来自BDS 5,0消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 角度(度),相对于真北(范围从 0 到 360)。 - */ -double trk50(std::string_view msg) { - std::string d = databin(msg); - if (d[11] == '0') { - return -1; - } - int sign = static_cast(d[12] - '0'); // 1 -> west - int value = bin2(d.substr(13, 10)); - if (sign) { - value -= 1024; - } - double trk = value * 90 / 512.0; - // convert from [-180, 180] to [0, 360] - if (trk < 0) { - trk = 360 + trk; - } - return trk; -} -/** - * @brief 获取地面速度,来自BDS 5,0消息。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 地面速度,单位为节(knots)。 - */ -double gs50(std::string_view msg) { - std::string d = databin(msg); - if (d[23] == '0') { - return -1; - } - double spd = bin2(d.substr(24, 10)) * 2; // kts - return spd; -} -/** - * @brief 获取航向角变化率,来自BDS 5,0消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 角度变化率(度/秒)。 - */ -double rtrk50(std::string_view msg) { - std::string d = databin(msg); - if (d[34] == '0' || d.substr(36, 9) == "111111111") { - return -1; - } - int sign = static_cast(d[35] - '0'); // 1 -> negative value - int value = bin2(d.substr(36, 9)); - if (sign) { - value -= 512; - } - double angle = value * 8 / 256.0; // degrees/sec - return angle; -} -/** - * @brief 获取飞机的真实空速,来自BDS 5,0消息。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 真实空速,单位为节(knots)。 - */ -double tas50(std::string_view msg) { - std::string d = databin(msg); - if (d[45] == '0') { - return -1; - } - double tas = bin2(d.substr(46, 10)) * 2; // kts - return tas; -} -// ------------------------------------------ -// BDS 5,3 -// Air-referenced state vector -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码5,3(航空参考状态矢量)。 - * - * @param msg 112位二进制字符串。 - * - * @return bool: 如果是BDS代码5,3则返回 true,否则返回 false。 - */ -bool is53(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - // status bit 1, 13, 24, 34, 47 - if (wrongstatus(d, 1, 3, 12) || wrongstatus(d, 13, 14, 23) || - wrongstatus(d, 24, 25, 33) || wrongstatus(d, 34, 35, 46) || - wrongstatus(d, 47, 49, 56)) { - return false; - } - double ias = ias53(msg); - if (ias != -1 && ias > 500) { - return false; - } - double mach = mach53(msg); - if (mach != -1 && mach > 1) { - return false; - } - double tas = tas53(msg); - if (tas != -1 && tas > 500) { - return false; - } - int vr = vr53(msg); - if (vr != -1 && abs(vr) > 8000) { - return false; - } - return true; -} -/** - * @brief 获取磁航向角,来自BDS 5,3消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 角度(度),相对于真北(范围从 0 到 360)。 - */ -double hdg53(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return -1; - } - int sign = d[1] - '0'; // 1 -> west - int value = bin2(d.substr(2, 10)); - if (sign) { - value -= 1024; - } - double hdg = static_cast(value) * 90.0 / 512.0; // degree - // convert from [-180, 180] to [0, 360] - if (hdg < 0) { - hdg += 360; - } - return hdg; -} -/** - * @brief 获取指示空速,来自BDS 5,3消息。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 指示空速,单位为节(knots)。 - */ -int ias53(std::string_view msg) { - std::string d = databin(msg); - if (d[12] == '0') { - return -1; - } - return bin2(d.substr(13, 10)); // knots -} -/** - * @brief 获取MACH数,来自BDS 5,3消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: MACH数。 - */ -double mach53(std::string_view msg) { - std::string d = databin(msg); - if (d[23] == '0') { - return -1; - } - return bin2(d.substr(24, 9)) * 0.008; -} -/** - * @brief 获取飞机的真实空速,来自BDS 5,3消息。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 真实空速,单位为节(knots)。 - */ -double tas53(std::string_view msg) { - std::string d = databin(msg); - if (d[33] == '0') { - return -1; - } - return bin2(d.substr(34, 12)) * 0.5; // kts -} -/** - * @brief 获取垂直速率。 - * - * @param msg 112位二进制字符串(BDS60)。 - * - * @return int: 垂直速率,单位为英尺/分钟。 - */ -int vr53(std::string_view msg) { - std::string d = databin(msg); - if (d[46] == '0') { - return -1; - } - int sign = d[47] - '0'; // 1 -> negative value, two's complement - int value = bin2(d.substr(48, 8)); - if (value == 0 || value == 255) { - // all zeros or all ones - return 0; - } - value = sign ? value - 256 : value; - return value * 64; // feet/min -} -// ------------------------------------------ -// BDS 6,0 -// Heading and speed report -// ------------------------------------------ -/** - * @brief 检查消息是否可能是BDS代码6,0。 - * - * @param msg 112位二进制字符串。 - * - * @return bool: 如果是BDS代码6,0则返回true,否则返回false。 - */ -bool is60(std::string_view msg) { - if (allzeros(msg)) { - return false; - } - std::string d = databin(msg); - // status bit 1, 13, 24, 35, 46 - if (wrongstatus(d, 1, 2, 12) || wrongstatus(d, 13, 14, 23) || - wrongstatus(d, 24, 25, 34) || wrongstatus(d, 35, 36, 45) || - wrongstatus(d, 46, 47, 56)) { - return false; - } - double ias = ias60(msg); - if (ias != -1 && ias > 500) { - return false; - } - double mach = mach60(msg); - if (mach != -1 && mach > 1) { - return false; - } - int vr_baro = vr60baro(msg); - if (vr_baro != -1 && abs(vr_baro) > 6000) { - return false; - } - int vr_ins = vr60ins(msg); - if (vr_ins != -1 && abs(vr_ins) > 6000) { - return false; - } - // additional check knowing altitude - if (mach != -1 && ias && - (decode_df(msg) == Downlink_Format::Comm_B_Altitude_Reply_20)) { - int alt = altcode(msg); - if (alt) { - double ias_ = aero::mach2cas(mach, alt * aero::ft) / aero::kts; - if (abs(ias - ias_) > 20) { + + // ------------------------------------------ + // BDS 4,4 + // Meteorological routine air report + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码4,4。 + * + * 气象常规航空报告。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码4,4则返回 true,否则返回 false。 + */ + bool is44(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 5, 35, 47, 50 + if (wrongstatus(d, 5, 6, 23) || wrongstatus(d, 35, 36, 46) || + wrongstatus(d, 47, 48, 49) || wrongstatus(d, 50, 51, 56)) { + return false; + } + // Bits 1-4 indicate source, values > 4 reserved + if (bin2(d.substr(0, 4)) > 4) { + return false; + } + auto w = wind44(msg); + if (w.has_value()) { + auto wind = w.value(); + int vw = wind.wind_speed; + int dw = wind.direction; + if (vw > 250) { return false; } } + auto tem = temp44(msg); + if (std::min(tem.temperature, tem.temperature_alternative) > 60 || + std::max(tem.temperature, tem.temperature_alternative) < -80) { + return false; + } + return true; } - return true; -} -/** - * @brief 获取飞机的磁航向。 - * - * @param msg 112位二进制字符串。 - * - * @return float: 磁航向角度(度),相对于磁北(范围从 0 到 360)。 - */ -double hdg60(std::string_view msg) { - std::string d = databin(msg); - if (d[0] == '0') { - return -1; + + /** + * @brief 获取风速和风向。 + * + * @param msg 112位二进制字符串。 + * + * @return (int, float): 风速(节,kt)、风向(度)。 + */ + RET wind44(std::string_view msg) { + std::string d = databin(msg); + int status = static_cast(d[4] - '0'); + if (status == 0) { + return std::nullopt; + } + int speed = bin2(d.substr(5, 9)); // knots + double direction = bin2(d.substr(14, 9)) * 180.0 / 256.0; // degrees + return Wind{speed, direction}; } - int sign = d[1] - '0'; // 1 -> west - int value = bin2(d.substr(2, 10)); - if (sign) { - value -= 1024; + + /** + * @brief 获取静态空气温度。 + * + * @param msg 112位二进制字符串。 + * + * @return float, float: 温度和替代温度,单位为摄氏度(Celsius)。 + * 注意:返回两个值是由于ICAO 9871 (2008) 附录 A-67 + * 中似乎存在的不一致性错误。 + */ + Temperature temp44(std::string_view msg) { + std::string d = databin(msg); + int sign = static_cast(d[23] - '0'); + int value = bin2(d.substr(24, 10)); + if (sign) { + value -= 1024; + } + double temp = value * 0.25; // Celsius + double temp_alternative = value * 0.125; // Celsius + return {temp, temp_alternative}; } - double hdg = static_cast(value) * 90.0f / 512.0f; // degree - // convert from [-180, 180] to [0, 360] - if (hdg < 0) { - hdg += 360; + + /** + * @brief 获取静态压力。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 静态压力,单位为 hPa。 + */ + int p44(std::string_view msg) { + std::string d = databin(msg); + if (d[34] == '0') { + return -1; + } + int p = bin2(d.substr(35, 11)); // hPa + return p; } - return hdg; -} -/** - * @brief 获取指示空速。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 指示空速,单位为节(knots)。 - */ -int ias60(std::string_view msg) { - std::string d = databin(msg); - if (d[12] == '0') { - return -1; + + /** + * @brief 获取湿度。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 湿度百分比,范围为 [0 - 100] %。 + */ + double hum44(std::string_view msg) { + std::string d = databin(msg); + if (d[49] == '0') { + return -1; + } + double hm = bin2(d.substr(50, 6)) * 100.0 / 64.0; // % + return hm; } - return bin2(d.substr(13, 10)); // kts -} -/** - * @brief 获取飞机的MACH数。 - * - * @param msg 112位二进制字符串。 - * - * @return float: MACH数。 - */ -double mach60(std::string_view msg) { - std::string d = databin(msg); - if (d[23] == '0') { - return -1; + + /** + * @brief 获取湍流等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 湍流等级。0 = 无湍流 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int turb44(std::string_view msg) { + std::string d = databin(msg); + if (d[46] == '0') { + return -1; + } + int turb = bin2(d.substr(47, 2)); + return turb; } - return bin2(d.substr(24, 10)) * 2.048 / 512.0; -} -/** - * @brief 从气压测量获取垂直速率,此值可能会非常嘈杂。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 垂直速率,单位为英尺/分钟。 - */ -int vr60baro(std::string_view msg) { - std::string d = databin(msg); - if (d[34] == '0') { - return -1; + + // ------------------------------------------ + // BDS 4,5 + // Meteorological hazard report + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码4,5。 + * + * 气象危害报告。 + * + * @param msg 112位二进制字符串。 + * + * @return 如果是BDS代码4,5则返回 true,否则返回 false。 + */ + bool is45(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 1, 4, 7, 10, 13, 16, 27, 39 + if (wrongstatus(d, 1, 2, 3) || wrongstatus(d, 4, 5, 6) || + wrongstatus(d, 7, 8, 9) || wrongstatus(d, 10, 11, 12) || + wrongstatus(d, 13, 14, 15) || wrongstatus(d, 16, 17, 26) || + wrongstatus(d, 27, 28, 38) || wrongstatus(d, 39, 40, 51)) { + return false; + } + // reserved + if (bin2(d.substr(51, 5)) != 0) { + return false; + } + auto temp = temp45(msg); + if (temp && (temp > 60 || temp < -80)) { + return false; + } + return true; } - int sign = d[35] - '0'; // 1 -> negative value, two's complement - int value = bin2(d.substr(36, 9)); - if (value == 0 || value == 511) { - // all zeros or all ones - return 0; + + /** + * @brief 获取湍流等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 湍流等级。0 = 无湍流 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int turb45(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return -1; + } + int turb = bin2(d.substr(1, 2)); + return turb; } - value = sign ? value - 512 : value; - return value * 32; // feet/min -} -/** - * @brief 由机载设备(IRS,AHRS)测量的垂直速率。 - * - * @param msg 112位二进制字符串。 - * - * @return int: 垂直速率,单位为英尺/分钟。 - */ -int vr60ins(std::string_view msg) { - std::string d = databin(msg); - if (d[45] == '0') { - return -1; + + /** + * @brief 获取风切变等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 风切变等级。0 = 无风切变 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int ws45(std::string_view msg) { + std::string d = databin(msg); + if (d[3] == '0') { + return -1; + } + int ws = bin2(d.substr(4, 2)); + return ws; } - int sign = d[46] - '0'; // 1 -> negative value, two's complement - int value = bin2(d.substr(47, 9)); - if (value == 0 || value == 511) { - // all zeros or all ones - return 0; + + /** + * @brief 获取风切变等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 风切变等级。0 = 无风切变 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int mb45(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return -1; + } + int mb = bin2(d.substr(7, 2)); + return mb; } - value = sign ? value - 512 : value; - return value * 32; // feet/min -} -// ------------------------------------------ -// BDS 6,1 -// ADS-B TC=28 -// Aircraft Airborne status -// ------------------------------------------ -/** - * @brief 检查飞机是否报告紧急情况。 - * - * 非紧急情况的子类型为零(无信息)或子类型为一且值为零(无紧急情况)。 - * 子类型 = 2 表示 ACAS RA 广播,参见 BDS 3,0。 - * - * @param msg 112位二进制消息字符串。 - * @return bool: 飞机是否已声明紧急情况。 - */ -bool is_emergency(std::string_view msg) { - CE(type_code(msg) != 28, - std::string(msg) + ": Not an airborne status message, expecting TC=28") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 3)); - CE(subtype == 2, std::string(msg) + ": Emergency message is ACAS-RA, not implemented") - int emergencyState = bin2(mb.substr(8, 3)); - return (subtype == 1 && emergencyState == 1); -} -/** - * @brief 解码飞机紧急状态。 - * - * 值 含义 - * --- ----------------------- - * 0 无紧急情况 - * 1 一般紧急情况 - * 2 救护/医疗 - * 3 燃料不足 - * 4 无通信 - * 5 非法通信 - * 6-7 保留 - * - * @param msg 112位二进制消息字符串。 - * @return int: 紧急状态。 - */ -int emergency_state(std::string_view msg) { - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 3)); - CE(subtype == 2, std::string(msg) + ": Emergency message is ACAS-RA, not implemented") - return bin2(mb.substr(8, 3)); -} -/** - * @brief 解码应答机代码。 - * - * 紧急值 1: 应答机代码 7700。 - * 紧急值 4: 应答机代码 7600。 - * 紧急值 5: 应答机代码 7500。 - * - * @param msg 112位二进制消息字符串。 - * @return string: 飞机应答机代码。 - */ -std::string emergency_squawk(std::string_view msg) { - CE(type_code(msg) != 28, - std::string(msg) + std::string(msg) + ": Not an airborne status message, expecting TC=28") - auto msgbin = msg; - auto idcode = msgbin.substr(43, 13); // 13 bits Mode A ID code - return decode_squawk13(idcode); -} -// ------------------------------------------ -// BDS 6,2 -// ADS-B TC=29 -// Target State and Status -// ------------------------------------------ -/** - * @brief 解码选定高度。 - * - * @param msg 56位二进制消息字符串 - * @return int: 选定高度(英尺)。 - * @return string: 来源('MCP/FCU' 或 'FMS')。 - */ -std::tuple selected_altitude(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, - std::string(msg) + ": ADS-B version 1 target state and status message does not contain " - "selected altitude, use target altitude instead") - int alt = bin2(mb.substr(9, 11)); - if (alt == 0) { - return std::make_tuple(0.0, std::string(msg)); + + /** + * @brief 获取结冰等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 结冰等级。0 = 无结冰 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int ic45(std::string_view msg) { + std::string d = databin(msg); + if (d[9] == '0') { + return -1; + } + int ic = bin2(d.substr(10, 2)); + return ic; } - alt = (alt - 1) * 32; - std::string altSource = (mb[8] == '0') ? "MCP/FCU" : "FMS"; - return std::make_tuple(alt, altSource); -} -/** - * @brief 解码目标高度。 - * - * @param msg 56位二进制消息字符串 - * @return int: 目标高度(英尺)。 - * @return string: 来源('MCP/FCU'、'保持模式' 或 'FMS/RNAV')。 - * @return string: 高度参考,可以是压力高度或气压修正高度('FL' 或 'MSL')。 - */ -std::tuple target_altitude(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, - std::string(msg) + ": ADS-B version 2 target state and status message does not contain " - "target altitude, use selected altitude instead") - int altAvail = bin2(mb.substr(7, 2)); - if (altAvail == 0) { - return std::make_tuple(-1, "N/A", ""); + + /** + * @brief 获取尾流涡旋等级。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 尾流涡旋等级。0 = 无尾流涡旋 (NIL),1 = 轻度 (Light),2 = 中度 + * (Moderate),3 = 严重 (Severe)。 + */ + int wv45(std::string_view msg) { + std::string d = databin(msg); + if (d[12] == '0') { + return -1; + } + int ws = bin2(d.substr(13, 2)); + return ws; } - std::string altSource; - if (altAvail == 1) { - altSource = "MCP/FCU"; + + /** + * @brief 获取静态空气温度。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 温度,单位为摄氏度(Celsius)。 + */ + double temp45(std::string_view msg) { + std::string d = databin(msg); + int sign = static_cast(d[16] - '0'); + int value = bin2(d.substr(17, 9)); + if (sign) { + value -= 512; + } + double temp = value * 0.25; // Celsius + return temp; } - else if (altAvail == 2) { - altSource = "Holding mode"; + + /** + * @brief 获取平均静态压力。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 静态压力,单位为 hPa。 + */ + int p45(std::string_view msg) { + std::string d = databin(msg); + if (d[26] == '0') { + return -1; + } + int p = bin2(d.substr(27, 11)); // hPa + return p; } - else { - altSource = "FMS/RNAV"; + + /** + * @brief 获取无线电高度。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 无线电高度,单位为英尺(ft)。 + */ + int rh45(std::string_view msg) { + std::string d = databin(msg); + if (d[38] == '0') { + return -1; + } + int rh = bin2(d.substr(39, 12)) * 16; // ft + return rh; } - std::string altRef = (mb[9] == '0') ? "FL" : "MSL"; - int alt = -1000 + bin2(mb.substr(15, 10)) * 100; - return std::make_tuple(alt, altSource, altRef); -} -/** - * @brief 解码垂直模式。 - * - * 值 含义 - * ----- ----------------------- - * 1 "获取"模式 - * 2 "捕获"或"维持"模式 - * 3 保留 - * - * @param msg 56位二进制消息字符串 - * @return int: 垂直模式。 - */ -Vertical_Horizontal_Type vertical_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, - std::string(msg) + ": ADS-B version 2 target state and status message does not contain " - "vertical mode, use vnav mode instead") - int verticalModeValue = bin2(mb.substr(13, 2)); - if (verticalModeValue == 0) { - return Vertical_Horizontal_Type::Reserved; + + // ------------------------------------------ + // BDS 5,0 + // Track and turn report + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码5,0(轨迹和转向报告)。 + * + * @param msg 112位二进制字符串。 + * + * @return bool: 如果是BDS代码5,0则返回 true,否则返回 false。 + */ + bool is50(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 1, 12, 24, 35, 46 + if (wrongstatus(d, 1, 3, 11) || wrongstatus(d, 12, 13, 23) || + wrongstatus(d, 24, 25, 34) || wrongstatus(d, 35, 36, 45) || + wrongstatus(d, 46, 47, 56)) { + return false; + } + auto roll = roll50(msg); + if (std::abs(roll) > 50) { + return false; + } + auto gs = gs50(msg); + if (gs != -1 && gs > 600) { + return false; + } + auto tas = tas50(msg); + if (tas != -1 && tas > 500) { + return false; + } + if (gs != -1 && std::abs(tas - gs) > 200) { + return false; + } + return true; } - return static_cast(verticalModeValue); -} -/** - * @brief 解码水平模式。 - * - * 值 含义 - * ----- ----------------------- - * 1 "获取"模式 - * 2 "捕获"或"维持"模式 - * 3 保留 - * - * @param msg 56位二进制消息字符串 - * @return int: 水平模式。 - */ -Vertical_Horizontal_Type horizontal_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, - std::string(msg) + ": ADS-B version 2 target state and status message does not contain " - "horizontal mode, use lnav mode instead") - int horizontalModeValue = bin2(mb.substr(25, 2)); - if (horizontalModeValue == 0) { - return Vertical_Horizontal_Type::Reserved; + + /** + * @brief 获取滚转角度,来自BDS 5,0消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 角度(度)。 + * 负值表示左翼下沉,正值表示右翼下沉。 + */ + double roll50(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return -1; + } + int sign = static_cast(d[1] - '0'); // 1 -> left wing down + int value = bin2(d.substr(2, 9)); + if (sign) { + value -= 512; + } + double angle = value * 45 / 256.0; // degrees + return angle; } - return static_cast(horizontalModeValue); -} -/** - * @brief 解码选定的航向。 - * - * @param msg 112位二进制消息字符串。 - * @return float: 选定的航向(度)。 - */ -double selected_heading(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, - std::string(msg) + ": ADS-B version 1 target state and status message does not contain " - "selected heading, use target angle instead") - if (mb[29] == '0') { - return -1; + + /** + * @brief 获取真航向角,来自BDS 5,0消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 角度(度),相对于真北(范围从 0 到 360)。 + */ + double trk50(std::string_view msg) { + std::string d = databin(msg); + if (d[11] == '0') { + return -1; + } + int sign = static_cast(d[12] - '0'); // 1 -> west + int value = bin2(d.substr(13, 10)); + if (sign) { + value -= 1024; + } + double trk = value * 90 / 512.0; + // convert from [-180, 180] to [0, 360] + if (trk < 0) { + trk = 360 + trk; + } + return trk; } - else { - int hdg_sign = mb[30] - '0'; - double hdg = - (hdg_sign + 1) * bin2(mb.substr(31, 8)) * (180.0 / 256.0); + + /** + * @brief 获取地面速度,来自BDS 5,0消息。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 地面速度,单位为节(knots)。 + */ + double gs50(std::string_view msg) { + std::string d = databin(msg); + if (d[23] == '0') { + return -1; + } + double spd = bin2(d.substr(24, 10)) * 2; // kts + return spd; + } + + /** + * @brief 获取航向角变化率,来自BDS 5,0消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 角度变化率(度/秒)。 + */ + double rtrk50(std::string_view msg) { + std::string d = databin(msg); + if (d[34] == '0' || d.substr(36, 9) == "111111111") { + return -1; + } + int sign = static_cast(d[35] - '0'); // 1 -> negative value + int value = bin2(d.substr(36, 9)); + if (sign) { + value -= 512; + } + double angle = value * 8 / 256.0; // degrees/sec + return angle; + } + + /** + * @brief 获取飞机的真实空速,来自BDS 5,0消息。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 真实空速,单位为节(knots)。 + */ + double tas50(std::string_view msg) { + std::string d = databin(msg); + if (d[45] == '0') { + return -1; + } + double tas = bin2(d.substr(46, 10)) * 2; // kts + return tas; + } + + // ------------------------------------------ + // BDS 5,3 + // Air-referenced state vector + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码5,3(航空参考状态矢量)。 + * + * @param msg 112位二进制字符串。 + * + * @return bool: 如果是BDS代码5,3则返回 true,否则返回 false。 + */ + bool is53(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 1, 13, 24, 34, 47 + if (wrongstatus(d, 1, 3, 12) || wrongstatus(d, 13, 14, 23) || + wrongstatus(d, 24, 25, 33) || wrongstatus(d, 34, 35, 46) || + wrongstatus(d, 47, 49, 56)) { + return false; + } + double ias = ias53(msg); + if (ias != -1 && ias > 500) { + return false; + } + double mach = mach53(msg); + if (mach != -1 && mach > 1) { + return false; + } + double tas = tas53(msg); + if (tas != -1 && tas > 500) { + return false; + } + int vr = vr53(msg); + if (vr != -1 && abs(vr) > 8000) { + return false; + } + return true; + } + + /** + * @brief 获取磁航向角,来自BDS 5,3消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 角度(度),相对于真北(范围从 0 到 360)。 + */ + double hdg53(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return -1; + } + int sign = d[1] - '0'; // 1 -> west + int value = bin2(d.substr(2, 10)); + if (sign) { + value -= 1024; + } + double hdg = static_cast(value) * 90.0 / 512.0; // degree + // convert from [-180, 180] to [0, 360] + if (hdg < 0) { + hdg += 360; + } return hdg; } -} -/** - * @brief 解码目标航向/航迹角。 - * - * @param msg 112位二进制消息字符串。 - * @return int: 目标角度(度)。 - * @return string: 角度类型('航向'或'航迹')。 - * @return string: 来源('MCP/FCU'、'自动驾驶模式'或'FMS/RNAV')。 - */ -std::tuple target_angle(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, - std::string(msg) + ": ADS-B version 2 target state and status message does not contain " - "target angle, use selected heading instead") - int angle_avail = bin2(mb.substr(25, 2)); - if (angle_avail == 0) { - return std::make_tuple(-1, "", "N/A"); - } - else { - int angle = bin2(mb.substr(27, 9)); - std::string angle_source; - if (angle_avail == 1) { - angle_source = "MCP/FCU"; + + /** + * @brief 获取指示空速,来自BDS 5,3消息。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 指示空速,单位为节(knots)。 + */ + int ias53(std::string_view msg) { + std::string d = databin(msg); + if (d[12] == '0') { + return -1; } - else if (angle_avail == 2) { - angle_source = "Autopilot mode"; + return bin2(d.substr(13, 10)); // knots + } + + /** + * @brief 获取MACH数,来自BDS 5,3消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: MACH数。 + */ + double mach53(std::string_view msg) { + std::string d = databin(msg); + if (d[23] == '0') { + return -1; } - else { - angle_source = "FMS/RNAV"; + return bin2(d.substr(24, 9)) * 0.008; + } + + /** + * @brief 获取飞机的真实空速,来自BDS 5,3消息。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 真实空速,单位为节(knots)。 + */ + double tas53(std::string_view msg) { + std::string d = databin(msg); + if (d[33] == '0') { + return -1; } - std::string angle_type = (mb[36] == '0') ? "Heading" : "Track"; - return std::make_tuple(angle, angle_type, angle_source); + return bin2(d.substr(34, 12)) * 0.5; // kts } -} -/** - * @brief 解码气压设置。 - * - * @param msg 56位二进制消息字符串 - * @return float: 气压设置(毫巴)。 - */ -double baro_pressure_setting(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " - "does not contain barometric pressure setting") - int baro = bin2(mb.substr(20, 9)); - if (baro == 0) { - return -1; + + /** + * @brief 获取垂直速率。 + * + * @param msg 112位二进制字符串(BDS60)。 + * + * @return int: 垂直速率,单位为英尺/分钟。 + */ + int vr53(std::string_view msg) { + std::string d = databin(msg); + if (d[46] == '0') { + return -1; + } + int sign = d[47] - '0'; // 1 -> negative value, two's complement + int value = bin2(d.substr(48, 8)); + if (value == 0 || value == 255) { + // all zeros or all ones + return 0; + } + value = sign ? value - 256 : value; + return value * 64; // feet/min } - return 800.0 + static_cast((baro - 1)) * 0.8; -} -/** - * @brief 解码自动驾驶仪接通状态。 - * - * @param msg 56位二进制消息字符串 - * @return bool: 自动驾驶仪是否已接通。 - */ -bool autopilot(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " - "does not contain autopilot engagement") - if (mb[46] == '0') { - return false; + + // ------------------------------------------ + // BDS 6,0 + // Heading and speed report + // ------------------------------------------ + /** + * @brief 检查消息是否可能是BDS代码6,0。 + * + * @param msg 112位二进制字符串。 + * + * @return bool: 如果是BDS代码6,0则返回true,否则返回false。 + */ + bool is60(std::string_view msg) { + if (allzeros(msg)) { + return false; + } + std::string d = databin(msg); + // status bit 1, 13, 24, 35, 46 + if (wrongstatus(d, 1, 2, 12) || wrongstatus(d, 13, 14, 23) || + wrongstatus(d, 24, 25, 34) || wrongstatus(d, 35, 36, 45) || + wrongstatus(d, 46, 47, 56)) { + return false; + } + double ias = ias60(msg); + if (ias != -1 && ias > 500) { + return false; + } + double mach = mach60(msg); + if (mach != -1 && mach > 1) { + return false; + } + int vr_baro = vr60baro(msg); + if (vr_baro != -1 && abs(vr_baro) > 6000) { + return false; + } + int vr_ins = vr60ins(msg); + if (vr_ins != -1 && abs(vr_ins) > 6000) { + return false; + } + // additional check knowing altitude + if (mach != -1 && ias && + (decode_df(msg) == Downlink_Format::Comm_B_Altitude_Reply_20)) { + int alt = altcode(msg); + if (alt) { + double ias_ = aero::mach2cas(mach, alt * aero::ft) / aero::kts; + if (abs(ias - ias_) > 20) { + return false; + } + } + } + return true; } - bool engaged = (mb[47] == '1'); - return engaged; -} -/** - * @brief 解码垂直导航(VNAV)模式。 - * - * @param msg 56位二进制消息字符串 - * @return bool: VNAV模式是否已接通。 - */ -bool vnav_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, - std::string(msg) + ": ADS-B version 1 target state and status message does not contain " - "vnav mode, use vertical mode instead") - if (mb[46] == '0') { - return false; + + /** + * @brief 获取飞机的磁航向。 + * + * @param msg 112位二进制字符串。 + * + * @return float: 磁航向角度(度),相对于磁北(范围从 0 到 360)。 + */ + double hdg60(std::string_view msg) { + std::string d = databin(msg); + if (d[0] == '0') { + return -1; + } + int sign = d[1] - '0'; // 1 -> west + int value = bin2(d.substr(2, 10)); + if (sign) { + value -= 1024; + } + double hdg = static_cast(value) * 90.0f / 512.0f; // degree + // convert from [-180, 180] to [0, 360] + if (hdg < 0) { + hdg += 360; + } + return hdg; } - bool engaged = (mb[48] == '1'); - return engaged; -} -/** - * @brief 解码高度保持模式。 - * - * @param msg 56位二进制消息字符串 - * @return bool: 高度保持模式是否已接通。 - */ -bool altitude_hold_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " - "does not contain altitude hold mode") - if (mb[46] == '0') { - return false; + + /** + * @brief 获取指示空速。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 指示空速,单位为节(knots)。 + */ + int ias60(std::string_view msg) { + std::string d = databin(msg); + if (d[12] == '0') { + return -1; + } + return bin2(d.substr(13, 10)); // kts } - bool altHoldMode = (mb[49] == '1'); - return altHoldMode; -} -/** - * @brief 解码进近模式。 - * - * @param msg 56位二进制消息字符串 - * @return bool 进近模式是否已接通。 - */ -bool approach_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " - "does not contain approach mode") - if (mb[46] == '0') { - return false; + + /** + * @brief 获取飞机的MACH数。 + * + * @param msg 112位二进制字符串。 + * + * @return float: MACH数。 + */ + double mach60(std::string_view msg) { + std::string d = databin(msg); + if (d[23] == '0') { + return -1; + } + return bin2(d.substr(24, 10)) * 2.048 / 512.0; } - bool appMode = (mb[51] == '1'); - return appMode; -} -/** - * @brief 解码LNAV模式。 - * - * @param msg 112位二进制消息字符串 - * @return bool 如果LNAV模式已启用,则返回true;否则返回false - */ -bool lnav_mode(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 0, - std::string(msg) + ": ADS-B version 1 target state and status message does not contain " - "lnav mode, use horizontal mode instead") - if (mb[46] == '0') { - return false; + + /** + * @brief 从气压测量获取垂直速率,此值可能会非常嘈杂。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 垂直速率,单位为英尺/分钟。 + */ + int vr60baro(std::string_view msg) { + std::string d = databin(msg); + if (d[34] == '0') { + return -1; + } + int sign = d[35] - '0'; // 1 -> negative value, two's complement + int value = bin2(d.substr(36, 9)); + if (value == 0 || value == 511) { + // all zeros or all ones + return 0; + } + value = sign ? value - 512 : value; + return value * 32; // feet/min } - bool lnavMode = (mb[53] == '1'); - return lnavMode; -} -/** - * @brief 解码TCAS/ACAS操作状态。 - * - * @param msg 112位二进制消息字符串 - * @return bool 如果TCAS/ACAS处于操作状态,则返回true;否则返回false - */ -bool tcas_operational(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - bool tcas; - if (subtype == 0) { - tcas = (mb[51] == '0'); + + /** + * @brief 由机载设备(IRS,AHRS)测量的垂直速率。 + * + * @param msg 112位二进制字符串。 + * + * @return int: 垂直速率,单位为英尺/分钟。 + */ + int vr60ins(std::string_view msg) { + std::string d = databin(msg); + if (d[45] == '0') { + return -1; + } + int sign = d[46] - '0'; // 1 -> negative value, two's complement + int value = bin2(d.substr(47, 9)); + if (value == 0 || value == 511) { + // all zeros or all ones + return 0; + } + value = sign ? value - 512 : value; + return value * 32; // feet/min } - else { - tcas = (mb[52] == '1'); + + // ------------------------------------------ + // BDS 6,1 + // ADS-B TC=28 + // Aircraft Airborne status + // ------------------------------------------ + /** + * @brief 检查飞机是否报告紧急情况。 + * + * 非紧急情况的子类型为零(无信息)或子类型为一且值为零(无紧急情况)。 + * 子类型 = 2 表示 ACAS RA 广播,参见 BDS 3,0。 + * + * @param msg 112位二进制消息字符串。 + * @return bool: 飞机是否已声明紧急情况。 + */ + bool is_emergency(std::string_view msg) { + CE(type_code(msg) != 28, + std::string(msg) + ": Not an airborne status message, expecting TC=28") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 3)); + CE(subtype == 2, std::string(msg) + ": Emergency message is ACAS-RA, not implemented") + int emergencyState = bin2(mb.substr(8, 3)); + return (subtype == 1 && emergencyState == 1); } - return tcas; -} -/** - * @brief 解码TCAS/ACAS解决咨询状态。 - * - * @param msg 112位二进制消息字符串 - * @return bool 如果TCAS/ACAS解决咨询处于激活状态,则返回true;否则返回false - */ -bool tcas_ra(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, std::string(msg) + ": ADS-B version 2 target state and status message " - "does not contain TCAS/ACAS RA") - return (mb[52] == '1'); -} -/** - * @brief 解码飞机的紧急状态。 - * - * 根据输入的28字节十六进制消息字符串,解码出飞机的紧急状态。 - * - * 紧急状态值的意义如下: - * - 0: 无紧急状态 - * - 1: 一般紧急状态 - * - 2: 救生/医疗紧急状态 - * - 3: 最低油量 - * - 4: 无法通信 - * - 5: 非法干扰 - * - 6: 坠机 - * - 7: 保留 - * - * @param msg 112位二进制消息字符串 - * @return int 紧急状态值(0-7) - */ -Emergency_Type emergency_status(std::string_view msg) { - int tc = type_code(msg); - CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") - auto mb = msg.substr(32); - int subtype = bin2(mb.substr(5, 2)); - CE(subtype == 1, std::string(msg) + ": ADS-B version 2 target state and status message " - "does not contain emergency status") - return static_cast(bin2(mb.substr(53, 3))); -} -// init -/** - * @brief 使用参考地速和航向来确定BDS50和BDS60。 - * - * @param msg 56位二进制消息字符串 - * @param spd_ref 参考速度(ADS-B地速),单位:节(kts) - * @param trk_ref 参考航向(ADS-B航向角),单位:度(deg) - * @param alt_ref 参考高度(ADS-B高度),单位:英尺(ft) - * - * @return 字符串或None:BDS版本,或可能的版本, - * 如果没有匹配的则返回None。 - */ -std::string is50or60(std::string_view msg, double spd_ref, double trk_ref, - double alt_ref) { - static auto vxy = [](double v, double angle) -> std::tuple { - double vx = v * sin(angle * M_PI / 180.0); - double vy = v * cos(angle * M_PI / 180.0); - return {vx, vy}; - }; - if (!(is50(msg) && is60(msg))) { - return ""; + + /** + * @brief 解码飞机紧急状态。 + * + * 值 含义 + * --- ----------------------- + * 0 无紧急情况 + * 1 一般紧急情况 + * 2 救护/医疗 + * 3 燃料不足 + * 4 无通信 + * 5 非法通信 + * 6-7 保留 + * + * @param msg 112位二进制消息字符串。 + * @return int: 紧急状态。 + */ + int emergency_state(std::string_view msg) { + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 3)); + CE(subtype == 2, std::string(msg) + ": Emergency message is ACAS-RA, not implemented") + return bin2(mb.substr(8, 3)); } - auto h60 = hdg60(msg); - auto m60 = mach60(msg); - auto i60 = ias60(msg); - if (static_cast(m60) && i60) { - double ias_ = - aero::mach2cas(m60, alt_ref) / 1.94384; // Convert ft/s to knots - if (std::abs(i60 - ias_) > 20) { - return "BDS50"; + + /** + * @brief 解码应答机代码。 + * + * 紧急值 1: 应答机代码 7700。 + * 紧急值 4: 应答机代码 7600。 + * 紧急值 5: 应答机代码 7500。 + * + * @param msg 112位二进制消息字符串。 + * @return string: 飞机应答机代码。 + */ + std::string emergency_squawk(std::string_view msg) { + CE(type_code(msg) != 28, + std::string(msg) + std::string(msg) + ": Not an airborne status message, expecting TC=28") + auto msgbin = msg; + auto idcode = msgbin.substr(43, 13); // 13 bits Mode A ID code + return decode_squawk13(idcode); + } + + // ------------------------------------------ + // BDS 6,2 + // ADS-B TC=29 + // Target State and Status + // ------------------------------------------ + /** + * @brief 解码选定高度。 + * + * @param msg 56位二进制消息字符串 + * @return int: 选定高度(英尺)。 + * @return string: 来源('MCP/FCU' 或 'FMS')。 + */ + std::tuple selected_altitude(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, + std::string(msg) + ": ADS-B version 1 target state and status message does not contain " + "selected altitude, use target altitude instead") + int alt = bin2(mb.substr(9, 11)); + if (alt == 0) { + return std::make_tuple(0.0, std::string(msg)); + } + alt = (alt - 1) * 32; + std::string altSource = (mb[8] == '0') ? "MCP/FCU" : "FMS"; + return std::make_tuple(alt, altSource); + } + + /** + * @brief 解码目标高度。 + * + * @param msg 56位二进制消息字符串 + * @return int: 目标高度(英尺)。 + * @return string: 来源('MCP/FCU'、'保持模式' 或 'FMS/RNAV')。 + * @return string: 高度参考,可以是压力高度或气压修正高度('FL' 或 'MSL')。 + */ + std::tuple target_altitude(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, + std::string(msg) + ": ADS-B version 2 target state and status message does not contain " + "target altitude, use selected altitude instead") + int altAvail = bin2(mb.substr(7, 2)); + if (altAvail == 0) { + return std::make_tuple(-1, "N/A", ""); + } + std::string altSource; + if (altAvail == 1) { + altSource = "MCP/FCU"; + } else if (altAvail == 2) { + altSource = "Holding mode"; + } else { + altSource = "FMS/RNAV"; + } + std::string altRef = (mb[9] == '0') ? "FL" : "MSL"; + int alt = -1000 + bin2(mb.substr(15, 10)) * 100; + return std::make_tuple(alt, altSource, altRef); + } + + /** + * @brief 解码垂直模式。 + * + * 值 含义 + * ----- ----------------------- + * 1 "获取"模式 + * 2 "捕获"或"维持"模式 + * 3 保留 + * + * @param msg 56位二进制消息字符串 + * @return int: 垂直模式。 + */ + Vertical_Horizontal_Type vertical_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, + std::string(msg) + ": ADS-B version 2 target state and status message does not contain " + "vertical mode, use vnav mode instead") + int verticalModeValue = bin2(mb.substr(13, 2)); + if (verticalModeValue == 0) { + return Vertical_Horizontal_Type::Reserved; + } + return static_cast(verticalModeValue); + } + + /** + * @brief 解码水平模式。 + * + * 值 含义 + * ----- ----------------------- + * 1 "获取"模式 + * 2 "捕获"或"维持"模式 + * 3 保留 + * + * @param msg 56位二进制消息字符串 + * @return int: 水平模式。 + */ + Vertical_Horizontal_Type horizontal_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, + std::string(msg) + ": ADS-B version 2 target state and status message does not contain " + "horizontal mode, use lnav mode instead") + int horizontalModeValue = bin2(mb.substr(25, 2)); + if (horizontalModeValue == 0) { + return Vertical_Horizontal_Type::Reserved; + } + return static_cast(horizontalModeValue); + } + + /** + * @brief 解码选定的航向。 + * + * @param msg 112位二进制消息字符串。 + * @return float: 选定的航向(度)。 + */ + double selected_heading(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, + std::string(msg) + ": ADS-B version 1 target state and status message does not contain " + "selected heading, use target angle instead") + if (mb[29] == '0') { + return -1; + } else { + int hdg_sign = mb[30] - '0'; + double hdg = + (hdg_sign + 1) * bin2(mb.substr(31, 8)) * (180.0 / 256.0); + return hdg; } } - if (!static_cast(h60) || !(static_cast(m60) && !i60)) { - return "BDS50,BDS60"; + + /** + * @brief 解码目标航向/航迹角。 + * + * @param msg 112位二进制消息字符串。 + * @return int: 目标角度(度)。 + * @return string: 角度类型('航向'或'航迹')。 + * @return string: 来源('MCP/FCU'、'自动驾驶模式'或'FMS/RNAV')。 + */ + std::tuple target_angle(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, + std::string(msg) + ": ADS-B version 2 target state and status message does not contain " + "target angle, use selected heading instead") + int angle_avail = bin2(mb.substr(25, 2)); + if (angle_avail == 0) { + return std::make_tuple(-1, "", "N/A"); + } else { + int angle = bin2(mb.substr(27, 9)); + std::string angle_source; + if (angle_avail == 1) { + angle_source = "MCP/FCU"; + } else if (angle_avail == 2) { + angle_source = "Autopilot mode"; + } else { + angle_source = "FMS/RNAV"; + } + std::string angle_type = (mb[36] == '0') ? "Heading" : "Track"; + return std::make_tuple(angle, angle_type, angle_source); + } } - // Assume BDS50 - auto h50 = trk50(msg); - auto v50 = gs50(msg); - if (!static_cast(h50) || !static_cast(v50)) { - return "BDS50,BDS60"; + + /** + * @brief 解码气压设置。 + * + * @param msg 56位二进制消息字符串 + * @return float: 气压设置(毫巴)。 + */ + double baro_pressure_setting(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " + "does not contain barometric pressure setting") + int baro = bin2(mb.substr(20, 9)); + if (baro == 0) { + return -1; + } + return 800.0 + static_cast((baro - 1)) * 0.8; } - auto XY5 = vxy(v50 * 1.94384, h50); // Convert knots to ft/s - auto XY6m = vxy(aero::mach2tas(m60, alt_ref), h60); - auto XY6i = vxy(aero::cas2tas(i60 * 1.94384, alt_ref), h60); - std::array, 3> X = {XY5, XY6m, XY6i}; - auto Mu = vxy(spd_ref * 1.94384, trk_ref); // Convert knots to ft/s - std::array dist{}; - for (size_t i = 0; i < X.size(); ++i) { - dist[i] = std::sqrt(std::pow(std::get<0>(X[i]) - std::get<0>(Mu), 2) + - std::pow(std::get<1>(X[i]) - std::get<1>(Mu), 2)); + + /** + * @brief 解码自动驾驶仪接通状态。 + * + * @param msg 56位二进制消息字符串 + * @return bool: 自动驾驶仪是否已接通。 + */ + bool autopilot(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " + "does not contain autopilot engagement") + if (mb[46] == '0') { + return false; + } + bool engaged = (mb[47] == '1'); + return engaged; } - auto min_index = - std::distance(dist.begin(), std::min_element(dist.begin(), dist.end())); - return (min_index == 0) ? "BDS50" : "BDS60"; // Adjust based on your logic -} -/** - * @brief 估计消息的最可能BDS代码。 - * - * 根据提供的112位二进制字符串消息,以及是否推断MRAR(BDS 44)和MHR(BDS - * 45)的指示, 估计消息的最可能BDS代码版本。 - * - * @param msg_bin 112位二进制字符串 - * @param mrar 是否推断MRAR(BDS 44)和MHR(BDS 45),默认为False - * @return String or None: BDS版本,或可能的版本,如果没有匹配的则返回None - */ -std::string infer(std::string_view msg_bin, bool mrar) { - auto df = SSR::decode_df(msg_bin); - if (allzeros(msg_bin)) { - return "EMPTY"; + + /** + * @brief 解码垂直导航(VNAV)模式。 + * + * @param msg 56位二进制消息字符串 + * @return bool: VNAV模式是否已接通。 + */ + bool vnav_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, + std::string(msg) + ": ADS-B version 1 target state and status message does not contain " + "vnav mode, use vertical mode instead") + if (mb[46] == '0') { + return false; + } + bool engaged = (mb[48] == '1'); + return engaged; } - // For ADS-B / Mode-S extended squitter - if (df == Downlink_Format::Extended_Squitter_17) { - auto tc = type_code(msg_bin); - if (!tc) { + + /** + * @brief 解码高度保持模式。 + * + * @param msg 56位二进制消息字符串 + * @return bool: 高度保持模式是否已接通。 + */ + bool altitude_hold_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " + "does not contain altitude hold mode") + if (mb[46] == '0') { + return false; + } + bool altHoldMode = (mb[49] == '1'); + return altHoldMode; + } + + /** + * @brief 解码进近模式。 + * + * @param msg 56位二进制消息字符串 + * @return bool 进近模式是否已接通。 + */ + bool approach_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, std::string(msg) + ": ADS-B version 1 target state and status message " + "does not contain approach mode") + if (mb[46] == '0') { + return false; + } + bool appMode = (mb[51] == '1'); + return appMode; + } + + /** + * @brief 解码LNAV模式。 + * + * @param msg 112位二进制消息字符串 + * @return bool 如果LNAV模式已启用,则返回true;否则返回false + */ + bool lnav_mode(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 0, + std::string(msg) + ": ADS-B version 1 target state and status message does not contain " + "lnav mode, use horizontal mode instead") + if (mb[46] == '0') { + return false; + } + bool lnavMode = (mb[53] == '1'); + return lnavMode; + } + + /** + * @brief 解码TCAS/ACAS操作状态。 + * + * @param msg 112位二进制消息字符串 + * @return bool 如果TCAS/ACAS处于操作状态,则返回true;否则返回false + */ + bool tcas_operational(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + bool tcas; + if (subtype == 0) { + tcas = (mb[51] == '0'); + } else { + tcas = (mb[52] == '1'); + } + return tcas; + } + + /** + * @brief 解码TCAS/ACAS解决咨询状态。 + * + * @param msg 112位二进制消息字符串 + * @return bool 如果TCAS/ACAS解决咨询处于激活状态,则返回true;否则返回false + */ + bool tcas_ra(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, std::string(msg) + ": ADS-B version 2 target state and status message " + "does not contain TCAS/ACAS RA") + return (mb[52] == '1'); + } + + /** + * @brief 解码飞机的紧急状态。 + * + * 根据输入的28字节十六进制消息字符串,解码出飞机的紧急状态。 + * + * 紧急状态值的意义如下: + * - 0: 无紧急状态 + * - 1: 一般紧急状态 + * - 2: 救生/医疗紧急状态 + * - 3: 最低油量 + * - 4: 无法通信 + * - 5: 非法干扰 + * - 6: 坠机 + * - 7: 保留 + * + * @param msg 112位二进制消息字符串 + * @return int 紧急状态值(0-7) + */ + Emergency_Type emergency_status(std::string_view msg) { + int tc = type_code(msg); + CE(tc != 29, std::string(msg) + ": Not a target state and status message, expecting TC=29") + auto mb = msg.substr(32); + int subtype = bin2(mb.substr(5, 2)); + CE(subtype == 1, std::string(msg) + ": ADS-B version 2 target state and status message " + "does not contain emergency status") + return static_cast(bin2(mb.substr(53, 3))); + } + + // init + /** + * @brief 使用参考地速和航向来确定BDS50和BDS60。 + * + * @param msg 56位二进制消息字符串 + * @param spd_ref 参考速度(ADS-B地速),单位:节(kts) + * @param trk_ref 参考航向(ADS-B航向角),单位:度(deg) + * @param alt_ref 参考高度(ADS-B高度),单位:英尺(ft) + * + * @return 字符串或None:BDS版本,或可能的版本, + * 如果没有匹配的则返回None。 + */ + std::string is50or60(std::string_view msg, double spd_ref, double trk_ref, + double alt_ref) { + static auto vxy = [](double v, double angle) -> std::tuple { + double vx = v * sin(angle * M_PI / 180.0); + double vy = v * cos(angle * M_PI / 180.0); + return {vx, vy}; + }; + if (!(is50(msg) && is60(msg))) { return ""; } - if (tc >= 1 && tc <= 4) return "BDS08"; // identification and category - if (tc >= 5 && tc <= 8) return "BDS06"; // surface movement - if (tc >= 9 && tc <= 18) return "BDS05"; // airborne position, baro-alt - if (tc == 19) return "BDS09"; // airborne velocity - if (tc >= 20 && tc <= 22) return "BDS05"; // airborne position, gnss-alt - if (tc == 28) return "BDS61"; // aircraft status - if (tc == 29) return "BDS62"; // target state and status - if (tc == 31) return "BDS65"; // operational status - } - // For Comm-B replies - bool IS10 = is10(msg_bin); - bool IS17 = is17(msg_bin); - bool IS20 = is20(msg_bin); - bool IS30 = is30(msg_bin); - bool IS40 = is40(msg_bin); - bool IS50 = is50(msg_bin); - bool IS60 = is60(msg_bin); - bool IS44 = is44(msg_bin); - bool IS45 = is45(msg_bin); - std::vector bds_result; - if (mrar) { - std::array allbds = { - "BDS10", "BDS17", "BDS20", - "BDS30", "BDS40", "BDS44", - "BDS45", "BDS50", "BDS60" - }; - 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]); + auto h60 = hdg60(msg); + auto m60 = mach60(msg); + auto i60 = ias60(msg); + if (static_cast(m60) && i60) { + double ias_ = + aero::mach2cas(m60, alt_ref) / 1.94384; // Convert ft/s to knots + if (std::abs(i60 - ias_) > 20) { + return "BDS50"; } } + if (!static_cast(h60) || !(static_cast(m60) && !i60)) { + return "BDS50,BDS60"; + } + // Assume BDS50 + auto h50 = trk50(msg); + auto v50 = gs50(msg); + if (!static_cast(h50) || !static_cast(v50)) { + return "BDS50,BDS60"; + } + auto XY5 = vxy(v50 * 1.94384, h50); // Convert knots to ft/s + auto XY6m = vxy(aero::mach2tas(m60, alt_ref), h60); + auto XY6i = vxy(aero::cas2tas(i60 * 1.94384, alt_ref), h60); + std::array, 3> X = {XY5, XY6m, XY6i}; + auto Mu = vxy(spd_ref * 1.94384, trk_ref); // Convert knots to ft/s + std::array dist{}; + for (size_t i = 0; i < X.size(); ++i) { + dist[i] = std::sqrt(std::pow(std::get<0>(X[i]) - std::get<0>(Mu), 2) + + std::pow(std::get<1>(X[i]) - std::get<1>(Mu), 2)); + } + auto min_index = + std::distance(dist.begin(), std::min_element(dist.begin(), dist.end())); + return (min_index == 0) ? "BDS50" : "BDS60"; // Adjust based on your logic } - else { - std::array allbds = { - "BDS10", "BDS17", "BDS20", "BDS30", - "BDS40", "BDS50", "BDS60" - }; - 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]); + + /** + * @brief 估计消息的最可能BDS代码。 + * + * 根据提供的112位二进制字符串消息,以及是否推断MRAR(BDS 44)和MHR(BDS + * 45)的指示, 估计消息的最可能BDS代码版本。 + * + * @param msg_bin 112位二进制字符串 + * @param mrar 是否推断MRAR(BDS 44)和MHR(BDS 45),默认为False + * @return String or None: BDS版本,或可能的版本,如果没有匹配的则返回None + */ + std::string infer(std::string_view msg_bin, bool mrar) { + auto df = SSR::decode_df(msg_bin); + if (allzeros(msg_bin)) { + return "EMPTY"; + } + // For ADS-B / Mode-S extended squitter + if (df == Downlink_Format::Extended_Squitter_17) { + auto tc = type_code(msg_bin); + if (!tc) { + return ""; + } + if (tc >= 1 && tc <= 4) return "BDS08"; // identification and category + if (tc >= 5 && tc <= 8) return "BDS06"; // surface movement + if (tc >= 9 && tc <= 18) return "BDS05"; // airborne position, baro-alt + if (tc == 19) return "BDS09"; // airborne velocity + if (tc >= 20 && tc <= 22) return "BDS05"; // airborne position, gnss-alt + if (tc == 28) return "BDS61"; // aircraft status + if (tc == 29) return "BDS62"; // target state and status + if (tc == 31) return "BDS65"; // operational status + } + // For Comm-B replies + bool IS10 = is10(msg_bin); + bool IS17 = is17(msg_bin); + bool IS20 = is20(msg_bin); + bool IS30 = is30(msg_bin); + bool IS40 = is40(msg_bin); + bool IS50 = is50(msg_bin); + bool IS60 = is60(msg_bin); + bool IS44 = is44(msg_bin); + bool IS45 = is45(msg_bin); + std::vector bds_result; + if (mrar) { + std::array allbds = { + "BDS10", "BDS17", "BDS20", + "BDS30", "BDS40", "BDS44", + "BDS45", "BDS50", "BDS60" + }; + 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 { + std::array allbds = { + "BDS10", "BDS17", "BDS20", "BDS30", + "BDS40", "BDS50", "BDS60" + }; + 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]); + } } } + std::sort(bds_result.begin(), bds_result.end()); + std::string result = + std::accumulate(bds_result.begin(), bds_result.end(), std::string(), + [](std::string a, std::string_view b) { + return a + (a.empty() ? "" : ",") + std::string(b); + }); + return result.empty() ? "" : std::string(result); } - std::sort(bds_result.begin(), bds_result.end()); - std::string result = - std::accumulate(bds_result.begin(), bds_result.end(), std::string(), - [](std::string a, std::string_view b) { - return a + (a.empty() ? "" : ",") + std::string(b); - }); - return result.empty() ? "" : std::string(result); -} -static bool bds_bit(std::string_view msg, int bit) { - return msg[bit - 1] == '1'; -} -static unsigned long long bds_bits(std::string_view msg, int first, int last) { - return bin2(msg.substr(first - 1, last - first + 1)); -} -static int bds10_score(std::string_view msg) { - if (bds_bits(msg, 1, 8) != 0x10) { - return 0; + + static bool bds_bit(std::string_view msg, int bit) { + return msg[bit - 1] == '1'; } - if (bds_bits(msg, 10, 14) != 0) { - return 0; + + static unsigned long long bds_bits(std::string_view msg, int first, int last) { + return bin2(msg.substr(first - 1, last - first + 1)); } - return 56; -} -static int bds17_score(std::string_view msg) { - if (bds_bits(msg, 25, 56) != 0) { - return 0; + + static int bds10_score(std::string_view msg) { + if (bds_bits(msg, 1, 8) != 0x10) { + return 0; + } + if (bds_bits(msg, 10, 14) != 0) { + return 0; + } + return 56; } - 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)) { + + static int bds17_score(std::string_view msg) { + if (bds_bits(msg, 25, 56) != 0) { + return 0; + } + int score = 0; + if (bds_bit(msg, 7)) { score += 1; + } else { + score -= 2; } - } - 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)) { + 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; + } + } 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 if (!bds_bit(msg, 16) && !bds_bit(msg, 24) && !bds_bit(msg, 9)) { - score += 1; - } - else { - score -= 6; - } - return score; -} -static int bds20_score(std::string_view 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(std::string_view msg) { - if (bds_bits(msg, 1, 8) != 0x30) { - return 0; - } - return 56; -} -static int bds40_score(std::string_view 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(std::string_view 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(std::string_view 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) { + } else { score -= 12; } - } - return score; -} -static int bds44_score(std::string_view 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; + 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; } - else { + return score; + } + + static int bds20_score(std::string_view msg) { + if (bds_bits(msg, 1, 8) != 0x20) { return 0; } - double wind_direction = wind_direction_raw * 180.0 / 256.0; - if (wind_direction >= 0 && wind_direction <= 360) { - score += 9; + 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; + } } - else { + return score; + } + + static int bds30_score(std::string_view msg) { + if (bds_bits(msg, 1, 8) != 0x30) { return 0; } + return 56; } - else { - 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 { + + static int bds40_score(std::string_view 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; } - else { - score += 1; + + static int bds50_score(std::string_view 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; } - if (turbulence_valid) { - if (turbulence_raw <= 3) { + + static int bds60_score(std::string_view 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(std::string_view 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 { score += 2; } - else { + 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; } - } - else { - score += 1; - } - if (humidity_valid) { - double humidity = humidity_raw * (100.0 / 64); - if (humidity >= 0 && humidity <= 100) { - score += 6; + if (pressure_valid) { + if (static_pressure_raw <= 2048) { + score += 11; + } else { + return 0; + } + } else { + score += 1; } - else { - return 0; + 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; } - else { - score += 1; - } - return score; -} -// std::vector 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(msg.substr(8, 5)) != 0 || bin2(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 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 infer2(std::string_view 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) { + + // std::vector 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(msg.substr(8, 5)) != 0 || bin2(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 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 infer2(std::string_view msg, bool mrar) { + auto df = SSR::decode_df(msg); + if (allzeros(msg)) { return {}; } - 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"}; - } - } - 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(msg.substr(8, 5)) != 0 || bin2(msg.substr(13, 6)) != 0) { - static Frequency_Limit mt; - if (mt.test()) { - std::cout << std::format("原因是 DR/UM 非 0 时,这条 DF20/DF21 下行应答里带了额外的通信控制状态 {} \n", msg); - } - } - 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)(std::string_view); - }; - std::vector 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 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; + if (df == Downlink_Format::Extended_Squitter_17) { + auto tc = type_code(msg); + if (!tc) { + return {}; + } + 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"}; } - std::cout << std::format("bds解码出了ambiguous score:{} best:[{}] hex:{} bin:{}\n", best_score, oss.str(), bin2hex(msg), msg); } - return {}; + bool debug = false; + 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(msg.substr(8, 5)) != 0 || bin2(msg.substr(13, 6)) != 0) { + static Frequency_Limit mt; + if (mt.test()) { + if (debug) std::cout << std::format("原因是 DR/UM 非 0 时,这条 DF20/DF21 下行应答里带了额外的通信控制状态 {} \n", msg); + } + } + std::string data = databin(msg); + if (allzeros(data)) { + static Frequency_Limit mt; + if (mt.test()) { + if (debug) std::cout << std::format("bds解码出了allzeros hex: {} bin:{}\n", bin2hex(msg), msg); + } + return {}; + } + struct Candidate { + const char *name; + + int (*score)(std::string_view); + }; + std::vector 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 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; + } + if (debug) + 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)) { + if (debug) std::cout << std::format("bds解码出了{} score:{}\n", best_name, best_score); + } + return {best_name}; } - 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