From 37d7d5b273bf673c88bbaa458f88271985100468 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 8 Dec 2015 15:49:20 -0800 Subject: [PATCH] Separate AIS packet and formatting code from app code. --- firmware/application/Makefile | 1 + firmware/application/ais_app.cpp | 220 ++------------------------- firmware/application/ais_app.hpp | 75 +-------- firmware/application/ais_packet.cpp | 228 ++++++++++++++++++++++++++++ firmware/application/ais_packet.hpp | 102 +++++++++++++ 5 files changed, 345 insertions(+), 281 deletions(-) create mode 100644 firmware/application/ais_packet.cpp create mode 100644 firmware/application/ais_packet.hpp diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 6b75b8a3..4642a7d0 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -170,6 +170,7 @@ CPPSRC = main.cpp \ spectrum_color_lut.cpp \ analog_audio_app.cpp \ ais_baseband.cpp \ + ais_packet.cpp \ ais_app.cpp \ tpms_app.cpp \ ert_app.cpp \ diff --git a/firmware/application/ais_app.cpp b/firmware/application/ais_app.cpp index 4abeeb60..91559976 100644 --- a/firmware/application/ais_app.cpp +++ b/firmware/application/ais_app.cpp @@ -21,124 +21,31 @@ #include "ais_app.hpp" +#include "string_format.hpp" + #include "portapack.hpp" using namespace portapack; -#include "crc.hpp" - -#include "string_format.hpp" - #include -namespace baseband { namespace ais { +namespace format { -using CRCFieldReader = ::FieldReader; - -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"); - } - - constexpr bool contains(const size_t bit_count) const { - return !is_above(bit_count) && !is_below(bit_count); - } - - constexpr bool is_above(const size_t bit_count) const { - return (min() > bit_count); - } - - constexpr bool is_below(const size_t bit_count) const { - return (max() < bit_count); - } - - constexpr size_t min() const { - return min_bytes * 8; - } - - constexpr 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 { - constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { - return packet_length_range[message_id].contains(length); - } -}; - -struct PacketTooLong { - constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { - return packet_length_range[message_id].is_below(length); - } -}; - -static std::string format_latlon_normalized(const int32_t normalized) { +static std::string 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 to_string_dec_int(degrees) + "." + to_string_dec_int(fraction, 6, '0'); } -static std::string format_mmsi( - const MMSI& mmsi +static std::string mmsi( + const baseband::ais::MMSI& mmsi ) { return to_string_dec_uint(mmsi, 9, '0'); } -static std::string format_datetime( - const DateTime& datetime +static std::string datetime( + const baseband::ais::DateTime& datetime ) { return to_string_dec_uint(datetime.year, 4, '0') + "/" + to_string_dec_uint(datetime.month, 2, '0') + "/" + @@ -148,7 +55,7 @@ static std::string format_datetime( to_string_dec_uint(datetime.second, 2, '0'); } -static std::string format_navigational_status(const unsigned int value) { +static std::string navigational_status(const unsigned int value) { switch(value) { case 0: return "under way w/engine"; case 1: return "at anchor"; @@ -168,113 +75,8 @@ static std::string format_navigational_status(const unsigned int value) { } } -static constexpr char char_to_ascii(const uint8_t c) { - return (c ^ 32) + 32; -} - -size_t Packet::length() const { - return packet_.size(); -} - -bool Packet::is_valid() const { - return length_valid() && crc_ok(); -} - -rtc::RTC Packet::received_at() const { - return received_at_; -} - -uint32_t Packet::message_id() const { - return field_.read(0, 6); -} - -MMSI Packet::user_id() const { - return field_.read(8, 30); -} - -MMSI Packet::source_id() const { - return field_.read(8, 30); -} - -uint32_t Packet::read(const size_t start_bit, const size_t length) const { - return field_.read(start_bit, length); -} - -std::string Packet::text( - const size_t start_bit, - const size_t character_count -) const { - std::string result; - result.reserve(character_count); - - const size_t character_length = 6; - const size_t end_bit = start_bit + character_count * character_length; - for(size_t i=start_bit; i(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)), - }; -} - -Latitude Packet::latitude(const size_t start_bit) const { - // 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, 27) << 5) / 32; -} - -Longitude Packet::longitude(const size_t start_bit) const { - // 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; -} - -bool Packet::crc_ok() const { - CRCFieldReader field_crc { packet_ }; - CRC ais_fcs { 0x1021, 0xffff, 0xffff }; - - for(size_t i=0; i #include #include -#include #include #include #include -namespace baseband { -namespace ais { - -struct DateTime { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; -}; - -using Latitude = int32_t; -using Longitude = int32_t; - -using MMSI = uint32_t; - -class Packet { -public: - constexpr Packet( - const rtc::RTC& received_at, - const baseband::Packet& packet - ) : packet_ { packet }, - received_at_ { received_at }, - field_ { packet_ } - { - } - - size_t length() const; - - bool is_valid() const; - - rtc::RTC received_at() const; - - uint32_t message_id() const; - MMSI user_id() const; - MMSI source_id() const; - - uint32_t read(const size_t start_bit, const size_t length) const; - - std::string text(const size_t start_bit, const size_t character_count) const; - - DateTime datetime(const size_t start_bit) const; - - Latitude latitude(const size_t start_bit) const; - Longitude longitude(const size_t start_bit) const; - - bool crc_ok() const; - -private: - using Reader = FieldReader; - - const baseband::Packet packet_; - const rtc::RTC received_at_; - const Reader field_; - - const size_t fcs_length = 16; - - size_t data_and_fcs_length() const; - size_t data_length() const; - - bool length_valid() const; -}; - -} /* namespace ais */ -} /* namespace baseband */ - class AISModel { public: AISModel(); diff --git a/firmware/application/ais_packet.cpp b/firmware/application/ais_packet.cpp new file mode 100644 index 00000000..62bb98e2 --- /dev/null +++ b/firmware/application/ais_packet.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ais_packet.hpp" + +#include "crc.hpp" + +#include + +namespace baseband { +namespace ais { + +using CRCFieldReader = ::FieldReader; + +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"); + } + + constexpr bool contains(const size_t bit_count) const { + return !is_above(bit_count) && !is_below(bit_count); + } + + constexpr bool is_above(const size_t bit_count) const { + return (min() > bit_count); + } + + constexpr bool is_below(const size_t bit_count) const { + return (max() < bit_count); + } + + constexpr size_t min() const { + return min_bytes * 8; + } + + constexpr 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 { + constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { + return packet_length_range[message_id].contains(length); + } +}; + +struct PacketTooLong { + constexpr bool operator()(const uint_fast8_t message_id, const size_t length) const { + return packet_length_range[message_id].is_below(length); + } +}; + +static constexpr char char_to_ascii(const uint8_t c) { + return (c ^ 32) + 32; +} + +size_t Packet::length() const { + return packet_.size(); +} + +bool Packet::is_valid() const { + return length_valid() && crc_ok(); +} + +rtc::RTC Packet::received_at() const { + return received_at_; +} + +uint32_t Packet::message_id() const { + return field_.read(0, 6); +} + +MMSI Packet::user_id() const { + return field_.read(8, 30); +} + +MMSI Packet::source_id() const { + return field_.read(8, 30); +} + +uint32_t Packet::read(const size_t start_bit, const size_t length) const { + return field_.read(start_bit, length); +} + +std::string Packet::text( + const size_t start_bit, + const size_t character_count +) const { + std::string result; + result.reserve(character_count); + + const size_t character_length = 6; + const size_t end_bit = start_bit + character_count * character_length; + for(size_t i=start_bit; i(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)), + }; +} + +Latitude Packet::latitude(const size_t start_bit) const { + // 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, 27) << 5) / 32; +} + +Longitude Packet::longitude(const size_t start_bit) const { + // 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; +} + +bool Packet::crc_ok() const { + CRCFieldReader field_crc { packet_ }; + CRC ais_fcs { 0x1021, 0xffff, 0xffff }; + + for(size_t i=0; i +#include +#include + +namespace baseband { +namespace ais { + +struct DateTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +using Latitude = int32_t; +using Longitude = int32_t; + +using MMSI = uint32_t; + +class Packet { +public: + constexpr Packet( + const rtc::RTC& received_at, + const baseband::Packet& packet + ) : packet_ { packet }, + received_at_ { received_at }, + field_ { packet_ } + { + } + + size_t length() const; + + bool is_valid() const; + + rtc::RTC received_at() const; + + uint32_t message_id() const; + MMSI user_id() const; + MMSI source_id() const; + + uint32_t read(const size_t start_bit, const size_t length) const; + + std::string text(const size_t start_bit, const size_t character_count) const; + + DateTime datetime(const size_t start_bit) const; + + Latitude latitude(const size_t start_bit) const; + Longitude longitude(const size_t start_bit) const; + + bool crc_ok() const; + +private: + using Reader = FieldReader; + + const baseband::Packet packet_; + const rtc::RTC received_at_; + const Reader field_; + + const size_t fcs_length = 16; + + size_t data_and_fcs_length() const; + size_t data_length() const; + + bool length_valid() const; +}; + +} /* namespace ais */ +} /* namespace baseband */ + +#endif/*__AIS_PACKET_H__*/