mirror of
https://github.com/peterantypas/maiana.git
synced 2025-05-15 23:10:11 -07:00
193 lines
5.0 KiB
C++
193 lines
5.0 KiB
C++
#include "ais.h"
|
|
|
|
namespace libais {
|
|
|
|
AisBitset::AisBitset() : num_bits(0), num_chars(0), current_position(0) {}
|
|
|
|
AIS_STATUS AisBitset::ParseNmeaPayload(const char *nmea_payload, int pad) {
|
|
assert(nmea_payload);
|
|
assert(pad >= 0 && pad < 6);
|
|
|
|
InitNmeaOrd();
|
|
|
|
num_bits = 0;
|
|
current_position = 0;
|
|
reset();
|
|
|
|
num_chars = strlen(nmea_payload);
|
|
|
|
const size_t max_chars = size()/6;
|
|
if (static_cast<size_t>(num_chars) > max_chars) {
|
|
#ifdef LIBAIS_DEBUG
|
|
std::cerr << "ERROR: message longer than max allowed size (" << max_chars
|
|
<< "): found " << num_chars << " characters in "
|
|
<< nmea_payload << std::endl;
|
|
#endif
|
|
num_chars = 0; // Don't leave an impression that there are any valid chars.
|
|
return AIS_ERR_MSG_TOO_LONG;
|
|
}
|
|
|
|
size_t bit = 0;
|
|
for (size_t idx = 0; nmea_payload[idx] != '\0' && idx < max_chars; idx++) {
|
|
int c = static_cast<int>(nmea_payload[idx]);
|
|
if (c < 48 || c > 119 || (c >= 88 && c <= 95)) {
|
|
// Make it clear that nothing valuable is in here.
|
|
reset();
|
|
num_chars = 0;
|
|
return AIS_ERR_BAD_NMEA_CHR;
|
|
}
|
|
for (size_t offset = 0; offset < 6; offset++) {
|
|
set(bit++, nmea_ord_[c].test(offset));
|
|
}
|
|
}
|
|
|
|
num_bits = num_chars * 6 - pad;
|
|
|
|
return AIS_OK;
|
|
}
|
|
|
|
const AisBitset& AisBitset::SeekRelative(int d) const {
|
|
assert((current_position + d) >= 0 && (current_position + d) < num_bits);
|
|
current_position += d;
|
|
return *this;
|
|
}
|
|
|
|
const AisBitset& AisBitset::SeekTo(size_t pos) const {
|
|
assert(pos < num_bits);
|
|
current_position = pos;
|
|
return *this;
|
|
}
|
|
|
|
bool AisBitset::operator[](size_t pos) const {
|
|
// TODO(schwehr): Prefer to use num_bits (includes pad) for checking bounds.
|
|
assert(pos < num_chars * 6);
|
|
|
|
assert(current_position == pos);
|
|
|
|
current_position = pos + 1;
|
|
return bitset<MAX_BITS>::operator[](pos);
|
|
}
|
|
|
|
unsigned int AisBitset::ToUnsignedInt(const size_t start,
|
|
const size_t len) const {
|
|
assert(len <= 32);
|
|
// TODO(schwehr): Prefer to use num_bits (includes pad) for checking bounds.
|
|
assert(start + len <= num_chars * 6);
|
|
|
|
assert(current_position == start);
|
|
|
|
unsigned int result = 0;
|
|
size_t end = start + len;
|
|
for (size_t i = start; i < end; ++i) {
|
|
result <<= 1;
|
|
if (test(i))
|
|
result |= 1;
|
|
}
|
|
|
|
current_position = end;
|
|
return result;
|
|
}
|
|
|
|
int AisBitset::ToInt(const size_t start, const size_t len) const {
|
|
assert(len <= 32);
|
|
// TODO(schwehr): Prefer to use num_bits (includes pad) for checking bounds.
|
|
assert(start + len <= num_chars * 6);
|
|
|
|
assert(current_position == start);
|
|
|
|
// Converting the sub-bitset to a signed number, per "Two's complement":
|
|
// - If negative, invert all the bits, then add 1.
|
|
bool is_positive = (len == 32 || !test(start));
|
|
int result = 0;
|
|
size_t end = start + len;
|
|
for (size_t i = start; i < end; ++i) {
|
|
result <<= 1;
|
|
if (test(i) == is_positive)
|
|
result |= 1;
|
|
}
|
|
current_position = end;
|
|
return is_positive ? result : -(result + 1);
|
|
}
|
|
|
|
string AisBitset::ToString(const size_t start, const size_t len) const {
|
|
assert(len % 6 == 0);
|
|
// TODO(schwehr): Prefer to use num_bits (includes pad) for checking bounds.
|
|
assert(start + len <= num_chars * 6);
|
|
|
|
assert(current_position == start);
|
|
|
|
const size_t num_char = len / 6;
|
|
string result(num_char, '@');
|
|
for (size_t char_idx = 0; char_idx < num_char; char_idx++) {
|
|
const int char_num = ToUnsignedInt(start + char_idx*6, 6);
|
|
result[char_idx] = bits_to_char_tbl_[char_num];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const AisPoint AisBitset::ToAisPoint(const size_t start,
|
|
const size_t point_size) const {
|
|
int lng_bits;
|
|
int lat_bits;
|
|
double divisor;
|
|
switch (point_size) {
|
|
case 35:
|
|
lng_bits = 18;
|
|
lat_bits = 17;
|
|
divisor = 600.0;
|
|
break;
|
|
case 49:
|
|
lng_bits = 25;
|
|
lat_bits = 24;
|
|
divisor = 60000.0; // 1/1000th minute
|
|
break;
|
|
case 55:
|
|
lng_bits = 28;
|
|
lat_bits = 27;
|
|
divisor = 600000.;
|
|
break;
|
|
default:
|
|
//std::cerr << "Unsupported point AIS size: " << point_size << std::endl;
|
|
assert(false);
|
|
return AisPoint(-1, -1);
|
|
}
|
|
double lng_deg = ToInt(start, lng_bits);
|
|
double lat_deg = ToInt(start + lng_bits, lat_bits);
|
|
return AisPoint(lng_deg / divisor, lat_deg / divisor);
|
|
}
|
|
|
|
|
|
// static private
|
|
|
|
void AisBitset::InitNmeaOrd() {
|
|
if (nmea_ord_initialized_) {
|
|
return;
|
|
}
|
|
|
|
for (int c = 48; c < 88; c++) {
|
|
int val = c - 48;
|
|
nmea_ord_[c] = Reverse(bitset<6>(val));
|
|
}
|
|
for (int c = 88; c < 128; c++) {
|
|
int val = c - 48;
|
|
val -= 8;
|
|
nmea_ord_[c] = Reverse(bitset<6>(val));
|
|
}
|
|
nmea_ord_initialized_ = true;
|
|
}
|
|
|
|
bitset<6> AisBitset::Reverse(const bitset<6> &bits) {
|
|
bitset<6> out;
|
|
for (size_t i = 0; i < 6; i++)
|
|
out[5 - i] = bits[i];
|
|
return out;
|
|
}
|
|
|
|
bool AisBitset::nmea_ord_initialized_ = false;
|
|
bitset<6> AisBitset::nmea_ord_[128];
|
|
// For decoding str bits inside of a binary message.
|
|
const char AisBitset::bits_to_char_tbl_[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"[\\]^- !\"#$%&`()*+,-./0123456789:;<=>?";
|
|
|
|
} // namespace libais
|