mirror of
https://github.com/peterantypas/maiana.git
synced 2025-05-15 23:10:11 -07:00
265 lines
9.8 KiB
C++
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
|