schwehr/libais

View on GitHub
src/libais/ais8_366_22.cpp

Summary

Maintainability
Test Coverage
// Defined sort of by Nav55, but being tweaked by the USCG and
// will hopefully become a RTCM standard.  Also hoping that this
// will be harmonized with the IMO Circ 289.

#include <cmath>
#include <string>

#include "ais.h"

namespace libais {

const char *shape_names[8] = {"Circle/Pt", "Rect", "Sector", "Polyline",
                              "Polygon", "Text", "Reserved_6", "Reserved_7"};

const char *ais8_366_22_notice_names[AIS8_366_22_NUM_NAMES] = {
  "Caution Area: Marine mammals habitat (no whales obs)",  // 0
  "Caution Area: Marine mammals in area – reduce speed",  // 1
  "Caution Area: Marine mammals in area – stay clear",  // 2
  "Caution Area: Marine mammals in area – report sightings",  // 3
  "Caution Area: Protected habitat – reduce speed",  // 4
  "Caution Area: Protected habitat – stay clear",  // 5
  "Caution Area: Protected habitat – no fishing or anchoring",  // 6
  "Caution Area: Derelicts (drifting objects)",  // 7
  "Caution Area: Traffic congestion",  // 8
  "Caution Area: Marine event",  // 9
  "Caution Area: Divers down",  // 10
  "Caution Area: Swim area",  // 11
  "Caution Area: Dredge operations",  // 12
  "Caution Area: Survey operations",  // 13
  "Caution Area: Underwater operation",  // 14
  "Caution Area: Seaplane operations",  // 15
  "Caution Area: Fishery – nets in water",  // 16
  "Caution Area: Cluster of fishing vessels",  // 17
  "Caution Area: Fairway closed",  // 18
  "Caution Area: Harbour closed",  // 19
  "Caution Area: Risk (define in Associated text field)",  // 20
  "Caution Area: Underwater vehicle operation",  // 21
  "(reserved for future use)",  // 22
  "Environmental Caution Area: Storm front (line squall)",  // 23
  "Environmental Caution Area: Hazardous sea ice",  // 24
  "Environmental Caution Area: Storm warning (cell or line of storms)",  // 25
  "Environmental Caution Area: High wind",  // 26
  "Environmental Caution Area: High waves",  // 27
  "Environmental Caution Area: Restricted visibility (fog, rain, etc.)",  // 28
  "Environmental Caution Area: Strong currents",  // 29
  "Environmental Caution Area: Heavy icing",  // 30
  "(reserved for future use)",  // 31
  "Restricted Area: Fishing prohibited",  // 32
  "Restricted Area: No anchoring.",  // 33
  "Restricted Area: Entry approval required prior to transit",  // 34
  "Restricted Area: Entry prohibited",  // 35
  "Restricted Area: Active military OPAREA",  // 36
  "Restricted Area: Firing – danger area.",  // 37
  "Restricted Area: Drifting Mines",  // 38
  "(reserved for future use)",  // 39
  "Anchorage Area: Anchorage open",  // 40
  "Anchorage Area: Anchorage closed",  // 41
  "Anchorage Area: Anchoring prohibited",  // 42
  "Anchorage Area: Deep draft anchorage",  // 43
  "Anchorage Area: Shallow draft anchorage",  // 44
  "Anchorage Area: Vessel transfer operations",  // 45
  "(reserved for future use)",  // 46
  "(reserved for future use)",  // 47
  "(reserved for future use)",  // 48
  "(reserved for future use)",  // 49
  "(reserved for future use)",  // 50
  "(reserved for future use)",  // 51
  "(reserved for future use)",  // 52
  "(reserved for future use)",  // 53
  "(reserved for future use)",  // 54
  "(reserved for future use)",  // 55
  "Security Alert – Level 1",  // 56
  "Security Alert – Level 2",  // 57
  "Security Alert – Level 3",  // 58
  "(reserved for future use)",  // 59
  "(reserved for future use)",  // 60
  "(reserved for future use)",  // 61
  "(reserved for future use)",  // 62
  "(reserved for future use)",  // 63
  "Distress Area: Vessel disabled and adrift",  // 64
  "Distress Area: Vessel sinking",  // 65
  "Distress Area: Vessel abandoning ship",  // 66
  "Distress Area: Vessel requests medical assistance",  // 67
  "Distress Area: Vessel flooding",  // 68
  "Distress Area: Vessel fire/explosion",  // 69
  "Distress Area: Vessel grounding",  // 70
  "Distress Area: Vessel collision",  // 71
  "Distress Area: Vessel listing/capsizing",  // 72
  "Distress Area: Vessel under assault",  // 73
  "Distress Area: Person overboard",  // 74
  "Distress Area: SAR area",  // 75
  "Distress Area: Pollution response area",  // 76
  "(reserved for future use)",  // 77
  "(reserved for future use)",  // 78
  "(reserved for future use)",  // 79
  "Instruction: Contact VTS at this point/juncture",  // 80
  "Instruction: Contact Port Administration at this point/juncture",  // 81
  "Instruction: Do not proceed beyond this point/juncture",  // 82
  "Instruction: Await instructions prior to proceeding beyond "
  "this point/juncture",  // 83
  "Proceed to this location – await instructions",  // 84
  "Clearance granted – proceed to berth",  // 85
  "(reserved for future use)",  // 86
  "(reserved for future use)",  // 87
  "Information: Pilot boarding position",  // 88
  "Information: Icebreaker waiting area",  // 89
  "Information: Places of refuge",  // 90
  "Information: Position of icebreakers",  // 91
  "Information: Location of response units",  // 92
  "VTS active target",  // 93
  "Rogue or suspicious vessel",  // 94
  "Vessel requesting non-distress assistance",  // 95
  "Chart Feature: Sunken vessel",  // 96
  "Chart Feature: Submerged object",  // 97
  "Chart Feature: Semi-submerged object",  // 98
  "Chart Feature: Shoal area",  // 99
  "Chart Feature: Shoal area due north",  // 100
  "Chart Feature: Shoal area due east",  // 101
  "Chart Feature: Shoal area due south",  // 102
  "Chart Feature: Shoal area due west",  // 103
  "Chart Feature: Channel obstruction",  // 104
  "Chart Feature: Reduced vertical clearance",  // 105
  "Chart Feature: Bridge closed",  // 106
  "Chart Feature: Bridge partially open",  // 107
  "Chart Feature: Bridge fully open",  // 108
  "(reserved for future use)",  // 109
  "(reserved for future use)",  // 110
  "(reserved for future use)",  // 111
  "Report from ship: Icing info",  // 112
  "(reserved for future use)",  // 113
  "Report from ship: Misc information - see associated text field",  // 114
  "(reserved for future use)",  // 115
  "(reserved for future use)",  // 116
  "(reserved for future use)",  // 117
  "(reserved for future use)",  // 118
  "(reserved for future use)",  // 119
  "Route: Recommended route",  // 120
  "Route: Alternative route",  // 121
  "Route: Recommended route through ice",  // 122
  "(reserved for future use)",  // 123
  "(reserved for future use)",  // 124
  "Other – Define in associated text field",  // 125
  "Cancellation – cancel area as identified by Message Linkage ID",  // 126
  "Undefined (default)"  // 127
};


Ais8_366_22::Ais8_366_22(const char *nmea_payload, const size_t pad)
    : Ais8(nmea_payload, pad), link_id(0), notice_type(0), month(0), day(0),
      utc_hour(0), utc_minute(0), duration_minutes(0) {
  assert(dac == 366);
  assert(fi == 22);

  if (!CheckStatus()) {
    return;
  }
  if (num_bits <= 208 || num_bits >= 1020) {
    status = AIS_ERR_BAD_BIT_COUNT;
    return;
  }

  link_id = bits.ToUnsignedInt(56, 10);
  notice_type = bits.ToUnsignedInt(66, 7);
  month = bits.ToUnsignedInt(73, 4);
  day = bits.ToUnsignedInt(77, 5);
  utc_hour = bits.ToUnsignedInt(82, 5);
  utc_minute = bits.ToUnsignedInt(87, 6);

  duration_minutes = bits.ToUnsignedInt(93, 18);

  const int num_sub_areas = static_cast<int>(floor((num_bits - 111)/90.));
  for (int area_idx = 0; area_idx < num_sub_areas; area_idx++) {
    std::unique_ptr<Ais8_366_22_SubArea> area =
        ais8_366_22_subarea_factory(bits, 111 + 90*area_idx);
    if (area) {
      sub_areas.push_back(std::move(area));
    } else {
      status = AIS_ERR_BAD_SUB_SUB_MSG;
      return;
    }
  }

  assert(bits.GetRemaining() == 0);
  status = AIS_OK;
}

// Lookup table for the scale factors to decode the length / distance fields.
// The index is the "Scale Factor"
static int scale_multipliers[4] = {1, 10, 100, 1000};

Ais8_366_22_Circle::Ais8_366_22_Circle(const AisBitset &bits,
                                       const size_t offset) {
  const int scale_factor = bits.ToUnsignedInt(offset + 3, 2);
  position = bits.ToAisPoint(offset + 5, 55);
  // TODO(schwehr): precision? And bit counts for radius  and spare?
  // TODO(schwehr): collapse these numbers
  radius_m =
      bits.ToUnsignedInt(offset + 60, 12) * scale_multipliers[scale_factor];
  spare = bits.ToUnsignedInt(offset + 72, 16);
}

Ais8_366_22_Rect::Ais8_366_22_Rect(const AisBitset &bits,
                                   const size_t offset) {
  const int scale_factor = bits.ToUnsignedInt(offset + 3, 2);
  position = bits.ToAisPoint(offset + 5, 55);
  e_dim_m =
      bits.ToUnsignedInt(offset + 60, 8) * scale_multipliers[scale_factor];
  n_dim_m =
      bits.ToUnsignedInt(offset + 68, 8) * scale_multipliers[scale_factor];
  orient_deg = bits.ToUnsignedInt(offset + 76, 9);
  spare = bits.ToUnsignedInt(offset + 85, 5);
}

Ais8_366_22_Sector::Ais8_366_22_Sector(const AisBitset &bits,
                                       const size_t offset) {
  const int scale_factor = bits.ToUnsignedInt(offset + 3, 2);
  position = bits.ToAisPoint(offset + 5, 55);
  radius_m =
      bits.ToUnsignedInt(offset + 60, 12) * scale_multipliers[scale_factor];
  left_bound_deg = bits.ToUnsignedInt(offset + 72, 9);
  right_bound_deg = bits.ToUnsignedInt(offset + 81, 9);
}

Ais8_366_22_Polyline::Ais8_366_22_Polyline(const AisBitset &bits,
                                           const size_t offset) {
  const int scale_factor = bits.ToUnsignedInt(offset + 3, 2);
  for (size_t i = 0; i < 4; i++) {
    const int angle = bits.ToUnsignedInt(offset + 5 + (i*21), 10);
    const int dist = bits.ToUnsignedInt(offset + 15 + (i*21), 11) *
                     scale_multipliers[scale_factor];
    if (dist == 0) {
      break;
    }
    angles.push_back(angle);
    dists_m.push_back(dist);
  }
  spare = bits[offset + 89];
}

// TODO(schwehr): Merge with Polyline.
Ais8_366_22_Polygon::Ais8_366_22_Polygon(const AisBitset &bits,
                                         const size_t offset) {
  const int scale_factor = bits.ToUnsignedInt(offset + 3, 2);
  for (size_t i = 0; i < 4; i++) {
    const int angle = bits.ToUnsignedInt(offset + 5 + (i*21), 10);
    const int dist = bits.ToUnsignedInt(offset + 15 + (i*21), 11) *
                     scale_multipliers[scale_factor];
    if (dist == 0) {
      break;
    }
    angles.push_back(angle);
    dists_m.push_back(dist);
  }
  spare = bits[offset + 89];
}

Ais8_366_22_Text::Ais8_366_22_Text(const AisBitset &bits,
                                   const size_t offset) {
  text = std::string(bits.ToString(offset + 3, 84));
  spare = bits.ToUnsignedInt(offset + 87, 3);
}

// Call the appropriate constructor
std::unique_ptr<Ais8_366_22_SubArea>
ais8_366_22_subarea_factory(const AisBitset &bits,
                            const size_t offset) {
  const Ais8_366_22_AreaShapeEnum area_shape =
      (Ais8_366_22_AreaShapeEnum)bits.ToUnsignedInt(offset, 3);

  switch (area_shape) {
  case AIS8_366_22_SHAPE_CIRCLE:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Circle(bits, offset));
  case AIS8_366_22_SHAPE_RECT:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Rect(bits, offset));
  case AIS8_366_22_SHAPE_SECTOR:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Sector(bits, offset));
  case AIS8_366_22_SHAPE_POLYLINE:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Polyline(bits, offset));
  case AIS8_366_22_SHAPE_POLYGON:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Polygon(bits, offset));
  case AIS8_366_22_SHAPE_TEXT:
    return std::unique_ptr<Ais8_366_22_SubArea>(new Ais8_366_22_Text(bits, offset));
  case AIS8_366_22_SHAPE_RESERVED_6:  // FALLTHROUGH
  case AIS8_366_22_SHAPE_RESERVED_7:  // FALLTHROUGH
    // Leave area as 0 to indicate error
    break;
  case AIS8_366_22_SHAPE_ERROR:
    break;
  default:
    assert(false);
  }
  return {};
}

}  // namespace libais