1
0
mirror of https://github.com/peterantypas/maiana.git synced 2025-05-15 23:10:11 -07:00
2021-09-09 08:14:28 -07:00

265 lines
9.8 KiB
C++

// The USCG RTCM SC 121 Environmental Sensor Message tested in
// Tampa, FL from 200[78] util ?
// http://www.dtic.mil/cgi-bin/GetTRDoc?AD=ADA504755
// Phase I Summary Report on AIS Transmit Project (Environmental Message)
#include "ais.h"
namespace libais {
Ais8_1_26_Location::Ais8_1_26_Location(const AisBitset &bits,
const size_t offset) {
position = bits.ToAisPoint(offset, 55);
z = bits.ToUnsignedInt(offset + 55, 11) / 10.;
owner = bits.ToUnsignedInt(offset + 66, 4);
timeout = bits.ToUnsignedInt(offset + 70, 3);
spare = bits.ToUnsignedInt(offset + 73, 12);
}
Ais8_1_26_Station::Ais8_1_26_Station(const AisBitset &bits,
const size_t offset) {
name = bits.ToString(offset, 84);
spare = bits.ToUnsignedInt(offset + 84, 1);
}
Ais8_1_26_Wind::Ais8_1_26_Wind(const AisBitset &bits,
const size_t offset) {
wind_speed = bits.ToUnsignedInt(offset, 7);
wind_gust = bits.ToUnsignedInt(offset + 7, 7); // knots
wind_dir = bits.ToUnsignedInt(offset + 14, 9);
wind_gust_dir = bits.ToUnsignedInt(offset + 23, 9);
sensor_type = bits.ToUnsignedInt(offset + 32, 3);
wind_forecast = bits.ToUnsignedInt(offset + 35, 7);
wind_gust_forecast = bits.ToUnsignedInt(offset + 42, 7); // knots
wind_dir_forecast = bits.ToUnsignedInt(offset + 49, 9);
utc_day_forecast = bits.ToUnsignedInt(offset + 58, 5);
utc_hour_forecast = bits.ToUnsignedInt(offset + 63, 5);
utc_min_forecast = bits.ToUnsignedInt(offset + 68, 6);
duration = bits.ToUnsignedInt(offset + 74, 8);
spare = bits.ToUnsignedInt(offset + 82, 3);
}
Ais8_1_26_WaterLevel::Ais8_1_26_WaterLevel(const AisBitset &bits,
const size_t offset) {
type = bits[offset];
level = bits.ToInt(offset + 1, 16) / 100.;
trend = bits.ToUnsignedInt(offset + 17, 2);
vdatum = bits.ToUnsignedInt(offset + 19, 5);
sensor_type = bits.ToUnsignedInt(offset + 24, 3);
forecast_type = bits[offset + 27];
level_forecast = bits.ToInt(offset + 28, 16) / 100.;
utc_day_forecast = bits.ToUnsignedInt(offset + 44, 5);
utc_hour_forecast = bits.ToUnsignedInt(offset + 49, 5);
utc_min_forecast = bits.ToUnsignedInt(offset + 54, 6);
duration = bits.ToUnsignedInt(offset + 60, 8);
spare = bits.ToUnsignedInt(offset + 68, 17);
}
Ais8_1_26_Curr2D::Ais8_1_26_Curr2D(const AisBitset &bits,
const size_t offset) {
for (size_t idx = 0; idx < 3; idx++) {
size_t start = offset + idx * 26;
currents[idx].speed = bits.ToUnsignedInt(start, 8) / 10.;
currents[idx].dir = bits.ToUnsignedInt(start + 8, 9);
currents[idx].depth = bits.ToUnsignedInt(start + 17, 9);
}
type = bits.ToUnsignedInt(offset + 78, 3);
spare = bits.ToUnsignedInt(offset + 81, 4);
}
Ais8_1_26_Curr3D::Ais8_1_26_Curr3D(const AisBitset &bits,
const size_t offset) {
for (size_t idx = 0; idx < 2; idx++) {
size_t start = offset + idx * 33;
currents[idx].north = bits.ToUnsignedInt(start, 8) / 10.;
currents[idx].east = bits.ToUnsignedInt(start + 8, 8) / 10.;
currents[idx].up = bits.ToUnsignedInt(start + 16, 8) / 10.;
currents[idx].depth = bits.ToUnsignedInt(start + 24, 9);
}
type = bits.ToUnsignedInt(offset + 66, 3);
spare = bits.ToUnsignedInt(offset + 69, 16);
}
Ais8_1_26_HorzFlow::Ais8_1_26_HorzFlow(const AisBitset &bits,
const size_t offset) {
for (size_t idx = 0; idx < 2; idx++) {
size_t start = offset + idx * 42;
currents[idx].bearing = bits.ToUnsignedInt(start, 9);
currents[idx].dist = bits.ToUnsignedInt(start + 9, 7);
currents[idx].speed = bits.ToUnsignedInt(start + 16, 8) / 10.;
currents[idx].dir = bits.ToUnsignedInt(start + 24, 9);
currents[idx].level = bits.ToUnsignedInt(start + 33, 9);
}
spare = bits[offset + 84];
}
Ais8_1_26_SeaState::Ais8_1_26_SeaState(const AisBitset &bits,
const size_t offset) {
swell_height = bits.ToUnsignedInt(offset, 8) / 10.;
swell_period = bits.ToUnsignedInt(offset + 8, 6);
swell_dir = bits.ToUnsignedInt(offset + 14, 9);
sea_state = bits.ToUnsignedInt(offset + 23, 4);
swell_sensor_type = bits.ToUnsignedInt(offset + 27, 3);
water_temp = bits.ToInt(offset + 30, 10) / 10.;
water_temp_depth = bits.ToUnsignedInt(offset + 40, 7) / 10.;
water_sensor_type = bits.ToUnsignedInt(offset + 47, 3);
wave_height = bits.ToUnsignedInt(offset + 50, 8) / 10.;
wave_period = bits.ToUnsignedInt(offset + 58, 6);
wave_dir = bits.ToUnsignedInt(offset + 64, 9);
wave_sensor_type = bits.ToUnsignedInt(offset + 73, 3);
salinity = bits.ToUnsignedInt(offset + 76, 9) / 10.;
}
Ais8_1_26_Salinity::Ais8_1_26_Salinity(const AisBitset &bits,
const size_t offset) {
water_temp = bits.ToUnsignedInt(offset, 10) / 10. - 10;
conductivity = bits.ToUnsignedInt(offset + 10, 10) / 100.;
pressure = bits.ToUnsignedInt(offset + 20, 16) / 10.;
salinity = bits.ToUnsignedInt(offset + 36, 9) / 10.;
salinity_type = bits.ToUnsignedInt(offset + 45, 2);
sensor_type = bits.ToUnsignedInt(offset + 47, 3);
spare[0] = bits.ToUnsignedInt(offset + 50, 32);
spare[1] = bits.ToUnsignedInt(offset + 82, 3);
}
Ais8_1_26_Wx::Ais8_1_26_Wx(const AisBitset &bits,
const size_t offset) {
air_temp = bits.ToInt(offset, 11) / 10.;
air_temp_sensor_type = bits.ToUnsignedInt(offset + 11, 3);
precip = bits.ToUnsignedInt(offset + 14, 2);
horz_vis = bits.ToUnsignedInt(offset + 16, 8) / 10.;
dew_point = bits.ToInt(offset + 24, 10) / 10.;
dew_point_type = bits.ToUnsignedInt(offset + 34, 3);
air_pressure = (bits.ToUnsignedInt(offset + 37, 9) + 800) / 100.0; // Pa.
air_pressure_trend = bits.ToUnsignedInt(offset + 46, 2);
air_pressor_type = bits.ToUnsignedInt(offset + 48, 3);
salinity = bits.ToUnsignedInt(offset + 51, 9) / 10.;
spare = bits.ToUnsignedInt(offset + 60, 25);
}
Ais8_1_26_AirDraught::Ais8_1_26_AirDraught(const AisBitset &bits,
const size_t offset) {
draught = bits.ToUnsignedInt(offset, 13) / 100.;
gap = bits.ToUnsignedInt(offset + 13, 13) / 10.;
trend = bits.ToUnsignedInt(offset + 26, 2);
forecast_gap = bits.ToUnsignedInt(offset + 28, 13) / 10.;
utc_day_forecast = bits.ToUnsignedInt(offset + 41, 5);
utc_hour_forecast = bits.ToUnsignedInt(offset + 46, 5);
utc_min_forecast = bits.ToUnsignedInt(offset + 51, 6);
spare = bits.ToUnsignedInt(offset + 57, 28);
}
// TODO(schwehr): Refactor to be like the 8:367:22 factory.
Ais8_1_26_SensorReport*
ais8_1_26_sensor_report_factory(const AisBitset &bits,
const size_t offset) {
const Ais8_1_26_SensorEnum rpt_type =
(Ais8_1_26_SensorEnum)bits.ToUnsignedInt(offset, 4);
// WARNING: out of order decoding
// Only get the report header if we can decode the type
const size_t rpt_start = offset + 27; // skip tp after site_id
bits.SeekTo(rpt_start);
Ais8_1_26_SensorReport *rpt = nullptr;
switch (rpt_type) {
case AIS8_1_26_SENSOR_LOCATION:
rpt = new Ais8_1_26_Location(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_STATION:
rpt = new Ais8_1_26_Station(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_WIND:
rpt = new Ais8_1_26_Wind(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_WATER_LEVEL:
rpt = new Ais8_1_26_WaterLevel(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_CURR_2D:
rpt = new Ais8_1_26_Curr2D(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_CURR_3D:
rpt = new Ais8_1_26_Curr3D(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_HORZ_FLOW:
rpt = new Ais8_1_26_HorzFlow(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_SEA_STATE:
rpt = new Ais8_1_26_SeaState(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_SALINITY:
rpt = new Ais8_1_26_Salinity(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_WX:
rpt = new Ais8_1_26_Wx(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_AIR_DRAUGHT:
rpt = new Ais8_1_26_AirDraught(bits, rpt_start);
break;
case AIS8_1_26_SENSOR_RESERVED_11: break; // Leave rpt == 0 to indicate error
case AIS8_1_26_SENSOR_RESERVED_12: break;
case AIS8_1_26_SENSOR_RESERVED_13: break;
case AIS8_1_26_SENSOR_RESERVED_14: break;
case AIS8_1_26_SENSOR_RESERVED_15: break;
default:
{} // Leave rpt == 0 to indicate error
}
if (!rpt)
return rpt;
rpt->report_type = rpt_type;
bits.SeekTo(offset + 4);
rpt->utc_day = bits.ToUnsignedInt(offset + 4, 5);
rpt->utc_hr = bits.ToUnsignedInt(offset + 9, 5);
rpt->utc_min = bits.ToUnsignedInt(offset + 14, 6);
rpt->site_id = bits.ToUnsignedInt(offset + 20, 7);
return rpt;
}
Ais8_1_26::Ais8_1_26(const char *nmea_payload, const size_t pad)
: Ais8(nmea_payload, pad) {
assert(dac == 1);
assert(fi == 26);
if (!CheckStatus()) {
return;
}
if (168 > num_bits || num_bits > 1098) {
status = AIS_ERR_BAD_BIT_COUNT;
return;
}
const size_t num_sensor_reports = (num_bits - 56) / AIS8_1_26_REPORT_SIZE;
// TODO(schwehr): what to do about extra data in sensor report msg 8_1_26?
// if ((num_bits - 56) % AIS8_1_26_REPORT_SIZE)
for (size_t report_idx = 0; report_idx < num_sensor_reports; report_idx++) {
const size_t start = 56 + report_idx * AIS8_1_26_REPORT_SIZE;
bits.SeekTo(start);
Ais8_1_26_SensorReport *sensor =
ais8_1_26_sensor_report_factory(bits, start);
if (sensor) {
reports.push_back(sensor);
} else {
status = AIS_ERR_BAD_SUB_SUB_MSG;
return;
}
}
// TODO(schwehr): Enable this assert after fixing the message.
// assert(bits.GetRemaining() == 0);
status = AIS_OK;
}
// TODO(schwehr): Use unique_ptr to manage memory.
Ais8_1_26::~Ais8_1_26() {
for (size_t i = 0; i < reports.size(); i++) {
delete reports[i];
reports[i] = nullptr;
}
}
} // namespace libais