From 10e1805895b749443882fdcf831855f183927a7f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 22 Mar 2019 11:21:39 +0000 Subject: [PATCH] Re-namespace all the things. This is mostly whitespace changes. --- common.h | 4 +- convert.cc | 446 +++++++++++----------- convert.h | 26 +- demodulator.cc | 702 ++++++++++++++++++----------------- demodulator.h | 32 +- dump978_main.cc | 7 +- faup978_main.cc | 4 +- faup978_reporter.cc | 6 +- faup978_reporter.h | 20 +- fec.cc | 15 +- fec.h | 8 +- message_dispatch.cc | 112 +++--- message_dispatch.h | 4 +- message_source.h | 8 +- sample_source.cc | 206 +++++------ sample_source.h | 14 +- skyview978_main.cc | 4 +- skyview_writer.cc | 4 +- skyview_writer.h | 58 ++- soapy_source.cc | 428 ++++++++++----------- soapy_source.h | 4 +- socket_input.cc | 8 +- socket_input.h | 6 +- socket_output.cc | 216 ++++++----- socket_output.h | 18 +- track.cc | 8 +- track.h | 8 +- uat_message.cc | 880 ++++++++++++++++++++++---------------------- uat_message.h | 6 +- uat_protocol.h | 4 +- 30 files changed, 1630 insertions(+), 1636 deletions(-) diff --git a/common.h b/common.h index 9b922dc..7827409 100644 --- a/common.h +++ b/common.h @@ -16,7 +16,7 @@ #include #include -namespace uat { +namespace flightaware::uat { typedef std::vector Bytes; typedef std::vector PhaseBuffer; @@ -28,6 +28,6 @@ namespace uat { const auto unix_epoch = std::chrono::system_clock::from_time_t(0); inline static std::uint64_t now_millis() { return std::chrono::duration_cast(std::chrono::system_clock::now() - unix_epoch).count(); } -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/convert.cc b/convert.cc index e04d902..e17c334 100644 --- a/convert.cc +++ b/convert.cc @@ -7,244 +7,244 @@ #include #include -namespace dump978 { - static inline std::uint16_t scaled_atan2(double y, double x) { - double ang = std::atan2(y, x) + M_PI; // atan2 returns [-pi..pi], normalize to [0..2*pi] - double scaled_ang = std::round(32768 * ang / M_PI); - return scaled_ang < 0 ? 0 : scaled_ang > 65535 ? 65535 : (std::uint16_t)scaled_ang; - } - - static inline double magsq(double i, double q) { return i * i + q * q; } - - SampleConverter::Pointer SampleConverter::Create(SampleFormat format) { - switch (format) { - case SampleFormat::CU8: - return Pointer(new CU8Converter()); - case SampleFormat::CS8: - return Pointer(new CS8Converter()); - case SampleFormat::CS16H: - return Pointer(new CS16HConverter()); - case SampleFormat::CF32H: - return Pointer(new CF32HConverter()); - default: - throw std::runtime_error("format not implemented yet"); - } +using namespace flightaware::uat; + +static inline std::uint16_t scaled_atan2(double y, double x) { + double ang = std::atan2(y, x) + M_PI; // atan2 returns [-pi..pi], normalize to [0..2*pi] + double scaled_ang = std::round(32768 * ang / M_PI); + return scaled_ang < 0 ? 0 : scaled_ang > 65535 ? 65535 : (std::uint16_t)scaled_ang; +} + +static inline double magsq(double i, double q) { return i * i + q * q; } + +SampleConverter::Pointer SampleConverter::Create(SampleFormat format) { + switch (format) { + case SampleFormat::CU8: + return Pointer(new CU8Converter()); + case SampleFormat::CS8: + return Pointer(new CS8Converter()); + case SampleFormat::CS16H: + return Pointer(new CS16HConverter()); + case SampleFormat::CF32H: + return Pointer(new CF32HConverter()); + default: + throw std::runtime_error("format not implemented yet"); } +} - CU8Converter::CU8Converter() : SampleConverter(SampleFormat::CU8) { +CU8Converter::CU8Converter() : SampleConverter(SampleFormat::CU8) { - cu8_alias u; + cu8_alias u; - unsigned i, q; - for (i = 0; i < 256; ++i) { - double d_i = (i - 127.5) / 128.0; - for (q = 0; q < 256; ++q) { - double d_q = (q - 127.5) / 128.0; - u.iq[0] = i; - u.iq[1] = q; - lookup_phase_[u.iq16] = scaled_atan2(d_q, d_i); - lookup_magsq_[u.iq16] = magsq(d_i, d_q); - } + unsigned i, q; + for (i = 0; i < 256; ++i) { + double d_i = (i - 127.5) / 128.0; + for (q = 0; q < 256; ++q) { + double d_q = (q - 127.5) / 128.0; + u.iq[0] = i; + u.iq[1] = q; + lookup_phase_[u.iq16] = scaled_atan2(d_q, d_i); + lookup_magsq_[u.iq16] = magsq(d_i, d_q); } } - - void CU8Converter::ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) { - const cu8_alias *in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 2; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 8) { - *out++ = lookup_phase_[in_iq[0].iq16]; - *out++ = lookup_phase_[in_iq[1].iq16]; - *out++ = lookup_phase_[in_iq[2].iq16]; - *out++ = lookup_phase_[in_iq[3].iq16]; - *out++ = lookup_phase_[in_iq[4].iq16]; - *out++ = lookup_phase_[in_iq[5].iq16]; - *out++ = lookup_phase_[in_iq[6].iq16]; - *out++ = lookup_phase_[in_iq[7].iq16]; - } - for (auto i = 0; i < n7; ++i, ++in_iq) { - *out++ = lookup_phase_[in_iq[0].iq16]; - } +} + +void CU8Converter::ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) { + const cu8_alias *in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 2; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 8) { + *out++ = lookup_phase_[in_iq[0].iq16]; + *out++ = lookup_phase_[in_iq[1].iq16]; + *out++ = lookup_phase_[in_iq[2].iq16]; + *out++ = lookup_phase_[in_iq[3].iq16]; + *out++ = lookup_phase_[in_iq[4].iq16]; + *out++ = lookup_phase_[in_iq[5].iq16]; + *out++ = lookup_phase_[in_iq[6].iq16]; + *out++ = lookup_phase_[in_iq[7].iq16]; } - - void CU8Converter::ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) { - const cu8_alias *in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 2; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 8) { - *out++ = lookup_magsq_[in_iq[0].iq16]; - *out++ = lookup_magsq_[in_iq[1].iq16]; - *out++ = lookup_magsq_[in_iq[2].iq16]; - *out++ = lookup_magsq_[in_iq[3].iq16]; - *out++ = lookup_magsq_[in_iq[4].iq16]; - *out++ = lookup_magsq_[in_iq[5].iq16]; - *out++ = lookup_magsq_[in_iq[6].iq16]; - *out++ = lookup_magsq_[in_iq[7].iq16]; - } - for (auto i = 0; i < n7; ++i, ++in_iq) { - *out++ = lookup_magsq_[in_iq[0].iq16]; - } + for (auto i = 0; i < n7; ++i, ++in_iq) { + *out++ = lookup_phase_[in_iq[0].iq16]; } - - CS8Converter::CS8Converter() : SampleConverter(SampleFormat::CS8) { - cs8_alias u; - - int i, q; - for (i = -128; i <= 127; ++i) { - double d_i = i / 128.0; - for (q = -128; q <= 127; ++q) { - double d_q = q / 128.0; - u.iq[0] = i; - u.iq[1] = q; - lookup_phase_[u.iq16] = scaled_atan2(d_q, d_i); - lookup_magsq_[u.iq16] = magsq(d_i, d_q); - } - } +} + +void CU8Converter::ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) { + const cu8_alias *in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 2; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 8) { + *out++ = lookup_magsq_[in_iq[0].iq16]; + *out++ = lookup_magsq_[in_iq[1].iq16]; + *out++ = lookup_magsq_[in_iq[2].iq16]; + *out++ = lookup_magsq_[in_iq[3].iq16]; + *out++ = lookup_magsq_[in_iq[4].iq16]; + *out++ = lookup_magsq_[in_iq[5].iq16]; + *out++ = lookup_magsq_[in_iq[6].iq16]; + *out++ = lookup_magsq_[in_iq[7].iq16]; } - - void CS8Converter::ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) { - auto in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 2; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 8) { - *out++ = lookup_phase_[in_iq[0].iq16]; - *out++ = lookup_phase_[in_iq[1].iq16]; - *out++ = lookup_phase_[in_iq[2].iq16]; - *out++ = lookup_phase_[in_iq[3].iq16]; - *out++ = lookup_phase_[in_iq[4].iq16]; - *out++ = lookup_phase_[in_iq[5].iq16]; - *out++ = lookup_phase_[in_iq[6].iq16]; - *out++ = lookup_phase_[in_iq[7].iq16]; - } - for (auto i = 0; i < n7; ++i, ++in_iq) { - *out++ = lookup_phase_[in_iq[0].iq16]; - } + for (auto i = 0; i < n7; ++i, ++in_iq) { + *out++ = lookup_magsq_[in_iq[0].iq16]; } +} - void CS8Converter::ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) { - auto in_iq = reinterpret_cast(&*begin); +CS8Converter::CS8Converter() : SampleConverter(SampleFormat::CS8) { + cs8_alias u; - // unroll the loop - const auto n = std::distance(begin, end) / 2; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 8) { - *out++ = lookup_magsq_[in_iq[0].iq16]; - *out++ = lookup_magsq_[in_iq[1].iq16]; - *out++ = lookup_magsq_[in_iq[2].iq16]; - *out++ = lookup_magsq_[in_iq[3].iq16]; - *out++ = lookup_magsq_[in_iq[4].iq16]; - *out++ = lookup_magsq_[in_iq[5].iq16]; - *out++ = lookup_magsq_[in_iq[6].iq16]; - *out++ = lookup_magsq_[in_iq[7].iq16]; - } - for (auto i = 0; i < n7; ++i, ++in_iq) { - *out++ = lookup_magsq_[in_iq[0].iq16]; + int i, q; + for (i = -128; i <= 127; ++i) { + double d_i = i / 128.0; + for (q = -128; q <= 127; ++q) { + double d_q = q / 128.0; + u.iq[0] = i; + u.iq[1] = q; + lookup_phase_[u.iq16] = scaled_atan2(d_q, d_i); + lookup_magsq_[u.iq16] = magsq(d_i, d_q); } } - - void CS16HConverter::ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) { - auto in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 4; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 16) { - *out++ = scaled_atan2(in_iq[1], in_iq[0]); - *out++ = scaled_atan2(in_iq[3], in_iq[2]); - *out++ = scaled_atan2(in_iq[5], in_iq[4]); - *out++ = scaled_atan2(in_iq[7], in_iq[6]); - *out++ = scaled_atan2(in_iq[9], in_iq[8]); - *out++ = scaled_atan2(in_iq[11], in_iq[10]); - *out++ = scaled_atan2(in_iq[13], in_iq[12]); - *out++ = scaled_atan2(in_iq[15], in_iq[14]); - } - for (auto i = 0; i < n7; ++i, in_iq += 2) { - *out++ = scaled_atan2(in_iq[1], in_iq[0]); - } +} + +void CS8Converter::ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 2; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 8) { + *out++ = lookup_phase_[in_iq[0].iq16]; + *out++ = lookup_phase_[in_iq[1].iq16]; + *out++ = lookup_phase_[in_iq[2].iq16]; + *out++ = lookup_phase_[in_iq[3].iq16]; + *out++ = lookup_phase_[in_iq[4].iq16]; + *out++ = lookup_phase_[in_iq[5].iq16]; + *out++ = lookup_phase_[in_iq[6].iq16]; + *out++ = lookup_phase_[in_iq[7].iq16]; } - - void CS16HConverter::ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) { - auto in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 4; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 16) { - *out++ = magsq(in_iq[1], in_iq[0]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[3], in_iq[2]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[5], in_iq[4]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[7], in_iq[6]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[9], in_iq[8]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[11], in_iq[10]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[13], in_iq[12]) / 32768.0 / 32768.0; - *out++ = magsq(in_iq[15], in_iq[14]) / 32768.0 / 32768.0; - } - for (auto i = 0; i < n7; ++i, in_iq += 2) { - *out++ = magsq(in_iq[1], in_iq[0]) / 32768.0 / 32768.0; - } + for (auto i = 0; i < n7; ++i, ++in_iq) { + *out++ = lookup_phase_[in_iq[0].iq16]; } - - void CF32HConverter::ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) { - auto in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 8; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 16) { - *out++ = scaled_atan2(in_iq[1], in_iq[0]); - *out++ = scaled_atan2(in_iq[3], in_iq[2]); - *out++ = scaled_atan2(in_iq[5], in_iq[4]); - *out++ = scaled_atan2(in_iq[7], in_iq[6]); - *out++ = scaled_atan2(in_iq[9], in_iq[8]); - *out++ = scaled_atan2(in_iq[11], in_iq[10]); - *out++ = scaled_atan2(in_iq[13], in_iq[12]); - *out++ = scaled_atan2(in_iq[15], in_iq[14]); - } - for (auto i = 0; i < n7; ++i, in_iq += 2) { - *out++ = scaled_atan2(in_iq[1], in_iq[0]); - } +} + +void CS8Converter::ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 2; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 8) { + *out++ = lookup_magsq_[in_iq[0].iq16]; + *out++ = lookup_magsq_[in_iq[1].iq16]; + *out++ = lookup_magsq_[in_iq[2].iq16]; + *out++ = lookup_magsq_[in_iq[3].iq16]; + *out++ = lookup_magsq_[in_iq[4].iq16]; + *out++ = lookup_magsq_[in_iq[5].iq16]; + *out++ = lookup_magsq_[in_iq[6].iq16]; + *out++ = lookup_magsq_[in_iq[7].iq16]; } - - void CF32HConverter::ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) { - auto in_iq = reinterpret_cast(&*begin); - - // unroll the loop - const auto n = std::distance(begin, end) / 8; - const auto n8 = n / 8; - const auto n7 = n & 7; - - for (auto i = 0; i < n8; ++i, in_iq += 16) { - *out++ = magsq(in_iq[1], in_iq[0]); - *out++ = magsq(in_iq[3], in_iq[2]); - *out++ = magsq(in_iq[5], in_iq[4]); - *out++ = magsq(in_iq[7], in_iq[6]); - *out++ = magsq(in_iq[9], in_iq[8]); - *out++ = magsq(in_iq[11], in_iq[10]); - *out++ = magsq(in_iq[13], in_iq[12]); - *out++ = magsq(in_iq[15], in_iq[14]); - } - for (auto i = 0; i < n7; ++i, in_iq += 2) { - *out++ = magsq(in_iq[1], in_iq[0]); - } + for (auto i = 0; i < n7; ++i, ++in_iq) { + *out++ = lookup_magsq_[in_iq[0].iq16]; + } +} + +void CS16HConverter::ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 4; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 16) { + *out++ = scaled_atan2(in_iq[1], in_iq[0]); + *out++ = scaled_atan2(in_iq[3], in_iq[2]); + *out++ = scaled_atan2(in_iq[5], in_iq[4]); + *out++ = scaled_atan2(in_iq[7], in_iq[6]); + *out++ = scaled_atan2(in_iq[9], in_iq[8]); + *out++ = scaled_atan2(in_iq[11], in_iq[10]); + *out++ = scaled_atan2(in_iq[13], in_iq[12]); + *out++ = scaled_atan2(in_iq[15], in_iq[14]); + } + for (auto i = 0; i < n7; ++i, in_iq += 2) { + *out++ = scaled_atan2(in_iq[1], in_iq[0]); + } +} + +void CS16HConverter::ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 4; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 16) { + *out++ = magsq(in_iq[1], in_iq[0]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[3], in_iq[2]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[5], in_iq[4]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[7], in_iq[6]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[9], in_iq[8]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[11], in_iq[10]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[13], in_iq[12]) / 32768.0 / 32768.0; + *out++ = magsq(in_iq[15], in_iq[14]) / 32768.0 / 32768.0; + } + for (auto i = 0; i < n7; ++i, in_iq += 2) { + *out++ = magsq(in_iq[1], in_iq[0]) / 32768.0 / 32768.0; + } +} + +void CF32HConverter::ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 8; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 16) { + *out++ = scaled_atan2(in_iq[1], in_iq[0]); + *out++ = scaled_atan2(in_iq[3], in_iq[2]); + *out++ = scaled_atan2(in_iq[5], in_iq[4]); + *out++ = scaled_atan2(in_iq[7], in_iq[6]); + *out++ = scaled_atan2(in_iq[9], in_iq[8]); + *out++ = scaled_atan2(in_iq[11], in_iq[10]); + *out++ = scaled_atan2(in_iq[13], in_iq[12]); + *out++ = scaled_atan2(in_iq[15], in_iq[14]); + } + for (auto i = 0; i < n7; ++i, in_iq += 2) { + *out++ = scaled_atan2(in_iq[1], in_iq[0]); + } +} + +void CF32HConverter::ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) { + auto in_iq = reinterpret_cast(&*begin); + + // unroll the loop + const auto n = std::distance(begin, end) / 8; + const auto n8 = n / 8; + const auto n7 = n & 7; + + for (auto i = 0; i < n8; ++i, in_iq += 16) { + *out++ = magsq(in_iq[1], in_iq[0]); + *out++ = magsq(in_iq[3], in_iq[2]); + *out++ = magsq(in_iq[5], in_iq[4]); + *out++ = magsq(in_iq[7], in_iq[6]); + *out++ = magsq(in_iq[9], in_iq[8]); + *out++ = magsq(in_iq[11], in_iq[10]); + *out++ = magsq(in_iq[13], in_iq[12]); + *out++ = magsq(in_iq[15], in_iq[14]); + } + for (auto i = 0; i < n7; ++i, in_iq += 2) { + *out++ = magsq(in_iq[1], in_iq[0]); } -} // namespace dump978 +} diff --git a/convert.h b/convert.h index b0a3cc3..0b2cb9f 100644 --- a/convert.h +++ b/convert.h @@ -11,7 +11,7 @@ #include "common.h" -namespace dump978 { +namespace flightaware::uat { // Describes a sample data layout: // CU8 - interleaved I/Q data, 8 bit unsigned integers // CS8 - interleaved I/Q data, 8 bit signed integers @@ -41,19 +41,19 @@ namespace dump978 { public: typedef std::shared_ptr Pointer; - SampleConverter(SampleFormat format) : format_(format), bytes_per_sample_(dump978::BytesPerSample(format)) {} + SampleConverter(SampleFormat format) : format_(format), bytes_per_sample_(flightaware::uat::BytesPerSample(format)) {} virtual ~SampleConverter() {} // Read samples from `begin` .. `end` and write one phase value per sample to // `out`. The input buffer should contain an integral number of samples // (trailing partial samples are ignored, not buffered). - virtual void ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) = 0; + virtual void ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) = 0; // Read samples from `begin` .. `end` and write one magnitude-squared value // per sample to `out`. The input buffer should contain an integral number of // samples (trailing partial samples are ignored, not buffered). - virtual void ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) = 0; + virtual void ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) = 0; SampleFormat Format() const { return format_; } unsigned BytesPerSample() const { return bytes_per_sample_; } @@ -70,8 +70,8 @@ namespace dump978 { public: CU8Converter(); - void ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) override; - void ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) override; + void ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) override; + void ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) override; private: union cu8_alias { @@ -87,8 +87,8 @@ namespace dump978 { public: CS8Converter(); - void ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) override; - void ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) override; + void ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) override; + void ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) override; private: union cs8_alias { @@ -103,16 +103,16 @@ namespace dump978 { class CS16HConverter : public SampleConverter { public: CS16HConverter() : SampleConverter(SampleFormat::CS16H) {} - void ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) override; - void ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) override; + void ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) override; + void ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) override; }; class CF32HConverter : public SampleConverter { public: CF32HConverter() : SampleConverter(SampleFormat::CF32H) {} - void ConvertPhase(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, uat::PhaseBuffer::iterator out) override; - void ConvertMagSq(uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end, std::vector::iterator out) override; + void ConvertPhase(Bytes::const_iterator begin, Bytes::const_iterator end, PhaseBuffer::iterator out) override; + void ConvertMagSq(Bytes::const_iterator begin, Bytes::const_iterator end, std::vector::iterator out) override; }; -}; // namespace dump978 +}; // namespace flightaware::uat #endif diff --git a/demodulator.cc b/demodulator.cc index 88c92fb..6d4c6ba 100644 --- a/demodulator.cc +++ b/demodulator.cc @@ -9,417 +9,415 @@ #include #include -using namespace uat; +using namespace flightaware::uat; -namespace dump978 { - SingleThreadReceiver::SingleThreadReceiver(SampleFormat format) : converter_(SampleConverter::Create(format)), demodulator_(new TwoMegDemodulator()) {} +SingleThreadReceiver::SingleThreadReceiver(SampleFormat format) : converter_(SampleConverter::Create(format)), demodulator_(new TwoMegDemodulator()) {} - // Handle samples in 'buffer' by: - // converting them to a phase buffer - // demodulating the phase buffer - // dispatching any demodulated messages - // preserving the end of the phase buffer for reuse in the next call - void SingleThreadReceiver::HandleSamples(std::uint64_t timestamp, uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end) { - assert(converter_); +// Handle samples in 'buffer' by: +// converting them to a phase buffer +// demodulating the phase buffer +// dispatching any demodulated messages +// preserving the end of the phase buffer for reuse in the next call +void SingleThreadReceiver::HandleSamples(std::uint64_t timestamp, Bytes::const_iterator begin, Bytes::const_iterator end) { + assert(converter_); - const auto buffer_bytes = std::distance(begin, end); - const auto buffer_samples = buffer_bytes / converter_->BytesPerSample(); + const auto buffer_bytes = std::distance(begin, end); + const auto buffer_samples = buffer_bytes / converter_->BytesPerSample(); - const auto previous_samples = saved_samples_; - const auto previous_bytes = previous_samples * converter_->BytesPerSample(); + const auto previous_samples = saved_samples_; + const auto previous_bytes = previous_samples * converter_->BytesPerSample(); - const auto total_samples = buffer_samples + previous_samples; - const auto total_bytes = total_samples * converter_->BytesPerSample(); + const auto total_samples = buffer_samples + previous_samples; + const auto total_bytes = total_samples * converter_->BytesPerSample(); - if (samples_.size() < total_bytes) { - samples_.resize(total_bytes); - } - - // TODO: rearrange things to avoid this copy - std::copy(begin, end, samples_.begin() + previous_bytes); - - if (phase_.size() < total_samples) { - phase_.resize(total_samples); - } + if (samples_.size() < total_bytes) { + samples_.resize(total_bytes); + } - converter_->ConvertPhase(samples_.begin(), samples_.begin() + total_bytes, phase_.begin()); - auto messages = demodulator_->Demodulate(phase_.begin(), phase_.begin() + total_samples); + // TODO: rearrange things to avoid this copy + std::copy(begin, end, samples_.begin() + previous_bytes); - if (!messages.empty()) { - SharedMessageVector dispatch = std::make_shared(); - dispatch->reserve(messages.size()); - for (auto &message : messages) { - std::vector magsq; - magsq.resize(std::distance(message.begin, message.end)); + if (phase_.size() < total_samples) { + phase_.resize(total_samples); + } - auto begin_sample = samples_.begin() + std::distance(phase_.cbegin(), message.begin) * converter_->BytesPerSample(); - auto end_sample = samples_.begin() + std::distance(phase_.cbegin(), message.end) * converter_->BytesPerSample(); + converter_->ConvertPhase(samples_.begin(), samples_.begin() + total_bytes, phase_.begin()); + auto messages = demodulator_->Demodulate(phase_.begin(), phase_.begin() + total_samples); - converter_->ConvertMagSq(begin_sample, end_sample, magsq.begin()); + if (!messages.empty()) { + SharedMessageVector dispatch = std::make_shared(); + dispatch->reserve(messages.size()); + for (auto &message : messages) { + std::vector magsq; + magsq.resize(std::distance(message.begin, message.end)); - auto total_power = 0.0; - for (auto m : magsq) { - total_power += m; - } + auto begin_sample = samples_.begin() + std::distance(phase_.cbegin(), message.begin) * converter_->BytesPerSample(); + auto end_sample = samples_.begin() + std::distance(phase_.cbegin(), message.end) * converter_->BytesPerSample(); - auto rssi = (total_power == 0 ? -1000 : 10 * std::log10(total_power / magsq.size())); - std::uint64_t message_timestamp = timestamp - (1000 * previous_samples / 2083333) + (1000 * std::distance(phase_.cbegin(), message.begin) / 2083333); + converter_->ConvertMagSq(begin_sample, end_sample, magsq.begin()); - dispatch->emplace_back(std::move(message.payload), message_timestamp, message.corrected_errors, rssi); + auto total_power = 0.0; + for (auto m : magsq) { + total_power += m; } - DispatchMessages(dispatch); - } + auto rssi = (total_power == 0 ? -1000 : 10 * std::log10(total_power / magsq.size())); + std::uint64_t message_timestamp = timestamp - (1000 * previous_samples / 2083333) + (1000 * std::distance(phase_.cbegin(), message.begin) / 2083333); - // preserve the tail of the sample buffer for next time - const auto tail_size = demodulator_->NumTrailingSamples(); - if (total_samples > tail_size) { - std::copy(samples_.end() - tail_size * converter_->BytesPerSample(), samples_.end(), samples_.begin()); - saved_samples_ = tail_size; - } else { - saved_samples_ = total_samples; + dispatch->emplace_back(std::move(message.payload), message_timestamp, message.corrected_errors, rssi); } - } - static inline std::int16_t PhaseDifference(std::uint16_t from, std::uint16_t to) { - int32_t difference = to - from; // lies in the range -65535 .. +65535 - if (difference >= 32768) // +32768..+65535 - return difference - 65536; // -> -32768..-1: always in range - else if (difference < -32768) // -65535..-32769 - return difference + 65536; // -> +1..32767: always in range - else - return difference; + DispatchMessages(dispatch); } - static inline bool SyncWordMatch(std::uint64_t word, std::uint64_t expected) { - std::uint64_t diff; - - if (word == expected) - return 1; - - diff = word ^ expected; // guaranteed nonzero - - // This is a bit-twiddling popcount - // hack, tweaked as we only care about - // "=N" set bits for fixed N - - // so we can bail out early after seeing N - // set bits. - // - // It relies on starting with a nonzero value - // with zero or more trailing clear bits - // after the last set bit: - // - // 010101010101010000 - // ^ - // Subtracting one, will flip the - // bits starting at the last set bit: - // - // 010101010101001111 - // ^ - // then we can use that as a bitwise-and - // mask to clear the lowest set bit: - // - // 010101010101000000 - // ^ - // And repeat until the value is zero - // or we have seen too many set bits. - - // >= 1 bit - diff &= (diff - 1); // clear lowest set bit - if (!diff) - return 1; // 1 bit error - - // >= 2 bits - diff &= (diff - 1); // clear lowest set bit - if (!diff) - return 1; // 2 bits error - - // >= 3 bits - diff &= (diff - 1); // clear lowest set bit - if (!diff) - return 1; // 3 bits error - - // >= 4 bits - diff &= (diff - 1); // clear lowest set bit - if (!diff) - return 1; // 4 bits error - - // > 4 bits in error, give up - return 0; + // preserve the tail of the sample buffer for next time + const auto tail_size = demodulator_->NumTrailingSamples(); + if (total_samples > tail_size) { + std::copy(samples_.end() - tail_size * converter_->BytesPerSample(), samples_.end(), samples_.begin()); + saved_samples_ = tail_size; + } else { + saved_samples_ = total_samples; } +} + +static inline std::int16_t PhaseDifference(std::uint16_t from, std::uint16_t to) { + int32_t difference = to - from; // lies in the range -65535 .. +65535 + if (difference >= 32768) // +32768..+65535 + return difference - 65536; // -> -32768..-1: always in range + else if (difference < -32768) // -65535..-32769 + return difference + 65536; // -> +1..32767: always in range + else + return difference; +} + +static inline bool SyncWordMatch(std::uint64_t word, std::uint64_t expected) { + std::uint64_t diff; + + if (word == expected) + return 1; + + diff = word ^ expected; // guaranteed nonzero + + // This is a bit-twiddling popcount + // hack, tweaked as we only care about + // "=N" set bits for fixed N - + // so we can bail out early after seeing N + // set bits. + // + // It relies on starting with a nonzero value + // with zero or more trailing clear bits + // after the last set bit: + // + // 010101010101010000 + // ^ + // Subtracting one, will flip the + // bits starting at the last set bit: + // + // 010101010101001111 + // ^ + // then we can use that as a bitwise-and + // mask to clear the lowest set bit: + // + // 010101010101000000 + // ^ + // And repeat until the value is zero + // or we have seen too many set bits. + + // >= 1 bit + diff &= (diff - 1); // clear lowest set bit + if (!diff) + return 1; // 1 bit error + + // >= 2 bits + diff &= (diff - 1); // clear lowest set bit + if (!diff) + return 1; // 2 bits error + + // >= 3 bits + diff &= (diff - 1); // clear lowest set bit + if (!diff) + return 1; // 3 bits error + + // >= 4 bits + diff &= (diff - 1); // clear lowest set bit + if (!diff) + return 1; // 4 bits error + + // > 4 bits in error, give up + return 0; +} #ifdef AUTO_CENTER - // check that there is a valid sync word starting at 'phase' - // that matches the sync word 'pattern'. Return a pair: - // first element is true if the sync word looks OK; second - // element has the dphi threshold to use for bit slicing - static inline std::pair CheckSyncWord(PhaseBuffer::const_iterator phase, std::uint64_t pattern) { - const unsigned MAX_SYNC_ERRORS = 4; - - std::int32_t dphi_zero_total = 0; - int zero_bits = 0; - std::int32_t dphi_one_total = 0; - int one_bits = 0; - - // find mean dphi for zero and one bits; - // take the mean of the two as our central value - - for (unsigned i = 0; i < SYNC_BITS; ++i) { - auto dphi = PhaseDifference(phase[i * 2], phase[i * 2 + 1]); - if (pattern & (1UL << (35 - i))) { - ++one_bits; - dphi_one_total += dphi; - } else { - ++zero_bits; - dphi_zero_total += dphi; - } +// check that there is a valid sync word starting at 'phase' +// that matches the sync word 'pattern'. Return a pair: +// first element is true if the sync word looks OK; second +// element has the dphi threshold to use for bit slicing +static inline std::pair CheckSyncWord(PhaseBuffer::const_iterator phase, std::uint64_t pattern) { + const unsigned MAX_SYNC_ERRORS = 4; + + std::int32_t dphi_zero_total = 0; + int zero_bits = 0; + std::int32_t dphi_one_total = 0; + int one_bits = 0; + + // find mean dphi for zero and one bits; + // take the mean of the two as our central value + + for (unsigned i = 0; i < SYNC_BITS; ++i) { + auto dphi = PhaseDifference(phase[i * 2], phase[i * 2 + 1]); + if (pattern & (1UL << (35 - i))) { + ++one_bits; + dphi_one_total += dphi; + } else { + ++zero_bits; + dphi_zero_total += dphi; } + } - dphi_zero_total /= zero_bits; - dphi_one_total /= one_bits; + dphi_zero_total /= zero_bits; + dphi_one_total /= one_bits; - std::int16_t center = (dphi_one_total + dphi_zero_total) / 2; + std::int16_t center = (dphi_one_total + dphi_zero_total) / 2; - // recheck sync word using our center value - unsigned error_bits = 0; - for (unsigned i = 0; i < SYNC_BITS; ++i) { - auto dphi = PhaseDifference(phase[i * 2], phase[i * 2 + 1]); + // recheck sync word using our center value + unsigned error_bits = 0; + for (unsigned i = 0; i < SYNC_BITS; ++i) { + auto dphi = PhaseDifference(phase[i * 2], phase[i * 2 + 1]); - if (pattern & (1UL << (35 - i))) { - if (dphi < center) - ++error_bits; - } else { - if (dphi > center) - ++error_bits; - } + if (pattern & (1UL << (35 - i))) { + if (dphi < center) + ++error_bits; + } else { + if (dphi > center) + ++error_bits; } - - return {(error_bits <= MAX_SYNC_ERRORS), center}; } -#endif - // demodulate 'bytes' bytes from samples at 'phase' using 'center' as the bit - // slicing threshold - static inline std::pair> DemodBits(PhaseBuffer::const_iterator phase, unsigned bytes, std::int16_t zero_slice, std::int16_t one_slice) { - std::pair> result_pair; - auto &result = result_pair.first; - auto &erasures = result_pair.second; - - result.reserve(bytes); - - for (unsigned i = 0; i < bytes; ++i) { - std::uint8_t b = 0; - bool erasure = false; - if (PhaseDifference(phase[0], phase[1]) > one_slice) - b |= 0x80; - else if (PhaseDifference(phase[0], phase[1]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[2], phase[3]) > one_slice) - b |= 0x40; - else if (PhaseDifference(phase[2], phase[3]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[4], phase[5]) > one_slice) - b |= 0x20; - else if (PhaseDifference(phase[4], phase[5]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[6], phase[7]) > one_slice) - b |= 0x10; - else if (PhaseDifference(phase[6], phase[7]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[8], phase[9]) > one_slice) - b |= 0x08; - else if (PhaseDifference(phase[8], phase[9]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[10], phase[11]) > one_slice) - b |= 0x04; - else if (PhaseDifference(phase[10], phase[11]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[12], phase[13]) > one_slice) - b |= 0x02; - else if (PhaseDifference(phase[12], phase[13]) > zero_slice) - erasure = true; - if (PhaseDifference(phase[14], phase[15]) > one_slice) - b |= 0x01; - else if (PhaseDifference(phase[14], phase[15]) > zero_slice) - erasure = true; - result.push_back(b); - if (erasure) - erasures.push_back(i); - phase += 16; - } + return {(error_bits <= MAX_SYNC_ERRORS), center}; +} +#endif - return result_pair; +// demodulate 'bytes' bytes from samples at 'phase' using 'center' as the bit +// slicing threshold +static inline std::pair> DemodBits(PhaseBuffer::const_iterator phase, unsigned bytes, std::int16_t zero_slice, std::int16_t one_slice) { + std::pair> result_pair; + auto &result = result_pair.first; + auto &erasures = result_pair.second; + + result.reserve(bytes); + + for (unsigned i = 0; i < bytes; ++i) { + std::uint8_t b = 0; + bool erasure = false; + if (PhaseDifference(phase[0], phase[1]) > one_slice) + b |= 0x80; + else if (PhaseDifference(phase[0], phase[1]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[2], phase[3]) > one_slice) + b |= 0x40; + else if (PhaseDifference(phase[2], phase[3]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[4], phase[5]) > one_slice) + b |= 0x20; + else if (PhaseDifference(phase[4], phase[5]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[6], phase[7]) > one_slice) + b |= 0x10; + else if (PhaseDifference(phase[6], phase[7]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[8], phase[9]) > one_slice) + b |= 0x08; + else if (PhaseDifference(phase[8], phase[9]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[10], phase[11]) > one_slice) + b |= 0x04; + else if (PhaseDifference(phase[10], phase[11]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[12], phase[13]) > one_slice) + b |= 0x02; + else if (PhaseDifference(phase[12], phase[13]) > zero_slice) + erasure = true; + if (PhaseDifference(phase[14], phase[15]) > one_slice) + b |= 0x01; + else if (PhaseDifference(phase[14], phase[15]) > zero_slice) + erasure = true; + result.push_back(b); + if (erasure) + erasures.push_back(i); + phase += 16; } - unsigned TwoMegDemodulator::NumTrailingSamples() { return (SYNC_BITS + UPLINK_BITS) * 2; } - - // Try to demodulate messages from `begin` .. `end` and return a list of - // messages. Messages that start near the end of the range may not be - // demodulated (less than (SYNC_BITS + UPLINK_BITS)*2 before the end of the - // buffer) - std::vector TwoMegDemodulator::Demodulate(PhaseBuffer::const_iterator begin, PhaseBuffer::const_iterator end) { - // We expect samples at twice the UAT bitrate. - // We look at phase difference between pairs of adjacent samples, i.e. - // sample 1 - sample 0 -> sync0 - // sample 2 - sample 1 -> sync1 - // sample 3 - sample 2 -> sync0 - // sample 4 - sample 3 -> sync1 - // ... - // - // We accumulate bits into two buffers, sync0 and sync1. - // Then we compare those buffers to the expected 36-bit sync word that - // should be at the start of each UAT frame. When (if) we find it, - // that tells us which sample to start decoding from. - - // Stop when we run out of remaining samples for a max-sized frame. - // Arrange for our caller to pass the trailing data back to us next time; - // ensure we don't consume any partial sync word we might be part-way - // through. This means we don't need to maintain state between calls. - - std::vector messages; - - const int trailing_samples = (SYNC_BITS + UPLINK_BITS) * 2; - if (std::distance(begin, end) < trailing_samples) { - return messages; - } + return result_pair; +} + +unsigned TwoMegDemodulator::NumTrailingSamples() { return (SYNC_BITS + UPLINK_BITS) * 2; } + +// Try to demodulate messages from `begin` .. `end` and return a list of +// messages. Messages that start near the end of the range may not be +// demodulated (less than (SYNC_BITS + UPLINK_BITS)*2 before the end of the +// buffer) +std::vector TwoMegDemodulator::Demodulate(PhaseBuffer::const_iterator begin, PhaseBuffer::const_iterator end) { + // We expect samples at twice the UAT bitrate. + // We look at phase difference between pairs of adjacent samples, i.e. + // sample 1 - sample 0 -> sync0 + // sample 2 - sample 1 -> sync1 + // sample 3 - sample 2 -> sync0 + // sample 4 - sample 3 -> sync1 + // ... + // + // We accumulate bits into two buffers, sync0 and sync1. + // Then we compare those buffers to the expected 36-bit sync word that + // should be at the start of each UAT frame. When (if) we find it, + // that tells us which sample to start decoding from. + + // Stop when we run out of remaining samples for a max-sized frame. + // Arrange for our caller to pass the trailing data back to us next time; + // ensure we don't consume any partial sync word we might be part-way + // through. This means we don't need to maintain state between calls. + + std::vector messages; + + const int trailing_samples = (SYNC_BITS + UPLINK_BITS) * 2; + if (std::distance(begin, end) < trailing_samples) { + return messages; + } - const auto limit = end - trailing_samples; - - unsigned sync_bits = 0; - std::uint64_t sync0 = 0, sync1 = 0; - const std::uint64_t SYNC_MASK = ((((std::uint64_t)1) << SYNC_BITS) - 1); - - for (auto probe = begin; probe < limit; probe += 2) { - auto d0 = PhaseDifference(probe[0], probe[1]); - auto d1 = PhaseDifference(probe[1], probe[2]); - - sync0 = ((sync0 << 1) | (d0 > 0 ? 1 : 0)) & SYNC_MASK; - sync1 = ((sync1 << 1) | (d1 > 0 ? 1 : 0)) & SYNC_MASK; - - if (++sync_bits < SYNC_BITS) - continue; // haven't fully populated sync0/1 yet - - // see if we have (the start of) a valid sync word - // when we find a match, try to demodulate both with that match - // and with the next position, and pick the one with fewer - // errors. - if (SyncWordMatch(sync0, DOWNLINK_SYNC_WORD)) { - auto start = probe - SYNC_BITS * 2 + 2; - auto message = DemodBest(start, true /* downlink */); - if (message) { - probe = message->end - 2; - sync_bits = 0; - messages.emplace_back(std::move(message.value())); - continue; - } + const auto limit = end - trailing_samples; + + unsigned sync_bits = 0; + std::uint64_t sync0 = 0, sync1 = 0; + const std::uint64_t SYNC_MASK = ((((std::uint64_t)1) << SYNC_BITS) - 1); + + for (auto probe = begin; probe < limit; probe += 2) { + auto d0 = PhaseDifference(probe[0], probe[1]); + auto d1 = PhaseDifference(probe[1], probe[2]); + + sync0 = ((sync0 << 1) | (d0 > 0 ? 1 : 0)) & SYNC_MASK; + sync1 = ((sync1 << 1) | (d1 > 0 ? 1 : 0)) & SYNC_MASK; + + if (++sync_bits < SYNC_BITS) + continue; // haven't fully populated sync0/1 yet + + // see if we have (the start of) a valid sync word + // when we find a match, try to demodulate both with that match + // and with the next position, and pick the one with fewer + // errors. + if (SyncWordMatch(sync0, DOWNLINK_SYNC_WORD)) { + auto start = probe - SYNC_BITS * 2 + 2; + auto message = DemodBest(start, true /* downlink */); + if (message) { + probe = message->end - 2; + sync_bits = 0; + messages.emplace_back(std::move(message.value())); + continue; } + } - if (SyncWordMatch(sync1, DOWNLINK_SYNC_WORD)) { - auto start = probe - SYNC_BITS * 2 + 3; - auto message = DemodBest(start, true /* downlink */); - if (message) { - probe = message->end - 2; - sync_bits = 0; - messages.emplace_back(std::move(message.value())); - continue; - } + if (SyncWordMatch(sync1, DOWNLINK_SYNC_WORD)) { + auto start = probe - SYNC_BITS * 2 + 3; + auto message = DemodBest(start, true /* downlink */); + if (message) { + probe = message->end - 2; + sync_bits = 0; + messages.emplace_back(std::move(message.value())); + continue; } + } - if (SyncWordMatch(sync0, UPLINK_SYNC_WORD)) { - auto start = probe - SYNC_BITS * 2 + 2; - auto message = DemodBest(start, false /* !downlink */); - if (message) { - probe = message->end - 2; - sync_bits = 0; - messages.emplace_back(std::move(message.value())); - continue; - } + if (SyncWordMatch(sync0, UPLINK_SYNC_WORD)) { + auto start = probe - SYNC_BITS * 2 + 2; + auto message = DemodBest(start, false /* !downlink */); + if (message) { + probe = message->end - 2; + sync_bits = 0; + messages.emplace_back(std::move(message.value())); + continue; } + } - if (SyncWordMatch(sync1, UPLINK_SYNC_WORD)) { - auto start = probe - SYNC_BITS * 2 + 3; - auto message = DemodBest(start, false /* !downlink */); - if (message) { - probe = message->end - 2; - sync_bits = 0; - messages.emplace_back(std::move(message.value())); - continue; - } + if (SyncWordMatch(sync1, UPLINK_SYNC_WORD)) { + auto start = probe - SYNC_BITS * 2 + 3; + auto message = DemodBest(start, false /* !downlink */); + if (message) { + probe = message->end - 2; + sync_bits = 0; + messages.emplace_back(std::move(message.value())); + continue; } } - - return messages; } - boost::optional TwoMegDemodulator::DemodBest(PhaseBuffer::const_iterator start, bool downlink) { - auto message0 = downlink ? DemodOneDownlink(start) : DemodOneUplink(start); - auto message1 = downlink ? DemodOneDownlink(start + 1) : DemodOneUplink(start + 1); + return messages; +} - if (!message0 && !message1) - return boost::none; +boost::optional TwoMegDemodulator::DemodBest(PhaseBuffer::const_iterator start, bool downlink) { + auto message0 = downlink ? DemodOneDownlink(start) : DemodOneUplink(start); + auto message1 = downlink ? DemodOneDownlink(start + 1) : DemodOneUplink(start + 1); - unsigned errors0 = (message0 ? message0->corrected_errors : 9999); - unsigned errors1 = (message1 ? message1->corrected_errors : 9999); + if (!message0 && !message1) + return boost::none; - if (errors0 <= errors1) - return message0; // should be move-eligible - else - return message1; // should be move-eligible - } + unsigned errors0 = (message0 ? message0->corrected_errors : 9999); + unsigned errors1 = (message1 ? message1->corrected_errors : 9999); - boost::optional TwoMegDemodulator::DemodOneDownlink(PhaseBuffer::const_iterator start) { + if (errors0 <= errors1) + return message0; // should be move-eligible + else + return message1; // should be move-eligible +} + +boost::optional TwoMegDemodulator::DemodOneDownlink(PhaseBuffer::const_iterator start) { #ifdef AUTO_CENTER - auto sync = CheckSyncWord(start, DOWNLINK_SYNC_WORD); - if (!sync.first) { - // Sync word had errors - return boost::none; - } + auto sync = CheckSyncWord(start, DOWNLINK_SYNC_WORD); + if (!sync.first) { + // Sync word had errors + return boost::none; + } - auto result = DemodBits(start + SYNC_BITS * 2, DOWNLINK_LONG_BYTES, sync.second, sync.second); + auto result = DemodBits(start + SYNC_BITS * 2, DOWNLINK_LONG_BYTES, sync.second, sync.second); #else - auto result = DemodBits(start + SYNC_BITS * 2, DOWNLINK_LONG_BYTES, 0, 0); + auto result = DemodBits(start + SYNC_BITS * 2, DOWNLINK_LONG_BYTES, 0, 0); #endif - auto &raw = result.first; - auto &erasures = result.second; - - bool success; - uat::Bytes corrected; - unsigned errors; - std::tie(success, corrected, errors) = fec_.CorrectDownlink(raw, erasures); - if (!success) { - // Error correction failed - return boost::none; - } - - auto bits = (corrected.size() == DOWNLINK_LONG_DATA_BYTES ? DOWNLINK_LONG_BITS : DOWNLINK_SHORT_BITS); - return Demodulator::Message{std::move(corrected), errors, start, start + (SYNC_BITS + bits) * 2}; + auto &raw = result.first; + auto &erasures = result.second; + + bool success; + Bytes corrected; + unsigned errors; + std::tie(success, corrected, errors) = fec_.CorrectDownlink(raw, erasures); + if (!success) { + // Error correction failed + return boost::none; } - boost::optional TwoMegDemodulator::DemodOneUplink(PhaseBuffer::const_iterator start) { + auto bits = (corrected.size() == DOWNLINK_LONG_DATA_BYTES ? DOWNLINK_LONG_BITS : DOWNLINK_SHORT_BITS); + return Demodulator::Message{std::move(corrected), errors, start, start + (SYNC_BITS + bits) * 2}; +} + +boost::optional TwoMegDemodulator::DemodOneUplink(PhaseBuffer::const_iterator start) { #ifdef AUTO_CENTER - auto sync = CheckSyncWord(start, UPLINK_SYNC_WORD); - if (!sync.first) { - // Sync word had errors - return boost::none; - } + auto sync = CheckSyncWord(start, UPLINK_SYNC_WORD); + if (!sync.first) { + // Sync word had errors + return boost::none; + } - auto result = DemodBits(start + SYNC_BITS * 2, UPLINK_BYTES, sync.second, sync.second); + auto result = DemodBits(start + SYNC_BITS * 2, UPLINK_BYTES, sync.second, sync.second); #else - auto result = DemodBits(start + SYNC_BITS * 2, UPLINK_BYTES, 0, 0); + auto result = DemodBits(start + SYNC_BITS * 2, UPLINK_BYTES, 0, 0); #endif - auto &raw = result.first; - auto &erasures = result.second; - - bool success; - uat::Bytes corrected; - unsigned errors; - std::tie(success, corrected, errors) = fec_.CorrectUplink(raw, erasures); + auto &raw = result.first; + auto &erasures = result.second; - if (!success) { - // Error correction failed - return boost::none; - } + bool success; + Bytes corrected; + unsigned errors; + std::tie(success, corrected, errors) = fec_.CorrectUplink(raw, erasures); - return Demodulator::Message{std::move(corrected), errors, start, start + (SYNC_BITS + UPLINK_BITS) * 2}; + if (!success) { + // Error correction failed + return boost::none; } -} // namespace dump978 + + return Demodulator::Message{std::move(corrected), errors, start, start + (SYNC_BITS + UPLINK_BITS) * 2}; +} diff --git a/demodulator.h b/demodulator.h index 381f7b8..9ee854a 100644 --- a/demodulator.h +++ b/demodulator.h @@ -16,58 +16,58 @@ #include "message_source.h" #include "uat_message.h" -namespace dump978 { +namespace flightaware::uat { class Demodulator { public: // Return value of Demodulate struct Message { - uat::Bytes payload; + Bytes payload; unsigned corrected_errors; - uat::PhaseBuffer::const_iterator begin; - uat::PhaseBuffer::const_iterator end; + PhaseBuffer::const_iterator begin; + PhaseBuffer::const_iterator end; }; virtual ~Demodulator() {} - virtual std::vector Demodulate(uat::PhaseBuffer::const_iterator begin, uat::PhaseBuffer::const_iterator end) = 0; + virtual std::vector Demodulate(PhaseBuffer::const_iterator begin, PhaseBuffer::const_iterator end) = 0; virtual unsigned NumTrailingSamples() = 0; protected: - uat::FEC fec_; + FEC fec_; }; class TwoMegDemodulator : public Demodulator { public: - std::vector Demodulate(uat::PhaseBuffer::const_iterator begin, uat::PhaseBuffer::const_iterator end) override; + std::vector Demodulate(PhaseBuffer::const_iterator begin, PhaseBuffer::const_iterator end) override; unsigned NumTrailingSamples() override; private: - boost::optional DemodBest(uat::PhaseBuffer::const_iterator begin, bool downlink); - boost::optional DemodOneDownlink(uat::PhaseBuffer::const_iterator begin); - boost::optional DemodOneUplink(uat::PhaseBuffer::const_iterator begin); + boost::optional DemodBest(PhaseBuffer::const_iterator begin, bool downlink); + boost::optional DemodOneDownlink(PhaseBuffer::const_iterator begin); + boost::optional DemodOneUplink(PhaseBuffer::const_iterator begin); }; - class Receiver : public uat::MessageSource { + class Receiver : public MessageSource { public: - virtual void HandleSamples(std::uint64_t timestamp, uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end) = 0; + virtual void HandleSamples(std::uint64_t timestamp, Bytes::const_iterator begin, Bytes::const_iterator end) = 0; }; class SingleThreadReceiver : public Receiver { public: SingleThreadReceiver(SampleFormat format); - void HandleSamples(std::uint64_t timestamp, uat::Bytes::const_iterator begin, uat::Bytes::const_iterator end) override; + void HandleSamples(std::uint64_t timestamp, Bytes::const_iterator begin, Bytes::const_iterator end) override; private: SampleConverter::Pointer converter_; std::unique_ptr demodulator_; - uat::Bytes samples_; + Bytes samples_; std::size_t saved_samples_ = 0; - uat::PhaseBuffer phase_; + PhaseBuffer phase_; }; -}; // namespace dump978 +}; // namespace flightaware::uat #endif diff --git a/dump978_main.cc b/dump978_main.cc index 4e032e7..ace839e 100644 --- a/dump978_main.cc +++ b/dump978_main.cc @@ -19,8 +19,7 @@ #include "soapy_source.h" #include "socket_output.h" -using namespace uat; -using namespace dump978; +using namespace flightaware::uat; namespace po = boost::program_options; using boost::asio::ip::tcp; @@ -50,7 +49,7 @@ void validate(boost::any &v, const std::vector &values, listen_opti } // Specializations of validate for --format -namespace dump978 { +namespace flightaware::uat { void validate(boost::any &v, const std::vector &values, SampleFormat *target_type, int) { po::validators::check_first_occurrence(v); const std::string &s = po::validators::get_single_string(values); @@ -70,7 +69,7 @@ namespace dump978 { v = boost::any(entry->second); } -} // namespace dump978 +} // namespace flightaware::uat #define EXIT_NO_RESTART (64) diff --git a/faup978_main.cc b/faup978_main.cc index 094fb46..d15f75a 100644 --- a/faup978_main.cc +++ b/faup978_main.cc @@ -14,8 +14,8 @@ #include "socket_input.h" #include "uat_message.h" -using namespace uat; -using namespace faup978; +using namespace flightaware::uat; +using namespace flightaware::faup978; namespace po = boost::program_options; using boost::asio::ip::tcp; diff --git a/faup978_reporter.cc b/faup978_reporter.cc index aa9ddff..cb6cc94 100644 --- a/faup978_reporter.cc +++ b/faup978_reporter.cc @@ -9,8 +9,8 @@ #include #include -using namespace uat; -using namespace faup978; +using namespace flightaware::uat; +using namespace flightaware::faup978; static const char *const TSV_VERSION = "4U"; @@ -74,7 +74,7 @@ void Reporter::PeriodicReport() { })); } -void Reporter::ReportOneAircraft(const uat::Tracker::AddressKey &key, const AircraftState &aircraft, std::uint64_t now) { +void Reporter::ReportOneAircraft(const Tracker::AddressKey &key, const AircraftState &aircraft, std::uint64_t now) { auto &last = reported_[key]; auto &last_state = last.report_state; diff --git a/faup978_reporter.h b/faup978_reporter.h index cb5166d..d981247 100644 --- a/faup978_reporter.h +++ b/faup978_reporter.h @@ -4,8 +4,8 @@ // All rights reserved. // Licensed under the 2-clause BSD license; see the LICENSE file -#ifndef FAUP1090_REPORTER_H -#define FAUP1090_REPORTER_H +#ifndef FAUP978_REPORTER_H +#define FAUP978_REPORTER_H #include #include @@ -17,11 +17,11 @@ #include "track.h" #include "uat_message.h" -namespace faup978 { +namespace flightaware::faup978 { struct ReportState { std::uint64_t slow_report_time = 0; std::uint64_t report_time = 0; - uat::AircraftState report_state; + flightaware::uat::AircraftState report_state; }; class Reporter : public std::enable_shared_from_this { @@ -35,14 +35,14 @@ namespace faup978 { void Start(); void Stop(); - void HandleMessages(uat::SharedMessageVector messages) { tracker_->HandleMessages(messages); } + void HandleMessages(flightaware::uat::SharedMessageVector messages) { tracker_->HandleMessages(messages); } private: - Reporter(boost::asio::io_service &service, std::chrono::milliseconds interval, std::chrono::milliseconds timeout) : service_(service), strand_(service), report_timer_(service), purge_timer_(service), interval_(interval), timeout_(timeout) { tracker_ = uat::Tracker::Create(service, timeout); } + Reporter(boost::asio::io_service &service, std::chrono::milliseconds interval, std::chrono::milliseconds timeout) : service_(service), strand_(service), report_timer_(service), purge_timer_(service), interval_(interval), timeout_(timeout) { tracker_ = flightaware::uat::Tracker::Create(service, timeout); } void PeriodicReport(); void PurgeOld(); - void ReportOneAircraft(const uat::Tracker::AddressKey &key, const uat::AircraftState &aircraft, std::uint64_t now); + void ReportOneAircraft(const flightaware::uat::Tracker::AddressKey &key, const flightaware::uat::AircraftState &aircraft, std::uint64_t now); boost::asio::io_service &service_; boost::asio::io_service::strand strand_; @@ -50,9 +50,9 @@ namespace faup978 { boost::asio::steady_timer purge_timer_; std::chrono::milliseconds interval_; std::chrono::milliseconds timeout_; - uat::Tracker::Pointer tracker_; - std::map reported_; + flightaware::uat::Tracker::Pointer tracker_; + std::map reported_; }; -} // namespace faup978 +} // namespace flightaware::faup978 #endif diff --git a/fec.cc b/fec.cc index 9c7e856..a29336c 100644 --- a/fec.cc +++ b/fec.cc @@ -9,9 +9,10 @@ extern "C" { #include "fec/rs.h" } -using namespace uat::fec; +using namespace flightaware::uat; +using namespace flightaware::uat::fec; -uat::FEC::FEC(void) { +FEC::FEC(void) { rs_downlink_short_ = ::init_rs_char( /* symsize */ 8, /* gfpoly */ DOWNLINK_SHORT_POLY, /* fcr */ 120, /* prim */ 1, /* nroots */ DOWNLINK_SHORT_ROOTS, @@ -26,14 +27,14 @@ uat::FEC::FEC(void) { /* pad */ UPLINK_BLOCK_PAD); } -uat::FEC::~FEC(void) { +FEC::~FEC(void) { ::free_rs_char(rs_downlink_short_); ::free_rs_char(rs_downlink_long_); ::free_rs_char(rs_uplink_); } -std::tuple uat::FEC::CorrectDownlink(const Bytes &raw, const std::vector &erasures) { - using R = std::tuple; +std::tuple FEC::CorrectDownlink(const Bytes &raw, const std::vector &erasures) { + using R = std::tuple; if (raw.size() != DOWNLINK_LONG_BYTES) { return R{false, {}, 0}; @@ -91,8 +92,8 @@ std::tuple uat::FEC::CorrectDownlink(const Bytes &ra return R{false, {}, 0}; } -std::tuple uat::FEC::CorrectUplink(const Bytes &raw, const std::vector &erasures) { - using R = std::tuple; +std::tuple FEC::CorrectUplink(const Bytes &raw, const std::vector &erasures) { + using R = std::tuple; if (raw.size() != UPLINK_BYTES) { return R{false, {}, 0}; diff --git a/fec.h b/fec.h index 4a11611..d1bb46d 100644 --- a/fec.h +++ b/fec.h @@ -11,7 +11,7 @@ #include "common.h" -namespace uat { +namespace flightaware::uat { // Deinterleaving and error-correction of UAT messages. // This delegates to the "fec" library (in fec/) for the actual Reed-Solomon // error-correction work. @@ -30,7 +30,7 @@ namespace uat { // uncorrectable // `erasures` is an optional vector of indexes into `raw` that should be // handled as erasures - std::tuple CorrectDownlink(const Bytes &raw, const std::vector &erasures = {}); + std::tuple CorrectDownlink(const Bytes &raw, const std::vector &erasures = {}); // Given UPLINK_BYTES of demodulated data, returns a tuple of: // bool - true if the message is good, false if it was uncorrectable. @@ -41,13 +41,13 @@ namespace uat { // uncorrectable // `erasures` is an optional vector of indexes into `raw` that should be // handled as erasures - std::tuple CorrectUplink(const Bytes &raw, const std::vector &erasures = {}); + std::tuple CorrectUplink(const Bytes &raw, const std::vector &erasures = {}); private: void *rs_uplink_; void *rs_downlink_short_; void *rs_downlink_long_; }; -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/message_dispatch.cc b/message_dispatch.cc index ee1b1db..037d28c 100644 --- a/message_dispatch.cc +++ b/message_dispatch.cc @@ -7,74 +7,74 @@ #include #include -namespace uat { - MessageDispatch::MessageDispatch() : next_handle_(0), busy_(0) {} +using namespace flightaware::uat; - MessageDispatch::Handle MessageDispatch::AddClient(MessageHandler handler) { - std::unique_lock lock(mutex_); +MessageDispatch::MessageDispatch() : next_handle_(0), busy_(0) {} - Handle h = next_handle_++; - clients_[h] = {handler, false}; - return h; - } +MessageDispatch::Handle MessageDispatch::AddClient(MessageHandler handler) { + std::unique_lock lock(mutex_); - void MessageDispatch::RemoveClient(Handle h) { - std::unique_lock lock(mutex_); + Handle h = next_handle_++; + clients_[h] = {handler, false}; + return h; +} - auto i = clients_.find(h); - if (i == clients_.end()) - return; +void MessageDispatch::RemoveClient(Handle h) { + std::unique_lock lock(mutex_); - i->second.deleted = true; - PurgeDeadClients(); - } + auto i = clients_.find(h); + if (i == clients_.end()) + return; - template class BusyCounter { - public: - BusyCounter(T &var) : var_(var), owned_(true) { ++var_; } + i->second.deleted = true; + PurgeDeadClients(); +} - BusyCounter(const T &) = delete; - BusyCounter &operator=(const T &) = delete; +template class BusyCounter { + public: + BusyCounter(T &var) : var_(var), owned_(true) { ++var_; } - ~BusyCounter() { release(); } + BusyCounter(const T &) = delete; + BusyCounter &operator=(const T &) = delete; - void release() { - if (owned_) { - --var_; - owned_ = false; - } - } + ~BusyCounter() { release(); } - private: - T &var_; - bool owned_; - }; + void release() { + if (owned_) { + --var_; + owned_ = false; + } + } - void MessageDispatch::Dispatch(SharedMessageVector messages) { - std::unique_lock lock(mutex_); + private: + T &var_; + bool owned_; +}; - BusyCounter counter(busy_); - for (auto i = clients_.begin(); i != clients_.end(); ++i) { - Client &c = i->second; - if (!c.deleted) - c.handler(messages); - } - counter.release(); +void MessageDispatch::Dispatch(SharedMessageVector messages) { + std::unique_lock lock(mutex_); - PurgeDeadClients(); + BusyCounter counter(busy_); + for (auto i = clients_.begin(); i != clients_.end(); ++i) { + Client &c = i->second; + if (!c.deleted) + c.handler(messages); } - - void MessageDispatch::PurgeDeadClients() { - // caller must lock! - if (busy_) - return; - - for (auto i = clients_.begin(); i != clients_.end();) { - Client &c = i->second; - if (c.deleted) - clients_.erase(i++); - else - ++i; - } + counter.release(); + + PurgeDeadClients(); +} + +void MessageDispatch::PurgeDeadClients() { + // caller must lock! + if (busy_) + return; + + for (auto i = clients_.begin(); i != clients_.end();) { + Client &c = i->second; + if (c.deleted) + clients_.erase(i++); + else + ++i; } -} // namespace uat +} diff --git a/message_dispatch.h b/message_dispatch.h index bfd0a0b..abf0dac 100644 --- a/message_dispatch.h +++ b/message_dispatch.h @@ -14,7 +14,7 @@ #include "uat_message.h" -namespace uat { +namespace flightaware::uat { class MessageDispatch { public: typedef unsigned Handle; @@ -44,6 +44,6 @@ namespace uat { std::map clients_; }; -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/message_source.h b/message_source.h index 2099aa4..4c4bc54 100644 --- a/message_source.h +++ b/message_source.h @@ -9,17 +9,17 @@ #include "uat_message.h" -namespace uat { +namespace flightaware::uat { class MessageSource { public: - typedef std::function Consumer; + typedef std::function Consumer; virtual ~MessageSource() {} void SetConsumer(Consumer consumer) { consumer_ = consumer; } protected: - void DispatchMessages(uat::SharedMessageVector messages) { + void DispatchMessages(SharedMessageVector messages) { if (consumer_) { consumer_(messages); } @@ -28,6 +28,6 @@ namespace uat { private: Consumer consumer_; }; -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/sample_source.cc b/sample_source.cc index b1bc163..d1e8d32 100644 --- a/sample_source.cc +++ b/sample_source.cc @@ -7,131 +7,131 @@ #include #include -namespace dump978 { - void FileSampleSource::Start() { - stream_.open(path_.native()); - if (!stream_.good()) { - auto ec = boost::system::error_code(errno, boost::system::system_category()); - stream_.close(); - DispatchError(ec); - return; - } +using namespace flightaware::uat; + +void FileSampleSource::Start() { + stream_.open(path_.native()); + if (!stream_.good()) { + auto ec = boost::system::error_code(errno, boost::system::system_category()); + stream_.close(); + DispatchError(ec); + return; + } - next_block_ = std::chrono::steady_clock::now(); - timestamp_ = 1; // always use synthetic timestamps for file sources + next_block_ = std::chrono::steady_clock::now(); + timestamp_ = 1; // always use synthetic timestamps for file sources - auto self = std::static_pointer_cast(shared_from_this()); - service_.post(std::bind(&FileSampleSource::ReadBlock, self, boost::system::error_code())); - } + auto self = std::static_pointer_cast(shared_from_this()); + service_.post(std::bind(&FileSampleSource::ReadBlock, self, boost::system::error_code())); +} - void FileSampleSource::Stop() { - timer_.cancel(); - if (stream_.is_open()) { - stream_.close(); - DispatchError(boost::asio::error::eof); - } +void FileSampleSource::Stop() { + timer_.cancel(); + if (stream_.is_open()) { + stream_.close(); + DispatchError(boost::asio::error::eof); } +} - void FileSampleSource::ReadBlock(const boost::system::error_code &ec) { - if (ec) { - if (ec == boost::asio::error::operation_aborted) { - return; - } - - stream_.close(); - DispatchError(ec); +void FileSampleSource::ReadBlock(const boost::system::error_code &ec) { + if (ec) { + if (ec == boost::asio::error::operation_aborted) { return; } - if (!stream_.is_open()) { - return; - } + stream_.close(); + DispatchError(ec); + return; + } - block_.resize(block_.capacity()); - stream_.read(reinterpret_cast(block_.data()), block_.size()); + if (!stream_.is_open()) { + return; + } - if (stream_.bad()) { - auto ec = boost::system::error_code(errno, boost::system::system_category()); - stream_.close(); - DispatchError(ec); - return; - } + block_.resize(block_.capacity()); + stream_.read(reinterpret_cast(block_.data()), block_.size()); - block_.resize(stream_.gcount() - (stream_.gcount() % alignment_)); - if (!block_.empty()) { - DispatchBuffer(timestamp_, block_); - timestamp_ += (block_.size() * 1000ULL / bytes_per_second_); - } + if (stream_.bad()) { + auto ec = boost::system::error_code(errno, boost::system::system_category()); + stream_.close(); + DispatchError(ec); + return; + } - if (stream_.eof()) { - stream_.close(); - DispatchError(boost::asio::error::eof); - return; - } + block_.resize(stream_.gcount() - (stream_.gcount() % alignment_)); + if (!block_.empty()) { + DispatchBuffer(timestamp_, block_); + timestamp_ += (block_.size() * 1000ULL / bytes_per_second_); + } - auto self = std::static_pointer_cast(shared_from_this()); - if (throttle_) { - auto delay = std::chrono::nanoseconds(1000000000ULL * block_.size() / bytes_per_second_); - next_block_ += delay; - timer_.expires_at(next_block_); - timer_.async_wait(std::bind(&FileSampleSource::ReadBlock, self, std::placeholders::_1)); - } else { - service_.post(std::bind(&FileSampleSource::ReadBlock, self, boost::system::error_code())); - } + if (stream_.eof()) { + stream_.close(); + DispatchError(boost::asio::error::eof); + return; } - // - // - // + auto self = std::static_pointer_cast(shared_from_this()); + if (throttle_) { + auto delay = std::chrono::nanoseconds(1000000000ULL * block_.size() / bytes_per_second_); + next_block_ += delay; + timer_.expires_at(next_block_); + timer_.async_wait(std::bind(&FileSampleSource::ReadBlock, self, std::placeholders::_1)); + } else { + service_.post(std::bind(&FileSampleSource::ReadBlock, self, boost::system::error_code())); + } +} - void StdinSampleSource::Start() { - stream_.assign(::dup(STDIN_FILENO)); - ScheduleRead(); +// +// +// + +void StdinSampleSource::Start() { + stream_.assign(::dup(STDIN_FILENO)); + ScheduleRead(); +} + +void StdinSampleSource::Stop() { + if (stream_.is_open()) { + stream_.close(); + DispatchError(boost::asio::error::eof); } +} - void StdinSampleSource::Stop() { - if (stream_.is_open()) { - stream_.close(); - DispatchError(boost::asio::error::eof); - } +void StdinSampleSource::ScheduleRead() { + if (!stream_.is_open()) { + return; } - void StdinSampleSource::ScheduleRead() { - if (!stream_.is_open()) { + auto self = shared_from_this(); + stream_.async_read_some(boost::asio::buffer(block_.data() + used_, block_.size() - used_), [this, self](const boost::system::error_code &ec, std::size_t bytes_transferred) { + if (ec) { + if (ec == boost::asio::error::operation_aborted) { + return; + } + + stream_.close(); + DispatchError(ec); return; } - auto self = shared_from_this(); - stream_.async_read_some(boost::asio::buffer(block_.data() + used_, block_.size() - used_), [this, self](const boost::system::error_code &ec, std::size_t bytes_transferred) { - if (ec) { - if (ec == boost::asio::error::operation_aborted) { - return; - } + used_ += bytes_transferred; - stream_.close(); - DispatchError(ec); - return; - } + // work out a starting timestamp + static auto unix_epoch = std::chrono::system_clock::from_time_t(0); + auto end_of_block = std::chrono::system_clock::now(); + auto start_of_block = end_of_block - (std::chrono::milliseconds(1000) * bytes_transferred / samples_per_second_ / alignment_); + std::uint64_t timestamp = std::chrono::duration_cast(start_of_block - unix_epoch).count(); - used_ += bytes_transferred; - - // work out a starting timestamp - static auto unix_epoch = std::chrono::system_clock::from_time_t(0); - auto end_of_block = std::chrono::system_clock::now(); - auto start_of_block = end_of_block - (std::chrono::milliseconds(1000) * bytes_transferred / samples_per_second_ / alignment_); - std::uint64_t timestamp = std::chrono::duration_cast(start_of_block - unix_epoch).count(); - - // fixme, don't copy! - auto trailing_bytes = used_ % alignment_; - auto leading_bytes = used_ - trailing_bytes; - - uat::Bytes buffer; - buffer.resize(leading_bytes); - std::copy(block_.begin(), block_.begin() + leading_bytes, buffer.begin()); - std::copy(block_.begin() + leading_bytes, block_.end(), block_.begin()); - used_ = trailing_bytes; - DispatchBuffer(timestamp, buffer); - ScheduleRead(); - }); - } -}; // namespace dump978 + // fixme, don't copy! + auto trailing_bytes = used_ % alignment_; + auto leading_bytes = used_ - trailing_bytes; + + Bytes buffer; + buffer.resize(leading_bytes); + std::copy(block_.begin(), block_.begin() + leading_bytes, buffer.begin()); + std::copy(block_.begin() + leading_bytes, block_.end(), block_.begin()); + used_ = trailing_bytes; + DispatchBuffer(timestamp, buffer); + ScheduleRead(); + }); +} diff --git a/sample_source.h b/sample_source.h index 3b2ccbb..dfa2bf5 100644 --- a/sample_source.h +++ b/sample_source.h @@ -21,11 +21,11 @@ #include "common.h" #include "convert.h" -namespace dump978 { +namespace flightaware::uat { class SampleSource : public std::enable_shared_from_this { public: typedef std::shared_ptr Pointer; - typedef std::function Consumer; + typedef std::function Consumer; virtual ~SampleSource() {} @@ -39,7 +39,7 @@ namespace dump978 { protected: SampleSource() {} - void DispatchBuffer(std::uint64_t timestamp, const uat::Bytes &buffer) { + void DispatchBuffer(std::uint64_t timestamp, const Bytes &buffer) { if (consumer_) { consumer_(timestamp, buffer, boost::system::error_code()); } @@ -47,7 +47,7 @@ namespace dump978 { void DispatchError(const boost::system::error_code &ec) { if (consumer_) { - consumer_(0, uat::Bytes(), ec); + consumer_(0, Bytes(), ec); } } @@ -90,7 +90,7 @@ namespace dump978 { std::ifstream stream_; boost::asio::steady_timer timer_; std::chrono::steady_clock::time_point next_block_; - uat::Bytes block_; + Bytes block_; std::uint64_t timestamp_; }; @@ -121,9 +121,9 @@ namespace dump978 { unsigned alignment_; std::size_t samples_per_second_; boost::asio::posix::stream_descriptor stream_; - uat::Bytes block_; + Bytes block_; std::size_t used_; }; -}; // namespace dump978 +}; // namespace flightaware::uat #endif diff --git a/skyview978_main.cc b/skyview978_main.cc index 723fe73..a80c098 100644 --- a/skyview978_main.cc +++ b/skyview978_main.cc @@ -14,8 +14,8 @@ #include "socket_input.h" #include "uat_message.h" -using namespace uat; -using namespace uat::skyview; +using namespace flightaware::uat; +using namespace flightaware::skyview; namespace po = boost::program_options; using boost::asio::ip::tcp; diff --git a/skyview_writer.cc b/skyview_writer.cc index 90b8b79..2a11616 100644 --- a/skyview_writer.cc +++ b/skyview_writer.cc @@ -13,8 +13,8 @@ #include "track.h" -using namespace uat; -using namespace uat::skyview; +using namespace flightaware::uat; +using namespace flightaware::skyview; void SkyviewWriter::Start() { nlohmann::json receiver_json; diff --git a/skyview_writer.h b/skyview_writer.h index b6deb84..f617d08 100644 --- a/skyview_writer.h +++ b/skyview_writer.h @@ -18,35 +18,33 @@ #include "track.h" #include "uat_message.h" -namespace uat { - namespace skyview { - class SkyviewWriter : public std::enable_shared_from_this { - public: - typedef std::shared_ptr Pointer; - - static Pointer Create(boost::asio::io_service &service, Tracker::Pointer tracker, const boost::filesystem::path &dir, std::chrono::milliseconds interval, unsigned history_count, std::chrono::milliseconds history_interval, boost::optional> location) { return Pointer(new SkyviewWriter(service, tracker, dir, interval, history_count, history_interval, location)); } - - void Start(); - void Stop(); - - private: - SkyviewWriter(boost::asio::io_service &service, Tracker::Pointer tracker, const boost::filesystem::path &dir, std::chrono::milliseconds interval, unsigned history_count, std::chrono::milliseconds history_interval, boost::optional> location) : service_(service), strand_(service), timer_(service), tracker_(tracker), dir_(dir), interval_(interval), history_count_(history_count), history_interval_(history_interval), location_(location) {} - - void PeriodicWrite(); - - boost::asio::io_service &service_; - boost::asio::io_service::strand strand_; - boost::asio::steady_timer timer_; - uat::Tracker::Pointer tracker_; - boost::filesystem::path dir_; - std::chrono::milliseconds interval_; - unsigned history_count_; - std::chrono::milliseconds history_interval_; - boost::optional> location_; - unsigned next_history_index_ = 0; - std::uint64_t next_history_time_ = 0; - }; - } // namespace skyview -} // namespace uat +namespace flightaware::skyview { + class SkyviewWriter : public std::enable_shared_from_this { + public: + typedef std::shared_ptr Pointer; + + static Pointer Create(boost::asio::io_service &service, flightaware::uat::Tracker::Pointer tracker, const boost::filesystem::path &dir, std::chrono::milliseconds interval, unsigned history_count, std::chrono::milliseconds history_interval, boost::optional> location) { return Pointer(new SkyviewWriter(service, tracker, dir, interval, history_count, history_interval, location)); } + + void Start(); + void Stop(); + + private: + SkyviewWriter(boost::asio::io_service &service, flightaware::uat::Tracker::Pointer tracker, const boost::filesystem::path &dir, std::chrono::milliseconds interval, unsigned history_count, std::chrono::milliseconds history_interval, boost::optional> location) : service_(service), strand_(service), timer_(service), tracker_(tracker), dir_(dir), interval_(interval), history_count_(history_count), history_interval_(history_interval), location_(location) {} + + void PeriodicWrite(); + + boost::asio::io_service &service_; + boost::asio::io_service::strand strand_; + boost::asio::steady_timer timer_; + flightaware::uat::Tracker::Pointer tracker_; + boost::filesystem::path dir_; + std::chrono::milliseconds interval_; + unsigned history_count_; + std::chrono::milliseconds history_interval_; + boost::optional> location_; + unsigned next_history_index_ = 0; + std::uint64_t next_history_time_ = 0; + }; +} // namespace flightaware::skyview #endif diff --git a/soapy_source.cc b/soapy_source.cc index 95018ad..a2153ff 100644 --- a/soapy_source.cc +++ b/soapy_source.cc @@ -12,269 +12,269 @@ #include #include -namespace dump978 { - std::atomic_bool SoapySampleSource::log_handler_registered_(false); - - static void SoapyLogger(const SoapySDRLogLevel logLevel, const char *message) { - // clang-format off - static std::map levels = { - {SOAPY_SDR_FATAL, "FATAL"}, - {SOAPY_SDR_CRITICAL, "CRITICAL"}, - {SOAPY_SDR_ERROR, "ERROR"}, - {SOAPY_SDR_WARNING, "WARNING"}, - {SOAPY_SDR_NOTICE, "NOTICE"}, - {SOAPY_SDR_INFO, "INFO"}, - {SOAPY_SDR_DEBUG, "DEBUG"}, - {SOAPY_SDR_TRACE, "TRACE"}, - {SOAPY_SDR_SSI, "SSI"} - }; - // clang-format on - - std::string level; - auto i = levels.find(logLevel); - if (i == levels.end()) - level = "UNKNOWN"; - else - level = i->second; - - std::cerr << "SoapySDR: " << level << ": " << message << std::endl; - } +using namespace flightaware::uat; + +std::atomic_bool SoapySampleSource::log_handler_registered_(false); + +static void SoapyLogger(const SoapySDRLogLevel logLevel, const char *message) { + // clang-format off + static std::map levels = { + {SOAPY_SDR_FATAL, "FATAL"}, + {SOAPY_SDR_CRITICAL, "CRITICAL"}, + {SOAPY_SDR_ERROR, "ERROR"}, + {SOAPY_SDR_WARNING, "WARNING"}, + {SOAPY_SDR_NOTICE, "NOTICE"}, + {SOAPY_SDR_INFO, "INFO"}, + {SOAPY_SDR_DEBUG, "DEBUG"}, + {SOAPY_SDR_TRACE, "TRACE"}, + {SOAPY_SDR_SSI, "SSI"} + }; + // clang-format on + + std::string level; + auto i = levels.find(logLevel); + if (i == levels.end()) + level = "UNKNOWN"; + else + level = i->second; + + std::cerr << "SoapySDR: " << level << ": " << message << std::endl; +} + +static std::string FormatToSoapy(SampleFormat format) { + // clang-format off + static const std::map lookup = { + { SampleFormat::CU8, SOAPY_SDR_CU8 }, + { SampleFormat::CS8, SOAPY_SDR_CS8 }, + { SampleFormat::CS16H, SOAPY_SDR_CS16 }, + { SampleFormat::CF32H, SOAPY_SDR_CF32 } + }; + // clang-format on - static std::string FormatToSoapy(SampleFormat format) { - // clang-format off - static const std::map lookup = { - { SampleFormat::CU8, SOAPY_SDR_CU8 }, - { SampleFormat::CS8, SOAPY_SDR_CS8 }, - { SampleFormat::CS16H, SOAPY_SDR_CS16 }, - { SampleFormat::CF32H, SOAPY_SDR_CF32 } - }; - // clang-format on - - auto i = lookup.find(format); - if (i != lookup.end()) { - return i->second; - } else { - return ""; - } + auto i = lookup.find(format); + if (i != lookup.end()) { + return i->second; + } else { + return ""; } +} + +static SampleFormat SoapyToFormat(std::string format) { + // clang-format off + static const std::map lookup = { + { SOAPY_SDR_CU8, SampleFormat::CU8 }, + { SOAPY_SDR_CS8, SampleFormat::CS8 }, + { SOAPY_SDR_CS16, SampleFormat::CS16H }, + { SOAPY_SDR_CF32, SampleFormat::CF32H } + }; + // clang-format on - static SampleFormat SoapyToFormat(std::string format) { - // clang-format off - static const std::map lookup = { - { SOAPY_SDR_CU8, SampleFormat::CU8 }, - { SOAPY_SDR_CS8, SampleFormat::CS8 }, - { SOAPY_SDR_CS16, SampleFormat::CS16H }, - { SOAPY_SDR_CF32, SampleFormat::CF32H } - }; - // clang-format on - - auto i = lookup.find(format); - if (i != lookup.end()) { - return i->second; - } else { - return SampleFormat::UNKNOWN; - } + auto i = lookup.find(format); + if (i != lookup.end()) { + return i->second; + } else { + return SampleFormat::UNKNOWN; } +} - class SoapySDRCategory : public boost::system::error_category { - public: - const char *name() const noexcept override { return "soapysdr"; } - std::string message(int ev) const override { return SoapySDR::errToStr(ev); } - }; +class SoapySDRCategory : public boost::system::error_category { + public: + const char *name() const noexcept override { return "soapysdr"; } + std::string message(int ev) const override { return SoapySDR::errToStr(ev); } +}; - static SoapySDRCategory soapysdr_category; +static SoapySDRCategory soapysdr_category; - SoapySampleSource::SoapySampleSource(boost::asio::io_service &service, const std::string &device_name, const boost::program_options::variables_map &options) : timer_(service), device_name_(device_name), options_(options) { - if (!log_handler_registered_.exchange(true)) { - SoapySDR::registerLogHandler(SoapyLogger); - } +SoapySampleSource::SoapySampleSource(boost::asio::io_service &service, const std::string &device_name, const boost::program_options::variables_map &options) : timer_(service), device_name_(device_name), options_(options) { + if (!log_handler_registered_.exchange(true)) { + SoapySDR::registerLogHandler(SoapyLogger); } +} - SoapySampleSource::~SoapySampleSource() { Stop(); } +SoapySampleSource::~SoapySampleSource() { Stop(); } - void SoapySampleSource::Init() { - device_ = {SoapySDR::Device::make(device_name_), &SoapySDR::Device::unmake}; - if (!device_) { - throw std::runtime_error("no suitable device found"); - } +void SoapySampleSource::Init() { + device_ = {SoapySDR::Device::make(device_name_), &SoapySDR::Device::unmake}; + if (!device_) { + throw std::runtime_error("no suitable device found"); + } - // hacky mchackerson - device_->setSampleRate(SOAPY_SDR_RX, 0, 2083333.0); - device_->setFrequency(SOAPY_SDR_RX, 0, 978000000); - device_->setBandwidth(SOAPY_SDR_RX, 0, 3.0e6); + // hacky mchackerson + device_->setSampleRate(SOAPY_SDR_RX, 0, 2083333.0); + device_->setFrequency(SOAPY_SDR_RX, 0, 978000000); + device_->setBandwidth(SOAPY_SDR_RX, 0, 3.0e6); - if (options_.count("sdr-auto-gain")) { - if (!device_->hasGainMode(SOAPY_SDR_RX, 0)) { - throw std::runtime_error("device does not support automatic gain mode"); - } - std::cerr << "SoapySDR: using automatic gain" << std::endl; - device_->setGainMode(SOAPY_SDR_RX, 0, true); - } else if (options_.count("sdr-gain")) { - auto gain = options_["sdr-gain"].as(); - std::cerr << "SoapySDR: using manual gain " << std::fixed << std::setprecision(1) << gain << " dB" << std::endl; - device_->setGainMode(SOAPY_SDR_RX, 0, false); - device_->setGain(SOAPY_SDR_RX, 0, gain); - } else { - auto range = device_->getGainRange(SOAPY_SDR_RX, 0); - std::cerr << "SoapySDR: using maximum manual gain " << std::fixed << std::setprecision(1) << range.maximum() << " dB" << std::endl; - device_->setGainMode(SOAPY_SDR_RX, 0, false); - device_->setGain(SOAPY_SDR_RX, 0, range.maximum()); + if (options_.count("sdr-auto-gain")) { + if (!device_->hasGainMode(SOAPY_SDR_RX, 0)) { + throw std::runtime_error("device does not support automatic gain mode"); } + std::cerr << "SoapySDR: using automatic gain" << std::endl; + device_->setGainMode(SOAPY_SDR_RX, 0, true); + } else if (options_.count("sdr-gain")) { + auto gain = options_["sdr-gain"].as(); + std::cerr << "SoapySDR: using manual gain " << std::fixed << std::setprecision(1) << gain << " dB" << std::endl; + device_->setGainMode(SOAPY_SDR_RX, 0, false); + device_->setGain(SOAPY_SDR_RX, 0, gain); + } else { + auto range = device_->getGainRange(SOAPY_SDR_RX, 0); + std::cerr << "SoapySDR: using maximum manual gain " << std::fixed << std::setprecision(1) << range.maximum() << " dB" << std::endl; + device_->setGainMode(SOAPY_SDR_RX, 0, false); + device_->setGain(SOAPY_SDR_RX, 0, range.maximum()); + } - if (options_.count("sdr-ppm")) { - auto ppm = options_["sdr-ppm"].as(); - if (ppm != 0) { + if (options_.count("sdr-ppm")) { + auto ppm = options_["sdr-ppm"].as(); + if (ppm != 0) { #ifdef SOAPY_SDR_API_HAS_FREQUENCY_CORRECTION_API - if (device_->hasFrequencyCorrection(SOAPY_SDR_RX, 0) && ppm != 0) { - std::cerr << "SoapySDR: using frequency correction " << std::fixed << std::setprecision(1) << ppm << " ppm" << std::endl; - device_->setFrequencyCorrection(SOAPY_SDR_RX, 0, ppm); - } else { - std::cerr << "SoapySDR: device does not support frequency correction, --sdr-ppm option ignored" << std::endl; - } + if (device_->hasFrequencyCorrection(SOAPY_SDR_RX, 0) && ppm != 0) { + std::cerr << "SoapySDR: using frequency correction " << std::fixed << std::setprecision(1) << ppm << " ppm" << std::endl; + device_->setFrequencyCorrection(SOAPY_SDR_RX, 0, ppm); + } else { + std::cerr << "SoapySDR: device does not support frequency correction, --sdr-ppm option ignored" << std::endl; + } #else - std::cerr << "SoapySDR: library version does not support frequency correction, --sdr-ppm option ignored" << std::endl; + std::cerr << "SoapySDR: library version does not support frequency correction, --sdr-ppm option ignored" << std::endl; #endif - } } + } - if (options_.count("sdr-antenna")) { - auto antenna = options_["sdr-antenna"].as(); - std::cerr << "SoapySDR: using antenna " << antenna << std::endl; - device_->setAntenna(SOAPY_SDR_RX, 0, antenna); - } + if (options_.count("sdr-antenna")) { + auto antenna = options_["sdr-antenna"].as(); + std::cerr << "SoapySDR: using antenna " << antenna << std::endl; + device_->setAntenna(SOAPY_SDR_RX, 0, antenna); + } - if (options_.count("sdr-device-settings")) { + if (options_.count("sdr-device-settings")) { #if defined(SOAPY_SDR_API_VERSION) && (SOAPY_SDR_API_VERSION >= 0x00060000) - for (auto kv : SoapySDR::KwargsFromString(options_["sdr-stream-settings"].as())) { - std::cerr << "SoapySDR: using device setting " << kv.first << "=" << kv.second << std::endl; - device_->writeSetting(kv.first, kv.second); - } + for (auto kv : SoapySDR::KwargsFromString(options_["sdr-stream-settings"].as())) { + std::cerr << "SoapySDR: using device setting " << kv.first << "=" << kv.second << std::endl; + device_->writeSetting(kv.first, kv.second); + } #else - std::cerr << "SoapySDR: --sdr-device-settings option ignored in this build" << std::endl; + std::cerr << "SoapySDR: --sdr-device-settings option ignored in this build" << std::endl; #endif - } + } - if (options_.count("format")) { - format_ = options_["format"].as(); - } + if (options_.count("format")) { + format_ = options_["format"].as(); + } - std::string soapy_format; + std::string soapy_format; + if (format_ == SampleFormat::UNKNOWN) { + double fullScale; + soapy_format = device_->getNativeStreamFormat(SOAPY_SDR_RX, 0, fullScale); + format_ = SoapyToFormat(soapy_format); if (format_ == SampleFormat::UNKNOWN) { - double fullScale; - soapy_format = device_->getNativeStreamFormat(SOAPY_SDR_RX, 0, fullScale); - format_ = SoapyToFormat(soapy_format); - if (format_ == SampleFormat::UNKNOWN) { - throw std::runtime_error("Unsupported native SDR format: " + soapy_format + "; try specifying --format"); - } - } else { - soapy_format = FormatToSoapy(format_); - if (soapy_format.empty()) { - throw std::runtime_error("unsupported sample format"); - } + throw std::runtime_error("Unsupported native SDR format: " + soapy_format + "; try specifying --format"); + } + } else { + soapy_format = FormatToSoapy(format_); + if (soapy_format.empty()) { + throw std::runtime_error("unsupported sample format"); } + } - std::vector channels = {0}; + std::vector channels = {0}; - SoapySDR::Kwargs stream_settings; - if (device_->getDriverKey() == "RTLSDR") { - // some soapysdr builds have a very low default here - stream_settings["buffsize"] = "262144"; - } + SoapySDR::Kwargs stream_settings; + if (device_->getDriverKey() == "RTLSDR") { + // some soapysdr builds have a very low default here + stream_settings["buffsize"] = "262144"; + } - if (options_.count("sdr-stream-settings")) { + if (options_.count("sdr-stream-settings")) { #if defined(SOAPY_SDR_API_VERSION) && (SOAPY_SDR_API_VERSION >= 0x00060000) - for (auto kv : SoapySDR::KwargsFromString(options_["sdr-stream-settings"].as())) { - std::cerr << "SoapySDR: using stream setting " << kv.first << "=" << kv.second << std::endl; - stream_settings[kv.first] = kv.second; - } + for (auto kv : SoapySDR::KwargsFromString(options_["sdr-stream-settings"].as())) { + std::cerr << "SoapySDR: using stream setting " << kv.first << "=" << kv.second << std::endl; + stream_settings[kv.first] = kv.second; + } #else - std::cerr << "SoapySDR: --sdr-stream-settings options ignored in this build" << std::endl; + std::cerr << "SoapySDR: --sdr-stream-settings options ignored in this build" << std::endl; #endif - } + } - stream_ = {device_->setupStream(SOAPY_SDR_RX, soapy_format, channels, stream_settings), std::bind(&SoapySDR::Device::closeStream, device_, std::placeholders::_1)}; - if (!stream_) { - throw std::runtime_error("failed to construct stream"); - } + stream_ = {device_->setupStream(SOAPY_SDR_RX, soapy_format, channels, stream_settings), std::bind(&SoapySDR::Device::closeStream, device_, std::placeholders::_1)}; + if (!stream_) { + throw std::runtime_error("failed to construct stream"); } +} - void SoapySampleSource::Start() { - if (!device_ || !stream_) { - Init(); - } +void SoapySampleSource::Start() { + if (!device_ || !stream_) { + Init(); + } - device_->activateStream(stream_.get()); + device_->activateStream(stream_.get()); - halt_ = false; - rx_thread_.reset(new std::thread(&SoapySampleSource::Run, this)); + halt_ = false; + rx_thread_.reset(new std::thread(&SoapySampleSource::Run, this)); - Keepalive(); - } + Keepalive(); +} - void SoapySampleSource::Keepalive() { - if (rx_thread_ && rx_thread_->joinable()) { - // Keep the io_service alive while the rx_thread is active - auto self(shared_from_this()); - timer_.expires_from_now(std::chrono::milliseconds(1000)); - timer_.async_wait([self, this](const boost::system::error_code &ec) { - if (!ec) { - Keepalive(); - } - }); - } +void SoapySampleSource::Keepalive() { + if (rx_thread_ && rx_thread_->joinable()) { + // Keep the io_service alive while the rx_thread is active + auto self(shared_from_this()); + timer_.expires_from_now(std::chrono::milliseconds(1000)); + timer_.async_wait([self, this](const boost::system::error_code &ec) { + if (!ec) { + Keepalive(); + } + }); } +} - void SoapySampleSource::Stop() { - if (stream_) { - // rtlsdr needs the rx thread to drain data before this returns.. - device_->deactivateStream(stream_.get()); - } - if (rx_thread_) { - halt_ = true; - rx_thread_->join(); - rx_thread_.reset(); - } - if (stream_) { - stream_.reset(); - } - if (device_) { - device_.reset(); - } +void SoapySampleSource::Stop() { + if (stream_) { + // rtlsdr needs the rx thread to drain data before this returns.. + device_->deactivateStream(stream_.get()); + } + if (rx_thread_) { + halt_ = true; + rx_thread_->join(); + rx_thread_.reset(); + } + if (stream_) { + stream_.reset(); } + if (device_) { + device_.reset(); + } +} - void SoapySampleSource::Run() { - const auto bytes_per_element = BytesPerSample(format_); - const auto elements = std::max(65536, device_->getStreamMTU(stream_.get())); +void SoapySampleSource::Run() { + const auto bytes_per_element = BytesPerSample(format_); + const auto elements = std::max(65536, device_->getStreamMTU(stream_.get())); - uat::Bytes block; - block.reserve(elements * bytes_per_element); + Bytes block; + block.reserve(elements * bytes_per_element); - while (!halt_) { - void *buffs[1] = {block.data()}; - int flags = 0; - long long time_ns; + while (!halt_) { + void *buffs[1] = {block.data()}; + int flags = 0; + long long time_ns; - block.resize(elements * bytes_per_element); - auto elements_read = device_->readStream(stream_.get(), buffs, elements, flags, time_ns, - /* timeout, microseconds */ 1000000); - if (halt_) { - break; - } + block.resize(elements * bytes_per_element); + auto elements_read = device_->readStream(stream_.get(), buffs, elements, flags, time_ns, + /* timeout, microseconds */ 1000000); + if (halt_) { + break; + } - if (elements_read < 0) { - DispatchError(boost::system::error_code{elements_read, soapysdr_category}); - break; - } + if (elements_read < 0) { + DispatchError(boost::system::error_code{elements_read, soapysdr_category}); + break; + } - block.resize(elements_read * bytes_per_element); + block.resize(elements_read * bytes_per_element); - // work out a starting timestamp - static auto unix_epoch = std::chrono::system_clock::from_time_t(0); - auto end_of_block = std::chrono::system_clock::now(); - auto start_of_block = end_of_block - (std::chrono::milliseconds(1000) * elements / 2083333); - std::uint64_t timestamp = std::chrono::duration_cast(start_of_block - unix_epoch).count(); + // work out a starting timestamp + static auto unix_epoch = std::chrono::system_clock::from_time_t(0); + auto end_of_block = std::chrono::system_clock::now(); + auto start_of_block = end_of_block - (std::chrono::milliseconds(1000) * elements / 2083333); + std::uint64_t timestamp = std::chrono::duration_cast(start_of_block - unix_epoch).count(); - DispatchBuffer(timestamp, block); - } + DispatchBuffer(timestamp, block); } -}; // namespace dump978 +} diff --git a/soapy_source.h b/soapy_source.h index 331b9ad..f0f64ba 100644 --- a/soapy_source.h +++ b/soapy_source.h @@ -15,7 +15,7 @@ #include "sample_source.h" -namespace dump978 { +namespace flightaware::uat { class SoapySampleSource : public SampleSource { public: static SampleSource::Pointer Create(boost::asio::io_service &service, const std::string &device_name, const boost::program_options::variables_map &options) { return Pointer(new SoapySampleSource(service, device_name, options)); } @@ -45,6 +45,6 @@ namespace dump978 { static std::atomic_bool log_handler_registered_; }; -}; // namespace dump978 +}; // namespace flightaware::uat #endif diff --git a/socket_input.cc b/socket_input.cc index cee443e..b5efa15 100644 --- a/socket_input.cc +++ b/socket_input.cc @@ -4,7 +4,7 @@ #include "socket_input.h" -using namespace uat; +using namespace flightaware::uat; using boost::asio::ip::tcp; #include @@ -178,7 +178,7 @@ static inline int hexvalue(char c) { } } -boost::optional RawInput::ParseLine(const std::string &line) { +boost::optional RawInput::ParseLine(const std::string &line) { if (line.size() < 2) { // too short return boost::none; @@ -202,7 +202,7 @@ boost::optional RawInput::ParseLine(const std::string &line) { } // parse hex digits - uat::Bytes payload; + Bytes payload; payload.reserve(hexlength / 2); for (decltype(eod) i = 1; i < eod; i += 2) { @@ -255,5 +255,5 @@ boost::optional RawInput::ParseLine(const std::string &line) { i = semicolon + 1; } - return uat::RawMessage(std::move(payload), t, rs, rssi); + return RawMessage(std::move(payload), t, rs, rssi); } diff --git a/socket_input.h b/socket_input.h index 350a37f..1aa49f8 100644 --- a/socket_input.h +++ b/socket_input.h @@ -16,7 +16,7 @@ #include "message_source.h" -namespace uat { +namespace flightaware::uat { class RawInput : public MessageSource, public std::enable_shared_from_this { public: typedef std::shared_ptr Pointer; @@ -35,7 +35,7 @@ namespace uat { void TryNextEndpoint(const boost::system::error_code &last_error); void ScheduleRead(); void ParseBuffer(); - boost::optional ParseLine(const std::string &line); + boost::optional ParseLine(const std::string &line); void HandleError(const boost::system::error_code &ec); boost::asio::io_service &service_; @@ -53,6 +53,6 @@ namespace uat { std::vector readbuf_; std::size_t used_; }; -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/socket_output.cc b/socket_output.cc index 8479210..7c92ea5 100644 --- a/socket_output.cc +++ b/socket_output.cc @@ -14,135 +14,133 @@ namespace asio = boost::asio; using boost::asio::ip::tcp; -using namespace uat; +using namespace flightaware::uat; -namespace dump978 { - SocketOutput::SocketOutput(asio::io_service &service, tcp::socket &&socket) : service_(service), strand_(service), socket_(std::move(socket)), peer_(socket_.remote_endpoint()), flush_pending_(false) {} +SocketOutput::SocketOutput(asio::io_service &service, tcp::socket &&socket) : service_(service), strand_(service), socket_(std::move(socket)), peer_(socket_.remote_endpoint()), flush_pending_(false) {} - void SocketOutput::Start() { ReadAndDiscard(); } +void SocketOutput::Start() { ReadAndDiscard(); } - void SocketOutput::ReadAndDiscard() { - auto self(shared_from_this()); - auto buf = std::make_shared(512); - socket_.async_read_some(asio::buffer(*buf), strand_.wrap([this, self, buf](const boost::system::error_code &ec, std::size_t len) { - if (ec) { - HandleError(ec); - } else { - ReadAndDiscard(); - } - })); - } +void SocketOutput::ReadAndDiscard() { + auto self(shared_from_this()); + auto buf = std::make_shared(512); + socket_.async_read_some(asio::buffer(*buf), strand_.wrap([this, self, buf](const boost::system::error_code &ec, std::size_t len) { + if (ec) { + HandleError(ec); + } else { + ReadAndDiscard(); + } + })); +} + +void SocketOutput::Write(SharedMessageVector messages) { + auto self(shared_from_this()); + strand_.dispatch([this, self, messages]() { + if (IsOpen()) { + InternalWrite(messages); + Flush(); + } + }); +} - void SocketOutput::Write(SharedMessageVector messages) { - auto self(shared_from_this()); - strand_.dispatch([this, self, messages]() { - if (IsOpen()) { - InternalWrite(messages); - Flush(); - } - }); - } +void SocketOutput::Flush() { + if (flush_pending_) + return; - void SocketOutput::Flush() { - if (flush_pending_) - return; + auto writebuf = std::make_shared(outbuf_.str()); + if (writebuf->empty()) + return; - auto writebuf = std::make_shared(outbuf_.str()); - if (writebuf->empty()) - return; + flush_pending_ = true; + outbuf_.str(std::string()); - flush_pending_ = true; - outbuf_.str(std::string()); + auto self(shared_from_this()); + async_write(socket_, boost::asio::buffer(*writebuf), strand_.wrap([this, self, writebuf](const boost::system::error_code &ec, size_t len) { + flush_pending_ = false; + if (ec) { + HandleError(ec); + return; + } - auto self(shared_from_this()); - async_write(socket_, boost::asio::buffer(*writebuf), strand_.wrap([this, self, writebuf](const boost::system::error_code &ec, size_t len) { - flush_pending_ = false; - if (ec) { - HandleError(ec); - return; - } + Flush(); // maybe some more data arrived + })); +} - Flush(); // maybe some more data arrived - })); +void SocketOutput::HandleError(const boost::system::error_code &ec) { + if (ec == boost::asio::error::eof) { + std::cerr << peer_ << ": connection closed" << std::endl; + } else if (ec != boost::asio::error::operation_aborted) { + std::cerr << peer_ << ": connection error: " << ec.message() << std::endl; } - void SocketOutput::HandleError(const boost::system::error_code &ec) { - if (ec == boost::asio::error::eof) { - std::cerr << peer_ << ": connection closed" << std::endl; - } else if (ec != boost::asio::error::operation_aborted) { - std::cerr << peer_ << ": connection error: " << ec.message() << std::endl; - } + Close(); +} - Close(); +void SocketOutput::Close() { + socket_.close(); + if (close_notifier_) { + close_notifier_(); } +} - void SocketOutput::Close() { - socket_.close(); - if (close_notifier_) { - close_notifier_(); - } +////////////// + +void RawOutput::InternalWrite(SharedMessageVector messages) { + for (const auto &message : *messages) { + Buf() << message << '\n'; } +} - ////////////// +////////////// - void RawOutput::InternalWrite(SharedMessageVector messages) { - for (const auto &message : *messages) { - Buf() << message << '\n'; +void JsonOutput::InternalWrite(SharedMessageVector messages) { + for (const auto &message : *messages) { + if (message.Type() == MessageType::DOWNLINK_SHORT || message.Type() == MessageType::DOWNLINK_LONG) { + Buf() << AdsbMessage(message).ToJson() << '\n'; } } - - ////////////// - - void JsonOutput::InternalWrite(SharedMessageVector messages) { - for (const auto &message : *messages) { - if (message.Type() == MessageType::DOWNLINK_SHORT || message.Type() == MessageType::DOWNLINK_LONG) { - Buf() << AdsbMessage(message).ToJson() << '\n'; +} + +////////////// + +SocketListener::SocketListener(asio::io_service &service, const tcp::endpoint &endpoint, MessageDispatch &dispatch, ConnectionFactory factory) : service_(service), acceptor_(service), endpoint_(endpoint), socket_(service), dispatch_(dispatch), factory_(factory) {} + +void SocketListener::Start() { + acceptor_.open(endpoint_.protocol()); + acceptor_.set_option(asio::socket_base::reuse_address(true)); + acceptor_.set_option(tcp::acceptor::reuse_address(true)); + + // We are v6 aware and bind separately to v4 and v6 addresses + if (endpoint_.protocol() == tcp::v6()) + acceptor_.set_option(asio::ip::v6_only(true)); + + acceptor_.bind(endpoint_); + acceptor_.listen(); + Accept(); +} + +void SocketListener::Close() { + acceptor_.cancel(); + socket_.close(); +} + +void SocketListener::Accept() { + auto self(shared_from_this()); + + acceptor_.async_accept(socket_, peer_, [this, self](const boost::system::error_code &ec) { + if (!ec) { + std::cerr << endpoint_ << ": accepted a connection from " << peer_ << std::endl; + auto new_output = factory_(service_, std::move(socket_)); + if (new_output) { + auto handle = dispatch_.AddClient(std::bind(&SocketOutput::Write, new_output, std::placeholders::_1)); + new_output->SetCloseNotifier([this, self, handle] { dispatch_.RemoveClient(handle); }); + new_output->Start(); } + } else { + if (ec == boost::system::errc::operation_canceled) + return; + std::cerr << endpoint_ << ": accept error: " << ec.message() << std::endl; } - } - - ////////////// - - SocketListener::SocketListener(asio::io_service &service, const tcp::endpoint &endpoint, MessageDispatch &dispatch, ConnectionFactory factory) : service_(service), acceptor_(service), endpoint_(endpoint), socket_(service), dispatch_(dispatch), factory_(factory) {} - - void SocketListener::Start() { - acceptor_.open(endpoint_.protocol()); - acceptor_.set_option(asio::socket_base::reuse_address(true)); - acceptor_.set_option(tcp::acceptor::reuse_address(true)); - - // We are v6 aware and bind separately to v4 and v6 addresses - if (endpoint_.protocol() == tcp::v6()) - acceptor_.set_option(asio::ip::v6_only(true)); - acceptor_.bind(endpoint_); - acceptor_.listen(); Accept(); - } - - void SocketListener::Close() { - acceptor_.cancel(); - socket_.close(); - } - - void SocketListener::Accept() { - auto self(shared_from_this()); - - acceptor_.async_accept(socket_, peer_, [this, self](const boost::system::error_code &ec) { - if (!ec) { - std::cerr << endpoint_ << ": accepted a connection from " << peer_ << std::endl; - auto new_output = factory_(service_, std::move(socket_)); - if (new_output) { - auto handle = dispatch_.AddClient(std::bind(&SocketOutput::Write, new_output, std::placeholders::_1)); - new_output->SetCloseNotifier([this, self, handle] { dispatch_.RemoveClient(handle); }); - new_output->Start(); - } - } else { - if (ec == boost::system::errc::operation_canceled) - return; - std::cerr << endpoint_ << ": accept error: " << ec.message() << std::endl; - } - - Accept(); - }); - } -}; // namespace dump978 + }); +} diff --git a/socket_output.h b/socket_output.h index 1816433..40575ac 100644 --- a/socket_output.h +++ b/socket_output.h @@ -17,13 +17,13 @@ #include "message_dispatch.h" #include "uat_message.h" -namespace dump978 { +namespace flightaware::uat { class SocketOutput : public std::enable_shared_from_this { public: typedef std::shared_ptr Pointer; virtual void Start(); - void Write(uat::SharedMessageVector messages); + void Write(SharedMessageVector messages); virtual void Close(); void SetCloseNotifier(std::function notifier) { close_notifier_ = notifier; } @@ -34,7 +34,7 @@ namespace dump978 { SocketOutput(boost::asio::io_service &service_, boost::asio::ip::tcp::socket &&socket_); std::ostringstream &Buf() { return outbuf_; } - virtual void InternalWrite(uat::SharedMessageVector messages) = 0; + virtual void InternalWrite(SharedMessageVector messages) = 0; private: void HandleError(const boost::system::error_code &ec); @@ -58,7 +58,7 @@ namespace dump978 { static Pointer Create(boost::asio::io_service &service, boost::asio::ip::tcp::socket &&socket) { return Pointer(new RawOutput(service, std::move(socket))); } protected: - void InternalWrite(uat::SharedMessageVector messages) override; + void InternalWrite(SharedMessageVector messages) override; private: RawOutput(boost::asio::io_service &service_, boost::asio::ip::tcp::socket &&socket_) : SocketOutput(service_, std::move(socket_)) {} @@ -70,7 +70,7 @@ namespace dump978 { static Pointer Create(boost::asio::io_service &service, boost::asio::ip::tcp::socket &&socket) { return Pointer(new JsonOutput(service, std::move(socket))); } protected: - void InternalWrite(uat::SharedMessageVector messages) override; + void InternalWrite(SharedMessageVector messages) override; private: JsonOutput(boost::asio::io_service &service_, boost::asio::ip::tcp::socket &&socket_) : SocketOutput(service_, std::move(socket_)) {} @@ -82,13 +82,13 @@ namespace dump978 { typedef std::function ConnectionFactory; // factory method, this class must always be constructed via make_shared - static Pointer Create(boost::asio::io_service &service, const boost::asio::ip::tcp::endpoint &endpoint, uat::MessageDispatch &dispatch, ConnectionFactory factory) { return Pointer(new SocketListener(service, endpoint, dispatch, factory)); } + static Pointer Create(boost::asio::io_service &service, const boost::asio::ip::tcp::endpoint &endpoint, MessageDispatch &dispatch, ConnectionFactory factory) { return Pointer(new SocketListener(service, endpoint, dispatch, factory)); } void Start(); void Close(); private: - SocketListener(boost::asio::io_service &service, const boost::asio::ip::tcp::endpoint &endpoint, uat::MessageDispatch &dispatch, ConnectionFactory factory); + SocketListener(boost::asio::io_service &service, const boost::asio::ip::tcp::endpoint &endpoint, MessageDispatch &dispatch, ConnectionFactory factory); void Accept(); @@ -97,9 +97,9 @@ namespace dump978 { boost::asio::ip::tcp::endpoint endpoint_; boost::asio::ip::tcp::socket socket_; boost::asio::ip::tcp::endpoint peer_; - uat::MessageDispatch &dispatch_; + MessageDispatch &dispatch_; ConnectionFactory factory_; }; -}; // namespace dump978 +}; // namespace flightaware::uat #endif diff --git a/track.cc b/track.cc index 55804b7..557ae83 100644 --- a/track.cc +++ b/track.cc @@ -4,12 +4,12 @@ #include "track.h" -using namespace uat; - #include #include -void AircraftState::UpdateFromMessage(const uat::AdsbMessage &message) { +using namespace flightaware::uat; + +void AircraftState::UpdateFromMessage(const AdsbMessage &message) { if (message.received_at < last_message_time) { // Out of order message return; @@ -152,7 +152,7 @@ void Tracker::HandleMessages(SharedMessageVector messages) { }); } -void Tracker::HandleMessage(const uat::AdsbMessage &message) { +void Tracker::HandleMessage(const AdsbMessage &message) { AddressKey key{message.address_qualifier, message.address}; auto i = aircraft_.find(key); if (i == aircraft_.end()) { diff --git a/track.h b/track.h index a108bfd..f9c1f0c 100644 --- a/track.h +++ b/track.h @@ -17,7 +17,7 @@ #include "uat_message.h" -namespace uat { +namespace flightaware::uat { class AgedFieldBase { public: operator bool() const { return Valid(); } @@ -136,7 +136,7 @@ namespace uat { return std::accumulate(rssi.begin(), rssi.end(), 0.0) / std::min(messages, rssi.size()); } - void UpdateFromMessage(const uat::AdsbMessage &message); + void UpdateFromMessage(const AdsbMessage &message); }; class Tracker : public std::enable_shared_from_this { @@ -159,7 +159,7 @@ namespace uat { private: Tracker(boost::asio::io_service &service, std::chrono::milliseconds timeout) : service_(service), strand_(service), timer_(service), timeout_(timeout) {} - void HandleMessage(const uat::AdsbMessage &message); + void HandleMessage(const AdsbMessage &message); boost::asio::io_service &service_; boost::asio::io_service::strand strand_; @@ -168,6 +168,6 @@ namespace uat { MapType aircraft_; std::uint32_t total_messages_ = 0; }; -}; // namespace uat +}; // namespace flightaware::uat #endif diff --git a/uat_message.cc b/uat_message.cc index 408cdf4..addf486 100644 --- a/uat_message.cc +++ b/uat_message.cc @@ -11,418 +11,419 @@ #include -namespace uat { - // - // streaming raw messages - // - - std::ostream &operator<<(std::ostream &os, const RawMessage &message) { - boost::io::ios_flags_saver ifs(os); - - switch (message.Type()) { - case MessageType::DOWNLINK_SHORT: - case MessageType::DOWNLINK_LONG: - os << '-'; - break; - case MessageType::UPLINK: - os << '+'; - break; - default: - throw std::logic_error("unexpected message type"); - } - - os << std::setfill('0'); - for (auto b : message.Payload()) { - os << std::hex << std::setw(2) << (int)b; - } +using namespace flightaware::uat; + +// +// streaming raw messages +// + +std::ostream &flightaware::uat::operator<<(std::ostream &os, const RawMessage &message) { + boost::io::ios_flags_saver ifs(os); + + switch (message.Type()) { + case MessageType::DOWNLINK_SHORT: + case MessageType::DOWNLINK_LONG: + os << '-'; + break; + case MessageType::UPLINK: + os << '+'; + break; + default: + throw std::logic_error("unexpected message type"); + } - os << ";"; - if (message.Errors() > 0) { - os << "rs=" << std::dec << std::setw(0) << message.Errors() << ';'; - } - if (message.Rssi() != 0) { - os << "rssi=" << std::dec << std::setprecision(1) << std::fixed << message.Rssi() << ';'; - } - if (message.ReceivedAt() != 0) { - os << "t=" << std::dec << std::setw(0) << (message.ReceivedAt() / 1000) << '.' << std::setfill('0') << std::setw(3) << (message.ReceivedAt() % 1000) << ';'; - } - return os; + os << std::setfill('0'); + for (auto b : message.Payload()) { + os << std::hex << std::setw(2) << (int)b; } - // - // decoding messages - // + os << ";"; + if (message.Errors() > 0) { + os << "rs=" << std::dec << std::setw(0) << message.Errors() << ';'; + } + if (message.Rssi() != 0) { + os << "rssi=" << std::dec << std::setprecision(1) << std::fixed << message.Rssi() << ';'; + } + if (message.ReceivedAt() != 0) { + os << "t=" << std::dec << std::setw(0) << (message.ReceivedAt() / 1000) << '.' << std::setfill('0') << std::setw(3) << (message.ReceivedAt() % 1000) << ';'; + } + return os; +} - AdsbMessage::AdsbMessage(const RawMessage &raw) { - if (raw.Type() != MessageType::DOWNLINK_SHORT && raw.Type() != MessageType::DOWNLINK_LONG) { - throw std::logic_error("can't parse this sort of message as a downlink ADS-B message"); - } +// +// decoding messages +// - // Metadata - received_at = raw.ReceivedAt(); - errors = raw.Errors(); - rssi = raw.Rssi(); - - // HDR - payload_type = raw.Bits(1, 1, 1, 5); - address_qualifier = static_cast(raw.Bits(1, 6, 1, 8)); - address = raw.Bits(2, 1, 4, 8); - - // Optional parts of the message - // DO-282B Table 2-10 "Composition of the ADS-B Payload" - switch (payload_type) { - case 0: - DecodeSV(raw); - break; - case 1: - DecodeSV(raw); - DecodeMS(raw); - DecodeAUXSV(raw); - break; - case 2: - DecodeSV(raw); - DecodeAUXSV(raw); - break; - case 3: - DecodeSV(raw); - DecodeMS(raw); - DecodeTS(raw, 30); - break; - case 4: - DecodeSV(raw); - DecodeTS(raw, 30); - break; - case 5: - DecodeSV(raw); - DecodeAUXSV(raw); - break; - case 6: - DecodeSV(raw); - DecodeTS(raw, 25); - DecodeAUXSV(raw); - break; - case 7: - case 8: - case 9: - case 10: - DecodeSV(raw); - break; +AdsbMessage::AdsbMessage(const RawMessage &raw) { + if (raw.Type() != MessageType::DOWNLINK_SHORT && raw.Type() != MessageType::DOWNLINK_LONG) { + throw std::logic_error("can't parse this sort of message as a downlink ADS-B message"); + } - default: - // 11..31, HDR only - break; + // Metadata + received_at = raw.ReceivedAt(); + errors = raw.Errors(); + rssi = raw.Rssi(); + + // HDR + payload_type = raw.Bits(1, 1, 1, 5); + address_qualifier = static_cast(raw.Bits(1, 6, 1, 8)); + address = raw.Bits(2, 1, 4, 8); + + // Optional parts of the message + // DO-282B Table 2-10 "Composition of the ADS-B Payload" + switch (payload_type) { + case 0: + DecodeSV(raw); + break; + case 1: + DecodeSV(raw); + DecodeMS(raw); + DecodeAUXSV(raw); + break; + case 2: + DecodeSV(raw); + DecodeAUXSV(raw); + break; + case 3: + DecodeSV(raw); + DecodeMS(raw); + DecodeTS(raw, 30); + break; + case 4: + DecodeSV(raw); + DecodeTS(raw, 30); + break; + case 5: + DecodeSV(raw); + DecodeAUXSV(raw); + break; + case 6: + DecodeSV(raw); + DecodeTS(raw, 25); + DecodeAUXSV(raw); + break; + case 7: + case 8: + case 9: + case 10: + DecodeSV(raw); + break; + + default: + // 11..31, HDR only + break; + } +} + +void AdsbMessage::DecodeSV(const RawMessage &raw) { + auto raw_lat = raw.Bits(5, 1, 7, 7); + auto raw_lon = raw.Bits(7, 8, 10, 7); + + auto raw_alt = raw.Bits(11, 1, 12, 4); + if (raw_alt != 0) { + auto altitude = (raw_alt - 41) * 25; + if (raw.Bit(10, 8)) { // 2.2.4.5.2.2 "ALTITUDE TYPE" field + geometric_altitude = altitude; + } else { + pressure_altitude = altitude; } } - void AdsbMessage::DecodeSV(const RawMessage &raw) { - auto raw_lat = raw.Bits(5, 1, 7, 7); - auto raw_lon = raw.Bits(7, 8, 10, 7); - - auto raw_alt = raw.Bits(11, 1, 12, 4); - if (raw_alt != 0) { - auto altitude = (raw_alt - 41) * 25; - if (raw.Bit(10, 8)) { // 2.2.4.5.2.2 "ALTITUDE TYPE" field - geometric_altitude = altitude; - } else { - pressure_altitude = altitude; - } - } + nic = raw.Bits(12, 5, 12, 8); - nic = raw.Bits(12, 5, 12, 8); + if (raw_lat != 0 || raw_lon != 0 || *nic != 0) { + // NB: north and south pole encode identically. We return north pole in this + // case + auto lat = raw_lat * 360.0 / 16777216.0; + if (lat > 90) + lat -= 180; - if (raw_lat != 0 || raw_lon != 0 || *nic != 0) { - // NB: north and south pole encode identically. We return north pole in this - // case - auto lat = raw_lat * 360.0 / 16777216.0; - if (lat > 90) - lat -= 180; + auto lon = raw_lon * 360.0 / 16777216.0; + if (lon > 180) + lon -= 360; - auto lon = raw_lon * 360.0 / 16777216.0; - if (lon > 180) - lon -= 360; + position = std::make_pair<>(RoundN(lat, 5), RoundN(lon, 5)); + } - position = std::make_pair<>(RoundN(lat, 5), RoundN(lon, 5)); + airground_state = static_cast(raw.Bits(13, 1, 13, 2)); + + // bit 13,3 reserved + + switch (*airground_state) { + case AirGroundState::AIRBORNE_SUBSONIC: + case AirGroundState::AIRBORNE_SUPERSONIC: { + int supersonic = (*airground_state == AirGroundState::AIRBORNE_SUPERSONIC ? 4 : 1); + int ns_sign = raw.Bit(13, 4) ? -1 : 1; + auto raw_ns = raw.Bits(13, 5, 14, 6); + if (raw_ns != 0) + north_velocity = supersonic * ns_sign * (raw_ns - 1); + + int ew_sign = raw.Bit(14, 7) ? -1 : 1; + auto raw_ew = raw.Bits(14, 8, 16, 1); + if (raw_ew != 0) + east_velocity = supersonic * ew_sign * (raw_ew - 1); + + // derive groundspeed, true track from north/east velocity for convenience + if (north_velocity && east_velocity) { // nb: testing for presence, not non-zero value + ground_speed = RoundN(std::sqrt(1.0 * (*north_velocity) * (*north_velocity) + 1.0 * (*east_velocity) * (*east_velocity)), 1); + auto angle = std::atan2(*east_velocity, *north_velocity) * 180.0 / M_PI; + if (angle < 0) + angle += 360.0; + true_track = RoundN(angle, 1); } - airground_state = static_cast(raw.Bits(13, 1, 13, 2)); - - // bit 13,3 reserved - - switch (*airground_state) { - case AirGroundState::AIRBORNE_SUBSONIC: - case AirGroundState::AIRBORNE_SUPERSONIC: { - int supersonic = (*airground_state == AirGroundState::AIRBORNE_SUPERSONIC ? 4 : 1); - int ns_sign = raw.Bit(13, 4) ? -1 : 1; - auto raw_ns = raw.Bits(13, 5, 14, 6); - if (raw_ns != 0) - north_velocity = supersonic * ns_sign * (raw_ns - 1); - - int ew_sign = raw.Bit(14, 7) ? -1 : 1; - auto raw_ew = raw.Bits(14, 8, 16, 1); - if (raw_ew != 0) - east_velocity = supersonic * ew_sign * (raw_ew - 1); - - // derive groundspeed, true track from north/east velocity for convenience - if (north_velocity && east_velocity) { // nb: testing for presence, not non-zero value - ground_speed = RoundN(std::sqrt(1.0 * (*north_velocity) * (*north_velocity) + 1.0 * (*east_velocity) * (*east_velocity)), 1); - auto angle = std::atan2(*east_velocity, *north_velocity) * 180.0 / M_PI; - if (angle < 0) - angle += 360.0; - true_track = RoundN(angle, 1); + vv_src = static_cast(raw.Bits(16, 2, 16, 2)); + int vv_sign = raw.Bit(16, 3) ? -1 : 1; + auto raw_vv = raw.Bits(16, 4, 17, 4); + if (raw_vv != 0) { + auto vertical_velocity = vv_sign * (raw_vv - 1) * 64; + switch (vv_src.get()) { + case VerticalVelocitySource::BAROMETRIC: + vertical_velocity_barometric = vertical_velocity; + break; + case VerticalVelocitySource::GEOMETRIC: + vertical_velocity_geometric = vertical_velocity; + break; } + } - vv_src = static_cast(raw.Bits(16, 2, 16, 2)); - int vv_sign = raw.Bit(16, 3) ? -1 : 1; - auto raw_vv = raw.Bits(16, 4, 17, 4); - if (raw_vv != 0) { - auto vertical_velocity = vv_sign * (raw_vv - 1) * 64; - switch (vv_src.get()) { - case VerticalVelocitySource::BAROMETRIC: - vertical_velocity_barometric = vertical_velocity; - break; - case VerticalVelocitySource::GEOMETRIC: - vertical_velocity_geometric = vertical_velocity; - break; - } - } + break; + } + + case AirGroundState::ON_GROUND: { + // 13,4 reserved + auto raw_gs = raw.Bits(13, 5, 14, 6); + if (raw_gs != 0) + ground_speed = (raw_gs - 1); + auto tah_type = raw.Bits(14, 7, 14, 8); + auto angle = RoundN(raw.Bits(15, 1, 16, 1) * 360.0 / 512.0, 1); + switch (tah_type) { // 2.2.4.5.2.6.4 / Table 2-28 "Track Angle/Heading Type" + case 0: // data unavailable + break; + case 1: // true track + true_track = angle; + break; + case 2: // magnetic heading + magnetic_heading = angle; + break; + case 3: // true heading + true_heading = angle; break; } - case AirGroundState::ON_GROUND: { - // 13,4 reserved - auto raw_gs = raw.Bits(13, 5, 14, 6); - if (raw_gs != 0) - ground_speed = (raw_gs - 1); - - auto tah_type = raw.Bits(14, 7, 14, 8); - auto angle = RoundN(raw.Bits(15, 1, 16, 1) * 360.0 / 512.0, 1); - switch (tah_type) { // 2.2.4.5.2.6.4 / Table 2-28 "Track Angle/Heading Type" - case 0: // data unavailable - break; - case 1: // true track - true_track = angle; - break; - case 2: // magnetic heading - magnetic_heading = angle; - break; - case 3: // true heading - true_heading = angle; - break; - } - - auto raw_av_size = raw.Bits(16, 2, 16, 5); - if (raw_av_size != 0) { - // DO-282B Table 2-35 - static std::array, 16> aircraft_sizes = {{{0, 0}, // no data - {15, 23}, - {25, 28.5}, - {25, 34}, - {35, 33}, - {35, 38}, - {45, 39.5}, - {45, 45}, - {55, 45}, - {55, 52}, - {65, 59.5}, - {65, 67}, - {75, 72.5}, - {75, 80}, - {85, 80}, - {85, 90}}}; - - aircraft_size = aircraft_sizes[raw_av_size]; - } + auto raw_av_size = raw.Bits(16, 2, 16, 5); + if (raw_av_size != 0) { + // DO-282B Table 2-35 + static std::array, 16> aircraft_sizes = {{{0, 0}, // no data + {15, 23}, + {25, 28.5}, + {25, 34}, + {35, 33}, + {35, 38}, + {45, 39.5}, + {45, 45}, + {55, 45}, + {55, 52}, + {65, 59.5}, + {65, 67}, + {75, 72.5}, + {75, 80}, + {85, 80}, + {85, 90}}}; + + aircraft_size = aircraft_sizes[raw_av_size]; + } - if (raw.Bit(16, 7)) { - // Longitudinal GPS offset - auto raw_gps_long = raw.Bits(16, 8, 17, 4); - if (raw_gps_long != 0) { - if (raw_gps_long == 1) { - gps_position_offset_applied = true; - } else { - gps_position_offset_applied = false; - gps_longitudinal_offset = (raw_gps_long - 1) * 2; - } + if (raw.Bit(16, 7)) { + // Longitudinal GPS offset + auto raw_gps_long = raw.Bits(16, 8, 17, 4); + if (raw_gps_long != 0) { + if (raw_gps_long == 1) { + gps_position_offset_applied = true; + } else { + gps_position_offset_applied = false; + gps_longitudinal_offset = (raw_gps_long - 1) * 2; } - } else { - // Lateral GPS offset - // We adopt the convention that left is negative - auto raw_gps_lat = raw.Bits(16, 8, 17, 2); - if (raw_gps_lat != 0) { - if (raw_gps_lat <= 3) { - gps_lateral_offset = raw_gps_lat * -2; - } else { - gps_lateral_offset = (raw_gps_lat - 4) * 2; - } + } + } else { + // Lateral GPS offset + // We adopt the convention that left is negative + auto raw_gps_lat = raw.Bits(16, 8, 17, 2); + if (raw_gps_lat != 0) { + if (raw_gps_lat <= 3) { + gps_lateral_offset = raw_gps_lat * -2; + } else { + gps_lateral_offset = (raw_gps_lat - 4) * 2; } } - - break; } - default: - // nothing; - break; - } + break; + } - switch (address_qualifier) { - case AddressQualifier::ADSB_ICAO: - case AddressQualifier::ADSB_OTHER: - case AddressQualifier::VEHICLE: - case AddressQualifier::FIXED_BEACON: - utc_coupled = raw.Bit(17, 5); - uplink_feedback = raw.Bits(17, 6, 17, 8); - break; + default: + // nothing; + break; + } - case AddressQualifier::TISB_ICAO: - case AddressQualifier::TISB_TRACKFILE: - case AddressQualifier::ADSR_OTHER: - tisb_site_id = raw.Bits(17, 5, 17, 8); + switch (address_qualifier) { + case AddressQualifier::ADSB_ICAO: + case AddressQualifier::ADSB_OTHER: + case AddressQualifier::VEHICLE: + case AddressQualifier::FIXED_BEACON: + utc_coupled = raw.Bit(17, 5); + uplink_feedback = raw.Bits(17, 6, 17, 8); + break; + + case AddressQualifier::TISB_ICAO: + case AddressQualifier::TISB_TRACKFILE: + case AddressQualifier::ADSR_OTHER: + tisb_site_id = raw.Bits(17, 5, 17, 8); + break; + + default: + // nothing + break; + } +} + +void AdsbMessage::DecodeTS(const RawMessage &raw, unsigned startbyte) { + // TS starts at byte 30 (§2.2.4.5.6) in payload type 3 or 4; + // or at byte 25 (§2.2.4.5.7) in payload type 6; + // the starting offset to use is passed by the caller + + auto raw_altitude = raw.Bits(startbyte + 0, 2, startbyte + 1, 4); + if (raw_altitude != 0) { + selected_altitude_type = static_cast(raw.Bits(startbyte + 0, 1, startbyte + 0, 1)); + switch (*selected_altitude_type) { + case SelectedAltitudeType::MCP_FCU: + selected_altitude_mcp = (raw_altitude - 1) * 32; break; - - default: - // nothing + case SelectedAltitudeType::FMS: + selected_altitude_fms = (raw_altitude - 1) * 32; break; } } - void AdsbMessage::DecodeTS(const RawMessage &raw, unsigned startbyte) { - // TS starts at byte 30 (§2.2.4.5.6) in payload type 3 or 4; - // or at byte 25 (§2.2.4.5.7) in payload type 6; - // the starting offset to use is passed by the caller - - auto raw_altitude = raw.Bits(startbyte + 0, 2, startbyte + 1, 4); - if (raw_altitude != 0) { - selected_altitude_type = static_cast(raw.Bits(startbyte + 0, 1, startbyte + 0, 1)); - switch (*selected_altitude_type) { - case SelectedAltitudeType::MCP_FCU: - selected_altitude_mcp = (raw_altitude - 1) * 32; - break; - case SelectedAltitudeType::FMS: - selected_altitude_fms = (raw_altitude - 1) * 32; - break; - } - } - - auto raw_bps = raw.Bits(startbyte + 1, 5, startbyte + 2, 5); - if (raw_bps != 0) - barometric_pressure_setting = 800 + (raw_bps - 1) * 0.8; - - if (raw.Bit(startbyte + 2, 6)) { - int heading_sign = raw.Bit(startbyte + 2, 7) ? -1 : 1; - auto heading = RoundN(raw.Bits(startbyte + 2, 8, startbyte + 3, 7) * 180.0 / 256.0, 1); - selected_heading = heading_sign * heading; - } + auto raw_bps = raw.Bits(startbyte + 1, 5, startbyte + 2, 5); + if (raw_bps != 0) + barometric_pressure_setting = 800 + (raw_bps - 1) * 0.8; - if (raw.Bit(startbyte + 3, 8)) { - mode_indicators.emplace(); - mode_indicators->autopilot = raw.Bit(startbyte + 4, 1); - mode_indicators->vnav = raw.Bit(startbyte + 4, 2); - mode_indicators->altitude_hold = raw.Bit(startbyte + 4, 3); - mode_indicators->approach = raw.Bit(startbyte + 4, 4); - mode_indicators->lnav = raw.Bit(startbyte + 4, 5); - } + if (raw.Bit(startbyte + 2, 6)) { + int heading_sign = raw.Bit(startbyte + 2, 7) ? -1 : 1; + auto heading = RoundN(raw.Bits(startbyte + 2, 8, startbyte + 3, 7) * 180.0 / 256.0, 1); + selected_heading = heading_sign * heading; + } - // 34,6 .. 34,8 reserved + if (raw.Bit(startbyte + 3, 8)) { + mode_indicators.emplace(); + mode_indicators->autopilot = raw.Bit(startbyte + 4, 1); + mode_indicators->vnav = raw.Bit(startbyte + 4, 2); + mode_indicators->altitude_hold = raw.Bit(startbyte + 4, 3); + mode_indicators->approach = raw.Bit(startbyte + 4, 4); + mode_indicators->lnav = raw.Bit(startbyte + 4, 5); } - void AdsbMessage::DecodeMS(const RawMessage &raw) { - static const char *base40_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ *??"; - auto raw1 = raw.Bits(18, 1, 19, 8); - auto raw2 = raw.Bits(20, 1, 21, 8); - auto raw3 = raw.Bits(22, 1, 23, 8); - - emitter_category = (raw1 / 1600) % 40; - - std::string raw_callsign; - raw_callsign.reserve(8); - raw_callsign.push_back(base40_alphabet[(raw1 / 40) % 40]); - raw_callsign.push_back(base40_alphabet[raw1 % 40]); - raw_callsign.push_back(base40_alphabet[(raw2 / 1600) % 40]); - raw_callsign.push_back(base40_alphabet[(raw2 / 40) % 40]); - raw_callsign.push_back(base40_alphabet[raw2 % 40]); - raw_callsign.push_back(base40_alphabet[(raw3 / 1600) % 40]); - raw_callsign.push_back(base40_alphabet[(raw3 / 40) % 40]); - raw_callsign.push_back(base40_alphabet[raw3 % 40]); - - // trim trailing spaces and code 37 - while (!raw_callsign.empty() && (raw_callsign.back() == ' ' || raw_callsign.back() == '*')) { - raw_callsign.pop_back(); - } + // 34,6 .. 34,8 reserved +} + +void AdsbMessage::DecodeMS(const RawMessage &raw) { + static const char *base40_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ *??"; + auto raw1 = raw.Bits(18, 1, 19, 8); + auto raw2 = raw.Bits(20, 1, 21, 8); + auto raw3 = raw.Bits(22, 1, 23, 8); + + emitter_category = (raw1 / 1600) % 40; + + std::string raw_callsign; + raw_callsign.reserve(8); + raw_callsign.push_back(base40_alphabet[(raw1 / 40) % 40]); + raw_callsign.push_back(base40_alphabet[raw1 % 40]); + raw_callsign.push_back(base40_alphabet[(raw2 / 1600) % 40]); + raw_callsign.push_back(base40_alphabet[(raw2 / 40) % 40]); + raw_callsign.push_back(base40_alphabet[raw2 % 40]); + raw_callsign.push_back(base40_alphabet[(raw3 / 1600) % 40]); + raw_callsign.push_back(base40_alphabet[(raw3 / 40) % 40]); + raw_callsign.push_back(base40_alphabet[raw3 % 40]); + + // trim trailing spaces and code 37 + while (!raw_callsign.empty() && (raw_callsign.back() == ' ' || raw_callsign.back() == '*')) { + raw_callsign.pop_back(); + } - if (!raw_callsign.empty()) { - if (raw.Bit(27, - 7)) { // CSID field, 1 = callsign, 0 = flightplan ID (aka squawk) - callsign.emplace(std::move(raw_callsign)); - } else { - flightplan_id.emplace(std::move(raw_callsign)); - } + if (!raw_callsign.empty()) { + if (raw.Bit(27, + 7)) { // CSID field, 1 = callsign, 0 = flightplan ID (aka squawk) + callsign.emplace(std::move(raw_callsign)); + } else { + flightplan_id.emplace(std::move(raw_callsign)); } - - emergency = static_cast(raw.Bits(24, 1, 24, 3)); - mops_version = raw.Bits(24, 4, 24, 6); - sil = raw.Bits(24, 7, 24, 8); - transmit_mso = raw.Bits(25, 1, 25, 6); - sda = raw.Bits(25, 7, 25, 8); - nac_p = raw.Bits(26, 1, 26, 4); - nac_v = raw.Bits(26, 5, 26, 7); - nic_baro = raw.Bits(26, 8, 26, 8); - - capability_codes.emplace(); - capability_codes->uat_in = raw.Bit(27, 1); - capability_codes->es_in = raw.Bit(27, 2); - capability_codes->tcas_operational = raw.Bit(27, 3); - - operational_modes.emplace(); - operational_modes->tcas_ra_active = raw.Bit(27, 4); - operational_modes->ident_active = raw.Bit(27, 5); - operational_modes->atc_services = raw.Bit(27, 6); - - sil_supplement = static_cast(raw.Bits(27, 8, 27, 8)); - gva = raw.Bits(28, 1, 28, 2); - single_antenna = raw.Bit(28, 3); - nic_supplement = raw.Bit(28, 4); - // 28,5 .. 29,8 reserved } - void AdsbMessage::DecodeAUXSV(const RawMessage &raw) { - auto raw_alt = raw.Bits(30, 1, 31, 4); - if (raw_alt != 0) { - auto altitude = (raw_alt - 41) * 25; - if (raw.Bit(10, 8)) { // 2.2.4.5.2.2 "ALTITUDE TYPE" field (in SV, which is - // always present when AUXSV is present) - pressure_altitude = altitude; - } else { - geometric_altitude = altitude; - } + emergency = static_cast(raw.Bits(24, 1, 24, 3)); + mops_version = raw.Bits(24, 4, 24, 6); + sil = raw.Bits(24, 7, 24, 8); + transmit_mso = raw.Bits(25, 1, 25, 6); + sda = raw.Bits(25, 7, 25, 8); + nac_p = raw.Bits(26, 1, 26, 4); + nac_v = raw.Bits(26, 5, 26, 7); + nic_baro = raw.Bits(26, 8, 26, 8); + + capability_codes.emplace(); + capability_codes->uat_in = raw.Bit(27, 1); + capability_codes->es_in = raw.Bit(27, 2); + capability_codes->tcas_operational = raw.Bit(27, 3); + + operational_modes.emplace(); + operational_modes->tcas_ra_active = raw.Bit(27, 4); + operational_modes->ident_active = raw.Bit(27, 5); + operational_modes->atc_services = raw.Bit(27, 6); + + sil_supplement = static_cast(raw.Bits(27, 8, 27, 8)); + gva = raw.Bits(28, 1, 28, 2); + single_antenna = raw.Bit(28, 3); + nic_supplement = raw.Bit(28, 4); + // 28,5 .. 29,8 reserved +} + +void AdsbMessage::DecodeAUXSV(const RawMessage &raw) { + auto raw_alt = raw.Bits(30, 1, 31, 4); + if (raw_alt != 0) { + auto altitude = (raw_alt - 41) * 25; + if (raw.Bit(10, 8)) { // 2.2.4.5.2.2 "ALTITUDE TYPE" field (in SV, which is + // always present when AUXSV is present) + pressure_altitude = altitude; + } else { + geometric_altitude = altitude; } } +} - // - // converting decoded messages to json - // +// +// converting decoded messages to json +// - NLOHMANN_JSON_SERIALIZE_ENUM(AddressQualifier, {{AddressQualifier::ADSB_ICAO, "adsb_icao"}, {AddressQualifier::ADSB_OTHER, "adsb_other"}, {AddressQualifier::TISB_ICAO, "tisb_icao"}, {AddressQualifier::TISB_TRACKFILE, "tisb_trackfile"}, {AddressQualifier::VEHICLE, "vehicle"}, {AddressQualifier::ADSB_ICAO, "fixed_beacon"}, {AddressQualifier::ADSB_ICAO, "adsr_other"}, {AddressQualifier::RESERVED_7, "reserved_7"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(AddressQualifier, {{AddressQualifier::ADSB_ICAO, "adsb_icao"}, {AddressQualifier::ADSB_OTHER, "adsb_other"}, {AddressQualifier::TISB_ICAO, "tisb_icao"}, {AddressQualifier::TISB_TRACKFILE, "tisb_trackfile"}, {AddressQualifier::VEHICLE, "vehicle"}, {AddressQualifier::ADSB_ICAO, "fixed_beacon"}, {AddressQualifier::ADSB_ICAO, "adsr_other"}, {AddressQualifier::RESERVED_7, "reserved_7"}}); - NLOHMANN_JSON_SERIALIZE_ENUM(AirGroundState, {{AirGroundState::AIRBORNE_SUBSONIC, "airborne"}, {AirGroundState::AIRBORNE_SUPERSONIC, "supersonic"}, {AirGroundState::ON_GROUND, "ground"}, {AirGroundState::RESERVED, "reserved"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(AirGroundState, {{AirGroundState::AIRBORNE_SUBSONIC, "airborne"}, {AirGroundState::AIRBORNE_SUPERSONIC, "supersonic"}, {AirGroundState::ON_GROUND, "ground"}, {AirGroundState::RESERVED, "reserved"}}); - NLOHMANN_JSON_SERIALIZE_ENUM(VerticalVelocitySource, {{VerticalVelocitySource::GEOMETRIC, "geometric"}, {VerticalVelocitySource::BAROMETRIC, "barometric"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(VerticalVelocitySource, {{VerticalVelocitySource::GEOMETRIC, "geometric"}, {VerticalVelocitySource::BAROMETRIC, "barometric"}}); - NLOHMANN_JSON_SERIALIZE_ENUM(EmergencyPriorityStatus, {{EmergencyPriorityStatus::NONE, "none"}, {EmergencyPriorityStatus::GENERAL, "general"}, {EmergencyPriorityStatus::MEDICAL, "medical"}, {EmergencyPriorityStatus::NORDO, "nordo"}, {EmergencyPriorityStatus::UNLAWFUL, "unlawful"}, {EmergencyPriorityStatus::DOWNED, "downed"}, {EmergencyPriorityStatus::RESERVED_7, "reserved_7"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(EmergencyPriorityStatus, {{EmergencyPriorityStatus::NONE, "none"}, {EmergencyPriorityStatus::GENERAL, "general"}, {EmergencyPriorityStatus::MEDICAL, "medical"}, {EmergencyPriorityStatus::NORDO, "nordo"}, {EmergencyPriorityStatus::UNLAWFUL, "unlawful"}, {EmergencyPriorityStatus::DOWNED, "downed"}, {EmergencyPriorityStatus::RESERVED_7, "reserved_7"}}); - NLOHMANN_JSON_SERIALIZE_ENUM(SILSupplement, {{SILSupplement::PER_HOUR, "per_hour"}, {SILSupplement::PER_SAMPLE, "per_sample"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(SILSupplement, {{SILSupplement::PER_HOUR, "per_hour"}, {SILSupplement::PER_SAMPLE, "per_sample"}}); - NLOHMANN_JSON_SERIALIZE_ENUM(SelectedAltitudeType, {{SelectedAltitudeType::MCP_FCU, "mcp_fcu"}, {SelectedAltitudeType::FMS, "fms"}}); +NLOHMANN_JSON_SERIALIZE_ENUM(SelectedAltitudeType, {{SelectedAltitudeType::MCP_FCU, "mcp_fcu"}, {SelectedAltitudeType::FMS, "fms"}}); - nlohmann::json AdsbMessage::ToJson() const { - nlohmann::json o; +nlohmann::json AdsbMessage::ToJson() const { + nlohmann::json o; - o["address_qualifier"] = address_qualifier; + o["address_qualifier"] = address_qualifier; - std::ostringstream os; - os << std::hex << std::setfill('0') << std::setw(6) << address; - o["address"] = os.str(); + std::ostringstream os; + os << std::hex << std::setfill('0') << std::setw(6) << address; + o["address"] = os.str(); #define EMIT(x) \ do { \ @@ -431,91 +432,90 @@ namespace uat { } \ } while (0) - if (position) - o["position"] = {{"lat", position->first}, {"lon", position->second}}; - - EMIT(pressure_altitude); - EMIT(geometric_altitude); - EMIT(nic); - EMIT(airground_state); - EMIT(north_velocity); - EMIT(east_velocity); - EMIT(vv_src); - EMIT(vertical_velocity_barometric); - EMIT(vertical_velocity_geometric); - EMIT(ground_speed); - EMIT(magnetic_heading); - EMIT(true_heading); - EMIT(true_track); - - if (aircraft_size) - o["aircraft_size"] = {{"length", aircraft_size->first}, {"width", aircraft_size->second}}; - - EMIT(gps_lateral_offset); - EMIT(gps_longitudinal_offset); - EMIT(gps_position_offset_applied); - EMIT(utc_coupled); - EMIT(uplink_feedback); - EMIT(tisb_site_id); - - EMIT(emitter_category); - EMIT(callsign); - EMIT(flightplan_id); - EMIT(emergency); - EMIT(mops_version); - EMIT(sil); - EMIT(transmit_mso); - EMIT(sda); - EMIT(nac_p); - EMIT(nac_v); - EMIT(nic_baro); - - if (capability_codes) { - auto &cc = o["capability_codes"] = nlohmann::json::object(); - cc["uat_in"] = capability_codes->uat_in; - cc["es_in"] = capability_codes->es_in; - cc["tcas_operational"] = capability_codes->tcas_operational; - } - - if (operational_modes) { - auto &om = o["operational_modes"] = nlohmann::json::object(); - om["tcas_ra_active"] = operational_modes->tcas_ra_active; - om["ident_active"] = operational_modes->ident_active; - om["atc_services"] = operational_modes->atc_services; - } - - EMIT(sil_supplement); - EMIT(gva); - EMIT(single_antenna); - EMIT(nic_supplement); - EMIT(selected_altitude_type); - EMIT(selected_altitude_mcp); - EMIT(selected_altitude_fms); - EMIT(barometric_pressure_setting); - EMIT(selected_heading); - - if (mode_indicators) { - // clang-format off - o["mode_indicators"] = { - { "autopilot", mode_indicators->autopilot }, - { "vnav", mode_indicators->vnav }, - { "altitude_hold", mode_indicators->altitude_hold }, - { "approach", mode_indicators->approach }, - { "lnav", mode_indicators->lnav } - }; - // clang-format on - } + if (position) + o["position"] = {{"lat", position->first}, {"lon", position->second}}; + + EMIT(pressure_altitude); + EMIT(geometric_altitude); + EMIT(nic); + EMIT(airground_state); + EMIT(north_velocity); + EMIT(east_velocity); + EMIT(vv_src); + EMIT(vertical_velocity_barometric); + EMIT(vertical_velocity_geometric); + EMIT(ground_speed); + EMIT(magnetic_heading); + EMIT(true_heading); + EMIT(true_track); + + if (aircraft_size) + o["aircraft_size"] = {{"length", aircraft_size->first}, {"width", aircraft_size->second}}; + + EMIT(gps_lateral_offset); + EMIT(gps_longitudinal_offset); + EMIT(gps_position_offset_applied); + EMIT(utc_coupled); + EMIT(uplink_feedback); + EMIT(tisb_site_id); + + EMIT(emitter_category); + EMIT(callsign); + EMIT(flightplan_id); + EMIT(emergency); + EMIT(mops_version); + EMIT(sil); + EMIT(transmit_mso); + EMIT(sda); + EMIT(nac_p); + EMIT(nac_v); + EMIT(nic_baro); + + if (capability_codes) { + auto &cc = o["capability_codes"] = nlohmann::json::object(); + cc["uat_in"] = capability_codes->uat_in; + cc["es_in"] = capability_codes->es_in; + cc["tcas_operational"] = capability_codes->tcas_operational; + } -#undef EMIT + if (operational_modes) { + auto &om = o["operational_modes"] = nlohmann::json::object(); + om["tcas_ra_active"] = operational_modes->tcas_ra_active; + om["ident_active"] = operational_modes->ident_active; + om["atc_services"] = operational_modes->atc_services; + } + EMIT(sil_supplement); + EMIT(gva); + EMIT(single_antenna); + EMIT(nic_supplement); + EMIT(selected_altitude_type); + EMIT(selected_altitude_mcp); + EMIT(selected_altitude_fms); + EMIT(barometric_pressure_setting); + EMIT(selected_heading); + + if (mode_indicators) { // clang-format off - o["metadata"] = { - { "received_at", received_at / 1000.0 }, - { "rssi", RoundN(rssi, 1) }, - { "errors", errors } + o["mode_indicators"] = { + { "autopilot", mode_indicators->autopilot }, + { "vnav", mode_indicators->vnav }, + { "altitude_hold", mode_indicators->altitude_hold }, + { "approach", mode_indicators->approach }, + { "lnav", mode_indicators->lnav } }; // clang-format on - - return o; } -}; // namespace uat + +#undef EMIT + + // clang-format off + o["metadata"] = { + { "received_at", received_at / 1000.0 }, + { "rssi", RoundN(rssi, 1) }, + { "errors", errors } + }; + // clang-format on + + return o; +} diff --git a/uat_message.h b/uat_message.h index 153bb05..743a9b0 100644 --- a/uat_message.h +++ b/uat_message.h @@ -17,7 +17,7 @@ #include "common.h" #include "uat_protocol.h" -namespace uat { +namespace flightaware::uat { class RawMessage { public: RawMessage() : type_(MessageType::INVALID), received_at_(0), errors_(0), rssi_(0) {} @@ -150,7 +150,7 @@ namespace uat { std::ostream &operator<<(std::ostream &os, const RawMessage &message); - typedef std::vector MessageVector; + typedef std::vector MessageVector; typedef std::shared_ptr SharedMessageVector; // 2.2.4.5.1.2 "ADDRESS QUALIFIER" field @@ -283,6 +283,6 @@ namespace uat { void DecodeMS(const RawMessage &raw); void DecodeAUXSV(const RawMessage &raw); }; -} // namespace uat +} // namespace flightaware::uat #endif diff --git a/uat_protocol.h b/uat_protocol.h index a20e6b2..7630f83 100644 --- a/uat_protocol.h +++ b/uat_protocol.h @@ -7,7 +7,7 @@ #ifndef UAT_PROTOCOL_H #define UAT_PROTOCOL_H -namespace uat { +namespace flightaware::uat { enum class MessageType { DOWNLINK_SHORT, DOWNLINK_LONG, UPLINK, INVALID }; const unsigned SYNC_BITS = 36; @@ -48,6 +48,6 @@ namespace uat { const int DOWNLINK_LONG_PAD = 255 - DOWNLINK_LONG_BYTES; const int UPLINK_BLOCK_PAD = 255 - UPLINK_BLOCK_BYTES; }; // namespace fec -}; // namespace uat +}; // namespace flightaware::uat #endif