/* * 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 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