From 4baf2a06f249e51857dee0d6749e9df9e9745677 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 2 Dec 2015 09:31:14 -0800 Subject: [PATCH] Move ais_baseband code to app_ais. --- firmware/application/app_ais.cpp | 329 ++++++++++++++++++++++++++++++ firmware/application/app_ais.hpp | 11 +- firmware/common/ais_baseband.cpp | 333 ------------------------------- firmware/common/ais_baseband.hpp | 4 - 4 files changed, 339 insertions(+), 338 deletions(-) diff --git a/firmware/application/app_ais.cpp b/firmware/application/app_ais.cpp index dbee7c74..8efd196d 100644 --- a/firmware/application/app_ais.cpp +++ b/firmware/application/app_ais.cpp @@ -24,6 +24,335 @@ #include "portapack.hpp" using namespace portapack; +#include "field_reader.hpp" +#include "crc.hpp" + +// TODO: Move string formatting elsewhere!!! +#include "ui_widget.hpp" + +namespace baseband { +namespace ais { + +decoded_packet packet_decode(const std::bitset<1024>& data, const size_t data_length); + +struct BitRemap { + size_t operator()(const size_t bit_index) const { + return bit_index ^ 7; + } +}; + +struct CRCBitRemap { + size_t operator()(const size_t bit_index) const { + return bit_index; + } +}; + +using FieldReader = ::FieldReader, BitRemap>; +using CRCFieldReader = ::FieldReader, CRCBitRemap>; + +struct PacketLengthRange { + constexpr PacketLengthRange( + ) : min_bytes { 0 }, + max_bytes { 0 } + { + } + + constexpr PacketLengthRange( + const uint16_t min_bits, + const uint16_t max_bits + ) : min_bytes { static_cast(min_bits / 8U) }, + max_bytes { static_cast(max_bits / 8U) } + { + // static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8"); + // static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8"); + } + + bool contains(const size_t bit_count) const { + return !is_above(bit_count) && !is_below(bit_count); + } + + bool is_above(const size_t bit_count) const { + return (min() > bit_count); + } + + bool is_below(const size_t bit_count) const { + return (max() < bit_count); + } + + size_t min() const { + return min_bytes * 8; + } + + size_t max() const { + return max_bytes * 8; + } + +private: + const uint8_t min_bytes; + const uint8_t max_bytes; +}; + +static constexpr std::array packet_length_range { { + { 0, 0 }, // 0 + { 168, 168 }, // 1 + { 168, 168 }, // 2 + { 168, 168 }, // 3 + { 168, 168 }, // 4 + { 424, 424 }, // 5 + { 0, 0 }, // 6 + { 0, 0 }, // 7 + { 0, 1008 }, // 8 + { 0, 0 }, // 9 + { 0, 0 }, // 10 + { 0, 0 }, // 11 + { 0, 0 }, // 12 + { 0, 0 }, // 13 + { 0, 0 }, // 14 + { 0, 0 }, // 15 + { 0, 0 }, // 16 + { 0, 0 }, // 17 + { 168, 168 }, // 18 + { 0, 0 }, // 19 + { 72, 160 }, // 20 + { 272, 360 }, // 21 + { 168, 168 }, // 22 + { 160, 160 }, // 23 + { 160, 168 }, // 24 + { 0, 168 }, // 25 + { 0, 0 }, // 26 + { 0, 0 }, // 27 + { 0, 0 }, // 28 + { 0, 0 }, // 29 + { 0, 0 }, // 30 + { 0, 0 }, // 31 +} }; + +struct PacketLengthValidator { + bool operator()(const uint_fast8_t message_id, const size_t length) { + return packet_length_range[message_id].contains(length); + } +}; + +struct PacketTooLong { + bool operator()(const uint_fast8_t message_id, const size_t length) { + return packet_length_range[message_id].is_below(length); + } +}; + +struct CRCCheck { + bool operator()(const std::bitset<1024>& payload, const size_t data_length) { + CRCFieldReader field_crc { payload }; + CRC ais_fcs { 0x1021 }; + + uint16_t crc_calculated = 0xffff; + + for(size_t i=0; i(field.read(start_bit, 27) << 5) / 32; +} + +static int32_t ais_longitude_normalized( + const FieldReader& field, + const size_t start_bit +) { + // Shifting and dividing is to sign-extend the source field. + // TODO: There's probably a more elegant way to do it. + return static_cast(field.read(start_bit, 28) << 4) / 16; +} + +static std::string ais_format_latlon_normalized(const int32_t normalized) { + const int32_t t = (normalized * 5) / 3; + const int32_t degrees = t / (100 * 10000); + const int32_t fraction = std::abs(t) % (100 * 10000); + return ui::to_string_dec_int(degrees) + "." + ui::to_string_dec_int(fraction, 6, '0'); +} + +static std::string ais_format_latitude( + const FieldReader& field, + const size_t start_bit +) { + const auto value = static_cast(field.read(start_bit, 27) << 5) / 32; + return ais_format_latlon_normalized(value); +} + +static std::string ais_format_longitude( + const FieldReader& field, + const size_t start_bit +) { + const auto value = static_cast(field.read(start_bit, 28) << 4) / 16; + return ais_format_latlon_normalized(value); +} + +struct ais_datetime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +static ais_datetime ais_get_datetime( + const FieldReader& field, + const size_t start_bit +) { + return { + static_cast(field.read(start_bit + 0, 14)), + static_cast(field.read(start_bit + 14, 4)), + static_cast(field.read(start_bit + 18, 5)), + static_cast(field.read(start_bit + 23, 5)), + static_cast(field.read(start_bit + 28, 6)), + static_cast(field.read(start_bit + 34, 6)), + }; +} + +static std::string ais_format_datetime( + const FieldReader& field, + const size_t start_bit +) { + const auto datetime = ais_get_datetime(field, start_bit); + return ui::to_string_dec_uint(datetime.year, 4) + "/" + + ui::to_string_dec_uint(datetime.month, 2, '0') + "/" + + ui::to_string_dec_uint(datetime.day, 2, '0') + " " + + ui::to_string_dec_uint(datetime.hour, 2, '0') + ":" + + ui::to_string_dec_uint(datetime.minute, 2, '0') + ":" + + ui::to_string_dec_uint(datetime.second, 2, '0'); +} + +static char ais_char_to_ascii(const uint8_t c) { + return (c ^ 32) + 32; +} + +static std::string ais_read_text( + const FieldReader& field, + const size_t start_bit, + const size_t character_count +) { + std::string result; + const size_t character_length = 6; + const size_t end_bit = start_bit + character_count * character_length; + for(size_t i=start_bit; i& payload, const size_t payload_length) { + // TODO: Unstuff here, not in baseband! + + // Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag). + const size_t data_and_fcs_length = payload_length - 7; + + if( data_and_fcs_length < 38 ) { + return { "short " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" }; + } + + const size_t extra_bits = data_and_fcs_length & 7; + if( extra_bits != 0 ) { + return { "extra bits " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" }; + } + + FieldReader field { payload }; + + const auto message_id = field.read(0, 6); + + const size_t data_length = data_and_fcs_length - 16; + PacketLengthValidator packet_length_valid; + if( !packet_length_valid(message_id, data_length) ) { + return { "bad length " + ui::to_string_dec_uint(data_length, 3), "" }; + } + + CRCCheck crc_ok; + if( !crc_ok(payload, data_length) ) { + return { "crc", "" }; + } + + const auto source_id = field.read(8, 30); + std::string result { ui::to_string_dec_uint(message_id, 2) + " " + ui::to_string_dec_uint(source_id, 10) }; + + switch(message_id) { + case 1: + case 2: + case 3: + { + const auto navigational_status = field.read(38, 4); + result += " " + ais_format_navigational_status(navigational_status); + result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 89)); + result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 61)); + } + break; + + case 4: + { + result += " " + ais_format_datetime(field, 38); + result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 107)); + result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 79)); + } + break; + + case 5: + { + const auto call_sign = ais_read_text(field, 70, 7); + const auto name = ais_read_text(field, 112, 20); + const auto destination = ais_read_text(field, 302, 20); + result += " \"" + call_sign + "\" \"" + name + "\" \"" + destination + "\""; + } + break; + + case 21: + { + const auto name = ais_read_text(field, 43, 20); + result += " \"" + name + "\" " + ais_format_latitude(field, 192) + " " + ais_format_longitude(field, 164); + } + break; + + default: + break; + } + + return { "OK", result }; +} + +} /* namespace ais */ +} /* namespace baseband */ + AISModel::AISModel() { receiver_model.set_baseband_configuration({ .mode = 3, diff --git a/firmware/application/app_ais.hpp b/firmware/application/app_ais.hpp index 19b4d018..5e5e315b 100644 --- a/firmware/application/app_ais.hpp +++ b/firmware/application/app_ais.hpp @@ -26,7 +26,16 @@ #include "message.hpp" #include "log_file.hpp" -#include "ais_baseband.hpp" +#include +#include + +namespace baseband { +namespace ais { + +using decoded_packet = std::pair; + +} /* namespace ais */ +} /* namespace baseband */ class AISModel { public: diff --git a/firmware/common/ais_baseband.cpp b/firmware/common/ais_baseband.cpp index dcd41491..d137a6a5 100644 --- a/firmware/common/ais_baseband.cpp +++ b/firmware/common/ais_baseband.cpp @@ -20,336 +20,3 @@ */ #include "ais_baseband.hpp" - -#include -#include -#include -#include - -#include "field_reader.hpp" -#include "crc.hpp" - -// TODO: Move string formatting elsewhere!!! -#include "ui_widget.hpp" - -namespace baseband { - -namespace ais { - -struct BitRemap { - size_t operator()(const size_t bit_index) const { - return bit_index ^ 7; - } -}; - -struct CRCBitRemap { - size_t operator()(const size_t bit_index) const { - return bit_index; - } -}; - -using FieldReader = ::FieldReader, BitRemap>; -using CRCFieldReader = ::FieldReader, CRCBitRemap>; - -struct PacketLengthRange { - constexpr PacketLengthRange( - ) : min_bytes { 0 }, - max_bytes { 0 } - { - } - - constexpr PacketLengthRange( - const uint16_t min_bits, - const uint16_t max_bits - ) : min_bytes { static_cast(min_bits / 8U) }, - max_bytes { static_cast(max_bits / 8U) } - { - // static_assert((min_bits & 7) == 0, "minimum bits not a multiple of 8"); - // static_assert((max_bits & 7) == 0, "minimum bits not a multiple of 8"); - } - - bool contains(const size_t bit_count) const { - return !is_above(bit_count) && !is_below(bit_count); - } - - bool is_above(const size_t bit_count) const { - return (min() > bit_count); - } - - bool is_below(const size_t bit_count) const { - return (max() < bit_count); - } - - size_t min() const { - return min_bytes * 8; - } - - size_t max() const { - return max_bytes * 8; - } - -private: - const uint8_t min_bytes; - const uint8_t max_bytes; -}; - -static constexpr std::array packet_length_range { { - { 0, 0 }, // 0 - { 168, 168 }, // 1 - { 168, 168 }, // 2 - { 168, 168 }, // 3 - { 168, 168 }, // 4 - { 424, 424 }, // 5 - { 0, 0 }, // 6 - { 0, 0 }, // 7 - { 0, 1008 }, // 8 - { 0, 0 }, // 9 - { 0, 0 }, // 10 - { 0, 0 }, // 11 - { 0, 0 }, // 12 - { 0, 0 }, // 13 - { 0, 0 }, // 14 - { 0, 0 }, // 15 - { 0, 0 }, // 16 - { 0, 0 }, // 17 - { 168, 168 }, // 18 - { 0, 0 }, // 19 - { 72, 160 }, // 20 - { 272, 360 }, // 21 - { 168, 168 }, // 22 - { 160, 160 }, // 23 - { 160, 168 }, // 24 - { 0, 168 }, // 25 - { 0, 0 }, // 26 - { 0, 0 }, // 27 - { 0, 0 }, // 28 - { 0, 0 }, // 29 - { 0, 0 }, // 30 - { 0, 0 }, // 31 -} }; - -struct PacketLengthValidator { - bool operator()(const uint_fast8_t message_id, const size_t length) { - return packet_length_range[message_id].contains(length); - } -}; - -struct PacketTooLong { - bool operator()(const uint_fast8_t message_id, const size_t length) { - return packet_length_range[message_id].is_below(length); - } -}; - -struct CRCCheck { - bool operator()(const std::bitset<1024>& payload, const size_t data_length) { - CRCFieldReader field_crc { payload }; - CRC ais_fcs { 0x1021 }; - - uint16_t crc_calculated = 0xffff; - - for(size_t i=0; i(field.read(start_bit, 27) << 5) / 32; -} - -static int32_t ais_longitude_normalized( - const FieldReader& field, - const size_t start_bit -) { - // Shifting and dividing is to sign-extend the source field. - // TODO: There's probably a more elegant way to do it. - return static_cast(field.read(start_bit, 28) << 4) / 16; -} - -static std::string ais_format_latlon_normalized(const int32_t normalized) { - const int32_t t = (normalized * 5) / 3; - const int32_t degrees = t / (100 * 10000); - const int32_t fraction = std::abs(t) % (100 * 10000); - return ui::to_string_dec_int(degrees) + "." + ui::to_string_dec_int(fraction, 6, '0'); -} - -static std::string ais_format_latitude( - const FieldReader& field, - const size_t start_bit -) { - const auto value = static_cast(field.read(start_bit, 27) << 5) / 32; - return ais_format_latlon_normalized(value); -} - -static std::string ais_format_longitude( - const FieldReader& field, - const size_t start_bit -) { - const auto value = static_cast(field.read(start_bit, 28) << 4) / 16; - return ais_format_latlon_normalized(value); -} - -struct ais_datetime { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; -}; - -static ais_datetime ais_get_datetime( - const FieldReader& field, - const size_t start_bit -) { - return { - static_cast(field.read(start_bit + 0, 14)), - static_cast(field.read(start_bit + 14, 4)), - static_cast(field.read(start_bit + 18, 5)), - static_cast(field.read(start_bit + 23, 5)), - static_cast(field.read(start_bit + 28, 6)), - static_cast(field.read(start_bit + 34, 6)), - }; -} - -static std::string ais_format_datetime( - const FieldReader& field, - const size_t start_bit -) { - const auto datetime = ais_get_datetime(field, start_bit); - return ui::to_string_dec_uint(datetime.year, 4) + "/" + - ui::to_string_dec_uint(datetime.month, 2, '0') + "/" + - ui::to_string_dec_uint(datetime.day, 2, '0') + " " + - ui::to_string_dec_uint(datetime.hour, 2, '0') + ":" + - ui::to_string_dec_uint(datetime.minute, 2, '0') + ":" + - ui::to_string_dec_uint(datetime.second, 2, '0'); -} - -static char ais_char_to_ascii(const uint8_t c) { - return (c ^ 32) + 32; -} - -static std::string ais_read_text( - const FieldReader& field, - const size_t start_bit, - const size_t character_count -) { - std::string result; - const size_t character_length = 6; - const size_t end_bit = start_bit + character_count * character_length; - for(size_t i=start_bit; i& payload, const size_t payload_length) { - // TODO: Unstuff here, not in baseband! - - // Subtract end flag (8 bits) - one unstuffing bit (occurs during end flag). - const size_t data_and_fcs_length = payload_length - 7; - - if( data_and_fcs_length < 38 ) { - return { "short " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" }; - } - - const size_t extra_bits = data_and_fcs_length & 7; - if( extra_bits != 0 ) { - return { "extra bits " + ui::to_string_dec_uint(data_and_fcs_length, 3), "" }; - } - - FieldReader field { payload }; - - const auto message_id = field.read(0, 6); - - const size_t data_length = data_and_fcs_length - 16; - PacketLengthValidator packet_length_valid; - if( !packet_length_valid(message_id, data_length) ) { - return { "bad length " + ui::to_string_dec_uint(data_length, 3), "" }; - } - - CRCCheck crc_ok; - if( !crc_ok(payload, data_length) ) { - return { "crc", "" }; - } - - const auto source_id = field.read(8, 30); - std::string result { ui::to_string_dec_uint(message_id, 2) + " " + ui::to_string_dec_uint(source_id, 10) }; - - switch(message_id) { - case 1: - case 2: - case 3: - { - const auto navigational_status = field.read(38, 4); - result += " " + ais_format_navigational_status(navigational_status); - result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 89)); - result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 61)); - } - break; - - case 4: - { - result += " " + ais_format_datetime(field, 38); - result += " " + ais_format_latlon_normalized(ais_latitude_normalized(field, 107)); - result += " " + ais_format_latlon_normalized(ais_longitude_normalized(field, 79)); - } - break; - - case 5: - { - const auto call_sign = ais_read_text(field, 70, 7); - const auto name = ais_read_text(field, 112, 20); - const auto destination = ais_read_text(field, 302, 20); - result += " \"" + call_sign + "\" \"" + name + "\" \"" + destination + "\""; - } - break; - - case 21: - { - const auto name = ais_read_text(field, 43, 20); - result += " \"" + name + "\" " + ais_format_latitude(field, 192) + " " + ais_format_longitude(field, 164); - } - break; - - default: - break; - } - - return { "OK", result }; -} - -} /* namespace ais */ -} /* namespace baseband */ diff --git a/firmware/common/ais_baseband.hpp b/firmware/common/ais_baseband.hpp index 2be4753b..c693d610 100644 --- a/firmware/common/ais_baseband.hpp +++ b/firmware/common/ais_baseband.hpp @@ -57,10 +57,6 @@ constexpr std::array, 32> rrc_taps_76k8_4t_p { { { 1.7970187432e-03f, -7.4434953528e-04f }, { 4.6850211151e-03f, -9.3190864122e-04f }, } }; -using decoded_packet = std::pair; - -decoded_packet packet_decode(const std::bitset<1024>& data, const size_t data_length); - } /* namespace ais */ } /* namespace baseband */