Keep the colleague's microbu-esp32c5 tree in this repository
obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but that tree was gitignored, so a clone of this repository could not build the firmware it ships. It is now committed here as ordinary files in its own folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP bridge's signature verification (--trust) used on the bench. Nothing is fetched from or pushed to the colleague's repository; this repository and its remotes carry everything. The folder's own .gitignore keeps build output, downloaded components and private key material out, as it did there; the committed file set is identical to that repository's tracked files. The ESP32-C5 is still flashed from obu-firmware/, which only takes vanetza-idf from microbu-esp32c5/, so the two stay separate folders. FLASHING.md says how to take a newer version of the colleague's tree (copy it over the folder, rebuild, test, commit).
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
set(CXX_SOURCES
|
||||
address.cpp
|
||||
areas.cpp
|
||||
basic_header.cpp
|
||||
beacon_header.cpp
|
||||
cbf_packet_buffer.cpp
|
||||
cbf_counter.cpp
|
||||
cbf_packet_identifier.cpp
|
||||
cbr_aggregator.cpp
|
||||
common_header.cpp
|
||||
data_confirm.cpp
|
||||
data_request.cpp
|
||||
dcc_field.cpp
|
||||
dcc_information_sharing.cpp
|
||||
dcc_mco_field.cpp
|
||||
duplicate_packet_list.cpp
|
||||
gbc_gac_header.cpp
|
||||
gbc_header.cpp
|
||||
gbc_memory.cpp
|
||||
header_type.cpp
|
||||
header_variant.cpp
|
||||
indication_context.cpp
|
||||
mib.cpp
|
||||
sequence_number.cpp
|
||||
lifetime.cpp
|
||||
location_table.cpp
|
||||
next_hop.cpp
|
||||
packet_buffer.cpp
|
||||
parser.cpp
|
||||
pdu.cpp
|
||||
pdu_conversion.cpp
|
||||
position_updater.cpp
|
||||
position_vector.cpp
|
||||
repeater.cpp
|
||||
router.cpp
|
||||
secured_pdu.cpp
|
||||
shb_header.cpp
|
||||
timestamp.cpp
|
||||
traffic_class.cpp
|
||||
tsb_header.cpp
|
||||
variant_pdu.cpp
|
||||
)
|
||||
|
||||
add_vanetza_component(geonet ${CXX_SOURCES})
|
||||
target_link_libraries(geonet PUBLIC Boost::date_time)
|
||||
target_link_libraries(geonet PUBLIC dcc geodesy net security)
|
||||
|
||||
add_test_subdirectory(tests)
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "address.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t Address::length_bytes;
|
||||
|
||||
namespace // anonymous namespace for local constants
|
||||
{
|
||||
constexpr uint16_t manually_configured_mask = 0x8000;
|
||||
constexpr uint16_t station_type_mask = 0x7c00;
|
||||
constexpr uint16_t country_code_mask = 0x03ff;
|
||||
constexpr unsigned station_type_shift = 10;
|
||||
} // namespace
|
||||
|
||||
Address::Address() :
|
||||
m_manually_configured(false),
|
||||
m_station_type(StationType::Unknown),
|
||||
m_country_code(0)
|
||||
{
|
||||
}
|
||||
|
||||
Address::Address(const MacAddress& addr) :
|
||||
m_manually_configured(false),
|
||||
m_station_type(StationType::Unknown),
|
||||
m_country_code(0),
|
||||
m_mid(addr)
|
||||
{
|
||||
}
|
||||
|
||||
bool Address::operator==(const Address& other) const
|
||||
{
|
||||
return (this->m_manually_configured == other.m_manually_configured &&
|
||||
this->m_station_type == other.m_station_type &&
|
||||
this->m_country_code == other.m_country_code &&
|
||||
this->m_mid == other.m_mid);
|
||||
}
|
||||
|
||||
bool Address::operator!=(const Address& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void serialize(const Address& addr, OutputArchive& ar)
|
||||
{
|
||||
uint16_t manuallyConfiguredAndTypeAndCountryCode = addr.country_code().raw();
|
||||
manuallyConfiguredAndTypeAndCountryCode |=
|
||||
(static_cast<uint16_t>(addr.station_type()) << station_type_shift) & station_type_mask;
|
||||
manuallyConfiguredAndTypeAndCountryCode |=
|
||||
addr.is_manually_configured() ? manually_configured_mask : 0x0000;
|
||||
serialize(host_cast(manuallyConfiguredAndTypeAndCountryCode), ar);
|
||||
serialize(ar, addr.mid());
|
||||
}
|
||||
|
||||
void deserialize(Address& addr, InputArchive& ar)
|
||||
{
|
||||
uint16_t tmp;
|
||||
deserialize(tmp, ar);
|
||||
addr.is_manually_configured((tmp & manually_configured_mask) != 0);
|
||||
addr.country_code(tmp & country_code_mask);
|
||||
addr.station_type(static_cast<StationType>((tmp & station_type_mask) >> station_type_shift));
|
||||
MacAddress mid;
|
||||
deserialize(ar, mid);
|
||||
addr.mid(mid);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
namespace gn = vanetza::geonet;
|
||||
size_t hash<gn::Address>::operator()(const gn::Address& addr) const
|
||||
{
|
||||
size_t seed = 0;
|
||||
boost::hash_combine(seed, addr.is_manually_configured());
|
||||
boost::hash_combine(seed, addr.station_type());
|
||||
boost::hash_combine(seed, addr.country_code().raw());
|
||||
boost::hash_range(seed, addr.mid().octets.begin(), addr.mid().octets.end());
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef GEONET_ADDRESS_HPP_MB8J1IVQ
|
||||
#define GEONET_ADDRESS_HPP_MB8J1IVQ
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/geonet/station_type.hpp>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t length_bytes = 8;
|
||||
|
||||
Address();
|
||||
explicit Address(const MacAddress&);
|
||||
bool is_manually_configured() const { return m_manually_configured; }
|
||||
void is_manually_configured(bool flag) { m_manually_configured = flag; }
|
||||
StationType station_type() const { return m_station_type; }
|
||||
void station_type(StationType type) { m_station_type = type; }
|
||||
BitNumber<unsigned, 10> country_code() const { return m_country_code; }
|
||||
void country_code(BitNumber<unsigned, 10> country) { m_country_code = country; }
|
||||
const MacAddress& mid() const { return m_mid; }
|
||||
void mid(const MacAddress& mid) { m_mid = mid; }
|
||||
|
||||
bool operator==(const Address& other) const;
|
||||
bool operator!=(const Address& other) const;
|
||||
|
||||
private:
|
||||
bool m_manually_configured; // 1 bit
|
||||
StationType m_station_type; // 5 bit
|
||||
BitNumber<unsigned, 10> m_country_code; // 10 bit (deprecated since 1.3.1)
|
||||
MacAddress m_mid; // 48 bit
|
||||
};
|
||||
|
||||
void serialize(const Address&, OutputArchive&);
|
||||
void deserialize(Address&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
|
||||
namespace std {
|
||||
|
||||
template<>
|
||||
struct hash<vanetza::geonet::Address>
|
||||
{
|
||||
size_t operator()(const vanetza::geonet::Address& addr) const;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif /* GEONET_ADDRESS_HPP_MB8J1IVQ */
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "areas.hpp"
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <boost/units/cmath.hpp>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
double geometric_function(const Circle& c, const CartesianPosition& p)
|
||||
{
|
||||
if (c.r.value() != 0.0) {
|
||||
const double x_over_r = p.x / c.r;
|
||||
const double y_over_r = p.y / c.r;
|
||||
return 1.0 - (x_over_r * x_over_r) - (y_over_r * y_over_r);
|
||||
} else {
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
};
|
||||
}
|
||||
|
||||
double geometric_function(const Rectangle& r, const CartesianPosition& p)
|
||||
{
|
||||
if (r.a.value() != 0.0 && r.b.value() != 0.0) {
|
||||
const double x_over_a = p.x / r.a;
|
||||
const double y_over_b = p.y / r.b;
|
||||
return std::min(1.0 - x_over_a * x_over_a, 1.0 - y_over_b * y_over_b);
|
||||
} else {
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
double geometric_function(const Ellipse& e, const CartesianPosition& p)
|
||||
{
|
||||
if (e.a.value() != 0.0 && e.b.value() != 0.0) {
|
||||
const double x_over_a = p.x / e.a;
|
||||
const double y_over_b = p.y / e.b;
|
||||
return 1.0 - (x_over_a * x_over_a) - (y_over_b * y_over_b);
|
||||
} else {
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
struct geometric_function_visitor : public boost::static_visitor<double>
|
||||
{
|
||||
geometric_function_visitor(const CartesianPosition& p) : point(p) {}
|
||||
|
||||
template<class SHAPE>
|
||||
double operator()(const SHAPE& s) const
|
||||
{
|
||||
return geometric_function(s, point);
|
||||
}
|
||||
|
||||
const CartesianPosition& point;
|
||||
};
|
||||
|
||||
double geometric_function(const decltype(Area::shape)& shape, const CartesianPosition& p)
|
||||
{
|
||||
geometric_function_visitor visitor(p);
|
||||
return boost::apply_visitor(visitor, shape);
|
||||
}
|
||||
|
||||
CartesianPosition canonicalize(const CartesianPosition& point, units::Angle azimuth)
|
||||
{
|
||||
using namespace boost::math::double_constants;
|
||||
// area.angle is azimuth angle of EN 302 931 V1.1.1
|
||||
const units::Angle zenith = half_pi * units::si::radian - azimuth;
|
||||
const double sin_z = sin(zenith);
|
||||
const double cos_z = cos(zenith);
|
||||
// rotate canonical point around origin clockwise: zenith = 90 deg - azimuth
|
||||
// other interpretation: rotate shape's long side onto abscissa
|
||||
CartesianPosition canonical;
|
||||
canonical.x = cos_z * point.x + sin_z * point.y;
|
||||
canonical.y = -sin_z * point.x + cos_z * point.y;
|
||||
return canonical;
|
||||
}
|
||||
|
||||
struct area_size_visitor : public boost::static_visitor<units::Area>
|
||||
{
|
||||
units::Area operator()(const Circle& circle) const
|
||||
{
|
||||
using namespace boost::math::double_constants;
|
||||
return pi * circle.r * circle.r;
|
||||
}
|
||||
|
||||
units::Area operator()(const Rectangle& rectangle) const
|
||||
{
|
||||
using namespace boost::math::double_constants;
|
||||
return 4.0 * rectangle.a * rectangle.b;
|
||||
}
|
||||
|
||||
units::Area operator()(const Ellipse& ellipse) const
|
||||
{
|
||||
using namespace boost::math::double_constants;
|
||||
return pi * ellipse.a * ellipse.b;
|
||||
}
|
||||
};
|
||||
|
||||
units::Area area_size(const Area& area)
|
||||
{
|
||||
return boost::apply_visitor(area_size_visitor(), area.shape);
|
||||
}
|
||||
|
||||
bool inside_or_at_border(const Area& area, const GeodeticPosition& geo_position)
|
||||
{
|
||||
const CartesianPosition local = local_cartesian(area.position, geo_position);
|
||||
const CartesianPosition canonical = canonicalize(local, area.angle);
|
||||
return !outside_shape(area.shape, canonical);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef AREAS_HPP_CVK1NIAI
|
||||
#define AREAS_HPP_CVK1NIAI
|
||||
|
||||
#include <vanetza/geodesy/geodesy.hpp>
|
||||
#include <vanetza/units/area.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
using geodesy::CartesianPosition;
|
||||
using geodesy::GeodeticPosition;
|
||||
using geodesy::distance;
|
||||
using geodesy::local_cartesian;
|
||||
|
||||
struct Circle
|
||||
{
|
||||
Circle() : r(1.0 * units::si::meters) {}
|
||||
units::Length r; // radius
|
||||
};
|
||||
|
||||
struct Rectangle
|
||||
{
|
||||
Rectangle() : a(1.0 * units::si::meters), b(1.0 * units::si::meters) {}
|
||||
units::Length a; // center to long side
|
||||
units::Length b; // center to short side
|
||||
};
|
||||
|
||||
struct Ellipse
|
||||
{
|
||||
Ellipse() : a(1.0 * units::si::meters), b(1.0 * units::si::meters) {}
|
||||
units::Length a; // long semi-axis
|
||||
units::Length b; // short semi-axis
|
||||
};
|
||||
|
||||
struct Area
|
||||
{
|
||||
boost::variant<Rectangle, Ellipse, Circle> shape;
|
||||
GeodeticPosition position;
|
||||
units::Angle angle;
|
||||
};
|
||||
|
||||
double geometric_function(const Circle&, const CartesianPosition&);
|
||||
double geometric_function(const Rectangle&, const CartesianPosition&);
|
||||
double geometric_function(const Ellipse&, const CartesianPosition&);
|
||||
double geometric_function(const decltype(Area::shape)&, const CartesianPosition&);
|
||||
|
||||
/**
|
||||
* Canonicalize a point in a shape's coordinate system w.r.t. its azimuth angle
|
||||
* \param point Point to canonicalize
|
||||
* \param azimuth Azimuth angle of shape's long side
|
||||
* \return canonical position (suitable for geometric_function)
|
||||
*/
|
||||
CartesianPosition canonicalize(const CartesianPosition& point, units::Angle azimuth);
|
||||
|
||||
/**
|
||||
* Check if positon is within or at border of area
|
||||
* \param area with shape, dimensions, azimuth and center point position
|
||||
* \param position Geodetic position to check against area
|
||||
* \return true if position is inside or at border
|
||||
*/
|
||||
bool inside_or_at_border(const Area&, const GeodeticPosition&);
|
||||
|
||||
/**
|
||||
* Calculate area size in square km.
|
||||
* \param area Area object
|
||||
* \return area size
|
||||
*/
|
||||
units::Area area_size(const Area&);
|
||||
|
||||
template<class SHAPE>
|
||||
bool inside_shape(const SHAPE& shape, const CartesianPosition& p)
|
||||
{
|
||||
return geometric_function(shape, p) > 0.0;
|
||||
}
|
||||
|
||||
template<class SHAPE>
|
||||
bool outside_shape(const SHAPE& shape, const CartesianPosition& p)
|
||||
{
|
||||
return geometric_function(shape, p) < 0.0;
|
||||
}
|
||||
|
||||
template<class SHAPE>
|
||||
bool at_shape_border(const SHAPE& shape, const CartesianPosition& p)
|
||||
{
|
||||
return geometric_function(shape, p) == 0.0;
|
||||
}
|
||||
|
||||
template<class SHAPE>
|
||||
bool at_center_point(const SHAPE& shape, const CartesianPosition& p)
|
||||
{
|
||||
return geometric_function(shape, p) == 1.0;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* AREAS_HPP_CVK1NIAI */
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <vanetza/geonet/basic_header.hpp>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t BasicHeader::length_bytes;
|
||||
|
||||
BasicHeader::BasicHeader() :
|
||||
version(0),
|
||||
next_header(NextHeaderBasic::Any),
|
||||
reserved(0),
|
||||
hop_limit(0)
|
||||
{
|
||||
}
|
||||
|
||||
BasicHeader::BasicHeader(const MIB& mib) :
|
||||
version(mib.itsGnProtocolVersion),
|
||||
next_header(NextHeaderBasic::Any),
|
||||
reserved(0),
|
||||
lifetime(mib.itsGnDefaultPacketLifetime),
|
||||
hop_limit(mib.itsGnDefaultHopLimit)
|
||||
{
|
||||
}
|
||||
|
||||
BasicHeader::BasicHeader(const DataRequest& request, const MIB& mib) :
|
||||
BasicHeader(mib)
|
||||
{
|
||||
if (mib.itsGnSecurity) {
|
||||
next_header = NextHeaderBasic::Secured;
|
||||
} else {
|
||||
next_header = NextHeaderBasic::Common;
|
||||
}
|
||||
|
||||
lifetime = request.maximum_lifetime;
|
||||
hop_limit = request.max_hop_limit;
|
||||
}
|
||||
|
||||
BasicHeader::BasicHeader(const ShbDataRequest& request, const MIB& mib) :
|
||||
BasicHeader(static_cast<const DataRequest&>(request), mib)
|
||||
{
|
||||
hop_limit = 1;
|
||||
}
|
||||
|
||||
void serialize(const BasicHeader& hdr, OutputArchive& ar)
|
||||
{
|
||||
uint8_t versionAndNextHeader = hdr.version.raw();
|
||||
versionAndNextHeader <<= 4;
|
||||
versionAndNextHeader |= static_cast<uint8_t>(hdr.next_header) & 0x0f;
|
||||
serialize(host_cast(versionAndNextHeader), ar);
|
||||
serialize(host_cast(hdr.reserved), ar);
|
||||
serialize(hdr.lifetime, ar);
|
||||
serialize(host_cast(hdr.hop_limit), ar);
|
||||
}
|
||||
|
||||
void deserialize(BasicHeader& hdr, InputArchive& ar)
|
||||
{
|
||||
uint8_t versionAndNextHeader;
|
||||
deserialize(versionAndNextHeader, ar);
|
||||
hdr.version = versionAndNextHeader >> 4;
|
||||
hdr.next_header = static_cast<NextHeaderBasic>(versionAndNextHeader & 0x0f);
|
||||
deserialize(hdr.reserved, ar);
|
||||
deserialize(hdr.lifetime, ar);
|
||||
deserialize(hdr.hop_limit, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef BASIC_HEADER_HPP_8QS7WLG3
|
||||
#define BASIC_HEADER_HPP_8QS7WLG3
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/geonet/lifetime.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct DataRequest;
|
||||
struct ShbDataRequest;
|
||||
|
||||
/// NextHeaderBasic specified in ETSI EN 302 636-4-1 v1.2.1, section 8.6.3
|
||||
enum class NextHeaderBasic : uint8_t
|
||||
{
|
||||
Any = 0,
|
||||
Common = 1,
|
||||
Secured = 2,
|
||||
};
|
||||
|
||||
/// BasicHeader specified in ETSI EN 302 636-4-1 v1.2.1, section 8.6
|
||||
struct BasicHeader
|
||||
{
|
||||
BasicHeader();
|
||||
BasicHeader(const MIB&);
|
||||
BasicHeader(const DataRequest&, const MIB&);
|
||||
BasicHeader(const ShbDataRequest&, const MIB&);
|
||||
|
||||
static constexpr std::size_t length_bytes = 3 + sizeof(Lifetime);
|
||||
|
||||
BitNumber<unsigned, 4> version;
|
||||
NextHeaderBasic next_header; // 4 bit
|
||||
uint8_t reserved;
|
||||
Lifetime lifetime;
|
||||
uint8_t hop_limit;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Serializes a BasicHeader into a binary archive
|
||||
* \param basic to serialize
|
||||
* \param ar to serialize in
|
||||
*/
|
||||
void serialize(const BasicHeader&, OutputArchive&);
|
||||
|
||||
/**
|
||||
* \brief Deserializes a BasicHeader from a binary archive
|
||||
* \param basic to deserialize
|
||||
* \param ar with a serialized BasicHeader at the beginning
|
||||
*/
|
||||
void deserialize(BasicHeader&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* BASIC_HEADER_HPP_8QS7WLG3 */
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "beacon_header.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t BeaconHeader::length_bytes;
|
||||
|
||||
void serialize(const BeaconHeader& hdr, OutputArchive& ar)
|
||||
{
|
||||
serialize(hdr.source_position, ar);
|
||||
}
|
||||
|
||||
void deserialize(BeaconHeader& hdr, InputArchive& ar)
|
||||
{
|
||||
deserialize(hdr.source_position, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef BEACON_HEADER_HPP_1NRWPHXO
|
||||
#define BEACON_HEADER_HPP_1NRWPHXO
|
||||
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct BeaconHeader
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t length_bytes = LongPositionVector::length_bytes;
|
||||
|
||||
LongPositionVector source_position;
|
||||
};
|
||||
|
||||
void serialize(const BeaconHeader&, OutputArchive&);
|
||||
void deserialize(BeaconHeader&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* BEACON_HEADER_HPP_1NRWPHXO */
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <vanetza/geonet/cbf_counter.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
void CbfCounterImmortal::add(const id_type& id)
|
||||
{
|
||||
++m_counters[id];
|
||||
}
|
||||
|
||||
void CbfCounterImmortal::increment(const id_type& id)
|
||||
{
|
||||
++m_counters[id];
|
||||
}
|
||||
|
||||
auto CbfCounterImmortal::counter(const id_type& id) const -> counter_type
|
||||
{
|
||||
counter_type count = 0;
|
||||
auto found = m_counters.find(id);
|
||||
if (found != m_counters.end()) {
|
||||
count = found->second;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void CbfCounterContending::add(const id_type& id)
|
||||
{
|
||||
m_counters[id] = 1;
|
||||
}
|
||||
|
||||
void CbfCounterContending::remove(const id_type& id)
|
||||
{
|
||||
m_counters.erase(id);
|
||||
}
|
||||
|
||||
CbfCounterFading::CbfCounterFading(Runtime& rt, Clock::duration lifetime) :
|
||||
m_counters(rt)
|
||||
{
|
||||
m_counters.set_lifetime(lifetime);
|
||||
}
|
||||
|
||||
void CbfCounterFading::add(const id_type& id)
|
||||
{
|
||||
++m_counters.refresh(id);
|
||||
m_counters.drop_expired();
|
||||
}
|
||||
|
||||
void CbfCounterFading::increment(const id_type& id)
|
||||
{
|
||||
++m_counters.get_value(id);
|
||||
}
|
||||
|
||||
auto CbfCounterFading::counter(const id_type& id) const -> counter_type
|
||||
{
|
||||
auto* count = m_counters.get_value_ptr(id);
|
||||
return count ? *count : 0;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef CBF_COUNTER_HPP_QTMUOGJS
|
||||
#define CBF_COUNTER_HPP_QTMUOGJS
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_identifier.hpp>
|
||||
#include <vanetza/geonet/soft_state_map.hpp>
|
||||
#include <cstddef>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
|
||||
// forward declaration
|
||||
class Runtime;
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Interface for duplicate packet counters.
|
||||
* This is used by the Contention Based Forwarding (CBF) packet buffer.
|
||||
*/
|
||||
class CbfCounter
|
||||
{
|
||||
public:
|
||||
using id_type = CbfPacketIdentifier;
|
||||
using counter_type = std::size_t;
|
||||
|
||||
/**
|
||||
* Packet has been added to buffer
|
||||
* \param id packet identifier
|
||||
*/
|
||||
virtual void add(const id_type& id) = 0;
|
||||
|
||||
/**
|
||||
* Packet has been removed from buffer.
|
||||
* \param id packet identifier
|
||||
*/
|
||||
virtual void remove(const id_type& id) = 0;
|
||||
|
||||
/**
|
||||
* Increment packet counter by one
|
||||
* \param id packet identifier
|
||||
*/
|
||||
virtual void increment(const id_type& id) = 0;
|
||||
|
||||
/**
|
||||
* Retrieve counter value
|
||||
* \param id packet identifier
|
||||
*/
|
||||
virtual counter_type counter(const id_type& id) const = 0;
|
||||
|
||||
virtual ~CbfCounter() = default;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Immortal CBF counters, i.e. they never expire.
|
||||
* \note Be aware, memory consumption is constantly growing of this implemenation!
|
||||
*/
|
||||
class CbfCounterImmortal : public virtual CbfCounter
|
||||
{
|
||||
public:
|
||||
void add(const id_type&) override;
|
||||
void remove(const id_type&) override {}
|
||||
void increment(const id_type&) override;
|
||||
counter_type counter(const id_type&) const override;
|
||||
|
||||
protected:
|
||||
std::unordered_map<id_type, counter_type> m_counters;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remembers only counter values for packets currently contending, i.e. stored in CBF buffer.
|
||||
* \note This is the ADVANCED routing behaviour of EN 302 636-4-1 v1.2.1
|
||||
*/
|
||||
class CbfCounterContending : public virtual CbfCounter, private CbfCounterImmortal
|
||||
{
|
||||
public:
|
||||
void add(const id_type&) override;
|
||||
void remove(const id_type&) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fading CBF counters
|
||||
*
|
||||
* Counters are removed from the internal table only after expiry, i.e. they are soft-state.
|
||||
* This fixes some serious GN flooding due to ADVANCED routing, see CbfCounterContending.
|
||||
*/
|
||||
class CbfCounterFading : public virtual CbfCounter
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Initialize fading counters
|
||||
* \param rt runtime used for soft-state behaviour
|
||||
* \param lifetime newly added counters are initialized with this lifetime
|
||||
*/
|
||||
CbfCounterFading(Runtime&, Clock::duration lifetime);
|
||||
|
||||
void add(const id_type&) override;
|
||||
void remove(const id_type&) override {}
|
||||
void increment(const id_type&) override;
|
||||
counter_type counter(const id_type&) const override;
|
||||
|
||||
private:
|
||||
SoftStateMap<id_type, counter_type> m_counters;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* CBF_COUNTER_HPP_QTMUOGJS */
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/cbf_counter.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_buffer.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
CbfPacket::CbfPacket(PendingPacket<GbcPdu>&& packet, const MacAddress& sender) :
|
||||
m_packet(std::move(packet)), m_sender(sender)
|
||||
{
|
||||
}
|
||||
|
||||
CbfPacket::CbfPacket(PendingPacket<GbcPdu, const MacAddress&>&& packet, const MacAddress& sender) :
|
||||
m_packet(PendingPacket<GbcPdu>(std::move(packet), cBroadcastMacAddress)), m_sender(sender)
|
||||
{
|
||||
}
|
||||
|
||||
const MacAddress& CbfPacket::sender() const
|
||||
{
|
||||
return m_sender;
|
||||
}
|
||||
|
||||
const Address& CbfPacket::source() const
|
||||
{
|
||||
return m_packet.pdu().extended().source_position.gn_addr;
|
||||
}
|
||||
|
||||
SequenceNumber CbfPacket::sequence_number() const
|
||||
{
|
||||
return m_packet.pdu().extended().sequence_number;
|
||||
}
|
||||
|
||||
Clock::duration CbfPacket::reduce_lifetime(Clock::duration d)
|
||||
{
|
||||
return m_packet.reduce_lifetime(d);
|
||||
}
|
||||
|
||||
std::size_t CbfPacket::length() const
|
||||
{
|
||||
return m_packet.length();
|
||||
}
|
||||
|
||||
|
||||
CbfPacketBuffer::CbfPacketBuffer(Runtime& rt, TimerCallback cb, std::unique_ptr<CbfCounter> cnt, std::size_t bytes) :
|
||||
m_runtime(rt), m_counter(std::move(cnt)),
|
||||
m_capacity_bytes(bytes), m_stored_bytes(0),
|
||||
m_timer_callback(cb)
|
||||
{
|
||||
}
|
||||
|
||||
CbfPacketBuffer::~CbfPacketBuffer()
|
||||
{
|
||||
m_runtime.cancel(this);
|
||||
}
|
||||
|
||||
bool CbfPacketBuffer::remove(const Identifier& id)
|
||||
{
|
||||
bool packet_dropped = false;
|
||||
|
||||
auto& id_map = m_timers.right;
|
||||
auto found = id_map.find(id);
|
||||
if (found != id_map.end()) {
|
||||
auto& packet = found->info;
|
||||
m_stored_bytes -= packet->length();
|
||||
m_counter->remove(id);
|
||||
m_packets.erase(packet);
|
||||
remove_timer(m_timers.project_left(found));
|
||||
packet_dropped = true;
|
||||
}
|
||||
|
||||
assert(m_packets.size() == m_timers.size());
|
||||
return packet_dropped;
|
||||
}
|
||||
|
||||
void CbfPacketBuffer::remove_timer(typename timer_bimap::left_map::iterator timer_it)
|
||||
{
|
||||
auto& timer_map = m_timers.left;
|
||||
auto successor = timer_map.erase(timer_it);
|
||||
if (successor == timer_map.begin() && !timer_map.empty()) {
|
||||
// erased timer was scheduled one, reschedule timer trigger
|
||||
schedule_timer();
|
||||
}
|
||||
}
|
||||
|
||||
void CbfPacketBuffer::add(CbfPacket&& packet, Clock::duration timeout)
|
||||
{
|
||||
if(timeout <= Clock::duration::zero()) return;
|
||||
m_stored_bytes += packet.length();
|
||||
const auto first_timer = m_timers.left.begin();
|
||||
|
||||
// do head drop if necessary
|
||||
while (m_stored_bytes > m_capacity_bytes && !m_packets.empty()) {
|
||||
m_stored_bytes -= m_packets.front().length();
|
||||
const auto id = identifier(m_packets.front());
|
||||
m_timers.right.erase(id);
|
||||
m_counter->remove(id);
|
||||
m_packets.pop_front();
|
||||
}
|
||||
|
||||
Timer timer = { m_runtime, timeout };
|
||||
const Identifier id = identifier(packet);
|
||||
m_packets.emplace_back(std::move(packet));
|
||||
using timer_value = timer_bimap::value_type;
|
||||
auto insertion = m_timers.insert(timer_value { timer, id, std::prev(m_packets.end()) });
|
||||
if (!insertion.second) {
|
||||
m_stored_bytes -= m_packets.back().length();
|
||||
m_packets.pop_back();
|
||||
} else {
|
||||
m_counter->add(id);
|
||||
}
|
||||
|
||||
// first expirying timer has changed (head drop or added packet)
|
||||
if (m_timers.left.begin() != first_timer) {
|
||||
schedule_timer();
|
||||
}
|
||||
assert(m_packets.size() == m_timers.size());
|
||||
}
|
||||
|
||||
void CbfPacketBuffer::update(const Identifier& id, Clock::duration timeout)
|
||||
{
|
||||
auto& id_map = m_timers.right;
|
||||
auto found = id_map.find(id);
|
||||
if (found != id_map.end()) {
|
||||
const Timer& timer = found->second;
|
||||
CbfPacket& cbf_packet = *found->info;
|
||||
reduce_lifetime(timer, cbf_packet);
|
||||
id_map.replace_data(found, Timer { m_runtime, timeout});
|
||||
m_counter->increment(id);
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<CbfPacket> CbfPacketBuffer::fetch(const Identifier& id)
|
||||
{
|
||||
boost::optional<CbfPacket> packet;
|
||||
|
||||
auto& id_map = m_timers.right;
|
||||
auto found = id_map.find(id);
|
||||
if (found != id_map.end()) {
|
||||
const Timer& timer = found->second;
|
||||
CbfPacket& cbf_packet = *found->info;
|
||||
bool valid_packet = reduce_lifetime(timer, cbf_packet);
|
||||
m_stored_bytes -= cbf_packet.length();
|
||||
if (valid_packet) {
|
||||
packet.emplace(std::move(cbf_packet));
|
||||
}
|
||||
m_counter->remove(id);
|
||||
m_packets.erase(found->info);
|
||||
remove_timer(m_timers.project_left(found));
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
const CbfPacket* CbfPacketBuffer::find(const Identifier& id) const
|
||||
{
|
||||
const auto& id_map = m_timers.right;
|
||||
auto found = id_map.find(id);
|
||||
return found != id_map.end() ? &(*found->info) : nullptr;
|
||||
}
|
||||
|
||||
std::size_t CbfPacketBuffer::counter(const Identifier& id) const
|
||||
{
|
||||
return m_counter->counter(id);
|
||||
}
|
||||
|
||||
void CbfPacketBuffer::flush()
|
||||
{
|
||||
// fetch all expired timers
|
||||
const Timer now { m_runtime, std::chrono::seconds(0) };
|
||||
auto end = m_timers.left.upper_bound(now);
|
||||
for (auto it = m_timers.left.begin(); it != end;) {
|
||||
// reduce LT by queuing time
|
||||
const Timer& timer = it->first;
|
||||
CbfPacket& packet = *it->info;
|
||||
bool valid_packet = reduce_lifetime(timer, packet);
|
||||
m_stored_bytes -= packet.length();
|
||||
if (valid_packet) {
|
||||
m_timer_callback(std::move(packet).packet());
|
||||
}
|
||||
|
||||
m_counter->remove(it->second);
|
||||
m_packets.erase(it->info);
|
||||
it = m_timers.left.erase(it);
|
||||
}
|
||||
|
||||
// schedule timer if not empty
|
||||
if (!m_timers.empty()) {
|
||||
schedule_timer();
|
||||
}
|
||||
}
|
||||
|
||||
bool CbfPacketBuffer::reduce_lifetime(const Timer& timer, CbfPacket& packet) const
|
||||
{
|
||||
const auto queuing_time = m_runtime.now() - timer.start;
|
||||
return packet.reduce_lifetime(queuing_time) > Clock::duration::zero();
|
||||
}
|
||||
|
||||
void CbfPacketBuffer::schedule_timer()
|
||||
{
|
||||
assert(!m_timers.empty());
|
||||
m_runtime.cancel(this);
|
||||
Runtime::Callback cb = [this](Clock::time_point) { flush(); };
|
||||
m_runtime.schedule(m_timers.left.begin()->first.expiry, cb, this);
|
||||
}
|
||||
|
||||
|
||||
CbfPacketBuffer::Timer::Timer(const Runtime& rt, Clock::duration timeout) :
|
||||
expiry(rt.now() + timeout), start(rt.now())
|
||||
{
|
||||
}
|
||||
|
||||
bool CbfPacketBuffer::Timer::operator<(const Timer& other) const
|
||||
{
|
||||
return this->expiry < other.expiry;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#ifndef CBF_PACKET_BUFFER_HPP_MU3RK5V1
|
||||
#define CBF_PACKET_BUFFER_HPP_MU3RK5V1
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_identifier.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/geonet/pending_packet.hpp>
|
||||
#include <vanetza/geonet/pdu_variant.hpp>
|
||||
#include <boost/bimap/bimap.hpp>
|
||||
#include <boost/bimap/multiset_of.hpp>
|
||||
#include <boost/bimap/unordered_set_of.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
// forward declarations
|
||||
namespace vanetza
|
||||
{
|
||||
class Runtime;
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
class Address;
|
||||
class CbfCounter;
|
||||
class CbfPacket;
|
||||
class Lifetime;
|
||||
|
||||
/**
|
||||
* CbfPacket enables handling of conventional packets in a CBF packet buffer.
|
||||
* It contains a GeoBroadcast PDU and the network layer payload.
|
||||
*/
|
||||
class CbfPacket
|
||||
{
|
||||
public:
|
||||
CbfPacket(PendingPacket<GbcPdu>&&, const MacAddress& sender);
|
||||
CbfPacket(PendingPacket<GbcPdu, const MacAddress&>&&, const MacAddress& sender);
|
||||
|
||||
CbfPacket(CbfPacket&&) = default;
|
||||
CbfPacket& operator=(CbfPacket&&) = default;
|
||||
|
||||
/**
|
||||
* Get sender of buffered packet
|
||||
* \return sender's link-layer address
|
||||
*/
|
||||
const MacAddress& sender() const;
|
||||
|
||||
/**
|
||||
* Get source address of buffered packet
|
||||
* \return source address
|
||||
*/
|
||||
const Address& source() const;
|
||||
|
||||
/**
|
||||
* Get sequence number of buffered packet
|
||||
* \return sequence number
|
||||
*/
|
||||
SequenceNumber sequence_number() const;
|
||||
|
||||
/**
|
||||
* Reduce lifetime of buffered packet
|
||||
* \param d reduce lifetime by this duration
|
||||
* \return remaining lifetime
|
||||
*/
|
||||
Clock::duration reduce_lifetime(Clock::duration d);
|
||||
|
||||
/**
|
||||
* Length of packet data in bytes (PDU including payload)
|
||||
* \return size of packet on wire
|
||||
*/
|
||||
std::size_t length() const;
|
||||
|
||||
PendingPacket<GbcPdu> packet() && { return std::move(m_packet); }
|
||||
|
||||
private:
|
||||
PendingPacket<GbcPdu> m_packet;
|
||||
MacAddress m_sender;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* CbfPacketBuffer facilitates implementation of contention based forwarding
|
||||
*/
|
||||
class CbfPacketBuffer
|
||||
{
|
||||
public:
|
||||
using TimerCallback = std::function<void(PendingPacket<GbcPdu>&&)>;
|
||||
using Identifier = CbfPacketIdentifier;
|
||||
|
||||
/**
|
||||
* Create CBF packet buffer with bounded capacity
|
||||
* \param rt Runtime instance used for internal timers
|
||||
* \param cb Callback invoked for each packet on expiry
|
||||
* \param cnt CBF counter implementation
|
||||
* \param bytes Buffer can hold at most this number of bytes
|
||||
*/
|
||||
CbfPacketBuffer(Runtime& rt, TimerCallback cb, std::unique_ptr<CbfCounter> cnt, std::size_t bytes);
|
||||
~CbfPacketBuffer();
|
||||
|
||||
/**
|
||||
* Enqueue a packet and start an associated timer expiring after timeout
|
||||
* \param packet Buffer this packet
|
||||
* \param timeout CBF timer expiration for this packet
|
||||
*/
|
||||
void add(CbfPacket&& packet, Clock::duration timeout);
|
||||
|
||||
/**
|
||||
* Try to remove a packet from buffer.
|
||||
* \param id packet identification
|
||||
* \return true if packet existed before removal
|
||||
*/
|
||||
bool remove(const Identifier& id);
|
||||
|
||||
/**
|
||||
* Update associated packet timer
|
||||
* \param id packet identification
|
||||
* \param timeout CBF timer expiration
|
||||
*/
|
||||
void update(const Identifier& id, Clock::duration timeout);
|
||||
|
||||
/**
|
||||
* Fetch a packet from buffer.
|
||||
*
|
||||
* Associated timer is automatically stopped and packet removed from buffer.
|
||||
* Packet lifetime is reduced by queueing time at return.
|
||||
*
|
||||
* \param id packet identification
|
||||
* \return packet if found in buffer
|
||||
*/
|
||||
boost::optional<CbfPacket> fetch(const Identifier& id);
|
||||
|
||||
/**
|
||||
* Find packet in buffer.
|
||||
* \param id packet identification
|
||||
* \return read-only pointer to packet, nullptr if not found
|
||||
*/
|
||||
const CbfPacket* find(const Identifier& id) const;
|
||||
|
||||
/**
|
||||
* Get counter associated with given packet
|
||||
* \note packet counter is incremented at each timer update
|
||||
* \param id packet identification
|
||||
* \return 0 if packet has never been buffered before
|
||||
*/
|
||||
std::size_t counter(const Identifier& packet) const;
|
||||
|
||||
private:
|
||||
|
||||
struct Timer
|
||||
{
|
||||
Timer(const Runtime&, Clock::duration timeout);
|
||||
Timer(const Timer&) = default;
|
||||
Timer& operator=(const Timer&) = default;
|
||||
bool operator<(const Timer&) const;
|
||||
|
||||
Clock::time_point expiry;
|
||||
Clock::time_point start;
|
||||
};
|
||||
|
||||
using timer_bimap = boost::bimaps::bimap<
|
||||
boost::bimaps::multiset_of<Timer>,
|
||||
boost::bimaps::unordered_set_of<Identifier, std::hash<Identifier>>,
|
||||
boost::bimaps::with_info<std::list<CbfPacket>::iterator>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Flush all expired packets
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Remove timer from map and reschedule timer event if necessary
|
||||
*/
|
||||
void remove_timer(typename timer_bimap::left_map::iterator);
|
||||
|
||||
/**
|
||||
* Schedule next timer event at runtime
|
||||
*/
|
||||
void schedule_timer();
|
||||
|
||||
/**
|
||||
* Reduce packet lifetime by queueing time
|
||||
* \param timer contains queueing start time
|
||||
* \param packet associated packet
|
||||
* \return true if packet remains valid, false if end of lifetime is reached
|
||||
*/
|
||||
bool reduce_lifetime(const Timer&, CbfPacket&) const;
|
||||
|
||||
std::list<CbfPacket> m_packets;
|
||||
timer_bimap m_timers;
|
||||
Runtime& m_runtime;
|
||||
std::unique_ptr<CbfCounter> m_counter;
|
||||
const std::size_t m_capacity_bytes;
|
||||
std::size_t m_stored_bytes;
|
||||
TimerCallback m_timer_callback;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* CBF_PACKET_BUFFER_HPP_MU3RK5V1 */
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#include <vanetza/geonet/cbf_packet_identifier.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_buffer.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
CbfPacketIdentifier identifier(const CbfPacket& packet)
|
||||
{
|
||||
return identifier(packet.source(), packet.sequence_number());
|
||||
}
|
||||
|
||||
CbfPacketIdentifier identifier(const Address& source, SequenceNumber sn)
|
||||
{
|
||||
return std::make_tuple(source, sn);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
size_t hash<vanetza::geonet::CbfPacketIdentifier>::operator()(const vanetza::geonet::CbfPacketIdentifier& id) const
|
||||
{
|
||||
using vanetza::geonet::Address;
|
||||
using vanetza::geonet::SequenceNumber;
|
||||
static_assert(tuple_size<vanetza::geonet::CbfPacketIdentifier>::value == 2, "Unexpected identifier tuple");
|
||||
|
||||
std::size_t seed = 0;
|
||||
const Address& source = get<0>(id);
|
||||
boost::hash_combine(seed, std::hash<Address>()(source));
|
||||
const SequenceNumber& sn = get<1>(id);
|
||||
boost::hash_combine(seed, static_cast<SequenceNumber::value_type>(sn));
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#ifndef CBF_PACKET_IDENTIFIER_HPP_HC6PLCML
|
||||
#define CBF_PACKET_IDENTIFIER_HPP_HC6PLCML
|
||||
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class CbfPacket;
|
||||
|
||||
using CbfPacketIdentifier = std::tuple<Address, SequenceNumber>;
|
||||
CbfPacketIdentifier identifier(const CbfPacket&);
|
||||
CbfPacketIdentifier identifier(const Address&, SequenceNumber);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// std::hash specialization for CbfPacketIdentifier
|
||||
template<> struct hash<vanetza::geonet::CbfPacketIdentifier>
|
||||
{
|
||||
size_t operator()(const vanetza::geonet::CbfPacketIdentifier&) const;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#endif /* CBF_PACKET_IDENTIFIER_HPP_HC6PLCML */
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <vanetza/geonet/cbr_aggregator.hpp>
|
||||
#include <vanetza/geonet/location_table.hpp>
|
||||
#include <vanetza/geonet/loctex_g5.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct CbrAggregatorUnit
|
||||
{
|
||||
CbrAggregatorUnit() : n(0) {}
|
||||
|
||||
void operator+=(dcc::ChannelLoad cbr)
|
||||
{
|
||||
// calculate average as double so numerator can be larger than 1.0 temporarily
|
||||
average = dcc::ChannelLoad {(average.value() * n + cbr.value()) / (n + 1)};
|
||||
++n;
|
||||
|
||||
// >= comparison: second largest value might equal largest value
|
||||
if (cbr >= maximum[0]) {
|
||||
maximum[1] = maximum[0];
|
||||
maximum[0] = cbr;
|
||||
} else if (cbr > maximum[1]) {
|
||||
maximum[1] = cbr;
|
||||
}
|
||||
}
|
||||
|
||||
dcc::ChannelLoad operator()(const dcc::ChannelLoad target) const
|
||||
{
|
||||
if (average > target) {
|
||||
return maximum[0];
|
||||
} else {
|
||||
return maximum[1];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned n;
|
||||
dcc::ChannelLoad average;
|
||||
std::array<dcc::ChannelLoad, 2> maximum;
|
||||
};
|
||||
|
||||
CbrAggregator::CbrAggregator() :
|
||||
m_one_hop_cbr(0.0), m_two_hop_cbr(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
void CbrAggregator::aggregate(ChannelLoad local, const LocationTable& lt, Timestamp lifetime, ChannelLoad target)
|
||||
{
|
||||
m_local_cbr[1] = m_local_cbr[0];
|
||||
m_local_cbr[0] = local;
|
||||
|
||||
CbrAggregatorUnit one_hop;
|
||||
CbrAggregatorUnit two_hop;
|
||||
|
||||
LocationTable::entry_visitor entry_visitor =
|
||||
[&](const MacAddress&, const LocationTableEntry& entry) {
|
||||
const LocTEX_G5* loctex = entry.extensions.find<LocTEX_G5>();
|
||||
if (loctex && loctex->local_update >= lifetime) {
|
||||
one_hop += loctex->dcc_mco.local_cbr();
|
||||
two_hop += loctex->dcc_mco.neighbour_cbr();
|
||||
}
|
||||
};
|
||||
lt.visit(entry_visitor);
|
||||
|
||||
m_one_hop_cbr = one_hop(target);
|
||||
m_two_hop_cbr = two_hop(target);
|
||||
m_global_cbr = std::max({ m_local_cbr[1], m_one_hop_cbr, m_two_hop_cbr});
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef CBR_AGGREGATOR_HPP_VUGJW6BW
|
||||
#define CBR_AGGREGATOR_HPP_VUGJW6BW
|
||||
|
||||
#include <vanetza/dcc/channel_load.hpp>
|
||||
#include <vanetza/geonet/timestamp.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class LocationTable;
|
||||
|
||||
|
||||
/**
|
||||
* CbrAggregator realises the CBR_G aggregation, originally specified by TS 102 636-4-2 V1.1.1
|
||||
* section 5.2.2 and carried forward by TS 103 836-4-2 V2.1.1 clause 5.3 (Release 2; the
|
||||
* five-step algorithm here, including using the previous cycle's local CBR in the final
|
||||
* max(), matches the current text exactly).
|
||||
*
|
||||
* Since this algorithm relies mainly on location table entries it is placed in the geonet module.
|
||||
*/
|
||||
class CbrAggregator
|
||||
{
|
||||
public:
|
||||
using ChannelLoad = dcc::ChannelLoad;
|
||||
|
||||
CbrAggregator();
|
||||
|
||||
/**
|
||||
* Get local channel busy ratio, i.e. CBR_L_0_Hop
|
||||
* \return CBR
|
||||
*/
|
||||
ChannelLoad get_local_cbr() const { return m_local_cbr[0]; }
|
||||
|
||||
/**
|
||||
* Get one-hop channel busy ratio, i.e. CBR_L_1_Hop
|
||||
* \return CBR
|
||||
*/
|
||||
ChannelLoad get_one_hop_cbr() const { return m_one_hop_cbr; }
|
||||
|
||||
/**
|
||||
* Get two-hop channel busy ratio, i.e. CBR_L_2_Hop
|
||||
* \return CBR
|
||||
*/
|
||||
ChannelLoad get_two_hop_cbr() const { return m_two_hop_cbr; }
|
||||
|
||||
/**
|
||||
* Get global channel busy ratio
|
||||
* \return CBR
|
||||
*/
|
||||
ChannelLoad get_global_cbr() const { return m_global_cbr; }
|
||||
|
||||
/**
|
||||
* Aggregate {1,2}-hop CBRs from received CBR values stored in location table
|
||||
* \parame local most recent local CBR measurement CBR_L_0_Hop
|
||||
* \param lt location table containing LocTEX_G5 entries
|
||||
* \param cbr_lifetime reject entries older than T_cbr
|
||||
* \param cbr_target reference value
|
||||
*/
|
||||
void aggregate(ChannelLoad cbr_local, const LocationTable& lt, Timestamp cbr_lifetime, ChannelLoad cbr_target);
|
||||
|
||||
private:
|
||||
std::array<ChannelLoad, 2> m_local_cbr;
|
||||
ChannelLoad m_one_hop_cbr;
|
||||
ChannelLoad m_two_hop_cbr;
|
||||
ChannelLoad m_global_cbr;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* CBR_AGGREGATOR_HPP_VUGJW6BW */
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "common_header.hpp"
|
||||
#include "data_request.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t CommonHeader::length_bytes;
|
||||
|
||||
CommonHeader::CommonHeader() :
|
||||
next_header(NextHeaderCommon::Any),
|
||||
reserved1(0),
|
||||
header_type(HeaderType::Any),
|
||||
flags(0),
|
||||
payload(0),
|
||||
maximum_hop_limit(0),
|
||||
reserved2(0)
|
||||
{
|
||||
}
|
||||
|
||||
CommonHeader::CommonHeader(const MIB& mib) :
|
||||
next_header(NextHeaderCommon::Any),
|
||||
reserved1(0),
|
||||
header_type(HeaderType::Any),
|
||||
traffic_class(mib.itsGnDefaultTrafficClass),
|
||||
flags(mib.itsGnIsMobile ? 0x80 : 0x00),
|
||||
payload(0),
|
||||
maximum_hop_limit(mib.itsGnDefaultHopLimit),
|
||||
reserved2(0)
|
||||
{
|
||||
}
|
||||
|
||||
CommonHeader::CommonHeader(const DataRequest& request, const MIB& mib) :
|
||||
CommonHeader(mib)
|
||||
{
|
||||
switch (request.upper_protocol) {
|
||||
case UpperProtocol::BTP_A:
|
||||
next_header = NextHeaderCommon::BTP_A;
|
||||
break;
|
||||
case UpperProtocol::BTP_B:
|
||||
next_header = NextHeaderCommon::BTP_B;
|
||||
break;
|
||||
case UpperProtocol::IPv6:
|
||||
next_header = NextHeaderCommon::IPv6;
|
||||
break;
|
||||
case UpperProtocol::Unknown:
|
||||
// No upper-layer header: e.g. a raw GN-DATA.request SDU (TS 103 836-4-1
|
||||
// clause 9.3 N-SAP), which carries no BTP/IPv6 framing to declare.
|
||||
next_header = NextHeaderCommon::Any;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unhandled upper protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
traffic_class = request.traffic_class;
|
||||
maximum_hop_limit = request.max_hop_limit;
|
||||
}
|
||||
|
||||
CommonHeader::CommonHeader(const ShbDataRequest& request, const MIB& mib) :
|
||||
CommonHeader(static_cast<const DataRequest&>(request), mib)
|
||||
{
|
||||
header_type = HeaderType::TSB_Single_Hop;
|
||||
maximum_hop_limit = 1;
|
||||
}
|
||||
|
||||
void serialize(const CommonHeader& hdr, OutputArchive& ar)
|
||||
{
|
||||
uint8_t nextHeaderAndReserved = static_cast<uint8_t>(hdr.next_header);
|
||||
nextHeaderAndReserved <<= 4;
|
||||
nextHeaderAndReserved |= hdr.reserved1.raw();
|
||||
serialize(host_cast(nextHeaderAndReserved), ar);
|
||||
serialize(host_cast(static_cast<std::underlying_type<HeaderType>::type>(hdr.header_type)), ar);
|
||||
serialize(hdr.traffic_class, ar);
|
||||
serialize(host_cast(hdr.flags), ar);
|
||||
serialize(host_cast(hdr.payload), ar);
|
||||
serialize(host_cast(hdr.maximum_hop_limit), ar);
|
||||
serialize(host_cast(hdr.reserved2), ar);
|
||||
}
|
||||
|
||||
void deserialize(CommonHeader& hdr, InputArchive& ar)
|
||||
{
|
||||
uint8_t nextHeaderAndReserved;
|
||||
deserialize(nextHeaderAndReserved, ar);
|
||||
hdr.next_header = static_cast<NextHeaderCommon>(nextHeaderAndReserved >> 4);
|
||||
hdr.reserved1 = nextHeaderAndReserved & 0x0f;
|
||||
typename std::underlying_type<HeaderType>::type headerType;
|
||||
deserialize(headerType, ar);
|
||||
hdr.header_type = static_cast<HeaderType>(headerType);
|
||||
deserialize(hdr.traffic_class, ar);
|
||||
deserialize(hdr.flags, ar);
|
||||
deserialize(hdr.payload, ar);
|
||||
deserialize(hdr.maximum_hop_limit, ar);
|
||||
deserialize(hdr.reserved2, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef COMMON_HEADER_HPP_SEFIWCT4
|
||||
#define COMMON_HEADER_HPP_SEFIWCT4
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/geonet/header_type.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/geonet/traffic_class.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct Area;
|
||||
struct DataRequest;
|
||||
struct ShbDataRequest;
|
||||
class LocationTable;
|
||||
|
||||
enum class NextHeaderCommon
|
||||
{
|
||||
Any = 0, BTP_A = 1, BTP_B = 2, IPv6 = 3
|
||||
};
|
||||
|
||||
struct CommonHeader
|
||||
{
|
||||
public:
|
||||
CommonHeader();
|
||||
CommonHeader(const MIB&);
|
||||
CommonHeader(const DataRequest&, const MIB&);
|
||||
CommonHeader(const ShbDataRequest&, const MIB&);
|
||||
|
||||
static constexpr std::size_t length_bytes = 8;
|
||||
|
||||
NextHeaderCommon next_header; // 4 bit
|
||||
BitNumber<unsigned, 4> reserved1;
|
||||
HeaderType header_type;
|
||||
TrafficClass traffic_class;
|
||||
uint8_t flags; // Bit 0: itsGnIsMobile
|
||||
uint16_t payload; // number of octets following whole GeoNet header
|
||||
uint8_t maximum_hop_limit;
|
||||
uint8_t reserved2;
|
||||
};
|
||||
|
||||
void serialize(const CommonHeader&, OutputArchive&);
|
||||
void deserialize(CommonHeader&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* COMMON_HEADER_HPP_SEFIWCT4 */
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "data_confirm.hpp"
|
||||
#include "data_request.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
DataConfirm& operator ^=(DataConfirm& lhs, DataConfirm::ResultCode rhs)
|
||||
{
|
||||
if (rhs != DataConfirm::ResultCode::Accepted) {
|
||||
lhs.result_code = rhs;
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DataConfirm::ResultCode validate_data_request(const DataRequest& req, const MIB& mib)
|
||||
{
|
||||
DataConfirm::ResultCode result = DataConfirm::ResultCode::Rejected_Unspecified;
|
||||
|
||||
// TODO: traffic class validation
|
||||
if (req.maximum_lifetime > mib.itsGnMaxPacketLifetime) {
|
||||
result = DataConfirm::ResultCode::Rejected_Max_Lifetime;
|
||||
} else if (req.repetition && req.repetition->interval < mib.itsGnMinPacketRepetitionInterval) {
|
||||
result = DataConfirm::ResultCode::Rejected_Min_Repetition_Interval;
|
||||
} else {
|
||||
result = DataConfirm::ResultCode::Accepted;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DataConfirm::ResultCode validate_data_request(const DataRequestWithArea& req, const MIB& mib)
|
||||
{
|
||||
if (area_size(req.destination) > mib.itsGnMaxGeoAreaSize) {
|
||||
return DataConfirm::ResultCode::Rejected_Max_Geo_Area_Size;
|
||||
} else {
|
||||
return validate_data_request(static_cast<const DataRequest&>(req), mib);
|
||||
}
|
||||
}
|
||||
|
||||
DataConfirm::ResultCode validate_payload(const std::unique_ptr<DownPacket>& payload, const MIB& mib)
|
||||
{
|
||||
DataConfirm::ResultCode result = DataConfirm::ResultCode::Rejected_Unspecified;
|
||||
|
||||
if (!payload) {
|
||||
// leave code to unspecified
|
||||
} else if (payload->size() > mib.itsGnMaxSduSize) {
|
||||
result = DataConfirm::ResultCode::Rejected_Max_SDU_Size;
|
||||
} else {
|
||||
result = DataConfirm::ResultCode::Accepted;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef DATA_CONFIRM_HPP_Z1WCMN8T
|
||||
#define DATA_CONFIRM_HPP_Z1WCMN8T
|
||||
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct DataRequest;
|
||||
struct DataRequestWithArea;
|
||||
|
||||
struct DataConfirm
|
||||
{
|
||||
enum class ResultCode {
|
||||
Accepted,
|
||||
Rejected_Max_SDU_Size,
|
||||
Rejected_Max_Lifetime,
|
||||
Rejected_Min_Repetition_Interval,
|
||||
Rejected_Unsupported_Traffic_Class,
|
||||
Rejected_Max_Geo_Area_Size,
|
||||
Rejected_Unspecified
|
||||
};
|
||||
|
||||
DataConfirm() : result_code(ResultCode::Accepted) {}
|
||||
DataConfirm(ResultCode code) : result_code(code) {}
|
||||
bool accepted() const { return result_code == ResultCode::Accepted; }
|
||||
bool rejected() const { return !accepted(); }
|
||||
ResultCode result_code;
|
||||
};
|
||||
|
||||
/**
|
||||
* XOR result code with DataConfirm's result code.
|
||||
* Replaces result code of DataConfirm only if new code is an error code.
|
||||
* \param lhs Operate on this DataConfirm
|
||||
* \param rhs XOR this ResultCode with lhs
|
||||
* \return reference to modified DataConfirm
|
||||
*/
|
||||
DataConfirm& operator ^=(DataConfirm& lhs, DataConfirm::ResultCode rhs);
|
||||
|
||||
DataConfirm::ResultCode validate_data_request(const DataRequest&, const MIB&);
|
||||
DataConfirm::ResultCode validate_data_request(const DataRequestWithArea&, const MIB&);
|
||||
DataConfirm::ResultCode validate_payload(const std::unique_ptr<DownPacket>&, const MIB&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DATA_CONFIRM_HPP_Z1WCMN8T */
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef DATA_INDICATION_HPP_DOJK9Q8T
|
||||
#define DATA_INDICATION_HPP_DOJK9Q8T
|
||||
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/its_aid.hpp>
|
||||
#include <vanetza/geonet/destination_variant.hpp>
|
||||
#include <vanetza/geonet/interface.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/security/decap_service.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* \brief GN-DATA.indication
|
||||
* \see EN 302 636-4-1 V1.4.1, section J.4
|
||||
*/
|
||||
struct DataIndication
|
||||
{
|
||||
UpperProtocol upper_protocol;
|
||||
TransportType transport_type;
|
||||
DestinationVariant destination;
|
||||
ShortPositionVector source_position;
|
||||
security::DecapReport security_report;
|
||||
boost::optional<ItsAid> its_aid;
|
||||
boost::optional<ByteBuffer> permissions;
|
||||
// TS 102 723-8 V2.0.0 clause 5 incorporates V1.1.1 Table 27:
|
||||
// preserve the optional SN-DECAP certificate_id through NF-SAP.
|
||||
boost::optional<security::HashedId8> certificate_id;
|
||||
TrafficClass traffic_class;
|
||||
boost::optional<Lifetime> remaining_packet_lifetime;
|
||||
boost::optional<unsigned> remaining_hop_limit;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DATA_INDICATION_HPP_DOJK9Q8T */
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "data_request.hpp"
|
||||
#include <vanetza/btp/data_request.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <boost/units/cmath.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
void decrement_by_one(DataRequest::Repetition& repetition)
|
||||
{
|
||||
const auto zero = 0.0 * units::si::seconds;
|
||||
if (repetition.maximum > zero && repetition.interval > zero
|
||||
&& repetition.maximum > repetition.interval) {
|
||||
repetition.maximum = repetition.maximum - repetition.interval;
|
||||
|
||||
} else {
|
||||
repetition.maximum = zero;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_further_repetition(const DataRequest& request)
|
||||
{
|
||||
bool repeat = false;
|
||||
if (request.repetition) {
|
||||
repeat = has_further_repetition(request.repetition.get());
|
||||
}
|
||||
return repeat;
|
||||
}
|
||||
|
||||
bool has_further_repetition(const DataRequest::Repetition& repetition)
|
||||
{
|
||||
const auto zero = 0.0 * units::si::seconds;
|
||||
return repetition.maximum > zero && repetition.interval > zero &&
|
||||
repetition.maximum >= repetition.interval;
|
||||
}
|
||||
|
||||
struct access_request_visitor : public boost::static_visitor<DataRequest&>
|
||||
{
|
||||
template<typename REQUEST>
|
||||
DataRequest& operator()(REQUEST& request)
|
||||
{
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
DataRequest& access_request(DataRequestVariant& variant)
|
||||
{
|
||||
access_request_visitor visitor;
|
||||
return boost::apply_visitor(visitor, variant);
|
||||
}
|
||||
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequest& gn)
|
||||
{
|
||||
gn.upper_protocol = geonet::UpperProtocol::BTP_B;
|
||||
gn.communication_profile = btp.gn.communication_profile;
|
||||
gn.its_aid = btp.gn.its_aid;
|
||||
if (btp.gn.maximum_lifetime) {
|
||||
gn.maximum_lifetime = *btp.gn.maximum_lifetime;
|
||||
}
|
||||
gn.repetition = btp.gn.repetition;
|
||||
if (btp.gn.maximum_hop_limit) {
|
||||
gn.max_hop_limit = *btp.gn.maximum_hop_limit;
|
||||
}
|
||||
gn.traffic_class = btp.gn.traffic_class;
|
||||
}
|
||||
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequestWithAddress& gn)
|
||||
{
|
||||
copy_request_parameters(btp, static_cast<DataRequest&>(gn));
|
||||
const Address* address = boost::get<Address>(&btp.gn.destination);
|
||||
if (address) {
|
||||
gn.destination = *address;
|
||||
} else {
|
||||
throw std::runtime_error("BTP-B data request lacks destination address");
|
||||
}
|
||||
}
|
||||
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequestWithArea& gn)
|
||||
{
|
||||
copy_request_parameters(btp, static_cast<DataRequest&>(gn));
|
||||
const Area* area = boost::get<Area>(&btp.gn.destination);
|
||||
if (area) {
|
||||
gn.destination = *area;
|
||||
} else {
|
||||
throw std::runtime_error("BTP-B data request lacks destination area");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef DATA_REQUEST_HPP_3JYISVXB
|
||||
#define DATA_REQUEST_HPP_3JYISVXB
|
||||
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/its_aid.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/areas.hpp>
|
||||
#include <vanetza/geonet/interface.hpp>
|
||||
#include <vanetza/geonet/lifetime.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/traffic_class.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
|
||||
// forward declaration
|
||||
namespace btp { struct DataRequestB; }
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct DataRequest
|
||||
{
|
||||
DataRequest(const MIB& mib, ItsAid its_aid = 0) :
|
||||
upper_protocol(UpperProtocol::BTP_A),
|
||||
communication_profile(CommunicationProfile::Unspecified),
|
||||
its_aid(its_aid),
|
||||
maximum_lifetime(mib.itsGnDefaultPacketLifetime),
|
||||
max_hop_limit(mib.itsGnDefaultHopLimit),
|
||||
traffic_class(mib.itsGnDefaultTrafficClass)
|
||||
{}
|
||||
|
||||
struct Repetition
|
||||
{
|
||||
units::Duration interval;
|
||||
units::Duration maximum;
|
||||
};
|
||||
|
||||
UpperProtocol upper_protocol;
|
||||
CommunicationProfile communication_profile;
|
||||
ItsAid its_aid;
|
||||
ByteBuffer permissions;
|
||||
// Security context information (TS 103 836-4-1 V2.2.1 Annex J.2), passed to SN-ENCAP.request
|
||||
ByteBuffer security_context;
|
||||
Lifetime maximum_lifetime;
|
||||
boost::optional<Repetition> repetition;
|
||||
unsigned max_hop_limit;
|
||||
TrafficClass traffic_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Decrement maximum repetition by one interval
|
||||
* \param repetition Repetition data structure
|
||||
*/
|
||||
void decrement_by_one(DataRequest::Repetition& repetition);
|
||||
|
||||
/**
|
||||
* \brief Test if request has to be repeated at least once more
|
||||
* \param request DataRequest
|
||||
* \return true if there is at least one repetition left
|
||||
*/
|
||||
bool has_further_repetition(const DataRequest&);
|
||||
|
||||
/**
|
||||
* \brief Test if at least one repetition is outstanding
|
||||
* \param repetition
|
||||
* \return true if there is at least one repetition left
|
||||
*/
|
||||
bool has_further_repetition(const DataRequest::Repetition&);
|
||||
|
||||
struct DataRequestWithAddress : public DataRequest
|
||||
{
|
||||
using DataRequest::DataRequest;
|
||||
Address destination;
|
||||
};
|
||||
|
||||
struct DataRequestWithArea : public DataRequest
|
||||
{
|
||||
using DataRequest::DataRequest;
|
||||
Area destination;
|
||||
};
|
||||
|
||||
struct GucDataRequest : public DataRequestWithAddress
|
||||
{
|
||||
using DataRequestWithAddress::DataRequestWithAddress;
|
||||
};
|
||||
|
||||
struct GbcDataRequest : public DataRequestWithArea
|
||||
{
|
||||
using DataRequestWithArea::DataRequestWithArea;
|
||||
};
|
||||
|
||||
struct GacDataRequest : public DataRequestWithArea
|
||||
{
|
||||
using DataRequestWithArea::DataRequestWithArea;
|
||||
};
|
||||
|
||||
struct ShbDataRequest : public DataRequest
|
||||
{
|
||||
using DataRequest::DataRequest;
|
||||
};
|
||||
|
||||
struct TsbDataRequest : public DataRequest
|
||||
{
|
||||
using DataRequest::DataRequest;
|
||||
};
|
||||
|
||||
using DataRequestVariant =
|
||||
boost::variant<
|
||||
GucDataRequest,
|
||||
GbcDataRequest,
|
||||
GacDataRequest,
|
||||
ShbDataRequest,
|
||||
TsbDataRequest
|
||||
>;
|
||||
|
||||
/**
|
||||
* Get access to common base data request class of all variants
|
||||
* \param variant DataRequestVariant object
|
||||
* \return reference to underlying DataRequest
|
||||
*/
|
||||
DataRequest& access_request(DataRequestVariant&);
|
||||
|
||||
/**
|
||||
* Copy request parameters from BTP-B request
|
||||
* \param btp BTP-B data request
|
||||
* \param gn GeoNet destination request
|
||||
*/
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequest& gn);
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequestWithAddress& gn);
|
||||
void copy_request_parameters(const btp::DataRequestB& btp, DataRequestWithArea& gn);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DATA_REQUEST_HPP_3JYISVXB */
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <vanetza/geonet/dcc_field.hpp>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct dcc_mco_extractor : boost::static_visitor< boost::optional<DccMcoField> >
|
||||
{
|
||||
using return_type = boost::optional<DccMcoField>;
|
||||
|
||||
return_type operator()(const DccMcoField& mco) const
|
||||
{
|
||||
return mco;
|
||||
}
|
||||
|
||||
return_type operator()(uint32_t raw) const
|
||||
{
|
||||
return DccMcoField { raw };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
return_type operator()(const T&)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
|
||||
boost::optional<DccMcoField> get_dcc_mco(const DccField& field)
|
||||
{
|
||||
return boost::apply_visitor(dcc_mco_extractor(), field);
|
||||
}
|
||||
|
||||
void serialize(const DccField& field, OutputArchive& ar)
|
||||
{
|
||||
struct serialize_visitor : boost::static_visitor<uint32_t>
|
||||
{
|
||||
uint32_t operator()(const DccMcoField& mco) const { return static_cast<uint32_t>(mco); }
|
||||
uint32_t operator()(const uint32_t& raw) const { return raw; }
|
||||
};
|
||||
|
||||
uint32_t raw = boost::apply_visitor(serialize_visitor(), field);
|
||||
serialize(host_cast(raw), ar);
|
||||
}
|
||||
|
||||
void deserialize(DccField& field, InputArchive& ar)
|
||||
{
|
||||
uint32_t raw;
|
||||
deserialize(raw, ar);
|
||||
field = raw;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef DCC_FIELD_HPP_EPTBYHAU
|
||||
#define DCC_FIELD_HPP_EPTBYHAU
|
||||
|
||||
#include <vanetza/geonet/dcc_mco_field.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/variant/variant.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* DccField represents the supported variants for the DCC field in SHB headers.
|
||||
*
|
||||
* \note Enclosed types are using "host byte order".
|
||||
* Byte order conversion is handled by serialize and deserialize functions.
|
||||
*/
|
||||
using DccField = boost::variant<DccMcoField, uint32_t>;
|
||||
|
||||
boost::optional<DccMcoField> get_dcc_mco(const DccField&);
|
||||
|
||||
void serialize(const DccField&, OutputArchive&);
|
||||
void deserialize(DccField&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DCC_FIELD_HPP_EPTBYHAU */
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef DCC_FIELD_GENERATOR_HPP_FVGALNWN
|
||||
#define DCC_FIELD_GENERATOR_HPP_FVGALNWN
|
||||
|
||||
#include <vanetza/geonet/dcc_field.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class DccFieldGenerator
|
||||
{
|
||||
public:
|
||||
virtual DccField generate_dcc_field() = 0;
|
||||
|
||||
virtual ~DccFieldGenerator() = default;
|
||||
};
|
||||
|
||||
class NullDccFieldGenerator : public DccFieldGenerator
|
||||
{
|
||||
public:
|
||||
DccField generate_dcc_field() override { return static_cast<uint32_t>(0); }
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DCC_DCC_FIELD_GENERATOR_HPP_FVGALNWN */
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <vanetza/geonet/dcc_information_sharing.hpp>
|
||||
#include <vanetza/geonet/location_table.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
DccInformationSharing::DccInformationSharing(Runtime& rt, const LocationTable& lt, dcc::ChannelLoad target, UnitInterval delay) :
|
||||
m_runtime(rt), m_location_table(lt), m_cbr_target(target), m_tx_power(0),
|
||||
m_trigger_interval(std::chrono::milliseconds(100)),
|
||||
m_last_aggregation(m_runtime.now()),
|
||||
on_global_cbr_update(m_update_hook)
|
||||
{
|
||||
Clock::duration initial = m_trigger_interval;
|
||||
initial *= delay.value();
|
||||
m_runtime.schedule(initial, [this](const Clock::time_point&) { trigger(); });
|
||||
}
|
||||
|
||||
DccInformationSharing::DccInformationSharing(Runtime& rt, const LocationTable& lt, dcc::ChannelLoad target) :
|
||||
DccInformationSharing(rt, lt, target, UnitInterval { 0.0 })
|
||||
{
|
||||
}
|
||||
|
||||
DccField DccInformationSharing::generate_dcc_field()
|
||||
{
|
||||
DccMcoField dcc_mco;
|
||||
dcc_mco.local_cbr(m_aggregator.get_local_cbr());
|
||||
dcc_mco.neighbour_cbr(m_aggregator.get_one_hop_cbr());
|
||||
dcc_mco.output_power(m_tx_power);
|
||||
return dcc_mco;
|
||||
}
|
||||
|
||||
void DccInformationSharing::update_local_cbr(dcc::ChannelLoad local_cbr)
|
||||
{
|
||||
m_cbr_local = local_cbr;
|
||||
}
|
||||
|
||||
void DccInformationSharing::set_tx_power(unsigned tx_power)
|
||||
{
|
||||
m_tx_power = tx_power;
|
||||
}
|
||||
|
||||
void DccInformationSharing::trigger()
|
||||
{
|
||||
m_aggregator.aggregate(m_cbr_local, m_location_table, m_last_aggregation, m_cbr_target);
|
||||
m_last_aggregation = m_runtime.now();
|
||||
m_update_hook(static_cast<const CbrAggregator&>(m_aggregator));
|
||||
m_runtime.schedule(m_trigger_interval, [this](const Clock::time_point&) { trigger(); });
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#ifndef DCC_INFORMATION_SHARING_HPP_GZCSHZLD
|
||||
#define DCC_INFORMATION_SHARING_HPP_GZCSHZLD
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/common/hook.hpp>
|
||||
#include <vanetza/common/unit_interval.hpp>
|
||||
#include <vanetza/geonet/cbr_aggregator.hpp>
|
||||
#include <vanetza/geonet/dcc_field_generator.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
|
||||
class Runtime;
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class LocationTable;
|
||||
|
||||
/**
|
||||
* DccInformationSharing realises the DCC_NET behaviour for ITS-G5, originally specified by
|
||||
* TS 102 636-4-2 V1.1.1 and carried forward by TS 103 836-4-2 V2.1.1 clauses 5.3-5.4
|
||||
* (Release 2; the CBR_G algorithm and trigger cadence implemented here match the current
|
||||
* text step-for-step). CBR_target corresponds to the Release-2 protocol constant
|
||||
* itsGNCBRTarget (TS 103 836-4-2 V2.1.1 Annex A: 0,62), which supersedes the value this
|
||||
* class's original author had to guess at from TS 102 687's undefined NDL_maxChannelUse.
|
||||
*/
|
||||
class DccInformationSharing : public DccFieldGenerator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create DCC_net instance
|
||||
*
|
||||
* \param rt Runtime for scheduling periodic update cycles
|
||||
* \param lt Location Table with LocTEX_G5 entries
|
||||
* \param target CBR_target value (usually NDL_maxChannelLoad)
|
||||
* \param delay Delaying first update cycle randomly
|
||||
*
|
||||
* \note Set random delay interval when multiple stations are created at the same time!
|
||||
* Values shall be distributed uniformly across full integer range.
|
||||
*/
|
||||
DccInformationSharing(Runtime& rt, const LocationTable& lt, dcc::ChannelLoad target, UnitInterval delay);
|
||||
DccInformationSharing(Runtime& rt, const LocationTable& lt, dcc::ChannelLoad target);
|
||||
|
||||
DccField generate_dcc_field() override;
|
||||
|
||||
/**
|
||||
* Update local CBR measurement
|
||||
*
|
||||
* Local measurement rate is decoupled from processing in DCC_net,
|
||||
* i.e. DccInformationSharing buffers the given value and the latest
|
||||
* measurement value when its internal update cycle runs.
|
||||
*
|
||||
* \param cbr local CBR measurement
|
||||
*/
|
||||
void update_local_cbr(dcc::ChannelLoad cbr);
|
||||
|
||||
/**
|
||||
* Set packet TX power
|
||||
*
|
||||
* \param power Packet transmission output power in dBm
|
||||
*/
|
||||
void set_tx_power(unsigned power);
|
||||
|
||||
private:
|
||||
void trigger();
|
||||
|
||||
Runtime& m_runtime;
|
||||
const LocationTable& m_location_table;
|
||||
const dcc::ChannelLoad m_cbr_target;
|
||||
dcc::ChannelLoad m_cbr_local;
|
||||
unsigned m_tx_power;
|
||||
CbrAggregator m_aggregator;
|
||||
Clock::duration m_trigger_interval;
|
||||
Timestamp m_last_aggregation;
|
||||
Hook<const CbrAggregator&> m_update_hook;
|
||||
|
||||
public:
|
||||
/**
|
||||
* on_global_cbr_update is called at each update cycle,
|
||||
* i.e. when a new global CBR has been calculated
|
||||
*/
|
||||
HookRegistry<const CbrAggregator&> on_global_cbr_update;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DCC_INFORMATION_SHARING_HPP_GZCSHZLD */
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "dcc_mco_field.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
DccMcoField::DccMcoField() :
|
||||
m_cbr_l0_hop(0), m_cbr_l1_hop(0), m_output_power(0)
|
||||
{
|
||||
}
|
||||
|
||||
DccMcoField::DccMcoField(uint32_t field)
|
||||
{
|
||||
m_cbr_l0_hop = field >> 24;
|
||||
m_cbr_l1_hop = field >> 16;
|
||||
m_output_power = field >> 11;
|
||||
}
|
||||
|
||||
DccMcoField::operator uint32_t() const
|
||||
{
|
||||
uint32_t field = m_cbr_l0_hop;
|
||||
|
||||
field <<= 8;
|
||||
field |= m_cbr_l1_hop;
|
||||
|
||||
field <<= 5;
|
||||
field |= m_output_power.raw();
|
||||
|
||||
field <<= 11;
|
||||
return field;
|
||||
}
|
||||
|
||||
void DccMcoField::local_cbr(const ChannelLoad& cbr)
|
||||
{
|
||||
m_cbr_l0_hop = std::floor(cbr.value() * 255.0);
|
||||
}
|
||||
|
||||
dcc::ChannelLoad DccMcoField::local_cbr() const
|
||||
{
|
||||
return ChannelLoad(m_cbr_l0_hop / 255.0);
|
||||
}
|
||||
|
||||
void DccMcoField::neighbour_cbr(const ChannelLoad& cbr)
|
||||
{
|
||||
m_cbr_l1_hop = std::floor(cbr.value() * 255.0);
|
||||
}
|
||||
|
||||
dcc::ChannelLoad DccMcoField::neighbour_cbr() const
|
||||
{
|
||||
return ChannelLoad(m_cbr_l1_hop / 255.0);
|
||||
}
|
||||
|
||||
void DccMcoField::output_power(unsigned dbm)
|
||||
{
|
||||
m_output_power = std::min(dbm, 31u);
|
||||
}
|
||||
|
||||
unsigned DccMcoField::output_power() const
|
||||
{
|
||||
return m_output_power.raw();
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef DCC_MCO_FIELD_HPP_RLZ4PQMF
|
||||
#define DCC_MCO_FIELD_HPP_RLZ4PQMF
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/dcc/channel_load.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* DCC-MCO (Multi Channel Operations) was introduced by TS 102 636-4-2 V1.1.1 and is carried
|
||||
* forward unchanged by TS 103 836-4-2 V2.1.1 clause 6.3.3 Table 3 (Release 2): Octet 40
|
||||
* CBR_L_0_Hop = floor(CBR_L_0_Hop x 255), Octet 41 CBR_L_1_Hop likewise, Octet 42 bits 0-4 TX
|
||||
* power [0;31] dBm, Octet 43 reserved. This field layout was cross-checked bit-for-bit against
|
||||
* the current V2.1.1 text; only this docstring's citation was stale.
|
||||
* DccMcoField implements the SHB header field extension.
|
||||
*/
|
||||
class DccMcoField
|
||||
{
|
||||
public:
|
||||
using ChannelLoad = dcc::ChannelLoad;
|
||||
|
||||
DccMcoField();
|
||||
|
||||
// copy operations
|
||||
DccMcoField(const DccMcoField&) = default;
|
||||
DccMcoField& operator=(const DccMcoField&) = default;
|
||||
|
||||
// conversion from/to 4 bytes (host byte order)
|
||||
explicit DccMcoField(uint32_t);
|
||||
DccMcoField& operator=(uint32_t);
|
||||
explicit operator uint32_t() const;
|
||||
|
||||
void local_cbr(const ChannelLoad&);
|
||||
ChannelLoad local_cbr() const;
|
||||
|
||||
void neighbour_cbr(const ChannelLoad&);
|
||||
ChannelLoad neighbour_cbr() const;
|
||||
|
||||
/**
|
||||
* Output power of packet transmission
|
||||
* \return [0; 31] dBm (values are cramped at limits)
|
||||
*/
|
||||
void output_power(unsigned dbm);
|
||||
unsigned output_power() const;
|
||||
|
||||
private:
|
||||
using cbr_type = uint8_t;
|
||||
using power_type = BitNumber<unsigned, 5>;
|
||||
|
||||
cbr_type m_cbr_l0_hop; /*< local CBR measurement */
|
||||
cbr_type m_cbr_l1_hop; /*< maximum CBR measurement from 1-hop neighbours */
|
||||
power_type m_output_power; /*< output power of packet transmission */
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DCC_MCO_FIELD_HPP_RLZ4PQMF */
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef DESTINATION_VARIANT_HPP_Y4TEVUXO
|
||||
#define DESTINATION_VARIANT_HPP_Y4TEVUXO
|
||||
|
||||
#include <vanetza/geonet/areas.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
typedef boost::variant<Address, Area, std::nullptr_t> DestinationVariant;
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DESTINATION_VARIANT_HPP_Y4TEVUXO */
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#include <vanetza/geonet/duplicate_packet_list.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
DuplicatePacketList::DuplicatePacketList(unsigned elements) :
|
||||
m_elements(elements)
|
||||
{
|
||||
assert(m_elements.size() == 0);
|
||||
}
|
||||
|
||||
bool DuplicatePacketList::check(SequenceNumber sn)
|
||||
{
|
||||
ListElement* element = find(sn);
|
||||
if (element) {
|
||||
++element->counter;
|
||||
return true;
|
||||
} else {
|
||||
m_elements.push_back(ListElement { sn });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned DuplicatePacketList::counter(SequenceNumber sn) const
|
||||
{
|
||||
for (auto& element : m_elements) {
|
||||
if (element.sequence_number == sn) {
|
||||
return element.counter;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DuplicatePacketList::ListElement* DuplicatePacketList::find(SequenceNumber sn)
|
||||
{
|
||||
for (auto& element : m_elements) {
|
||||
if (element.sequence_number == sn) {
|
||||
return &element;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DuplicatePacketList::ListElement::ListElement(SequenceNumber sn) :
|
||||
sequence_number(sn), counter(1)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
#ifndef DUPLICATE_PACKET_LIST_HPP_T8JWKCKG
|
||||
#define DUPLICATE_PACKET_LIST_HPP_T8JWKCKG
|
||||
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Duplicate Packet List for a single source SO.
|
||||
* Those objects area meant as extension to LocationTableEntry.
|
||||
*
|
||||
* \see EN 302 636-4-1 v1.3.1 Annex A.2
|
||||
*/
|
||||
class DuplicatePacketList
|
||||
{
|
||||
public:
|
||||
DuplicatePacketList(unsigned elements);
|
||||
|
||||
/**
|
||||
* Duplicate packet detection based on sequence number.
|
||||
*
|
||||
* Sequence number will be included in list afterwards.
|
||||
* \param sn sequence number
|
||||
* \return true if its a duplicate
|
||||
*/
|
||||
bool check(SequenceNumber);
|
||||
|
||||
/**
|
||||
* Retrieve duplicate packet counter
|
||||
* \param sn sequence number
|
||||
* \return number of duplicates seen for given sequence number
|
||||
*/
|
||||
unsigned counter(SequenceNumber) const;
|
||||
|
||||
private:
|
||||
struct ListElement
|
||||
{
|
||||
ListElement(SequenceNumber);
|
||||
|
||||
SequenceNumber sequence_number;
|
||||
unsigned counter;
|
||||
};
|
||||
|
||||
ListElement* find(SequenceNumber);
|
||||
|
||||
boost::circular_buffer<ListElement> m_elements;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* DUPLICATE_PACKET_LIST_HPP_T8JWKCKG */
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef EXTENDED_PDU_HPP_TL2WFH9W
|
||||
#define EXTENDED_PDU_HPP_TL2WFH9W
|
||||
|
||||
#include <vanetza/geonet/basic_header.hpp>
|
||||
#include <vanetza/geonet/common_header.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/byte_buffer_sink.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
template<class HEADER> class ExtendedPduConstRefs;
|
||||
|
||||
template<class HEADER>
|
||||
class ExtendedPdu : public Pdu
|
||||
{
|
||||
public:
|
||||
using SecuredMessage = security::SecuredMessage;
|
||||
using ExtendedHeader = HEADER;
|
||||
|
||||
ExtendedPdu() = default;
|
||||
ExtendedPdu(const ExtendedPdu& pdu) :
|
||||
m_basic(pdu.m_basic), m_common(pdu.m_common), m_extended(pdu.m_extended),
|
||||
m_secured(pdu.m_secured) {}
|
||||
ExtendedPdu& operator=(const ExtendedPdu&) = default;
|
||||
ExtendedPdu(const MIB& mib) : m_basic(mib), m_common(mib) {}
|
||||
ExtendedPdu(const DataRequest& request, const MIB& mib) :
|
||||
m_basic(request, mib), m_common(request, mib) {}
|
||||
ExtendedPdu(const BasicHeader& basic, const CommonHeader& common, const HEADER& extended) :
|
||||
m_basic(basic), m_common(common), m_extended(extended) {}
|
||||
ExtendedPdu(const BasicHeader& basic, const CommonHeader& common, const HEADER& extended,
|
||||
const SecuredMessage& secured) :
|
||||
m_basic(basic), m_common(common), m_extended(extended), m_secured(secured) {}
|
||||
ExtendedPdu(const ExtendedPduConstRefs<HEADER>& pdu) :
|
||||
m_basic(pdu.basic()), m_common(pdu.common()), m_extended(pdu.extended()),
|
||||
m_secured(pdu.secured() ? boost::make_optional(*pdu.secured()) : boost::none) {}
|
||||
|
||||
BasicHeader& basic() override { return m_basic; }
|
||||
const BasicHeader& basic() const override { return m_basic; }
|
||||
CommonHeader& common() override { return m_common; }
|
||||
const CommonHeader& common() const override { return m_common; }
|
||||
HeaderConstRefVariant extended_variant() const override { return m_extended; }
|
||||
HEADER& extended() { return m_extended; }
|
||||
const HEADER& extended() const { return m_extended; }
|
||||
SecuredMessage* secured() override { return m_secured.get_ptr(); }
|
||||
const SecuredMessage* secured() const override { return m_secured.get_ptr(); }
|
||||
void secured(const SecuredMessage& smsg) override
|
||||
{
|
||||
m_secured = smsg;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pdu> clone() const override
|
||||
{
|
||||
return std::unique_ptr<ExtendedPdu> { new ExtendedPdu(*this) };
|
||||
}
|
||||
|
||||
private:
|
||||
BasicHeader m_basic;
|
||||
CommonHeader m_common;
|
||||
HEADER m_extended;
|
||||
boost::optional<SecuredMessage> m_secured;
|
||||
};
|
||||
|
||||
template<class HEADER>
|
||||
class ExtendedPduConstRefs : public ConstAccessiblePdu
|
||||
{
|
||||
public:
|
||||
using SecuredMessage = security::SecuredMessage;
|
||||
using ExtendedHeader = HEADER;
|
||||
|
||||
ExtendedPduConstRefs(const BasicHeader& basic, const CommonHeader& common, const HEADER& extended) :
|
||||
mr_basic(basic), mr_common(common), mr_extended(extended), mp_secured(nullptr) {}
|
||||
ExtendedPduConstRefs(const BasicHeader& basic, const CommonHeader& common, const HEADER& extended,
|
||||
const SecuredMessage* secured) :
|
||||
mr_basic(basic), mr_common(common), mr_extended(extended), mp_secured(secured) {}
|
||||
|
||||
const BasicHeader& basic() const override { return mr_basic; }
|
||||
const CommonHeader& common() const override { return mr_common; }
|
||||
HeaderConstRefVariant extended_variant() const override { return mr_extended; }
|
||||
const HEADER& extended() const { return mr_extended; }
|
||||
const SecuredMessage* secured() const override { return mp_secured; }
|
||||
|
||||
std::unique_ptr<Pdu> clone() const override
|
||||
{
|
||||
return std::unique_ptr<ExtendedPdu<HEADER>> {
|
||||
mp_secured ?
|
||||
new ExtendedPdu<HEADER>(mr_basic, mr_common, mr_extended, *mp_secured) :
|
||||
new ExtendedPdu<HEADER>(mr_basic, mr_common, mr_extended)
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
const BasicHeader& mr_basic;
|
||||
const CommonHeader& mr_common;
|
||||
const HEADER& mr_extended;
|
||||
const SecuredMessage* mp_secured;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Serialize relevant header parts for signing
|
||||
* Uses common and extended headers.
|
||||
* \param pdu containing GN headers
|
||||
* \return binary form of relevant header parts
|
||||
*/
|
||||
template<class HEADER>
|
||||
ByteBuffer convert_for_signing(const ExtendedPdu<HEADER>& pdu)
|
||||
{
|
||||
ByteBuffer buf;
|
||||
byte_buffer_sink sink(buf);
|
||||
|
||||
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
|
||||
OutputArchive ar(stream);
|
||||
|
||||
serialize(pdu.common(), ar);
|
||||
serialize(pdu.extended(), ar);
|
||||
|
||||
stream.close();
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* EXTENDED_PDU_HPP_TL2WFH9W */
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "gbc_gac_header.hpp"
|
||||
#include "areas.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class set_distance_visitor : public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
set_distance_visitor(GbcGacHeader& hdr) : m_header(hdr) {}
|
||||
|
||||
void operator()(const Circle& circle)
|
||||
{
|
||||
m_header.distance_a = circle.r;
|
||||
m_header.distance_b = 0;
|
||||
}
|
||||
|
||||
void operator()(const Rectangle& rect)
|
||||
{
|
||||
m_header.distance_a = rect.a;
|
||||
m_header.distance_b = rect.b;
|
||||
}
|
||||
|
||||
void operator()(const Ellipse& elip)
|
||||
{
|
||||
m_header.distance_a = elip.a;
|
||||
m_header.distance_b = elip.b;
|
||||
}
|
||||
|
||||
private:
|
||||
GbcGacHeader& m_header;
|
||||
};
|
||||
|
||||
class get_distance_visitor : public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
get_distance_visitor(const GbcGacHeader& hdr) : m_header(hdr) {}
|
||||
|
||||
void operator()(Circle& circle)
|
||||
{
|
||||
circle.r = m_header.distance_a;
|
||||
}
|
||||
|
||||
void operator()(Rectangle& rect)
|
||||
{
|
||||
rect.a = m_header.distance_a;
|
||||
rect.b = m_header.distance_b;
|
||||
}
|
||||
|
||||
void operator()(Ellipse& elip)
|
||||
{
|
||||
elip.a = m_header.distance_a;
|
||||
elip.b = m_header.distance_b;
|
||||
}
|
||||
|
||||
private:
|
||||
const GbcGacHeader& m_header;
|
||||
};
|
||||
|
||||
constexpr std::size_t GbcGacHeader::length_bytes;
|
||||
|
||||
void GbcGacHeader::destination(const Area& area)
|
||||
{
|
||||
this->position(area.position);
|
||||
angle = static_cast<angle_u16t>(area.angle);
|
||||
set_distance_visitor visitor(*this);
|
||||
boost::apply_visitor(visitor, area.shape);
|
||||
}
|
||||
|
||||
Area GbcGacHeader::destination(const decltype(Area::shape)& shape) const
|
||||
{
|
||||
Area area;
|
||||
area.shape = shape;
|
||||
area.position = this->position();
|
||||
area.angle = static_cast<units::Angle>(angle);
|
||||
get_distance_visitor visitor(*this);
|
||||
boost::apply_visitor(visitor, area.shape);
|
||||
return area;
|
||||
}
|
||||
|
||||
void GbcGacHeader::position(const GeodeticPosition& position)
|
||||
{
|
||||
geo_area_pos_latitude = geo_angle_i32t::from_value(std::round(position.latitude.value() * 1e7));
|
||||
geo_area_pos_longitude = geo_angle_i32t::from_value(std::round(position.longitude.value() * 1e7));
|
||||
}
|
||||
|
||||
GeodeticPosition GbcGacHeader::position() const
|
||||
{
|
||||
return GeodeticPosition(
|
||||
static_cast<units::GeoAngle>(geo_area_pos_latitude),
|
||||
static_cast<units::GeoAngle>(geo_area_pos_longitude)
|
||||
);
|
||||
}
|
||||
|
||||
void serialize(const GbcGacHeader& header, OutputArchive& ar)
|
||||
{
|
||||
geonet::serialize(header.sequence_number, ar);
|
||||
geonet::serialize(host_cast(header.reserved1), ar);
|
||||
geonet::serialize(header.source_position, ar);
|
||||
geonet::serialize(header.geo_area_pos_latitude, ar);
|
||||
geonet::serialize(header.geo_area_pos_longitude, ar);
|
||||
geonet::serialize(header.distance_a, ar);
|
||||
geonet::serialize(header.distance_b, ar);
|
||||
geonet::serialize(header.angle, ar);
|
||||
geonet::serialize(host_cast(header.reserved2), ar);
|
||||
}
|
||||
|
||||
void deserialize(GbcGacHeader& header, InputArchive& ar)
|
||||
{
|
||||
geonet::deserialize(header.sequence_number, ar);
|
||||
geonet::deserialize(header.reserved1, ar);
|
||||
geonet::deserialize(header.source_position, ar);
|
||||
geonet::deserialize(header.geo_area_pos_latitude, ar);
|
||||
geonet::deserialize(header.geo_area_pos_longitude, ar);
|
||||
geonet::deserialize(header.distance_a, ar);
|
||||
geonet::deserialize(header.distance_b, ar);
|
||||
geonet::deserialize(header.angle, ar);
|
||||
geonet::deserialize(header.reserved2, ar);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace geonet
|
||||
} // namepsace vanetza
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef GBC_GAC_HEADER_HPP_FS5DH20M
|
||||
#define GBC_GAC_HEADER_HPP_FS5DH20M
|
||||
|
||||
#include <vanetza/geonet/areas.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/geonet/units.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/**
|
||||
* GeoBroadcast GUC and GeoAnycast GAC have identical header layout.
|
||||
* We use this class as common base class to reduce duplicate code.
|
||||
*/
|
||||
struct GbcGacHeader
|
||||
{
|
||||
static constexpr std::size_t length_bytes = 20 + LongPositionVector::length_bytes;
|
||||
|
||||
SequenceNumber sequence_number;
|
||||
uint16_t reserved1 = 0;
|
||||
LongPositionVector source_position;
|
||||
geo_angle_i32t geo_area_pos_latitude;
|
||||
geo_angle_i32t geo_area_pos_longitude;
|
||||
distance_u16t distance_a;
|
||||
distance_u16t distance_b;
|
||||
angle_u16t angle;
|
||||
uint16_t reserved2 = 0;
|
||||
|
||||
void destination(const Area&);
|
||||
GeodeticPosition position() const;
|
||||
void position(const GeodeticPosition&);
|
||||
|
||||
protected:
|
||||
Area destination(const decltype(Area::shape)&) const;
|
||||
};
|
||||
|
||||
void serialize(const GbcGacHeader&, OutputArchive&);
|
||||
void deserialize(GbcGacHeader&, InputArchive&);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* GBC_GAC_HEADER_HPP_FS5DH20M */
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "gbc_header.hpp"
|
||||
#include "areas.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
Area GeoBroadcastHeader::destination(HeaderType ht) const
|
||||
{
|
||||
decltype(Area::shape) shape;
|
||||
switch (ht) {
|
||||
case HeaderType::GeoBroadcast_Circle:
|
||||
shape = Circle();
|
||||
break;
|
||||
case HeaderType::GeoBroadcast_Rect:
|
||||
shape = Rectangle();
|
||||
break;
|
||||
case HeaderType::GeoBroadcast_Elip:
|
||||
shape = Ellipse();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid GBC header type");
|
||||
break;
|
||||
}
|
||||
|
||||
return detail::GbcGacHeader::destination(shape);
|
||||
}
|
||||
|
||||
void serialize(const GeoBroadcastHeader& header, OutputArchive& ar)
|
||||
{
|
||||
detail::serialize(header, ar);
|
||||
}
|
||||
|
||||
void deserialize(GeoBroadcastHeader& header, InputArchive& ar)
|
||||
{
|
||||
detail::deserialize(header, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef GBC_HEADER_HPP_AWYN1M7E
|
||||
#define GBC_HEADER_HPP_AWYN1M7E
|
||||
|
||||
#include <vanetza/geonet/gbc_gac_header.hpp>
|
||||
#include <vanetza/geonet/header_type.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct GeoBroadcastHeader : public detail::GbcGacHeader
|
||||
{
|
||||
public:
|
||||
using detail::GbcGacHeader::destination;
|
||||
Area destination(HeaderType) const;
|
||||
};
|
||||
|
||||
void serialize(const GeoBroadcastHeader&, OutputArchive&);
|
||||
void deserialize(GeoBroadcastHeader&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* GBC_HEADER_HPP_AWYN1M7E */
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <vanetza/geonet/gbc_memory.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
void GbcMemory::capacity(std::size_t num)
|
||||
{
|
||||
m_capacity = num < 1 ? 1 : num;
|
||||
|
||||
// remove excessive identifiers
|
||||
auto& by_queue_index = m_identifiers.get<by_queue>();
|
||||
while (by_queue_index.size() > m_capacity) {
|
||||
by_queue_index.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t GbcMemory::size() const
|
||||
{
|
||||
return m_identifiers.size();
|
||||
}
|
||||
|
||||
bool GbcMemory::remember(const PacketIdentifier& id)
|
||||
{
|
||||
auto& by_packet_index = m_identifiers.get<by_packet>();
|
||||
auto found_packet = by_packet_index.find(id);
|
||||
if (found_packet == by_packet_index.end()) {
|
||||
// make space for one identifier
|
||||
auto& by_queue_index = m_identifiers.get<by_queue>();
|
||||
while (!by_queue_index.empty() && by_queue_index.size() >= m_capacity) {
|
||||
by_queue_index.pop_front();
|
||||
}
|
||||
|
||||
by_queue_index.push_back(id);
|
||||
return false;
|
||||
} else {
|
||||
// packet is already known, just move it to end of queue
|
||||
auto found_queue = m_identifiers.project<by_queue>(found_packet);
|
||||
auto& by_queue_index = m_identifiers.get<by_queue>();
|
||||
by_queue_index.relocate(by_queue_index.end(), found_queue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GbcMemory::knows(const PacketIdentifier& id) const
|
||||
{
|
||||
return m_identifiers.get<by_packet>().find(id) != m_identifiers.get<by_packet>().end();
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef GBC_MEMORY_HPP_032N8PRJ
|
||||
#define GBC_MEMORY_HPP_032N8PRJ
|
||||
|
||||
#include <vanetza/geonet/cbf_packet_identifier.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* GbcMemory remembers previously seens GBC packets.
|
||||
*
|
||||
* GBC packets are identified by their (GN addr, sequence number) tuple.
|
||||
* The size of GbcMemory is bounded, i.e. it will forget old packets in favour of recent ones.
|
||||
*/
|
||||
class GbcMemory
|
||||
{
|
||||
public:
|
||||
using PacketIdentifier = CbfPacketIdentifier;
|
||||
|
||||
/**
|
||||
* Forget a packet if memory exceeds upper limit of stored identifiers
|
||||
* \param num upper limit of remembered packets
|
||||
*/
|
||||
void capacity(std::size_t num);
|
||||
|
||||
/**
|
||||
* Number of currently known packets
|
||||
* \return number of packets
|
||||
*/
|
||||
std::size_t size() const;
|
||||
|
||||
/**
|
||||
* Remember a particular packet
|
||||
* \param id packet identifier
|
||||
* \return true if packet is already known
|
||||
*/
|
||||
bool remember(const PacketIdentifier& id);
|
||||
|
||||
/**
|
||||
* Check if a particular packet is known
|
||||
* \param id packet identifier
|
||||
* \return true if packet is known
|
||||
*/
|
||||
bool knows(const PacketIdentifier& id) const;
|
||||
|
||||
private:
|
||||
std::size_t m_capacity = 1;
|
||||
|
||||
struct by_packet {};
|
||||
using packet_index = boost::multi_index::hashed_unique<
|
||||
boost::multi_index::tag<by_packet>,
|
||||
boost::multi_index::identity<PacketIdentifier>,
|
||||
std::hash<PacketIdentifier>
|
||||
>;
|
||||
struct by_queue {};
|
||||
using queue_index = boost::multi_index::sequenced<boost::multi_index::tag<by_queue>>;
|
||||
|
||||
using container_type = boost::multi_index_container<PacketIdentifier,
|
||||
boost::multi_index::indexed_by<queue_index, packet_index>>;
|
||||
container_type m_identifiers;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* GBC_MEMORY_HPP_032N8PRJ */
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef GUC_HEADER_HPP_UEGE3IFW
|
||||
#define GUC_HEADER_HPP_UEGE3IFW
|
||||
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct GeoUnicastHeader
|
||||
{
|
||||
static constexpr std::size_t length_bytes = 4 +
|
||||
LongPositionVector::length_bytes +
|
||||
ShortPositionVector::length_bytes;
|
||||
|
||||
SequenceNumber sequence_number;
|
||||
uint16_t reserved;
|
||||
LongPositionVector source_position;
|
||||
ShortPositionVector destination_position;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* GUC_HEADER_HPP_UEGE3IFW */
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "header_type.hpp"
|
||||
#include "areas.hpp"
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct gbc_header_type_visitor : public boost::static_visitor<HeaderType>
|
||||
{
|
||||
HeaderType operator()(const Circle&) const
|
||||
{
|
||||
return HeaderType::GeoBroadcast_Circle;
|
||||
}
|
||||
|
||||
HeaderType operator()(const Rectangle&) const
|
||||
{
|
||||
return HeaderType::GeoBroadcast_Rect;
|
||||
}
|
||||
|
||||
HeaderType operator()(const Ellipse&) const
|
||||
{
|
||||
return HeaderType::GeoBroadcast_Elip;
|
||||
}
|
||||
};
|
||||
|
||||
HeaderType gbc_header_type(const Area& area)
|
||||
{
|
||||
return boost::apply_visitor(gbc_header_type_visitor(), area.shape);
|
||||
}
|
||||
|
||||
struct gac_header_type_visitor : public boost::static_visitor<HeaderType>
|
||||
{
|
||||
HeaderType operator()(const Circle&) const
|
||||
{
|
||||
return HeaderType::GeoAnycast_Circle;
|
||||
}
|
||||
|
||||
HeaderType operator()(const Rectangle&) const
|
||||
{
|
||||
return HeaderType::GeoAnycast_Rect;
|
||||
}
|
||||
|
||||
HeaderType operator()(const Ellipse&) const
|
||||
{
|
||||
return HeaderType::GeoAnycast_Elip;
|
||||
}
|
||||
};
|
||||
|
||||
HeaderType gac_header_type(const Area& area)
|
||||
{
|
||||
return boost::apply_visitor(gac_header_type_visitor(), area.shape);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef HEADER_TYPE_HPP_U5FGWR9N
|
||||
#define HEADER_TYPE_HPP_U5FGWR9N
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
// forward declaration
|
||||
struct Area;
|
||||
|
||||
enum class HeaderType : uint8_t
|
||||
{
|
||||
Any = 0x00,
|
||||
Beacon = 0x10,
|
||||
GeoUnicast = 0x20,
|
||||
GeoAnycast_Circle = 0x30,
|
||||
GeoAnycast_Rect = 0x31,
|
||||
GeoAnycast_Elip = 0x32,
|
||||
GeoBroadcast_Circle = 0x40,
|
||||
GeoBroadcast_Rect = 0x41,
|
||||
GeoBroadcast_Elip = 0x42,
|
||||
TSB_Single_Hop = 0x50,
|
||||
TSB_Multi_Hop = 0x51,
|
||||
LS_Request = 0x60,
|
||||
LS_Reply = 0x61
|
||||
};
|
||||
|
||||
HeaderType gbc_header_type(const Area&);
|
||||
HeaderType gac_header_type(const Area&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* HEADER_TYPE_HPP_U5FGWR9N */
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <vanetza/geonet/header_variant.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
|
||||
class HeaderVariantVisitor : public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
HeaderVariantVisitor(OutputArchive& ar) :
|
||||
m_archive(ar)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void operator()(const T& header)
|
||||
{
|
||||
serialize(header, m_archive);
|
||||
}
|
||||
|
||||
private:
|
||||
OutputArchive& m_archive;
|
||||
};
|
||||
|
||||
void serialize(const HeaderVariant& header, OutputArchive& ar)
|
||||
{
|
||||
HeaderVariantVisitor visit(ar);
|
||||
boost::apply_visitor(visit, header);
|
||||
}
|
||||
|
||||
void serialize(const HeaderRefVariant& header, OutputArchive& ar)
|
||||
{
|
||||
HeaderVariantVisitor visitor(ar);
|
||||
boost::apply_visitor(visitor, header);
|
||||
}
|
||||
|
||||
void serialize(const HeaderConstRefVariant& header, OutputArchive& ar)
|
||||
{
|
||||
HeaderVariantVisitor visitor(ar);
|
||||
boost::apply_visitor(visitor, header);
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class HeaderVariantLengthVisitor : public boost::static_visitor<std::size_t>
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
std::size_t operator()(const T&)
|
||||
{
|
||||
return T::length_bytes;
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t get_length(const HeaderVariant& header)
|
||||
{
|
||||
HeaderVariantLengthVisitor visit;
|
||||
return boost::apply_visitor(visit, header);
|
||||
}
|
||||
|
||||
std::size_t get_length(const HeaderRefVariant& header)
|
||||
{
|
||||
HeaderVariantLengthVisitor visitor;
|
||||
return boost::apply_visitor(visitor, header);
|
||||
}
|
||||
|
||||
std::size_t get_length(const HeaderConstRefVariant& header)
|
||||
{
|
||||
HeaderVariantLengthVisitor visitor;
|
||||
return boost::apply_visitor(visitor, header);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef HEADER_VARIANT_HPP
|
||||
#define HEADER_VARIANT_HPP
|
||||
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/geonet/beacon_header.hpp>
|
||||
#include <vanetza/geonet/gbc_header.hpp>
|
||||
#include <vanetza/geonet/shb_header.hpp>
|
||||
#include <vanetza/geonet/tsb_header.hpp>
|
||||
#include <boost/variant/variant.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
typedef boost::variant<BeaconHeader, GeoBroadcastHeader, ShbHeader, TsbHeader> HeaderVariant;
|
||||
typedef boost::variant<BeaconHeader&, GeoBroadcastHeader&, ShbHeader&, TsbHeader&> HeaderRefVariant;
|
||||
typedef boost::variant<const BeaconHeader&, const GeoBroadcastHeader&, const ShbHeader&, const TsbHeader&> HeaderConstRefVariant;
|
||||
|
||||
/** \brief get the length of the underlying header type
|
||||
*
|
||||
* \param header the header to get the length from
|
||||
* \return std::size_t
|
||||
*
|
||||
*/
|
||||
std::size_t get_length(const HeaderVariant& header);
|
||||
std::size_t get_length(const HeaderRefVariant& header);
|
||||
std::size_t get_length(const HeaderConstRefVariant& header);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
/** \brief serialize the underlying header type to an OutputArchive
|
||||
*
|
||||
* \param header the header to serialize
|
||||
* \param ar the archive to write to
|
||||
*
|
||||
*/
|
||||
void serialize(const vanetza::geonet::HeaderVariant& header, vanetza::OutputArchive& ar);
|
||||
void serialize(const vanetza::geonet::HeaderRefVariant& header, vanetza::OutputArchive& ar);
|
||||
void serialize(const vanetza::geonet::HeaderConstRefVariant& header, vanetza::OutputArchive& ar);
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // HEADER_VARIANT_HPP
|
||||
@@ -0,0 +1,177 @@
|
||||
#include <vanetza/geonet/indication_context.hpp>
|
||||
#include <vanetza/geonet/pdu_conversion.hpp>
|
||||
#include <vanetza/geonet/secured_pdu.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
IndicationContextDeserialize::IndicationContextDeserialize(UpPacketPtr packet, CohesivePacket& cohesive, const LinkLayer& ll) :
|
||||
IndicationContextBasic(ll),
|
||||
m_packet(std::move(packet)), m_cohesive_packet(cohesive),
|
||||
m_parser(cohesive[OsiLayer::Network])
|
||||
{
|
||||
}
|
||||
|
||||
const BasicHeader* IndicationContextDeserialize::parse_basic()
|
||||
{
|
||||
auto bytes = m_parser.parse_basic(pdu().basic());
|
||||
return bytes > 0 ? &pdu().basic() : nullptr;
|
||||
}
|
||||
|
||||
const CommonHeader* IndicationContextDeserialize::parse_common()
|
||||
{
|
||||
auto bytes = m_parser.parse_common(pdu().common());
|
||||
return bytes > 0 ? &pdu().common() : nullptr;
|
||||
}
|
||||
|
||||
const IndicationContext::SecuredMessage* IndicationContextDeserialize::parse_secured()
|
||||
{
|
||||
IndicationContext::SecuredMessage tmp;
|
||||
auto bytes = m_parser.parse_secured(tmp);
|
||||
if (bytes > 0) {
|
||||
pdu().secured(std::move(tmp));
|
||||
return pdu().secured();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<HeaderConstRefVariant> IndicationContextDeserialize::parse_extended(HeaderType ht)
|
||||
{
|
||||
auto bytes = m_parser.parse_extended(pdu().extended_variant(), ht);
|
||||
return boost::optional<HeaderConstRefVariant>(bytes > 0, pdu().extended_variant());
|
||||
}
|
||||
|
||||
IndicationContext::UpPacketPtr IndicationContextDeserialize::finish()
|
||||
{
|
||||
m_cohesive_packet.set_boundary(OsiLayer::Network, m_parser.parsed_bytes());
|
||||
m_cohesive_packet.trim(OsiLayer::Transport, pdu().common().payload);
|
||||
return std::move(m_packet);
|
||||
}
|
||||
|
||||
|
||||
IndicationContextCast::IndicationContextCast(UpPacketPtr packet, ChunkPacket& chunk, const LinkLayer& ll) :
|
||||
IndicationContextBasic(ll), m_packet(std::move(packet))
|
||||
{
|
||||
Pdu* casted_pdu = pdu_cast(chunk.layer(OsiLayer::Network));
|
||||
if (casted_pdu) {
|
||||
pdu() = *casted_pdu;
|
||||
} else {
|
||||
throw std::runtime_error("Casting to Pdu failed");
|
||||
}
|
||||
}
|
||||
|
||||
const BasicHeader* IndicationContextCast::parse_basic()
|
||||
{
|
||||
return &pdu().basic();
|
||||
}
|
||||
|
||||
const CommonHeader* IndicationContextCast::parse_common()
|
||||
{
|
||||
return &pdu().common();
|
||||
}
|
||||
|
||||
const IndicationContext::SecuredMessage* IndicationContextCast::parse_secured()
|
||||
{
|
||||
return pdu().secured();
|
||||
}
|
||||
|
||||
boost::optional<HeaderConstRefVariant> IndicationContextCast::parse_extended(HeaderType)
|
||||
{
|
||||
return boost::optional<HeaderConstRefVariant> { pdu().extended_variant() };
|
||||
}
|
||||
|
||||
IndicationContext::UpPacketPtr IndicationContextCast::finish()
|
||||
{
|
||||
// payload should be already in place (if any)
|
||||
return std::move(m_packet);
|
||||
}
|
||||
|
||||
|
||||
IndicationContextSecuredDeserialize::IndicationContextSecuredDeserialize(IndicationContextBasic& parent, CohesivePacket& payload) :
|
||||
IndicationContextSecured(parent),
|
||||
m_packet(payload),
|
||||
m_parser(payload[OsiLayer::Network])
|
||||
{
|
||||
}
|
||||
|
||||
const CommonHeader* IndicationContextSecuredDeserialize::parse_common()
|
||||
{
|
||||
auto bytes = m_parser.parse_common(pdu().common());
|
||||
return bytes > 0 ? &pdu().common() : nullptr;
|
||||
}
|
||||
|
||||
boost::optional<HeaderConstRefVariant> IndicationContextSecuredDeserialize::parse_extended(HeaderType ht)
|
||||
{
|
||||
auto bytes = m_parser.parse_extended(pdu().extended_variant(), ht);
|
||||
return boost::optional<HeaderConstRefVariant>(bytes > 0, pdu().extended_variant());
|
||||
}
|
||||
|
||||
IndicationContext::UpPacketPtr IndicationContextSecuredDeserialize::finish()
|
||||
{
|
||||
m_packet.set_boundary(OsiLayer::Network, m_parser.parsed_bytes());
|
||||
auto packet = m_parent.finish();
|
||||
(*packet) = m_packet;
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
IndicationContextSecuredCast::IndicationContextSecuredCast(IndicationContextBasic& parent, ChunkPacket& packet) :
|
||||
IndicationContextSecured(parent),
|
||||
m_packet(parent.finish())
|
||||
{
|
||||
SecuredPdu* secured_pdu = secured_pdu_cast(packet.layer(OsiLayer::Network));
|
||||
if (secured_pdu) {
|
||||
pdu().common() = secured_pdu->common;
|
||||
pdu().extended_variant() = secured_pdu->extended;
|
||||
} else {
|
||||
throw std::runtime_error("Casting to SecuredPdu failed");
|
||||
}
|
||||
|
||||
struct parent_packet_visitor : public boost::static_visitor<>
|
||||
{
|
||||
parent_packet_visitor(ChunkPacket& _secured_payload) : secured_payload(_secured_payload) {}
|
||||
|
||||
void operator()(ChunkPacket& packet)
|
||||
{
|
||||
packet.merge(secured_payload, OsiLayer::Transport, max_osi_layer());
|
||||
}
|
||||
|
||||
void operator()(CohesivePacket& packet)
|
||||
{
|
||||
// CohesivePacket and casting PDUs will probably never happen...
|
||||
ByteBuffer buffer(secured_payload.size());
|
||||
for (auto layer : osi_layer_range(OsiLayer::Transport, max_osi_layer())) {
|
||||
ByteBuffer layer_buffer;
|
||||
secured_payload.layer(layer).convert(layer_buffer);
|
||||
buffer.insert(buffer.end(), layer_buffer.begin(), layer_buffer.end());
|
||||
}
|
||||
packet = CohesivePacket(std::move(buffer), OsiLayer::Transport);
|
||||
}
|
||||
|
||||
ChunkPacket& secured_payload;
|
||||
};
|
||||
|
||||
parent_packet_visitor visitor(packet);
|
||||
boost::apply_visitor(visitor, *m_packet);
|
||||
}
|
||||
|
||||
const CommonHeader* IndicationContextSecuredCast::parse_common()
|
||||
{
|
||||
return &pdu().common();
|
||||
}
|
||||
|
||||
boost::optional<HeaderConstRefVariant> IndicationContextSecuredCast::parse_extended(HeaderType)
|
||||
{
|
||||
return boost::optional<HeaderConstRefVariant> { pdu().extended_variant() };
|
||||
}
|
||||
|
||||
IndicationContext::UpPacketPtr IndicationContextSecuredCast::finish()
|
||||
{
|
||||
return std::move(m_packet);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,149 @@
|
||||
#ifndef INDICATION_CONTEXT_HPP_UWOD2BSQ
|
||||
#define INDICATION_CONTEXT_HPP_UWOD2BSQ
|
||||
|
||||
#include <vanetza/geonet/data_indication.hpp>
|
||||
#include <vanetza/geonet/link_layer.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/geonet/parser.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/geonet/variant_pdu.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* IndicationContext is used for parsing incoming packets.
|
||||
* For each indication (packet reception) a new context should be created.
|
||||
*/
|
||||
class IndicationContext
|
||||
{
|
||||
public:
|
||||
using UpPacketPtr = std::unique_ptr<UpPacket>;
|
||||
using SecuredMessage = security::SecuredMessage;
|
||||
using LinkLayer = geonet::LinkLayer;
|
||||
|
||||
// parser commands
|
||||
virtual const BasicHeader* parse_basic() = 0;
|
||||
virtual const CommonHeader* parse_common() = 0;
|
||||
virtual const SecuredMessage* parse_secured() = 0;
|
||||
virtual boost::optional<HeaderConstRefVariant> parse_extended(HeaderType) = 0;
|
||||
|
||||
// access to data structures related to indication
|
||||
virtual const VariantPdu& pdu() const = 0;
|
||||
virtual VariantPdu& pdu() = 0;
|
||||
virtual const LinkLayer& link_layer() const = 0;
|
||||
virtual DataIndication& service_primitive() = 0;
|
||||
|
||||
/**
|
||||
* Finish usage of IndicationContext and release owned packet
|
||||
* \return owned packet
|
||||
*/
|
||||
virtual UpPacketPtr finish() = 0;
|
||||
|
||||
virtual ~IndicationContext() = default;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* IndicationContextBasic represents the first phase of packet reception,
|
||||
* i.e. it is the context for unsecured headers
|
||||
*/
|
||||
class IndicationContextBasic : public IndicationContext
|
||||
{
|
||||
public:
|
||||
IndicationContextBasic(const LinkLayer& ll) : m_link_layer(ll) {}
|
||||
const LinkLayer& link_layer() const override { return m_link_layer; }
|
||||
DataIndication& service_primitive() override { return m_service_primitive; }
|
||||
VariantPdu& pdu() override { return m_pdu; }
|
||||
const VariantPdu& pdu() const override { return m_pdu; }
|
||||
|
||||
protected:
|
||||
LinkLayer m_link_layer;
|
||||
DataIndication m_service_primitive;
|
||||
VariantPdu m_pdu;
|
||||
};
|
||||
|
||||
class IndicationContextDeserialize : public IndicationContextBasic
|
||||
{
|
||||
public:
|
||||
IndicationContextDeserialize(UpPacketPtr, CohesivePacket&, const LinkLayer&);
|
||||
const BasicHeader* parse_basic() override;
|
||||
const CommonHeader* parse_common() override;
|
||||
const SecuredMessage* parse_secured() override;
|
||||
boost::optional<HeaderConstRefVariant> parse_extended(HeaderType) override;
|
||||
UpPacketPtr finish() override;
|
||||
|
||||
private:
|
||||
UpPacketPtr m_packet;
|
||||
CohesivePacket& m_cohesive_packet;
|
||||
Parser m_parser;
|
||||
};
|
||||
|
||||
class IndicationContextCast : public IndicationContextBasic
|
||||
{
|
||||
public:
|
||||
IndicationContextCast(UpPacketPtr, ChunkPacket&, const LinkLayer&);
|
||||
const BasicHeader* parse_basic() override;
|
||||
const CommonHeader* parse_common() override;
|
||||
const SecuredMessage* parse_secured() override;
|
||||
boost::optional<HeaderConstRefVariant> parse_extended(HeaderType) override;
|
||||
UpPacketPtr finish() override;
|
||||
|
||||
private:
|
||||
UpPacketPtr m_packet;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* IndicationContextSecured is used for the (optional) second phase of packet reception,
|
||||
* i.e. handling the payload contained in a secured message's payload
|
||||
*/
|
||||
class IndicationContextSecured : public IndicationContext
|
||||
{
|
||||
public:
|
||||
IndicationContextSecured(IndicationContextBasic& parent) : m_parent(parent) {}
|
||||
const LinkLayer& link_layer() const override { return m_parent.link_layer(); }
|
||||
DataIndication& service_primitive() override { return m_parent.service_primitive(); }
|
||||
VariantPdu& pdu() override { return m_parent.pdu(); }
|
||||
const VariantPdu& pdu() const override { return m_parent.pdu(); }
|
||||
const BasicHeader* parse_basic() override { return nullptr; }
|
||||
const SecuredMessage* parse_secured() override { return nullptr; }
|
||||
|
||||
protected:
|
||||
IndicationContextBasic& m_parent;
|
||||
};
|
||||
|
||||
class IndicationContextSecuredDeserialize : public IndicationContextSecured
|
||||
{
|
||||
public:
|
||||
IndicationContextSecuredDeserialize(IndicationContextBasic&, CohesivePacket&);
|
||||
const CommonHeader* parse_common() override;
|
||||
boost::optional<HeaderConstRefVariant> parse_extended(HeaderType) override;
|
||||
UpPacketPtr finish() override;
|
||||
|
||||
private:
|
||||
CohesivePacket& m_packet;
|
||||
Parser m_parser;
|
||||
};
|
||||
|
||||
class IndicationContextSecuredCast : public IndicationContextSecured
|
||||
{
|
||||
public:
|
||||
IndicationContextSecuredCast(IndicationContextBasic&, ChunkPacket&);
|
||||
const CommonHeader* parse_common() override;
|
||||
boost::optional<HeaderConstRefVariant> parse_extended(HeaderType) override;
|
||||
UpPacketPtr finish() override;
|
||||
|
||||
private:
|
||||
UpPacketPtr m_packet;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* INDICATION_CONTEXT_HPP_UWOD2BSQ */
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef INTERFACE_HPP_KN7EDWOX
|
||||
#define INTERFACE_HPP_KN7EDWOX
|
||||
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
enum class UpperProtocol { Unknown, BTP_A, BTP_B, IPv6 };
|
||||
enum class TransportType { GUC, GAC, GBC, TSB, SHB };
|
||||
typedef InterfaceType CommunicationProfile;
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* INTERFACE_HPP_KN7EDWOX */
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "lifetime.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include <vanetza/common/byte_order.hpp>
|
||||
#include <boost/units/cmath.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
const Lifetime Lifetime::zero()
|
||||
{
|
||||
return Lifetime();
|
||||
}
|
||||
|
||||
Lifetime::Lifetime()
|
||||
{
|
||||
set(Base::Fifty_Milliseconds, 0);
|
||||
}
|
||||
|
||||
Lifetime::Lifetime(Base base, BitNumber<uint8_t, 6> multiplier)
|
||||
{
|
||||
set(base, multiplier);
|
||||
}
|
||||
|
||||
void Lifetime::set(Base base, BitNumber<uint8_t, 6> multiplier)
|
||||
{
|
||||
m_lifetime = multiplier.raw() << 2 | (static_cast<uint8_t>(base) & base_mask);
|
||||
}
|
||||
|
||||
bool Lifetime::operator<(const Lifetime& other) const
|
||||
{
|
||||
return this->decode() < other.decode();
|
||||
}
|
||||
|
||||
bool Lifetime::operator==(const Lifetime& other) const
|
||||
{
|
||||
const units::Duration diff = this->decode() - other.decode();
|
||||
// 50 ms is the smallest non-zero value Lifetime can represent
|
||||
const auto min_value = 0.050 * units::si::seconds;
|
||||
return abs(diff) < min_value;
|
||||
}
|
||||
|
||||
void Lifetime::encode(units::Duration duration)
|
||||
{
|
||||
double seconds = duration / boost::units::si::seconds;
|
||||
if (seconds >= 630.0) {
|
||||
set(Base::Hundred_Seconds, std::lround(seconds / 100.0));
|
||||
} else if (seconds >= 63.0) {
|
||||
set(Base::Ten_Seconds, std::lround(seconds / 10.0));
|
||||
} else if (seconds >= 3.15) {
|
||||
set(Base::One_Second, std::lround(seconds));
|
||||
} else {
|
||||
set(Base::Fifty_Milliseconds, std::lround(seconds / 0.050));
|
||||
}
|
||||
}
|
||||
|
||||
units::Duration Lifetime::decode() const
|
||||
{
|
||||
using vanetza::units::si::seconds;
|
||||
Base base = static_cast<Base>(m_lifetime & base_mask);
|
||||
const double multiplier = (m_lifetime & multiplier_mask) >> 2;
|
||||
units::Duration unit;
|
||||
|
||||
switch (base) {
|
||||
case Base::Fifty_Milliseconds:
|
||||
unit = 0.050 * seconds;
|
||||
break;
|
||||
case Base::One_Second:
|
||||
unit = 1.0 * seconds;
|
||||
// already done
|
||||
break;
|
||||
case Base::Ten_Seconds:
|
||||
unit = 10.0 * seconds;
|
||||
break;
|
||||
case Base::Hundred_Seconds:
|
||||
unit = 100.0 * seconds;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Decoding of Lifetime::Base failed");
|
||||
break;
|
||||
};
|
||||
|
||||
return multiplier * unit;
|
||||
}
|
||||
|
||||
void serialize(const Lifetime& lifetime, OutputArchive& ar)
|
||||
{
|
||||
serialize(host_cast(lifetime.raw()), ar);
|
||||
}
|
||||
|
||||
void deserialize(Lifetime& lifetime, InputArchive& ar)
|
||||
{
|
||||
uint8_t raw;
|
||||
deserialize(raw, ar);
|
||||
lifetime.raw(raw);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef LIFETIME_HPP_XYTSNW3J
|
||||
#define LIFETIME_HPP_XYTSNW3J
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class Lifetime : public boost::totally_ordered<Lifetime>
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t multiplier_mask = 0xFC;
|
||||
static constexpr uint8_t base_mask = 0x03;
|
||||
|
||||
enum class Base {
|
||||
Fifty_Milliseconds = 0,
|
||||
One_Second = 1,
|
||||
Ten_Seconds = 2,
|
||||
Hundred_Seconds = 3
|
||||
};
|
||||
|
||||
static const Lifetime zero();
|
||||
|
||||
Lifetime();
|
||||
Lifetime(Base base, BitNumber<uint8_t, 6> multiplier);
|
||||
void set(Base base, BitNumber<uint8_t, 6> multiplier);
|
||||
uint8_t raw() const { return m_lifetime; }
|
||||
void raw(uint8_t raw) { m_lifetime = raw; }
|
||||
|
||||
bool operator<(const Lifetime&) const;
|
||||
bool operator==(const Lifetime&) const;
|
||||
|
||||
/**
|
||||
* Decodes stored lifetime
|
||||
* \return lifetime as duration quantity
|
||||
*/
|
||||
units::Duration decode() const;
|
||||
|
||||
/**
|
||||
* Encode duration in lifetime object
|
||||
* \note Precision loss might occur
|
||||
* \param duration Lifetime duration
|
||||
*/
|
||||
void encode(units::Duration);
|
||||
|
||||
private:
|
||||
uint8_t m_lifetime;
|
||||
};
|
||||
|
||||
void serialize(const Lifetime&, OutputArchive&);
|
||||
void deserialize(Lifetime&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* LIFETIME_HPP_XYTSNW3J */
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef LINK_LAYER_HPP_F2JBRUTL
|
||||
#define LINK_LAYER_HPP_F2JBRUTL
|
||||
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct LinkLayer
|
||||
{
|
||||
MacAddress sender;
|
||||
MacAddress destination;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* LINK_LAYER_HPP_F2JBRUTL */
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#ifndef LOCATION_SERVICE_HEADER_HPP_0BWHQP7J
|
||||
#define LOCATION_SERVICE_HEADER_HPP_0BWHQP7J
|
||||
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct LsRequestHeader
|
||||
{
|
||||
static constexpr std::size_t length_bytes = 4 +
|
||||
LongPositionVector::length_bytes +
|
||||
Address::length_bytes;
|
||||
|
||||
SequenceNumber sequence_number;
|
||||
uint16_t reserved;
|
||||
LongPositionVector source_position;
|
||||
Address request_addr;
|
||||
};
|
||||
|
||||
struct LsReplyHeader
|
||||
{
|
||||
static constexpr std::size_t length_bytes = 4 +
|
||||
LongPositionVector::length_bytes +
|
||||
ShortPositionVector::length_bytes;
|
||||
|
||||
SequenceNumber sequence_number;
|
||||
uint16_t reserved;
|
||||
LongPositionVector source_position;
|
||||
ShortPositionVector destination_position;
|
||||
};
|
||||
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* LOCATION_SERVICE_HEADER_HPP_0BWHQP7J */
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "location_table.hpp"
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
static_assert(std::numeric_limits<double>::has_quiet_NaN, "quiet NaN value unavailable");
|
||||
|
||||
LocationTableEntry::LocationTableEntry(const Runtime& rt) :
|
||||
m_runtime(rt), m_is_neighbour(Clock::time_point::min()), m_has_position_vector(false),
|
||||
m_pdr(std::numeric_limits<double>::quiet_NaN()), m_pdr_update(rt.now())
|
||||
{
|
||||
}
|
||||
|
||||
StationType LocationTableEntry::station_type() const
|
||||
{
|
||||
return geonet_address().station_type();
|
||||
}
|
||||
|
||||
const Address& LocationTableEntry::geonet_address() const
|
||||
{
|
||||
return m_position_vector.gn_addr;
|
||||
}
|
||||
|
||||
const MacAddress& LocationTableEntry::link_layer_address() const
|
||||
{
|
||||
return geonet_address().mid();
|
||||
}
|
||||
|
||||
bool LocationTableEntry::is_neighbour() const
|
||||
{
|
||||
return m_is_neighbour > m_runtime.now();
|
||||
}
|
||||
|
||||
void LocationTableEntry::update_pdr(std::size_t packet_size, double beta)
|
||||
{
|
||||
using namespace vanetza::units;
|
||||
|
||||
if (std::isnan(m_pdr)) {
|
||||
m_pdr = 0.0;
|
||||
m_pdr_update = m_runtime.now();
|
||||
} else if (beta > 0.0 && beta < 1.0) {
|
||||
const std::chrono::duration<double> time_period = m_runtime.now() - m_pdr_update;
|
||||
if (time_period.count() > 0.0) {
|
||||
double instant_pdr = packet_size / time_period.count();
|
||||
m_pdr *= beta;
|
||||
m_pdr += (1.0 - beta) * instant_pdr;
|
||||
m_pdr_update = m_runtime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LocationTableEntry::set_position_vector(const LongPositionVector& pv)
|
||||
{
|
||||
if (is_valid(pv)) {
|
||||
m_has_position_vector = true;
|
||||
m_position_vector = pv;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LocationTableEntry::update_position_vector(const LongPositionVector& lpv)
|
||||
{
|
||||
if (has_position_vector()) {
|
||||
if (get_position_vector().timestamp < lpv.timestamp) {
|
||||
return set_position_vector(lpv);
|
||||
}
|
||||
} else {
|
||||
return set_position_vector(lpv);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocationTableEntry::set_neighbour(bool flag)
|
||||
{
|
||||
m_is_neighbour = flag ? Clock::time_point::max() : Clock::time_point::min();
|
||||
}
|
||||
|
||||
void LocationTableEntry::set_neighbour(bool flag, Clock::duration expiry)
|
||||
{
|
||||
if (flag && expiry > Clock::duration::zero()) {
|
||||
m_is_neighbour = m_runtime.now() + expiry;
|
||||
} else {
|
||||
set_neighbour(flag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LocationTable::LocationTable(const MIB& mib, Runtime& rt) :
|
||||
m_table(rt, LocationTableEntryCreator(rt))
|
||||
{
|
||||
m_table.set_lifetime(std::chrono::seconds(mib.itsGnLifetimeLocTE / units::si::seconds));
|
||||
}
|
||||
|
||||
bool LocationTable::has_entry(const Address& addr) const
|
||||
{
|
||||
return m_table.has_value(addr.mid());
|
||||
}
|
||||
|
||||
bool LocationTable::has_neighbours() const
|
||||
{
|
||||
bool found_neighbour = false;
|
||||
for (const auto& entry : m_table.map()) {
|
||||
if (entry.second.is_neighbour()) {
|
||||
found_neighbour = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found_neighbour;
|
||||
}
|
||||
|
||||
auto LocationTable::neighbours() const -> neighbour_range
|
||||
{
|
||||
const entry_predicate neighbour_predicate =
|
||||
[](const MacAddress&, const LocationTableEntry& entry) {
|
||||
return entry.is_neighbour();
|
||||
};
|
||||
return filter(neighbour_predicate);
|
||||
}
|
||||
|
||||
auto LocationTable::filter(const entry_predicate& predicate) const -> entry_range
|
||||
{
|
||||
using namespace boost::adaptors;
|
||||
std::function<bool(const typename table_type::value_type&)> filter_fn =
|
||||
[predicate](const typename table_type::value_type& v) {
|
||||
return predicate(v.first, v.second);
|
||||
};
|
||||
return m_table.map() | filtered(filter_fn) | map_values;
|
||||
}
|
||||
|
||||
void LocationTable::visit(const entry_visitor& visitor) const
|
||||
{
|
||||
for (const auto& entry : m_table.map()) {
|
||||
visitor(entry.first, entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
LocationTableEntry& LocationTable::update(const LongPositionVector& lpv)
|
||||
{
|
||||
LocationTableEntry* entry = m_table.get_value_ptr(lpv.gn_addr.mid());
|
||||
if (entry && entry->has_position_vector()) {
|
||||
if (entry->update_position_vector(lpv)) {
|
||||
m_table.refresh(lpv.gn_addr.mid());
|
||||
}
|
||||
} else {
|
||||
entry = &m_table.refresh(lpv.gn_addr.mid());
|
||||
entry->update_position_vector(lpv);
|
||||
}
|
||||
return *entry;
|
||||
}
|
||||
|
||||
LocationTableEntry& LocationTable::get_or_create_entry(const Address& addr)
|
||||
{
|
||||
return m_table.get_value(addr.mid());
|
||||
}
|
||||
|
||||
LocationTableEntry& LocationTable::get_or_create_entry(const MacAddress& mac)
|
||||
{
|
||||
return m_table.get_value(mac);
|
||||
}
|
||||
|
||||
const LocationTableEntry* LocationTable::get_entry(const Address& addr) const
|
||||
{
|
||||
return m_table.get_value_ptr(addr.mid());
|
||||
}
|
||||
|
||||
const LocationTableEntry* LocationTable::get_entry(const MacAddress& mac) const
|
||||
{
|
||||
return m_table.get_value_ptr(mac);
|
||||
}
|
||||
|
||||
const LongPositionVector* LocationTable::get_position(const Address& addr) const
|
||||
{
|
||||
return get_position(addr.mid());
|
||||
}
|
||||
|
||||
const LongPositionVector* LocationTable::get_position(const MacAddress& mac) const
|
||||
{
|
||||
const LongPositionVector* position = nullptr;
|
||||
auto* entry = m_table.get_value_ptr(mac);
|
||||
if (entry && entry->has_position_vector()) {
|
||||
position = &entry->get_position_vector();
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifndef LOCATION_TABLE_HPP_EMPVZSHQ
|
||||
#define LOCATION_TABLE_HPP_EMPVZSHQ
|
||||
|
||||
#include <vanetza/common/object_container.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <vanetza/geonet/soft_state_map.hpp>
|
||||
#include <vanetza/geonet/station_type.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class LocationTableEntry
|
||||
{
|
||||
public:
|
||||
LocationTableEntry(const Runtime& rt);
|
||||
|
||||
const Address& geonet_address() const;
|
||||
const MacAddress& link_layer_address() const;
|
||||
StationType station_type() const;
|
||||
|
||||
/**
|
||||
* Get packed data rate (PDR) of corresponding source.
|
||||
* \return PDR in bytes per second, might be not-a-number
|
||||
*/
|
||||
double get_pdr() const { return m_pdr; }
|
||||
|
||||
/**
|
||||
* Update packet data rate.
|
||||
* See Annex B of EN 302 636-4-1 for details.
|
||||
* \param packet_size received number of bytes
|
||||
* \param beta weight factor for exponential moving average ]0; 1[
|
||||
*/
|
||||
void update_pdr(std::size_t packet_size, double beta = 0.5);
|
||||
|
||||
/**
|
||||
* Check if position vector has been set before
|
||||
* \return false after entry initialization
|
||||
* true after set_position_vector invocations
|
||||
*/
|
||||
bool has_position_vector() const { return m_has_position_vector; }
|
||||
|
||||
/**
|
||||
* Get stored position vector
|
||||
* \return position vector (empty until set_position_vector invocation)
|
||||
*/
|
||||
const LongPositionVector& get_position_vector() const { return m_position_vector; }
|
||||
|
||||
/**
|
||||
* Update stored position vector (only after time stamp check)
|
||||
* \param pv source position vector
|
||||
* \return true if position vector passed time stamp check
|
||||
*/
|
||||
bool update_position_vector(const LongPositionVector& pv);
|
||||
|
||||
/**
|
||||
* Check if this entry belongs to a direct neighbour
|
||||
* \return true if direct neighbour
|
||||
*/
|
||||
bool is_neighbour() const;
|
||||
|
||||
/**
|
||||
* Set neighbour relation
|
||||
* \param flag true if entry represents a direct neighbour
|
||||
*/
|
||||
void set_neighbour(bool flag);
|
||||
|
||||
/**
|
||||
* Set neighbour relation with expiry
|
||||
* \param flag true if entry represents a direct neighbour
|
||||
* \param expiry reset neighbour relation to false after expiry
|
||||
*/
|
||||
void set_neighbour(bool flag, Clock::duration expiry);
|
||||
|
||||
ObjectContainer extensions;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Set stored position vector (without timestamp check)
|
||||
* \param pv source position vector
|
||||
*/
|
||||
bool set_position_vector(const LongPositionVector& pv);
|
||||
|
||||
const Runtime& m_runtime;
|
||||
Clock::time_point m_is_neighbour;
|
||||
bool m_has_position_vector;
|
||||
LongPositionVector m_position_vector;
|
||||
double m_pdr; /*< packet data rate in bytes per second */
|
||||
Clock::time_point m_pdr_update;
|
||||
};
|
||||
|
||||
class LocationTableEntryCreator
|
||||
{
|
||||
public:
|
||||
LocationTableEntryCreator(const Runtime& rt) : m_runtime(rt) {}
|
||||
LocationTableEntry operator()() { return LocationTableEntry(m_runtime); }
|
||||
|
||||
private:
|
||||
const Runtime& m_runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* GeoNetworking LocationTable
|
||||
* See section 7.1 of EN 302 636-4-1 for details.
|
||||
*/
|
||||
class LocationTable
|
||||
{
|
||||
public:
|
||||
using table_type = SoftStateMap<MacAddress, LocationTableEntry, LocationTableEntryCreator>;
|
||||
using entry_visitor = std::function<void(const MacAddress&, const LocationTableEntry&)>;
|
||||
using entry_predicate = std::function<bool(const MacAddress&, const LocationTableEntry&)>;
|
||||
using entry_range =
|
||||
boost::select_second_const_range<
|
||||
boost::filtered_range<
|
||||
std::function<bool(const typename table_type::value_type&)>,
|
||||
const typename table_type::map_range>>;
|
||||
using neighbour_range = entry_range;
|
||||
|
||||
LocationTable(const MIB&, Runtime&);
|
||||
bool has_entry(const Address&) const;
|
||||
LocationTableEntry& update(const LongPositionVector&);
|
||||
LocationTableEntry& get_or_create_entry(const Address&);
|
||||
LocationTableEntry& get_or_create_entry(const MacAddress&);
|
||||
const LocationTableEntry* get_entry(const Address&) const;
|
||||
const LocationTableEntry* get_entry(const MacAddress&) const;
|
||||
const LongPositionVector* get_position(const Address&) const;
|
||||
const LongPositionVector* get_position(const MacAddress&) const;
|
||||
bool has_neighbours() const;
|
||||
neighbour_range neighbours() const;
|
||||
entry_range filter(const entry_predicate&) const;
|
||||
void visit(const entry_visitor&) const;
|
||||
void drop_expired() { m_table.drop_expired(); }
|
||||
|
||||
private:
|
||||
table_type m_table;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* LOCATION_TABLE_HPP_EMPVZSHQ */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef LOCTEX_G5_HPP_BVRKEPHW
|
||||
#define LOCTEX_G5_HPP_BVRKEPHW
|
||||
|
||||
#include <vanetza/geonet/dcc_mco_field.hpp>
|
||||
#include <vanetza/geonet/timestamp.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Media-dependent extension to the Location Table Entry (LocTE) for ITS-G5
|
||||
*/
|
||||
struct LocTEX_G5
|
||||
{
|
||||
Timestamp local_update; /*< TST_G5: last update time (local time stamp) */
|
||||
Timestamp source_update; /*< TST_SO_PV_G5: SO PV timestamp from SHB header */
|
||||
DccMcoField dcc_mco;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* LOCTEX_G5_HPP_BVRKEPHW */
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "mib.hpp"
|
||||
#include <boost/units/systems/si/prefixes.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
using namespace vanetza::units::si;
|
||||
using vanetza::units::degrees;
|
||||
using boost::units::si::kilo;
|
||||
using boost::units::si::milli;
|
||||
|
||||
const auto milliseconds = milli * seconds;
|
||||
|
||||
ManagementInformationBase::ManagementInformationBase() :
|
||||
itsGnLocalAddrConfMethod(AddrConfMethod::Managed),
|
||||
itsGnProtocolVersion(1),
|
||||
itsGnIsMobile(true),
|
||||
itsGnIfType(InterfaceType::ITS_G5),
|
||||
itsGnMinimumUpdateFrequencyEPV(1.0 / (1000.0 * milliseconds)),
|
||||
itsGnPaiInterval(80 * meters),
|
||||
itsGnMaxSduSize(1398),
|
||||
itsGnMaxGeoNetworkingHeaderSize(88),
|
||||
itsGnLifetimeLocTE(20 * seconds),
|
||||
itsGnSecurity(false),
|
||||
itsGnSnDecapResultHandling(SecurityDecapHandling::Strict),
|
||||
itsGnLocationServiceMaxRetrans(10),
|
||||
itsGnLocationServiceRetransmitTimer(1 * seconds),
|
||||
itsGnLocationServicePacketBufferSize(1024),
|
||||
itsGnBeaconServiceRetransmitTimer(3 * seconds),
|
||||
itsGnBeaconServiceMaxJitter(itsGnBeaconServiceRetransmitTimer / 4.0),
|
||||
itsGnDefaultHopLimit(10),
|
||||
itsGnDPLLength(8),
|
||||
itsGnMaxPacketLifetime(Lifetime::Base::Hundred_Seconds, 6),
|
||||
itsGnDefaultPacketLifetime(Lifetime::Base::Ten_Seconds, 6),
|
||||
itsGnMaxPacketDataRate(100),
|
||||
itsGnMaxPacketDataRateEmaBeta(0.9),
|
||||
itsGnMaxGeoAreaSize(10 * kilo * kilo * square_meters),
|
||||
itsGnMinPacketRepetitionInterval(100 * milliseconds),
|
||||
itsGnNonAreaForwardingAlgorithm(UnicastForwarding::Greedy),
|
||||
itsGnAreaForwardingAlgorithm(BroadcastForwarding::CBF),
|
||||
itsGnCbfMinTime(1 * milliseconds),
|
||||
itsGnCbfMaxTime(100 * milliseconds),
|
||||
itsGnDefaultMaxCommunicationRange(1000 * meters),
|
||||
itsGnBroadcastCBFDefSectorAngle(30 * degrees),
|
||||
itsGnUcForwardingPacketBufferSize(256),
|
||||
itsGnBcForwardingPacketBufferSize(1024),
|
||||
itsGnCbfPacketBufferSize(256),
|
||||
itsGnDefaultTrafficClass(false, false, 0),
|
||||
vanetzaDefaultSeed(0xc0114c2c),
|
||||
vanetzaCbfMaxCounter(1),
|
||||
vanetzaDeferInitialBeacon(Clock::duration::zero()),
|
||||
vanetzaDisableBeaconing(false),
|
||||
vanetzaMultiHopDuplicateAddressDetection(false),
|
||||
vanetzaFadingCbfCounter(false),
|
||||
vanetzaFadingCbfCounterLifetime(4.0 * itsGnCbfMaxTime),
|
||||
vanetzaNeighbourFlagExpiry(Clock::duration::zero()),
|
||||
vanetzaGbcMemoryCapacity(0),
|
||||
vanetzaGbcPassUpOutsideDestination(false)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef MIB_HPP_U3WJ4WES
|
||||
#define MIB_HPP_U3WJ4WES
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/lifetime.hpp>
|
||||
#include <vanetza/geonet/station_type.hpp>
|
||||
#include <vanetza/geonet/traffic_class.hpp>
|
||||
#include <vanetza/units/angle.hpp>
|
||||
#include <vanetza/units/area.hpp>
|
||||
#include <vanetza/units/frequency.hpp>
|
||||
#include <vanetza/units/length.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
enum class UnicastForwarding {
|
||||
Unspecified = 0,
|
||||
Greedy = 1,
|
||||
CBF = 2
|
||||
};
|
||||
|
||||
enum class BroadcastForwarding {
|
||||
Unspecified = 0,
|
||||
SIMPLE = 1,
|
||||
CBF = 2,
|
||||
Advanced = 3
|
||||
};
|
||||
|
||||
enum class AddrConfMethod {
|
||||
Auto = 0,
|
||||
Managed = 1,
|
||||
Anonymous = 2
|
||||
};
|
||||
|
||||
enum class InterfaceType {
|
||||
Unspecified = 0,
|
||||
ITS_G5 = 1,
|
||||
LTE_V2X = 2
|
||||
};
|
||||
|
||||
enum class SecurityDecapHandling {
|
||||
Strict = 0,
|
||||
Non_Strict = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* The Management Information Base (MIB) defines the GeoNetworking protocol constants.
|
||||
*
|
||||
* \see EN 302 636-4-1 v1.3.1 Annex H
|
||||
*/
|
||||
struct ManagementInformationBase
|
||||
{
|
||||
ManagementInformationBase();
|
||||
|
||||
Address itsGnLocalGnAddr;
|
||||
AddrConfMethod itsGnLocalAddrConfMethod;
|
||||
unsigned itsGnProtocolVersion;
|
||||
bool itsGnIsMobile;
|
||||
InterfaceType itsGnIfType;
|
||||
units::Frequency itsGnMinimumUpdateFrequencyEPV;
|
||||
units::Length itsGnPaiInterval;
|
||||
unsigned itsGnMaxSduSize;
|
||||
unsigned itsGnMaxGeoNetworkingHeaderSize;
|
||||
units::Duration itsGnLifetimeLocTE;
|
||||
bool itsGnSecurity;
|
||||
SecurityDecapHandling itsGnSnDecapResultHandling;
|
||||
unsigned itsGnLocationServiceMaxRetrans;
|
||||
units::Duration itsGnLocationServiceRetransmitTimer;
|
||||
unsigned itsGnLocationServicePacketBufferSize; // byte
|
||||
units::Duration itsGnBeaconServiceRetransmitTimer;
|
||||
units::Duration itsGnBeaconServiceMaxJitter;
|
||||
unsigned itsGnDefaultHopLimit;
|
||||
unsigned itsGnDPLLength;
|
||||
Lifetime itsGnMaxPacketLifetime;
|
||||
Lifetime itsGnDefaultPacketLifetime;
|
||||
unsigned itsGnMaxPacketDataRate; // kbyte/s
|
||||
double itsGnMaxPacketDataRateEmaBeta; // percentage ]0; 1[
|
||||
units::Area itsGnMaxGeoAreaSize;
|
||||
units::Duration itsGnMinPacketRepetitionInterval;
|
||||
UnicastForwarding itsGnNonAreaForwardingAlgorithm;
|
||||
BroadcastForwarding itsGnAreaForwardingAlgorithm;
|
||||
units::Duration itsGnCbfMinTime;
|
||||
units::Duration itsGnCbfMaxTime;
|
||||
units::Length itsGnDefaultMaxCommunicationRange;
|
||||
units::Angle itsGnBroadcastCBFDefSectorAngle;
|
||||
unsigned itsGnUcForwardingPacketBufferSize; // kbyte
|
||||
unsigned itsGnBcForwardingPacketBufferSize; // kbyte
|
||||
unsigned itsGnCbfPacketBufferSize; // kbyte
|
||||
TrafficClass itsGnDefaultTrafficClass;
|
||||
std::uint32_t vanetzaDefaultSeed; /*< default seed for internal random number generator */
|
||||
std::size_t vanetzaCbfMaxCounter; /*< maximum counter value used for Advanced routing */
|
||||
Clock::duration vanetzaDeferInitialBeacon; /*< defer first beacon by given duration */
|
||||
bool vanetzaDisableBeaconing; /*< disable transmission of beacons entirely */
|
||||
bool vanetzaMultiHopDuplicateAddressDetection; /*< execute DAD for multi-hop packets */
|
||||
bool vanetzaFadingCbfCounter; /*< use fading counters for CBF packet buffer */
|
||||
units::Duration vanetzaFadingCbfCounterLifetime; /*< lifetime until counter vanishes */
|
||||
Clock::duration vanetzaNeighbourFlagExpiry; /*< reset LocTE neighbour state without explicit updates */
|
||||
std::size_t vanetzaGbcMemoryCapacity; /*< do not pass up duplicate GBC packets (0 to disable this filter) */
|
||||
bool vanetzaGbcPassUpOutsideDestination; /*< pass up received GBC packets even when outside of destination area */
|
||||
};
|
||||
|
||||
// This name is too clumsy to write it out every time
|
||||
typedef ManagementInformationBase MIB;
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* MIB_HPP_U3WJ4WES */
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "next_hop.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
NextHop::NextHop() : m_state(State::Discarded)
|
||||
{
|
||||
}
|
||||
|
||||
bool NextHop::discarded() const
|
||||
{
|
||||
return m_state == State::Discarded;
|
||||
}
|
||||
|
||||
bool NextHop::buffered() const
|
||||
{
|
||||
return m_state == State::Buffered;
|
||||
}
|
||||
|
||||
bool NextHop::valid() const
|
||||
{
|
||||
return m_state == State::Valid;
|
||||
}
|
||||
|
||||
const MacAddress& NextHop::mac() const
|
||||
{
|
||||
return m_destination;
|
||||
}
|
||||
|
||||
bool NextHop::process() &&
|
||||
{
|
||||
if (valid()) {
|
||||
PendingPacket<GbcPdu>(std::move(m_packet), m_destination).process();
|
||||
m_state = State::Discarded;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NextHop::transmit(Packet&& packet, const MacAddress& destination)
|
||||
{
|
||||
m_state = NextHop::State::Valid;
|
||||
m_packet = std::move(packet);
|
||||
m_destination = destination;
|
||||
}
|
||||
|
||||
void NextHop::discard()
|
||||
{
|
||||
m_state = NextHop::State::Discarded;
|
||||
}
|
||||
|
||||
void NextHop::buffer()
|
||||
{
|
||||
m_state = NextHop::State::Buffered;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef NEXT_HOP_HPP_ON0AKMBY
|
||||
#define NEXT_HOP_HPP_ON0AKMBY
|
||||
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/geonet/pending_packet.hpp>
|
||||
#include <vanetza/geonet/pdu_variant.hpp>
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* NextHop is the result of GeoNet forwarding algorithms.
|
||||
* It may convey a destination link-layer address or one of the states "discarded" and "buffered".
|
||||
*/
|
||||
class NextHop
|
||||
{
|
||||
public:
|
||||
using Packet = PendingPacket<GbcPdu, const MacAddress&>;
|
||||
|
||||
NextHop();
|
||||
NextHop(NextHop&&) = default;
|
||||
NextHop& operator=(NextHop&&) = default;
|
||||
|
||||
/**
|
||||
* Test if forwarding decision is to discard the packet.
|
||||
* \return true if discarded
|
||||
*/
|
||||
bool discarded() const;
|
||||
|
||||
/**
|
||||
* Test if forwarding decided to buffer the packet.
|
||||
* \return true if packet got buffered
|
||||
*/
|
||||
bool buffered() const;
|
||||
|
||||
/**
|
||||
* Test if stored packet and link-layer address are valid.
|
||||
* \return if valid
|
||||
*/
|
||||
bool valid() const;
|
||||
|
||||
/**
|
||||
* Access stored link-layer address.
|
||||
*
|
||||
* The returned address is only meaningful if NextHop is valid.
|
||||
* \return link-layer address
|
||||
*/
|
||||
const MacAddress& mac() const;
|
||||
|
||||
/**
|
||||
* Prepare for immediate packet transmission (not discarded, not buffered).
|
||||
*
|
||||
* valid() will return true after invocation of this method.
|
||||
* \param packet the packet to be transmitted
|
||||
* \param destination link-layer destination address
|
||||
*/
|
||||
void transmit(Packet&& packet, const MacAddress& destination);
|
||||
|
||||
/**
|
||||
* Set the NextHop state to discarded.
|
||||
*/
|
||||
void discard();
|
||||
|
||||
/**
|
||||
* Set the NextHop state to buffered.
|
||||
*/
|
||||
void buffer();
|
||||
|
||||
/**
|
||||
* Invoke further packet processing.
|
||||
*
|
||||
* It is safe to call this method though packet has been discarded or buffered.
|
||||
* \return true if a valid packet (after previous transmit()) has been processed
|
||||
*/
|
||||
bool process() &&;
|
||||
|
||||
private:
|
||||
enum class State { Valid, Discarded, Buffered };
|
||||
|
||||
State m_state;
|
||||
MacAddress m_destination;
|
||||
Packet m_packet;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* NEXT_HOP_HPP_ON0AKMBY */
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef PACKET_HPP_LFURGMBS
|
||||
#define PACKET_HPP_LFURGMBS
|
||||
|
||||
#include <vanetza/net/packet.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
// This header is only provided for compatibility reasons
|
||||
// New code should rely on net/packet.hpp only
|
||||
|
||||
using vanetza::DownPacket;
|
||||
using vanetza::UpPacket;
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PACKET_HPP_LFURGMBS */
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "packet_buffer.hpp"
|
||||
#include "basic_header.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
namespace packet_buffer
|
||||
{
|
||||
|
||||
Expiry::Expiry(Clock::time_point now, Clock::duration lifetime) :
|
||||
m_buffered_since(now), m_expires_at(now)
|
||||
{
|
||||
m_expires_at += lifetime;
|
||||
}
|
||||
|
||||
bool Expiry::is_expired(Clock::time_point now) const
|
||||
{
|
||||
return (m_expires_at < now);
|
||||
}
|
||||
|
||||
} // namespace packet_buffer
|
||||
|
||||
|
||||
PacketBuffer::PacketBuffer(std::size_t capacity) :
|
||||
m_capacity(capacity), m_stored(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool PacketBuffer::push(data_ptr data, Clock::time_point now)
|
||||
{
|
||||
assert(data);
|
||||
bool pushed = false;
|
||||
drop_expired(now);
|
||||
if (drop(data->length())) {
|
||||
m_stored += data->length();
|
||||
m_nodes.push_back(std::make_tuple(
|
||||
expiry_type(now, data->reduce_lifetime(Clock::duration::zero())),
|
||||
std::move(data)
|
||||
));
|
||||
pushed = true;
|
||||
}
|
||||
return pushed;
|
||||
}
|
||||
|
||||
void PacketBuffer::flush(Clock::time_point now)
|
||||
{
|
||||
decltype(m_nodes) nodes;
|
||||
std::swap(m_nodes, nodes);
|
||||
m_stored = 0;
|
||||
|
||||
for (auto& node : nodes) {
|
||||
const auto& expiry = std::get<0>(node);
|
||||
if (!expiry.is_expired(now)) {
|
||||
auto& data = std::get<1>(node);
|
||||
const auto queuing_time = now - expiry.buffered_since();
|
||||
data->reduce_lifetime(queuing_time);
|
||||
data->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketBuffer::drop_head()
|
||||
{
|
||||
bool dropped = !m_nodes.empty();
|
||||
if (dropped) {
|
||||
m_stored -= std::get<1>(m_nodes.front())->length();
|
||||
m_nodes.pop_front();
|
||||
}
|
||||
return dropped;
|
||||
}
|
||||
|
||||
void PacketBuffer::drop_expired(Clock::time_point now)
|
||||
{
|
||||
std::list<decltype(m_nodes)::iterator> expired;
|
||||
for (auto it = m_nodes.begin(), end = m_nodes.end(); it != end;) {
|
||||
if (std::get<0>(*it).is_expired(now)) {
|
||||
m_stored -= std::get<1>(*it)->length();
|
||||
it = m_nodes.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketBuffer::drop(std::size_t bytes)
|
||||
{
|
||||
while (free() < bytes && drop_head()) {}
|
||||
return free() >= bytes;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef PACKET_BUFFER_HPP_U97KIBQC
|
||||
#define PACKET_BUFFER_HPP_U97KIBQC
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/geonet/timestamp.hpp>
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
namespace packet_buffer
|
||||
{
|
||||
|
||||
class Expiry
|
||||
{
|
||||
public:
|
||||
Expiry(Clock::time_point now, Clock::duration lifetime);
|
||||
bool is_expired(Clock::time_point now) const;
|
||||
Clock::time_point buffered_since() const { return m_buffered_since; }
|
||||
Clock::time_point expires_at() const { return m_expires_at; }
|
||||
|
||||
private:
|
||||
Clock::time_point m_buffered_since;
|
||||
Clock::time_point m_expires_at;
|
||||
};
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Length of packet data
|
||||
* \return length in bytes
|
||||
*/
|
||||
virtual std::size_t length() const = 0;
|
||||
|
||||
/**
|
||||
* Reduce lifetime associated with data
|
||||
* \param d reduce lifetime by given duration
|
||||
* \return remaining lifetime (never negative)
|
||||
*/
|
||||
virtual Clock::duration reduce_lifetime(Clock::duration d) = 0;
|
||||
|
||||
/**
|
||||
* Flush data
|
||||
* \note length() and lifetime() should not be called afterwards!
|
||||
*/
|
||||
virtual void flush() = 0;
|
||||
|
||||
virtual ~Data() {}
|
||||
};
|
||||
|
||||
} // namespace packet_buffer
|
||||
|
||||
|
||||
/**
|
||||
* PacketBuffer with bounded capacity, packet expiry and head-drop
|
||||
*/
|
||||
class PacketBuffer
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<packet_buffer::Data> data_ptr;
|
||||
|
||||
/**
|
||||
* Create PacketBuffer with given capacity
|
||||
* \param capacity Buffer can store this many bytes
|
||||
*/
|
||||
PacketBuffer(std::size_t capacity);
|
||||
|
||||
/**
|
||||
* Push one packet into buffer
|
||||
* \param packet Packet data
|
||||
* \param t Current time
|
||||
* \return true if packet has been pushed successfully
|
||||
*/
|
||||
bool push(data_ptr packet, Clock::time_point t);
|
||||
|
||||
/**
|
||||
* Flush packets from buffer. Expired packets are dropped.
|
||||
* \note Some packets might remain in buffer (re-added during flushing)
|
||||
* \param t Current time
|
||||
*/
|
||||
void flush(Clock::time_point t);
|
||||
|
||||
private:
|
||||
typedef packet_buffer::Expiry expiry_type;
|
||||
typedef std::tuple<expiry_type, data_ptr> node_type;
|
||||
|
||||
std::size_t free() const { return m_capacity - m_stored; }
|
||||
std::size_t capacity() const { return m_capacity; }
|
||||
|
||||
/**
|
||||
* Push one new element into buffer list
|
||||
* \note capacity is not checked by this method, has to be done manually
|
||||
* \param expiry Expiry data
|
||||
* \param packet Packet data
|
||||
*/
|
||||
void push(expiry_type&& expiry, data_ptr packet);
|
||||
|
||||
/**
|
||||
* Drop current head element
|
||||
* \return true if head element was dropped
|
||||
*/
|
||||
bool drop_head();
|
||||
|
||||
/**
|
||||
* Drop all packets with expired timestamp
|
||||
* \param t current time
|
||||
*/
|
||||
void drop_expired(Clock::time_point t);
|
||||
|
||||
/**
|
||||
* Drop as many packets as required to store given number of bytes.
|
||||
* Packets at the head of the list are dropped first.
|
||||
* \param bytes require #bytes free capacity
|
||||
* \return true if there is enough capacity left for #bytes
|
||||
*/
|
||||
bool drop(std::size_t bytes);
|
||||
|
||||
std::list<node_type> m_nodes;
|
||||
std::size_t m_capacity;
|
||||
std::size_t m_stored;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PACKET_BUFFER_HPP_U97KIBQC */
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "parser.hpp"
|
||||
#include "basic_header.hpp"
|
||||
#include "common_header.hpp"
|
||||
#include <vanetza/security/exception.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
Parser::Parser(ByteBuffer::const_iterator begin, ByteBuffer::const_iterator end) :
|
||||
m_byte_buffer_source(begin, end),
|
||||
m_stream(m_byte_buffer_source),
|
||||
m_archive(m_stream),
|
||||
m_read_bytes(0)
|
||||
{
|
||||
}
|
||||
|
||||
Parser::Parser(boost::iterator_range<ByteBuffer::const_iterator> range) :
|
||||
Parser(range.begin(), range.end())
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t Parser::parse_basic(BasicHeader& basic)
|
||||
{
|
||||
std::size_t bytes = 0;
|
||||
try {
|
||||
deserialize(basic, m_archive);
|
||||
bytes = BasicHeader::length_bytes;
|
||||
} catch (InputArchive::Exception&) {
|
||||
}
|
||||
|
||||
m_read_bytes += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::size_t Parser::parse_common(CommonHeader& common)
|
||||
{
|
||||
std::size_t bytes = 0;
|
||||
try {
|
||||
deserialize(common, m_archive);
|
||||
bytes = CommonHeader::length_bytes;
|
||||
} catch (InputArchive::Exception&) {
|
||||
}
|
||||
|
||||
m_read_bytes += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::size_t Parser::parse_secured(security::SecuredMessage& secured)
|
||||
{
|
||||
std::size_t bytes = 0;
|
||||
if (m_archive.is_good()) {
|
||||
try {
|
||||
std::uint8_t sec_first_byte = m_archive.peek_byte();
|
||||
if (sec_first_byte < 3) {
|
||||
security::v2::SecuredMessage msg;
|
||||
bytes = security::v2::deserialize(m_archive, msg);
|
||||
secured = std::move(msg);
|
||||
} else if (sec_first_byte == 3) {
|
||||
security::v3::SecuredMessage msg;
|
||||
bytes = security::v3::deserialize(m_archive, msg);
|
||||
secured = std::move(msg);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
m_read_bytes += bytes;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
template<typename EXTENDED>
|
||||
std::size_t deserialize_extended(InputArchive& archive, HeaderVariant& extended)
|
||||
{
|
||||
EXTENDED header;
|
||||
deserialize(header, archive);
|
||||
extended = std::move(header);
|
||||
return EXTENDED::length_bytes;
|
||||
}
|
||||
|
||||
std::size_t Parser::parse_extended(HeaderVariant& extended, HeaderType ht)
|
||||
{
|
||||
std::size_t bytes = 0;
|
||||
|
||||
try {
|
||||
switch (ht) {
|
||||
case HeaderType::TSB_Single_Hop:
|
||||
bytes = deserialize_extended<ShbHeader>(m_archive, extended);
|
||||
break;
|
||||
case HeaderType::TSB_Multi_Hop:
|
||||
bytes = deserialize_extended<TsbHeader>(m_archive, extended);
|
||||
break;
|
||||
case HeaderType::GeoBroadcast_Circle:
|
||||
case HeaderType::GeoBroadcast_Rect:
|
||||
case HeaderType::GeoBroadcast_Elip:
|
||||
bytes = deserialize_extended<GeoBroadcastHeader>(m_archive, extended);
|
||||
break;
|
||||
case HeaderType::Beacon:
|
||||
bytes = deserialize_extended<BeaconHeader>(m_archive, extended);
|
||||
break;
|
||||
case HeaderType::Any:
|
||||
case HeaderType::GeoUnicast:
|
||||
case HeaderType::GeoAnycast_Circle:
|
||||
case HeaderType::GeoAnycast_Rect:
|
||||
case HeaderType::GeoAnycast_Elip:
|
||||
case HeaderType::LS_Request:
|
||||
case HeaderType::LS_Reply:
|
||||
// unimplemented types
|
||||
break;
|
||||
default:
|
||||
// invalid types
|
||||
break;
|
||||
}
|
||||
} catch (InputArchive::Exception&) {
|
||||
}
|
||||
|
||||
m_read_bytes += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::size_t Parser::parsed_bytes() const
|
||||
{
|
||||
return m_read_bytes;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef PARSER_HPP_IBDRMPKB
|
||||
#define PARSER_HPP_IBDRMPKB
|
||||
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/byte_buffer_source.hpp>
|
||||
#include <vanetza/geonet/header_type.hpp>
|
||||
#include <vanetza/geonet/header_variant.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
// forward declarations
|
||||
struct BasicHeader;
|
||||
struct CommonHeader;
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser(ByteBuffer::const_iterator begin, ByteBuffer::const_iterator end);
|
||||
Parser(boost::iterator_range<ByteBuffer::const_iterator> range);
|
||||
|
||||
std::size_t parse_basic(BasicHeader&);
|
||||
std::size_t parse_common(CommonHeader&);
|
||||
std::size_t parse_secured(security::SecuredMessage&);
|
||||
std::size_t parse_extended(HeaderVariant&, HeaderType);
|
||||
std::size_t parsed_bytes() const;
|
||||
|
||||
private:
|
||||
byte_buffer_source m_byte_buffer_source;
|
||||
boost::iostreams::stream_buffer<byte_buffer_source> m_stream;
|
||||
InputArchive m_archive;
|
||||
std::size_t m_read_bytes;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PARSER_HPP_IBDRMPKB */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <vanetza/geonet/basic_header.hpp>
|
||||
#include <vanetza/geonet/common_header.hpp>
|
||||
#include <vanetza/geonet/header_variant.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
void serialize(const ConstAccessiblePdu& pdu, OutputArchive& ar)
|
||||
{
|
||||
serialize(pdu.basic(), ar);
|
||||
if (pdu.secured()) {
|
||||
security::serialize(ar, *pdu.secured());
|
||||
} else {
|
||||
geonet::serialize(pdu.common(), ar);
|
||||
boost::serialize(pdu.extended_variant(), ar);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t get_length(const ConstAccessiblePdu& pdu)
|
||||
{
|
||||
std::size_t length = BasicHeader::length_bytes;
|
||||
if (pdu.secured()) {
|
||||
length += security::get_size(*pdu.secured());
|
||||
} else {
|
||||
length += CommonHeader::length_bytes;
|
||||
length += get_length(pdu.extended_variant());
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef PDU_HPP_PQEC9PDO
|
||||
#define PDU_HPP_PQEC9PDO
|
||||
|
||||
#include <vanetza/geonet/header_variant.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/security/secured_message.hpp>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct BasicHeader;
|
||||
struct CommonHeader;
|
||||
|
||||
class Pdu;
|
||||
|
||||
class ConstAccessiblePdu
|
||||
{
|
||||
public:
|
||||
using SecuredMessage = security::SecuredMessage;
|
||||
|
||||
virtual const BasicHeader& basic() const = 0;
|
||||
virtual const CommonHeader& common() const = 0;
|
||||
virtual const SecuredMessage* secured() const = 0;
|
||||
virtual HeaderConstRefVariant extended_variant() const = 0;
|
||||
virtual std::unique_ptr<Pdu> clone() const = 0;
|
||||
virtual ~ConstAccessiblePdu() = default;
|
||||
};
|
||||
|
||||
class Pdu : public ConstAccessiblePdu
|
||||
{
|
||||
public:
|
||||
using ConstAccessiblePdu::basic;
|
||||
using ConstAccessiblePdu::common;
|
||||
using ConstAccessiblePdu::secured;
|
||||
|
||||
virtual BasicHeader& basic() = 0;
|
||||
virtual CommonHeader& common() = 0;
|
||||
virtual SecuredMessage* secured() = 0;
|
||||
virtual void secured(const SecuredMessage&) = 0;
|
||||
};
|
||||
|
||||
void serialize(const ConstAccessiblePdu&, OutputArchive&);
|
||||
inline void serialize(const Pdu& pdu, OutputArchive& ar)
|
||||
{
|
||||
serialize(static_cast<const ConstAccessiblePdu&>(pdu), ar);
|
||||
}
|
||||
|
||||
std::size_t get_length(const ConstAccessiblePdu&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PDU_HPP_PQEC9PDO */
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "pdu_conversion.hpp"
|
||||
#include "serialization_buffer.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace convertible
|
||||
{
|
||||
|
||||
typedef std::unique_ptr<vanetza::geonet::Pdu> PduPtr;
|
||||
|
||||
void byte_buffer_impl<PduPtr>::convert(ByteBuffer& dest) const
|
||||
{
|
||||
assert(m_pdu);
|
||||
dest.clear();
|
||||
geonet::serialize_into_buffer(*m_pdu, dest);
|
||||
}
|
||||
|
||||
std::size_t byte_buffer_impl<PduPtr>::size() const
|
||||
{
|
||||
assert(m_pdu);
|
||||
return get_length(*m_pdu);
|
||||
}
|
||||
|
||||
std::unique_ptr<byte_buffer> byte_buffer_impl<PduPtr>::duplicate() const
|
||||
{
|
||||
assert(m_pdu);
|
||||
PduPtr duplicate { m_pdu->clone() };
|
||||
std::unique_ptr<byte_buffer> result {
|
||||
new byte_buffer_impl<PduPtr>(std::move(duplicate))
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace convertible
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
Pdu* pdu_cast(ByteBufferConvertible& convertible)
|
||||
{
|
||||
using convertible_pdu_t = convertible::byte_buffer_impl<std::unique_ptr<Pdu>>;
|
||||
auto convertible_pdu = dynamic_cast<convertible_pdu_t*>(convertible.ptr());
|
||||
return convertible_pdu ? convertible_pdu->m_pdu.get() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef PDU_CONVERSION_HPP_XLCSI42E
|
||||
#define PDU_CONVERSION_HPP_XLCSI42E
|
||||
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/byte_buffer_convertible.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace convertible
|
||||
{
|
||||
|
||||
template<>
|
||||
struct byte_buffer_impl<std::unique_ptr<vanetza::geonet::Pdu>> : public byte_buffer
|
||||
{
|
||||
byte_buffer_impl(std::unique_ptr<vanetza::geonet::Pdu> pdu) :
|
||||
m_pdu(std::move(pdu)) {}
|
||||
|
||||
void convert(ByteBuffer& dest) const override;
|
||||
std::size_t size() const override;
|
||||
std::unique_ptr<byte_buffer> duplicate() const override;
|
||||
|
||||
const std::unique_ptr<vanetza::geonet::Pdu> m_pdu;
|
||||
};
|
||||
|
||||
} // namespace convertible
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Fetch PDU from byte buffer convertible
|
||||
* \param conv source convertible
|
||||
* \return PDU pointer or nullptr if cast failed
|
||||
* */
|
||||
Pdu* pdu_cast(ByteBufferConvertible& conv);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PDU_CONVERSION_HPP_XLCSI42E */
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef PDU_VARIANT_HPP_AAEVKD5M
|
||||
#define PDU_VARIANT_HPP_AAEVKD5M
|
||||
|
||||
#include <vanetza/geonet/extended_pdu.hpp>
|
||||
#include <vanetza/geonet/beacon_header.hpp>
|
||||
#include <vanetza/geonet/gbc_header.hpp>
|
||||
#include <vanetza/geonet/shb_header.hpp>
|
||||
#include <vanetza/geonet/tsb_header.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
typedef ExtendedPdu<ShbHeader> ShbPdu;
|
||||
typedef ExtendedPdu<TsbHeader> TsbPdu;
|
||||
typedef ExtendedPdu<BeaconHeader> BeaconPdu;
|
||||
typedef ExtendedPdu<GeoBroadcastHeader> GbcPdu;
|
||||
|
||||
typedef boost::variant<BeaconPdu, GbcPdu, ShbPdu> PduVariant;
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PDU_VARIANT_HPP_AAEVKD5M */
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef PENDING_PACKET_HPP_ZHSCP1UI
|
||||
#define PENDING_PACKET_HPP_ZHSCP1UI
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/lifetime.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* PendingPacket combines PDU, payload and custom action code for pending processing steps.
|
||||
* This makes it easy to resume packet forwarding steps after buffering, for example.
|
||||
*/
|
||||
template<typename PDU, typename... Args>
|
||||
class PendingPacket
|
||||
{
|
||||
public:
|
||||
using Packet = std::tuple<std::unique_ptr<PDU>, std::unique_ptr<DownPacket>>;
|
||||
using Function = std::function<void(Packet&&, Args&&...)>;
|
||||
|
||||
PendingPacket() = default;
|
||||
|
||||
PendingPacket(Packet&& packet, const Function& fn) :
|
||||
m_packet(std::move(packet)), m_function(fn) {}
|
||||
|
||||
template<typename... OtherArgs>
|
||||
PendingPacket(PendingPacket<PDU, OtherArgs...>&& other, std::function<void(PendingPacket<PDU, OtherArgs...>&&, Args&&...)> fn) :
|
||||
m_packet(std::move(other).packet())
|
||||
{
|
||||
auto other_fn = other.action();
|
||||
m_function = [fn, other_fn](Packet&& packet, Args&&... args) {
|
||||
fn(PendingPacket<PDU, OtherArgs...> { std::move(packet), other_fn }, std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename... OtherArgs, typename... T>
|
||||
PendingPacket(PendingPacket<PDU, OtherArgs...>&& other, T&&... ts) :
|
||||
m_packet(std::move(other).packet())
|
||||
{
|
||||
typename PendingPacket<PDU, OtherArgs...>::Function other_action = other.action();
|
||||
std::function<void(Packet&&)> bound = std::bind(other_action, std::placeholders::_1, std::forward<T>(ts)...);
|
||||
m_function = [bound](Packet&& packet, Args&&... args) {
|
||||
bound(std::move(packet), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
void process(Args&&... args)
|
||||
{
|
||||
if (std::get<0>(m_packet) && std::get<1>(m_packet)) {
|
||||
m_function(std::move(m_packet), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t length() const
|
||||
{
|
||||
const PDU* pdu = std::get<0>(m_packet).get();
|
||||
const DownPacket* payload = std::get<1>(m_packet).get();
|
||||
return (pdu ? get_length(*pdu) : 0) + (payload ? payload->size(OsiLayer::Transport, max_osi_layer()) : 0);
|
||||
}
|
||||
|
||||
Clock::duration reduce_lifetime(Clock::duration queuing_time)
|
||||
{
|
||||
Clock::duration remaining = Clock::duration::zero();
|
||||
PDU* pdu_ptr = std::get<0>(m_packet).get();
|
||||
if (pdu_ptr) {
|
||||
using vanetza::units::clock_cast;
|
||||
Clock::duration packet_lifetime = clock_cast(pdu_ptr->basic().lifetime.decode());
|
||||
if (queuing_time <= Clock::duration::zero()) {
|
||||
remaining = packet_lifetime;
|
||||
} else if (queuing_time < packet_lifetime) {
|
||||
remaining = packet_lifetime - queuing_time;
|
||||
pdu_ptr->basic().lifetime.encode(clock_cast(remaining));
|
||||
} else {
|
||||
pdu_ptr->basic().lifetime = Lifetime::zero();
|
||||
}
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
const PDU& pdu() const
|
||||
{
|
||||
const PDU* ptr = std::get<0>(m_packet).get();
|
||||
assert(ptr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
const DownPacket& payload() const
|
||||
{
|
||||
const DownPacket* ptr = std::get<1>(m_packet).get();
|
||||
assert(ptr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
Function action() const { return m_function; }
|
||||
|
||||
Packet packet() && { return std::move(m_packet); }
|
||||
|
||||
PendingPacket duplicate()
|
||||
{
|
||||
const PDU* pdu_ptr = std::get<0>(m_packet).get();
|
||||
std::unique_ptr<PDU> pdu_dup { pdu_ptr ? new PDU(*pdu_ptr) : nullptr };
|
||||
std::unique_ptr<DownPacket> payload_dup;
|
||||
if (!pdu_ptr || pdu_ptr->secured()) {
|
||||
payload_dup.reset(new DownPacket());
|
||||
} else if (const DownPacket* payload_ptr = std::get<1>(m_packet).get()) {
|
||||
payload_dup = vanetza::duplicate(*payload_ptr);
|
||||
}
|
||||
return PendingPacket(std::make_tuple(std::move(pdu_dup), std::move(payload_dup)), m_function);
|
||||
}
|
||||
|
||||
private:
|
||||
Packet m_packet;
|
||||
Function m_function;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* PENDING_PACKET_HPP_ZHSCP1UI */
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <vanetza/common/position_provider.hpp>
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <vanetza/geonet/position_updater.hpp>
|
||||
#include <vanetza/geonet/router.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <chrono>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
PositionUpdater::PositionUpdater(Runtime& rt, PositionProvider& position, Router& router) :
|
||||
m_runtime(rt), m_positioning(position), m_router(router)
|
||||
{
|
||||
update_rate(m_router.get_mib().itsGnMinimumUpdateFrequencyEPV);
|
||||
}
|
||||
|
||||
PositionUpdater::~PositionUpdater()
|
||||
{
|
||||
m_runtime.cancel(this);
|
||||
}
|
||||
|
||||
void PositionUpdater::schedule()
|
||||
{
|
||||
if (m_interval > Clock::duration::zero()) {
|
||||
m_runtime.schedule(m_interval, [this](Clock::time_point) {
|
||||
m_router.update_position(m_positioning.position_fix());
|
||||
schedule();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
void PositionUpdater::update_rate(Clock::duration interval)
|
||||
{
|
||||
m_interval = interval;
|
||||
m_runtime.cancel(this); /*< cancel previously scheduled callback */
|
||||
schedule();
|
||||
}
|
||||
|
||||
void PositionUpdater::update_rate(units::Frequency rate)
|
||||
{
|
||||
if (rate > units::Frequency::from_value(0.0)) {
|
||||
using namespace std::chrono;
|
||||
const duration<double> interval { 1.0 / rate / units::si::second };
|
||||
update_rate(duration_cast<Clock::duration>(interval));
|
||||
} else {
|
||||
update_rate(Clock::duration::zero());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef POSITION_UPDATER_HPP_KMWXTJRO
|
||||
#define POSITION_UPDATER_HPP_KMWXTJRO
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/units/frequency.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
|
||||
// forward declarations
|
||||
class Runtime;
|
||||
class PositionProvider;
|
||||
namespace geonet { class Router; }
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* PositionUpdater helps updating a Router's position vector periodically
|
||||
*/
|
||||
class PositionUpdater
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create PositionUpdater scheduling position updates read from position provider
|
||||
* \note the default update rate is derived from router's MIB.
|
||||
* \param runtime where updater schedules its callback
|
||||
* \param pos position updates are read from this source
|
||||
* \param router position sink
|
||||
*/
|
||||
PositionUpdater(Runtime& runtime, PositionProvider& pos, Router& router);
|
||||
~PositionUpdater();
|
||||
|
||||
/**
|
||||
* Change rate at which update is looking up new positions
|
||||
* \note MIB setting is not affected
|
||||
* \note an update interval smaller than zero disables updates
|
||||
* \param interval update interval
|
||||
*/
|
||||
void update_rate(Clock::duration interval);
|
||||
void update_rate(units::Frequency);
|
||||
|
||||
private:
|
||||
void schedule();
|
||||
|
||||
Runtime& m_runtime;
|
||||
PositionProvider& m_positioning;
|
||||
Router& m_router;
|
||||
Clock::duration m_interval;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* POSITION_UPDATER_HPP_KMWXTJRO */
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "areas.hpp"
|
||||
#include "position_vector.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t LongPositionVector::length_bytes;
|
||||
constexpr std::size_t ShortPositionVector::length_bytes;
|
||||
|
||||
LongPositionVector::LongPositionVector() : position_accuracy_indicator(false)
|
||||
{
|
||||
}
|
||||
|
||||
GeodeticPosition LongPositionVector::position() const
|
||||
{
|
||||
return GeodeticPosition {
|
||||
static_cast<units::GeoAngle>(latitude),
|
||||
static_cast<units::GeoAngle>(longitude)
|
||||
};
|
||||
}
|
||||
|
||||
ShortPositionVector::ShortPositionVector(const LongPositionVector& lpv) :
|
||||
gn_addr(lpv.gn_addr), timestamp(lpv.timestamp),
|
||||
latitude(lpv.latitude), longitude(lpv.longitude)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const LongPositionVector& lhs, const LongPositionVector& rhs)
|
||||
{
|
||||
return lhs.gn_addr == rhs.gn_addr
|
||||
&& lhs.timestamp == rhs.timestamp
|
||||
&& lhs.latitude == rhs.latitude
|
||||
&& lhs.longitude == rhs.longitude
|
||||
&& lhs.speed == rhs.speed
|
||||
&& lhs.heading == rhs.heading
|
||||
&& lhs.position_accuracy_indicator == rhs.position_accuracy_indicator;
|
||||
}
|
||||
|
||||
bool operator!=(const LongPositionVector& lhs, const LongPositionVector& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(const ShortPositionVector& lhs, const ShortPositionVector& rhs)
|
||||
{
|
||||
return lhs.gn_addr == rhs.gn_addr
|
||||
&& lhs.timestamp == rhs.timestamp
|
||||
&& lhs.latitude == rhs.latitude
|
||||
&& lhs.longitude == rhs.longitude;
|
||||
}
|
||||
|
||||
bool operator!=(const ShortPositionVector& lhs, const ShortPositionVector& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool is_empty(const LongPositionVector& pv)
|
||||
{
|
||||
static const LongPositionVector zero;
|
||||
return pv == zero;
|
||||
}
|
||||
|
||||
bool is_valid(const LongPositionVector& pv)
|
||||
{
|
||||
static const geo_angle_i32t limit_lat { 90.0 * units::degree };
|
||||
static const geo_angle_i32t limit_lon { 180.0 * units::degree };
|
||||
static const heading_u16t limit_hdg { 360.0 * units::degree };
|
||||
|
||||
if (is_empty(pv)) {
|
||||
return false;
|
||||
} else if (pv.latitude < -limit_lat || pv.latitude > limit_lat) {
|
||||
return false;
|
||||
} else if (pv.longitude < -limit_lon || pv.longitude > limit_lon) {
|
||||
return false;
|
||||
} else if (pv.heading > limit_hdg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void serialize(const LongPositionVector& lpv, OutputArchive& ar)
|
||||
{
|
||||
serialize(lpv.gn_addr, ar);
|
||||
serialize(lpv.timestamp, ar);
|
||||
serialize(lpv.latitude, ar);
|
||||
serialize(lpv.longitude, ar);
|
||||
uint16_t paiAndSpeed = lpv.speed.value().raw();
|
||||
paiAndSpeed |= lpv.position_accuracy_indicator ? 0x8000 : 0x0000;
|
||||
serialize(host_cast(paiAndSpeed), ar);
|
||||
serialize(lpv.heading, ar);
|
||||
}
|
||||
|
||||
void deserialize(LongPositionVector& lpv, InputArchive& ar)
|
||||
{
|
||||
deserialize(lpv.gn_addr, ar);
|
||||
deserialize(lpv.timestamp, ar);
|
||||
deserialize(lpv.latitude, ar);
|
||||
deserialize(lpv.longitude, ar);
|
||||
uint16_t paiAndSpeed = 0;
|
||||
deserialize(paiAndSpeed, ar);
|
||||
lpv.position_accuracy_indicator = ((paiAndSpeed & 0x8000) != 0);
|
||||
lpv.speed = LongPositionVector::speed_u15t::from_value(paiAndSpeed);
|
||||
deserialize(lpv.heading, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef POSITION_VECTOR_HPP_WJAGEOCS
|
||||
#define POSITION_VECTOR_HPP_WJAGEOCS
|
||||
|
||||
#include <vanetza/common/bit_number.hpp>
|
||||
#include <vanetza/geodesy/position.hpp>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <vanetza/geonet/timestamp.hpp>
|
||||
#include <vanetza/geonet/units.hpp>
|
||||
#include <boost/units/quantity.hpp>
|
||||
#include <boost/units/systems/si/velocity.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
using geodesy::GeodeticPosition;
|
||||
|
||||
class LongPositionVector
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t length_bytes = 24;
|
||||
typedef boost::units::quantity<boost::units::make_scaled_unit<
|
||||
boost::units::si::velocity,
|
||||
boost::units::scale<10, boost::units::static_rational<-2>>
|
||||
>::type, BitNumber<uint_fast16_t, 15>> speed_u15t; // 1/100 m/s
|
||||
|
||||
Address gn_addr;
|
||||
Timestamp timestamp; // ms since 2004-01-01 00:00:00.000
|
||||
geo_angle_i32t latitude;
|
||||
geo_angle_i32t longitude;
|
||||
bool position_accuracy_indicator;
|
||||
speed_u15t speed;
|
||||
heading_u16t heading;
|
||||
|
||||
LongPositionVector();
|
||||
GeodeticPosition position() const;
|
||||
};
|
||||
|
||||
bool operator==(const LongPositionVector& lhs, const LongPositionVector& rhs);
|
||||
bool operator!=(const LongPositionVector& lhs, const LongPositionVector& rhs);
|
||||
|
||||
/**
|
||||
* Check if position vector is empty (default)
|
||||
* \return true if all data fields are zero
|
||||
*/
|
||||
bool is_empty(const LongPositionVector&);
|
||||
|
||||
/**
|
||||
* Check if position vector contains valid data,
|
||||
* i.e. not empty and latitude, longitude, and heading values within range
|
||||
* \return true if all data fields are valid
|
||||
*/
|
||||
bool is_valid(const LongPositionVector&);
|
||||
|
||||
void serialize(const LongPositionVector&, OutputArchive&);
|
||||
void deserialize(LongPositionVector&, InputArchive&);
|
||||
|
||||
class ShortPositionVector
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t length_bytes = 20;
|
||||
|
||||
ShortPositionVector() = default;
|
||||
ShortPositionVector(const ShortPositionVector&) = default;
|
||||
ShortPositionVector& operator=(const ShortPositionVector&) = default;
|
||||
explicit ShortPositionVector(const LongPositionVector&);
|
||||
|
||||
Address gn_addr;
|
||||
Timestamp timestamp;
|
||||
geo_angle_i32t latitude;
|
||||
geo_angle_i32t longitude;
|
||||
};
|
||||
|
||||
bool operator==(const ShortPositionVector& lhs, const ShortPositionVector& rhs);
|
||||
bool operator!=(const ShortPositionVector& lhs, const ShortPositionVector& rhs);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* POSITION_VECTOR_HPP_WJAGEOCS */
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "repeater.hpp"
|
||||
#include "data_request.hpp"
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
Repeater::Repetition::Repetition(const DataRequestVariant& request, const DownPacket& payload) :
|
||||
m_request(request), m_payload(new DownPacket(payload))
|
||||
{
|
||||
}
|
||||
|
||||
Repeater::Repeater(Runtime& rt, const Callback& cb) : m_repeat_fn(cb), m_runtime(rt)
|
||||
{
|
||||
assert(m_repeat_fn);
|
||||
}
|
||||
|
||||
Repeater::~Repeater()
|
||||
{
|
||||
m_runtime.cancel(this);
|
||||
}
|
||||
|
||||
void Repeater::add(const DataRequestVariant& request,
|
||||
const DataRequest::Repetition& repetition, const DownPacket& payload)
|
||||
{
|
||||
if (has_further_repetition(repetition)) {
|
||||
const auto next_repetition = m_runtime.now() + units::clock_cast(repetition.interval);
|
||||
m_repetitions.emplace_front(request, payload);
|
||||
auto added = m_repetitions.begin();
|
||||
auto then = std::placeholders::_1;
|
||||
m_runtime.schedule(next_repetition, std::bind<void>(&Repeater::trigger, this, added, then), this);
|
||||
}
|
||||
}
|
||||
|
||||
void Repeater::trigger(std::list<Repetition>::iterator rep, Clock::time_point invocation)
|
||||
{
|
||||
Repetition& repetition = *rep;
|
||||
DataRequest& request = access_request(repetition.m_request);
|
||||
assert(request.repetition);
|
||||
|
||||
// reduce remaining interval by one step and occurred triggering delay
|
||||
decrement_by_one(*request.repetition);
|
||||
auto delayed = m_runtime.now() - invocation;
|
||||
request.repetition->maximum -= units::clock_cast(delayed);
|
||||
|
||||
// reset repetition data if this is the last repetition
|
||||
if (!has_further_repetition(request)) {
|
||||
request.repetition.reset();
|
||||
}
|
||||
|
||||
m_repeat_fn(repetition.m_request, std::move(repetition.m_payload));
|
||||
m_repetitions.erase(rep);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef REPEATER_HPP_AH49FXB1
|
||||
#define REPEATER_HPP_AH49FXB1
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
// forward declaration
|
||||
class Runtime;
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Repeater eases handling of packet repetitions.
|
||||
* Packet repetitions are repeated requests with same payload.
|
||||
*/
|
||||
class Repeater
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void(const DataRequestVariant&, std::unique_ptr<DownPacket>)>;
|
||||
|
||||
/**
|
||||
* Create a Repeater instance
|
||||
* \param rt runtime used for scheduling repetitions
|
||||
* \param cb callback to be invoked when repetition is due
|
||||
*/
|
||||
Repeater(Runtime& rt, const Callback& cb);
|
||||
~Repeater();
|
||||
|
||||
/**
|
||||
* Add another repetition
|
||||
* \param request any kind of GeoNet data request
|
||||
* \param payload request's payload
|
||||
*/
|
||||
template<class REQUEST>
|
||||
void add(const REQUEST& request, const DownPacket& payload)
|
||||
{
|
||||
if (request.repetition) {
|
||||
add(request, *request.repetition, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Repetition
|
||||
{
|
||||
Repetition(const DataRequestVariant&, const DownPacket&);
|
||||
|
||||
DataRequestVariant m_request;
|
||||
std::unique_ptr<DownPacket> m_payload;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add packet repetition to internal book keeping
|
||||
*/
|
||||
void add(const DataRequestVariant&, const DataRequest::Repetition&, const DownPacket&);
|
||||
|
||||
/**
|
||||
* Triggered when repetition is due according to runtime
|
||||
*/
|
||||
void trigger(std::list<Repetition>::iterator, Clock::time_point);
|
||||
|
||||
std::list<Repetition> m_repetitions;
|
||||
Callback m_repeat_fn;
|
||||
Runtime& m_runtime;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* REPEATER_HPP_AH49FXB1 */
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#ifndef REPETITION_DISPATCHER_HPP_GO4BAEVL
|
||||
#define REPETITION_DISPATCHER_HPP_GO4BAEVL
|
||||
|
||||
#include <vanetza/geonet/router.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class RepetitionDispatcher : public boost::static_visitor<void>
|
||||
{
|
||||
public:
|
||||
RepetitionDispatcher(Router& _router, std::unique_ptr<DownPacket> _payload)
|
||||
: router(_router), payload(std::move(_payload))
|
||||
{}
|
||||
|
||||
template<class REQUEST>
|
||||
void operator()(const REQUEST& request)
|
||||
{
|
||||
router.request(request, std::move(payload));
|
||||
}
|
||||
|
||||
private:
|
||||
Router& router;
|
||||
std::unique_ptr<DownPacket> payload;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* REPETITION_DISPATCHER_HPP_GO4BAEVL */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,582 @@
|
||||
#ifndef ROUTER_HPP_UKYYCAR0
|
||||
#define ROUTER_HPP_UKYYCAR0
|
||||
|
||||
#include <vanetza/common/byte_order.hpp>
|
||||
#include <vanetza/common/hook.hpp>
|
||||
#include <vanetza/common/its_aid.hpp>
|
||||
#include <vanetza/access/ethertype.hpp>
|
||||
#include <vanetza/geonet/beacon_header.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_buffer.hpp>
|
||||
#include <vanetza/geonet/common_header.hpp>
|
||||
#include <vanetza/geonet/extended_pdu.hpp>
|
||||
#include <vanetza/geonet/gbc_header.hpp>
|
||||
#include <vanetza/geonet/gbc_memory.hpp>
|
||||
#include <vanetza/geonet/interface.hpp>
|
||||
#include <vanetza/geonet/location_table.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <vanetza/geonet/packet_buffer.hpp>
|
||||
#include <vanetza/geonet/pending_packet.hpp>
|
||||
#include <vanetza/geonet/pdu.hpp>
|
||||
#include <vanetza/geonet/pdu_variant.hpp>
|
||||
#include <vanetza/geonet/repeater.hpp>
|
||||
#include <vanetza/geonet/sequence_number.hpp>
|
||||
#include <vanetza/geonet/shb_header.hpp>
|
||||
#include <vanetza/geonet/timestamp.hpp>
|
||||
#include <vanetza/units/length.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
#include <vanetza/security/security_entity.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <map>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
|
||||
// forward declarations
|
||||
class MacAddress;
|
||||
struct PositionFix;
|
||||
class Runtime;
|
||||
|
||||
namespace dcc
|
||||
{
|
||||
struct DataRequest;
|
||||
class RequestInterface;
|
||||
} // namespace dcc
|
||||
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
extern const access::EtherType ether_type;
|
||||
|
||||
class DccFieldGenerator;
|
||||
class IndicationContext;
|
||||
class IndicationContextBasic;
|
||||
class NextHop;
|
||||
class TransportInterface;
|
||||
struct ShbDataRequest;
|
||||
struct GbcDataRequest;
|
||||
struct DataConfirm;
|
||||
struct DataIndication;
|
||||
struct LinkLayer;
|
||||
|
||||
/**
|
||||
* Router is the central entity for GeoNet communication
|
||||
*
|
||||
* Incoming and outgoing GeoNet packets are handled by the router.
|
||||
* It may even dispatch own packets (beacons) if necessary.
|
||||
*
|
||||
* This implementation follows EN 302 636-4-1 v1.3.1
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<DataRequest> DataRequestPtr;
|
||||
typedef std::unique_ptr<Pdu> PduPtr;
|
||||
typedef std::unique_ptr<DownPacket> DownPacketPtr;
|
||||
typedef std::unique_ptr<UpPacket> UpPacketPtr;
|
||||
|
||||
using PendingPacketForwarding = PendingPacket<GbcPdu, const MacAddress&>;
|
||||
|
||||
/// Reason for packet drop used by drop hook
|
||||
enum class PacketDropReason
|
||||
{
|
||||
Parse_Basic_Header,
|
||||
Parse_Common_Header,
|
||||
Parse_Secured_Header,
|
||||
Parse_Extended_Header,
|
||||
ITS_Protocol_Version,
|
||||
Decap_Unsuccessful_Non_Strict,
|
||||
Decap_Unsuccessful_Strict,
|
||||
Hop_Limit,
|
||||
Payload_Size,
|
||||
Security_Entity_Missing,
|
||||
Packet_Size,
|
||||
Internal_Error,
|
||||
};
|
||||
|
||||
// Reason for stopping packet forwarding
|
||||
enum class ForwardingStopReason
|
||||
{
|
||||
Hop_Limit,
|
||||
Source_PDR,
|
||||
Sender_PDR,
|
||||
Outside_Destination_Area
|
||||
};
|
||||
|
||||
Router(Runtime&, const MIB&);
|
||||
~Router();
|
||||
|
||||
/**
|
||||
* \brief Request to send payload per single hop broadcast (SHB).
|
||||
* If security is enabled, the message gets encapsulated in a security envelope.
|
||||
* Returns whether data was valid to be sent.
|
||||
*
|
||||
* \param request
|
||||
* \param payload from upper layers
|
||||
* \return result code if packet has been accepted
|
||||
*/
|
||||
DataConfirm request(const ShbDataRequest&, DownPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Request to send payload per GeoBroadcast (GBC).
|
||||
* If security is enabled, the message gets encapsulated in a security envelope.
|
||||
* Returns whether data was valid to be sent.
|
||||
*
|
||||
* \param request
|
||||
* \param payload from upper layers
|
||||
* \return result code if packet has been accepted
|
||||
*/
|
||||
DataConfirm request(const GbcDataRequest&, DownPacketPtr);
|
||||
|
||||
// These three requests are not supported yet
|
||||
DataConfirm request(const GucDataRequest&, DownPacketPtr);
|
||||
DataConfirm request(const GacDataRequest&, DownPacketPtr);
|
||||
DataConfirm request(const TsbDataRequest&, DownPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Handle the received packet on network layer.
|
||||
* Packet handling involves these steps:
|
||||
* - header processing
|
||||
* - packet forwarding
|
||||
* - passing to transport layer
|
||||
* - security decapsulation
|
||||
*
|
||||
* \param packet received packet from access layer
|
||||
* \param sender MAC address of sender
|
||||
* \param destination MAC address of destination (might be broadcast)
|
||||
*/
|
||||
void indicate(UpPacketPtr, const MacAddress& sender, const MacAddress& destination);
|
||||
|
||||
/**
|
||||
* \brief When a packet is dropped, this Hook is invoked
|
||||
* \tparam PacketDropReason why Router decided to drop packet
|
||||
*/
|
||||
Hook<PacketDropReason> packet_dropped;
|
||||
|
||||
/**
|
||||
* \brief When packet forwarding is stopped, this Hook is invoked
|
||||
* \tparam ForwardingStopReason why Router decided not to forward packet
|
||||
*/
|
||||
Hook<ForwardingStopReason> forwarding_stopped;
|
||||
|
||||
/**
|
||||
* \brief Update router's local position vector
|
||||
*
|
||||
* \param fix current position fix
|
||||
*/
|
||||
void update_position(const PositionFix&);
|
||||
|
||||
/**
|
||||
* \brief Register a transport protocol handler.
|
||||
*
|
||||
* \param proto register handler for this upper protocol
|
||||
* \param ifc use this interface or disable handling if nullptr
|
||||
*/
|
||||
void set_transport_handler(UpperProtocol proto, TransportInterface* ifc);
|
||||
|
||||
/**
|
||||
* \brief Register security entity used when itsGnSecurity is enabled
|
||||
*
|
||||
* \param entity security entity
|
||||
*/
|
||||
void set_security_entity(security::SecurityEntity* entity);
|
||||
|
||||
/**
|
||||
* \brief Register access layer interface
|
||||
*
|
||||
* \param ifc interface used for passing packets down to access layer
|
||||
*/
|
||||
void set_access_interface(dcc::RequestInterface* ifc);
|
||||
|
||||
/**
|
||||
* \brief Register generator for DCC-MCO fields
|
||||
*
|
||||
* \param dcc DCC-MCO field generator or nullptr for disabling feature
|
||||
*/
|
||||
void set_dcc_field_generator(DccFieldGenerator* dcc);
|
||||
|
||||
/**
|
||||
* \brief Set Router's own GeoNetworking address
|
||||
*
|
||||
* \param addr
|
||||
*/
|
||||
void set_address(const Address&);
|
||||
|
||||
/**
|
||||
* \brief Get Management Information Base (MIB)
|
||||
* \return read-only reference to MIB
|
||||
*/
|
||||
const MIB& get_mib() const { return m_mib; }
|
||||
|
||||
/**
|
||||
* \brief Get the Contention-Based-Forwarding buffer
|
||||
*
|
||||
* \return read-only reference to CBF packet buffer
|
||||
*/
|
||||
const CbfPacketBuffer& get_cbf_buffer() const { return m_cbf_buffer; }
|
||||
|
||||
/**
|
||||
* \brief Get the LocationTable.
|
||||
* The table holds information about neighbouring ITS-Routers.
|
||||
*
|
||||
* \return read-only reference to LocationTable
|
||||
*/
|
||||
const LocationTable& get_location_table() const { return m_location_table; }
|
||||
|
||||
/**
|
||||
* \brief Get the local position vector.
|
||||
* This vector describes the current position of the router.
|
||||
*
|
||||
* \return read-only reference to LongPositionVector
|
||||
*/
|
||||
const LongPositionVector& get_local_position_vector() const { return m_local_position_vector; }
|
||||
|
||||
/**
|
||||
* rief Flush the broadcast and unicast forwarding buffers now.
|
||||
* TS 102 723-8 V1.1.1 clause 6.3.1.3: between an identifier change PREPARE and COMMIT
|
||||
* "caches shall be flushed" so no packet with the old identifier stays queued.
|
||||
*/
|
||||
void flush_forwarding_buffers();
|
||||
|
||||
/**
|
||||
* \brief Check if router is outside the sectorial contention area
|
||||
* See TS 102 636-4-1 v1.2.3 section E.4 and figure E.2 for details.
|
||||
*
|
||||
* \param sender
|
||||
* \param forwarder
|
||||
* \return bool true if either sender or forwarder is outside
|
||||
*/
|
||||
bool outside_sectorial_contention_area(const MacAddress& sender, const MacAddress& forwarder) const;
|
||||
|
||||
/**
|
||||
* \brief Set seed for internal random number generator (RNG)
|
||||
* RNG is used e.g. for random Beacon jitter
|
||||
*
|
||||
* \param seed reset RNG's state to this seed
|
||||
*/
|
||||
void set_random_seed(std::uint_fast32_t seed);
|
||||
|
||||
/**
|
||||
* Forwarding algorithm selection procedure as given by Annex D
|
||||
* \param pdu GeoNetworking PDU
|
||||
* \param payload packet payload
|
||||
* \param ll link-layer control info (unavailable for source operations)
|
||||
* \return routing decision (next hop's address, buffered, or discarded)
|
||||
*/
|
||||
NextHop forwarding_algorithm_selection(PendingPacketForwarding&&, const LinkLayer* ll = nullptr);
|
||||
|
||||
private:
|
||||
typedef std::map<UpperProtocol, TransportInterface*> transport_map_t;
|
||||
|
||||
/**
|
||||
* \brief Send Beacon packet to all neighbours with updated position vector.
|
||||
* Only to be called when the beacon timer expires.
|
||||
*/
|
||||
void on_beacon_timer_expired();
|
||||
|
||||
/**
|
||||
* \brief Reschedule timer for next Beacon transmission
|
||||
* Timer will be scheduled according to MIB's Beacon timer settings.
|
||||
*/
|
||||
void reset_beacon_timer();
|
||||
|
||||
/**
|
||||
* \brief Reschedule timer for next Beacon transmission
|
||||
* \param next Duration until next transmission
|
||||
*/
|
||||
void reset_beacon_timer(Clock::duration next);
|
||||
|
||||
/**
|
||||
* \brief Process BasicHeader at packet indication.
|
||||
* \param ctx Context holding data for further parsing
|
||||
*/
|
||||
void indicate_basic(IndicationContextBasic&);
|
||||
|
||||
/**
|
||||
* \brief Process CommonHeader at packet indication.
|
||||
* \param ctx Context holding data for further parsing
|
||||
* \param basic Previously decoded BasicHeader
|
||||
*/
|
||||
void indicate_common(IndicationContext&, const BasicHeader&);
|
||||
|
||||
/**
|
||||
* \brief Process ExtendedHeader at packet indication.
|
||||
* \param ctx Context holding data for further parsing
|
||||
* \param common Previously decoded CommonHeader
|
||||
*/
|
||||
void indicate_extended(IndicationContext&, const CommonHeader&);
|
||||
|
||||
/**
|
||||
* \brief Process SecuredMessage at packet indication.
|
||||
* \param ctx Context holding data for further parsing
|
||||
* \param basic Previously decoded BasicHeader
|
||||
*/
|
||||
void indicate_secured(IndicationContextBasic&, const BasicHeader&);
|
||||
|
||||
/**
|
||||
* \brief Process ExtendedHeader information.
|
||||
* Update router's LocationTable and neighbour relationship.
|
||||
*
|
||||
* \param pdu containing the ExtendedHeader
|
||||
* \param packet received packet
|
||||
* \param ll link-layer control info
|
||||
* \return pass up decision (always false for Beacons)
|
||||
*/
|
||||
bool process_extended(const ExtendedPduConstRefs<BeaconHeader>&, const UpPacket&, const LinkLayer& ll);
|
||||
|
||||
/**
|
||||
* \brief Process ExtendedHeader information.
|
||||
* Update router's LocationTable and neighbour relationship.
|
||||
* Pass packet up to transport layer for further processing.
|
||||
*
|
||||
* \param pdu containing the ExtendedHeader
|
||||
* \param packet received packet
|
||||
* \param ll link-layer control info
|
||||
* \return pass up decision (true for all non-duplicate SHBs)
|
||||
*/
|
||||
bool process_extended(const ExtendedPduConstRefs<ShbHeader>&, const UpPacket&, const LinkLayer& ll);
|
||||
|
||||
/**
|
||||
* \brief packet handling of received TSB packet
|
||||
*
|
||||
* \param pdu PDU with TSB header
|
||||
* \param packet received packet
|
||||
* \param ll link-layer control info
|
||||
* \return pass up decision (true for all non-duplicate TSBs)
|
||||
*/
|
||||
bool process_extended(const ExtendedPduConstRefs<TsbHeader>&, const UpPacket&, const LinkLayer& ll);
|
||||
|
||||
/**
|
||||
* \brief Process ExtendedHeader information.
|
||||
* Update router's LocationTable and neighbour relationship.
|
||||
* Pass packet up to transport layer for further processing.
|
||||
* Forward packets.
|
||||
*
|
||||
* \param pdu containing the ExtendedHeader
|
||||
* \param packet received packet
|
||||
* \param ll link-layer control info
|
||||
* \return pass up decision (depends on addressed area and router position)
|
||||
*/
|
||||
bool process_extended(const ExtendedPduConstRefs<GeoBroadcastHeader>&, const UpPacket&, const LinkLayer& ll);
|
||||
|
||||
/**
|
||||
* \brief Send all packets in the broadcast forwarding buffer with expired waiting time.
|
||||
*/
|
||||
void flush_broadcast_forwarding_buffer();
|
||||
|
||||
/**
|
||||
* \brief Send all matching packets in the unicast forwarding buffer with expired waiting time.
|
||||
* \param addr unicast packets for this address
|
||||
*/
|
||||
void flush_unicast_forwarding_buffer(const Address& addr);
|
||||
|
||||
/**
|
||||
* \brief Executes media specific functionalities
|
||||
* Details are described in TS 102 636-4-2.
|
||||
*
|
||||
* \param profile e.g. ITS-G5
|
||||
*/
|
||||
void execute_media_procedures(CommunicationProfile);
|
||||
|
||||
/**
|
||||
* \brief Executes ITS-G5 media specific procedures
|
||||
* Details are described in TS 102 636-4-2.
|
||||
*/
|
||||
void execute_itsg5_procedures();
|
||||
|
||||
/**
|
||||
* \brief Pass down the packet to the access layer.
|
||||
*
|
||||
* \param addr MAC address of destination
|
||||
* \param pdu header information
|
||||
* \param payload Packet payload
|
||||
*/
|
||||
void pass_down(const MacAddress&, PduPtr, DownPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Send packet using the information in the DataRequest.
|
||||
* The packet is formed using the data in PDU and payload.
|
||||
*
|
||||
* \param request containing transmission parameters
|
||||
* \param pdu header information
|
||||
* \param payload Packet payload
|
||||
*/
|
||||
void pass_down(const dcc::DataRequest&, PduPtr, DownPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Pass packet up to the transport layer.
|
||||
*
|
||||
* \param ind containing network information
|
||||
* \param packet payload to be passed up to the next layer
|
||||
*/
|
||||
void pass_up(const DataIndication&, UpPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Decide if GBC packet shall be passed up to transport layer.
|
||||
*
|
||||
* \param within_destination is router located within destination area
|
||||
* \param gbc GeoBroadcast header
|
||||
*
|
||||
* \return true if packet shall be passed up
|
||||
*/
|
||||
bool decide_pass_up(bool within_destination, const GeoBroadcastHeader& gbc);
|
||||
|
||||
/**
|
||||
* \brief Helper method to handle duplicate addresses.
|
||||
* If own address collides with the address of a received packet
|
||||
* Router's address is set to a new random address.
|
||||
* \note Behaviour depends on MIB's itsGnLocalAddrConfMethod.
|
||||
*
|
||||
* \param source address of source (from packet header)
|
||||
* \param sender address of sender (link layer)
|
||||
*/
|
||||
void detect_duplicate_address(const Address& source, const MacAddress& sender);
|
||||
|
||||
/**
|
||||
* \brief Detect duplicate packets
|
||||
* See EN 302 636-4-1 v1.3.1 Annex A.2
|
||||
*
|
||||
* \param source source address
|
||||
* \param sn sequence number
|
||||
* \return true if packet is detected as a duplicate
|
||||
*/
|
||||
bool detect_duplicate_packet(const Address& source, SequenceNumber sn);
|
||||
|
||||
/**
|
||||
* \brief Determine next hop for greedy forwarding.
|
||||
* See EN 302 636-4-1 v1.3.1 Annex E.2
|
||||
*
|
||||
* \param pdu
|
||||
* \param payload
|
||||
* \return next hop
|
||||
*/
|
||||
NextHop greedy_forwarding(PendingPacketForwarding&&);
|
||||
|
||||
/**
|
||||
* \brief Determine next hop for non-area contention-based forwarding
|
||||
* See EN 302 636-4-1 v1.3.1 Annex E.3
|
||||
*
|
||||
* \param pdu
|
||||
* \param payload
|
||||
* \param sender optional sender MAC address (if not first hop)
|
||||
* \return next hop
|
||||
*/
|
||||
NextHop non_area_contention_based_forwarding(PendingPacketForwarding&&, const MacAddress* sender);
|
||||
|
||||
/**
|
||||
* \brief Determine next hop for area contention-based forwarding
|
||||
* See EN 302 636-4-1 v1.3.1 Annex F.3
|
||||
*
|
||||
* \param pdu
|
||||
* \param payload
|
||||
* \param sender optional sender MAC address (if not first hop)
|
||||
* \return next hop
|
||||
*/
|
||||
NextHop area_contention_based_forwarding(PendingPacketForwarding&&, const MacAddress* sender);
|
||||
|
||||
/**
|
||||
* \brief Determine CBF buffering time for a packet.
|
||||
* Complies to EN 302 636-4-1 v1.3.1 Annex E.3 (non-area CBF, eq. E.1) and F.3 (area CBF, eq. F.1)
|
||||
*
|
||||
* \param dist distance or progress (interpretation depends on non-area vs. area CBF)
|
||||
* \return CBF time-out
|
||||
*/
|
||||
units::Duration timeout_cbf(units::Length distance) const;
|
||||
|
||||
/**
|
||||
* \brief Determine (area) CBF buffering time for a packet from a sender
|
||||
*
|
||||
* This is a shortcut for a re-curring pattern in Annex F.3 and F.4:
|
||||
* 1) sender position is looked up in location table
|
||||
* 2) position accuracy of sender is validated (if it is found)
|
||||
* 3) progress is then distance between sender and local router
|
||||
*
|
||||
* \param sender MAC address of sender
|
||||
* \return CBF time-out
|
||||
*/
|
||||
units::Duration timeout_cbf(const MacAddress& sender) const;
|
||||
|
||||
/**
|
||||
* \brief Determine next hop for area advanced forwarding
|
||||
* See EN 302 636-4-1 v1.3.1 Annex F.4
|
||||
*
|
||||
* \param pdu
|
||||
* \param payload
|
||||
* \param ll optional link-layer control info (if not source operations)
|
||||
* \return next hop
|
||||
*/
|
||||
NextHop area_advanced_forwarding(PendingPacketForwarding&&, const LinkLayer* sender);
|
||||
|
||||
/**
|
||||
* \brief Callback function for dispatching a packet repetition.
|
||||
* Invoked by Repeater when a scheduled repetition is due.
|
||||
*
|
||||
* \param request
|
||||
* \param payload
|
||||
*/
|
||||
void dispatch_repetition(const DataRequestVariant&, DownPacketPtr);
|
||||
|
||||
/**
|
||||
* \brief Encaspulate a packet according to security profile
|
||||
*
|
||||
* \param aid ITS-AID
|
||||
* \param ssp Service Specific Permissions
|
||||
* \param pdu PDU
|
||||
* \param packet Packet with payload
|
||||
*/
|
||||
DownPacketPtr encap_packet(ItsAid aid, ByteBuffer ssp, ByteBuffer context, Pdu& pdu, DownPacketPtr packet);
|
||||
|
||||
/**
|
||||
* \brief Create an initialized Single-Hop-Broadcast PDU
|
||||
*
|
||||
* \param request
|
||||
* \return PDU object
|
||||
*/
|
||||
std::unique_ptr<ShbPdu> create_shb_pdu(const ShbDataRequest&);
|
||||
|
||||
/**
|
||||
* \brief Create an initialzed Beacon PDU
|
||||
*
|
||||
* \return PDU object
|
||||
*/
|
||||
std::unique_ptr<BeaconPdu> create_beacon_pdu();
|
||||
|
||||
/**
|
||||
* \brief Create an initialized GeoBroadcast PDU
|
||||
*
|
||||
* \param request
|
||||
* \return PDU object
|
||||
*/
|
||||
std::unique_ptr<GbcPdu> create_gbc_pdu(const GbcDataRequest&);
|
||||
|
||||
const MIB& m_mib;
|
||||
Runtime& m_runtime;
|
||||
dcc::RequestInterface* m_request_interface;
|
||||
DccFieldGenerator* m_dcc_field_generator;
|
||||
security::SecurityEntity* m_security_entity;
|
||||
transport_map_t m_transport_ifcs;
|
||||
LocationTable m_location_table;
|
||||
PacketBuffer m_bc_forward_buffer;
|
||||
PacketBuffer m_uc_forward_buffer;
|
||||
CbfPacketBuffer m_cbf_buffer;
|
||||
LongPositionVector m_local_position_vector;
|
||||
SequenceNumber m_local_sequence_number;
|
||||
Repeater m_repeater;
|
||||
std::mt19937 m_random_gen;
|
||||
GbcMemory m_gbc_memory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string representation of packet drop reason
|
||||
* \param pdr packet drop reason code
|
||||
* \return string representation
|
||||
*/
|
||||
std::string stringify(Router::PacketDropReason pdr);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* ROUTER_HPP_UKYYCAR0 */
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <vanetza/geonet/secured_pdu.hpp>
|
||||
#include <vanetza/geonet/serialization_buffer.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
SecuredPdu::SecuredPdu(const Pdu& pdu) :
|
||||
common(pdu.common()), extended(pdu.extended_variant())
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(const SecuredPdu& pdu, OutputArchive& ar)
|
||||
{
|
||||
serialize(pdu.common, ar);
|
||||
serialize(pdu.extended, ar);
|
||||
}
|
||||
|
||||
SecuredPdu* secured_pdu_cast(ByteBufferConvertible& convertible)
|
||||
{
|
||||
using convertible_pdu_t = convertible::byte_buffer_impl<SecuredPdu>;
|
||||
auto convertible_pdu = dynamic_cast<convertible_pdu_t*>(convertible.ptr());
|
||||
return convertible_pdu ? &convertible_pdu->pdu : nullptr;
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
|
||||
namespace convertible
|
||||
{
|
||||
|
||||
byte_buffer_impl<geonet::SecuredPdu>::byte_buffer_impl(const geonet::SecuredPdu& _pdu) :
|
||||
pdu(_pdu)
|
||||
{
|
||||
}
|
||||
|
||||
void byte_buffer_impl<geonet::SecuredPdu>::convert(ByteBuffer& buffer) const
|
||||
{
|
||||
geonet::serialize_into_buffer(pdu, buffer);
|
||||
}
|
||||
|
||||
std::size_t byte_buffer_impl<geonet::SecuredPdu>::size() const
|
||||
{
|
||||
using namespace geonet;
|
||||
return CommonHeader::length_bytes + get_length(pdu.extended);
|
||||
}
|
||||
|
||||
std::unique_ptr<byte_buffer> byte_buffer_impl<geonet::SecuredPdu>::duplicate() const
|
||||
{
|
||||
return std::unique_ptr<byte_buffer_impl> { new byte_buffer_impl(*this) };
|
||||
}
|
||||
|
||||
} // namespace convertible
|
||||
} // namespace vanetza
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef SECURED_PDU_HPP_TVERYI91
|
||||
#define SECURED_PDU_HPP_TVERYI91
|
||||
|
||||
#include <vanetza/common/byte_buffer_convertible.hpp>
|
||||
#include <vanetza/geonet/common_header.hpp>
|
||||
#include <vanetza/geonet/extended_pdu.hpp>
|
||||
#include <vanetza/geonet/header_variant.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* Secured PDU consists of GeoNetworking headers belonging to a secured message's payload
|
||||
*/
|
||||
class SecuredPdu
|
||||
{
|
||||
public:
|
||||
template<typename EXT>
|
||||
SecuredPdu(const ExtendedPdu<EXT>& pdu) :
|
||||
common(pdu.common()), extended(pdu.extended())
|
||||
{
|
||||
}
|
||||
|
||||
SecuredPdu(const Pdu&);
|
||||
|
||||
CommonHeader common;
|
||||
HeaderVariant extended;
|
||||
};
|
||||
|
||||
void serialize(const SecuredPdu&, OutputArchive&);
|
||||
|
||||
/**
|
||||
* Cast secured PDU from byte buffer convertible
|
||||
* \param conv source convertible
|
||||
* \return secured PDU pointer or nullptr if cast failed
|
||||
*/
|
||||
SecuredPdu* secured_pdu_cast(ByteBufferConvertible&);
|
||||
|
||||
} // namespace geonet
|
||||
|
||||
namespace convertible
|
||||
{
|
||||
|
||||
template<>
|
||||
struct byte_buffer_impl<geonet::SecuredPdu> : public byte_buffer
|
||||
{
|
||||
byte_buffer_impl(const geonet::SecuredPdu&);
|
||||
void convert(ByteBuffer&) const override;
|
||||
std::size_t size() const override;
|
||||
std::unique_ptr<byte_buffer> duplicate() const override;
|
||||
|
||||
geonet::SecuredPdu pdu;
|
||||
};
|
||||
|
||||
} // namespace convertible
|
||||
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SECURED_PDU_HPP_TVERYI91 */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "sequence_number.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
bool SequenceNumber::operator<(SequenceNumber other) const
|
||||
{
|
||||
return ((other.m_number > m_number && other.m_number - m_number <= SequenceNumber::max/2) ||
|
||||
(m_number > other.m_number && m_number - other.m_number > SequenceNumber::max/2));
|
||||
}
|
||||
|
||||
SequenceNumber SequenceNumber::operator++(int)
|
||||
{
|
||||
SequenceNumber tmp = *this;
|
||||
++m_number;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void serialize(const SequenceNumber& sn, OutputArchive& ar)
|
||||
{
|
||||
serialize(host_cast(static_cast<SequenceNumber::value_type>(sn)), ar);
|
||||
}
|
||||
|
||||
void deserialize(SequenceNumber& sn, InputArchive& ar)
|
||||
{
|
||||
SequenceNumber::value_type tmp = 0;
|
||||
deserialize(tmp, ar);
|
||||
sn = SequenceNumber(tmp);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef SEQUENCE_NUMBER_HPP_HEO4A3XC
|
||||
#define SEQUENCE_NUMBER_HPP_HEO4A3XC
|
||||
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
class SequenceNumber :
|
||||
public boost::totally_ordered<SequenceNumber>,
|
||||
public boost::additive<SequenceNumber>
|
||||
{
|
||||
public:
|
||||
using value_type = uint16_t;
|
||||
static constexpr value_type max = std::numeric_limits<value_type>::max();
|
||||
|
||||
SequenceNumber() : m_number(0) {}
|
||||
explicit SequenceNumber(value_type number) : m_number(number) {}
|
||||
explicit operator value_type() const { return m_number; }
|
||||
bool operator<(SequenceNumber other) const;
|
||||
bool operator==(SequenceNumber other) const { return m_number == other.m_number; }
|
||||
void operator+=(SequenceNumber other) { m_number += other.m_number; }
|
||||
void operator-=(SequenceNumber other) { m_number -= other.m_number; }
|
||||
SequenceNumber operator++(int);
|
||||
|
||||
private:
|
||||
value_type m_number;
|
||||
};
|
||||
|
||||
void serialize(const SequenceNumber&, OutputArchive&);
|
||||
void deserialize(SequenceNumber&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SEQUENCE_NUMBER_HPP_HEO4A3XC */
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef SERIALIZATION_HPP_XBJELAF0
|
||||
#define SERIALIZATION_HPP_XBJELAF0
|
||||
|
||||
#include <vanetza/common/serialization.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
using vanetza::InputArchive;
|
||||
using vanetza::OutputArchive;
|
||||
|
||||
template<typename T>
|
||||
void serialize(T&& t, OutputArchive& oa)
|
||||
{
|
||||
using vanetza::serialize;
|
||||
serialize(oa, std::forward<T>(t));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void deserialize(T&& t, InputArchive& ia)
|
||||
{
|
||||
using vanetza::deserialize;
|
||||
deserialize(ia, std::forward<T>(t));
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SERIALIZATION_HPP_XBJELAF0 */
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef SERIALIZATION_BUFFER_HPP_8G2XAHRG
|
||||
#define SERIALIZATION_BUFFER_HPP_8G2XAHRG
|
||||
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/common/byte_buffer_sink.hpp>
|
||||
#include <vanetza/common/byte_buffer_source.hpp>
|
||||
#include <vanetza/geonet/serialization.hpp>
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
/**
|
||||
* This function is deprecated.
|
||||
* It will be removed as soon as geonet::serialize signatures are
|
||||
* compatible with common::serialize_into_buffer
|
||||
*/
|
||||
template<typename T>
|
||||
void serialize_into_buffer(const T& t, ByteBuffer& buf)
|
||||
{
|
||||
byte_buffer_sink sink(buf);
|
||||
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
|
||||
OutputArchive ar(stream);
|
||||
serialize(t, ar);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is deprecated.
|
||||
* It will be removed as soon as geonet::deserialize signatures are
|
||||
* compatible with common::deserialize_into_buffer
|
||||
*/
|
||||
template<typename T>
|
||||
void deserialize_from_buffer(T& t, const ByteBuffer& buf)
|
||||
{
|
||||
byte_buffer_source source(buf);
|
||||
boost::iostreams::stream_buffer<byte_buffer_source> stream(source);
|
||||
InputArchive ar(stream);
|
||||
deserialize(t, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SERIALIZATION_BUFFER_HPP_8G2XAHRG */
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "shb_header.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
constexpr std::size_t ShbHeader::length_bytes;
|
||||
|
||||
ShbHeader::ShbHeader() : dcc(0u)
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(const ShbHeader& hdr, OutputArchive& ar)
|
||||
{
|
||||
serialize(hdr.source_position, ar);
|
||||
serialize(hdr.dcc, ar);
|
||||
}
|
||||
|
||||
void deserialize(ShbHeader& hdr, InputArchive& ar)
|
||||
{
|
||||
deserialize(hdr.source_position, ar);
|
||||
deserialize(hdr.dcc, ar);
|
||||
}
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef SHB_HEADER_HPP_MRLDRPNK
|
||||
#define SHB_HEADER_HPP_MRLDRPNK
|
||||
|
||||
#include <vanetza/geonet/dcc_field.hpp>
|
||||
#include <vanetza/geonet/position_vector.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
struct ShbHeader
|
||||
{
|
||||
ShbHeader();
|
||||
|
||||
static constexpr std::size_t length_bytes = 4 + LongPositionVector::length_bytes;
|
||||
|
||||
LongPositionVector source_position;
|
||||
DccField dcc; /* < this field is "reserved" in 102 636-4-1 */
|
||||
};
|
||||
|
||||
void serialize(const ShbHeader&, OutputArchive&);
|
||||
void deserialize(ShbHeader&, InputArchive&);
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SHB_HEADER_HPP_MRLDRPNK */
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
#ifndef SOFT_STATE_MAP_HPP_B0MARRWZ
|
||||
#define SOFT_STATE_MAP_HPP_B0MARRWZ
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <boost/heap/binomial_heap.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
template<typename VALUE>
|
||||
struct SoftStateDefaultCreator
|
||||
{
|
||||
VALUE operator()() { return VALUE(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SoftStateMap is a map data structure with expiring entries
|
||||
* \tparam KEY key type
|
||||
* \tparam VALUE mapped type
|
||||
* \tparam CTOR optional creator of values
|
||||
*/
|
||||
template<typename KEY, typename VALUE, typename CTOR = SoftStateDefaultCreator<VALUE>>
|
||||
class SoftStateMap
|
||||
{
|
||||
public:
|
||||
using key_type = KEY;
|
||||
using mapped_type = VALUE;
|
||||
using value_type = std::pair<const key_type&, mapped_type&>;
|
||||
using creator_type = CTOR;
|
||||
|
||||
private:
|
||||
class ExpiryWithKey : public Clock::time_point
|
||||
{
|
||||
public:
|
||||
ExpiryWithKey() = default;
|
||||
ExpiryWithKey(const key_type& key, Clock::time_point expiry) :
|
||||
Clock::time_point(expiry), m_key(key) {}
|
||||
|
||||
const key_type& key() const { return m_key; }
|
||||
|
||||
private:
|
||||
key_type m_key;
|
||||
};
|
||||
using heap_type = boost::heap::binomial_heap<ExpiryWithKey, boost::heap::compare<std::greater<ExpiryWithKey>>>;
|
||||
|
||||
struct ValueWithHandle
|
||||
{
|
||||
ValueWithHandle(mapped_type&& v) : value(std::move(v)) {}
|
||||
|
||||
typename heap_type::handle_type handle;
|
||||
mapped_type value;
|
||||
|
||||
mapped_type& operator*() { return value; }
|
||||
const mapped_type& operator*() const { return value; }
|
||||
};
|
||||
using map_type = std::unordered_map<key_type, ValueWithHandle>;
|
||||
|
||||
using data_range = boost::iterator_range<typename map_type::iterator>;
|
||||
using data_filter = std::function<bool(const typename map_type::value_type&)>;
|
||||
using data_transform = std::function<value_type(typename map_type::value_type&)>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct SoftStateMap
|
||||
* \param rt runtime object
|
||||
* \note This constructor is only available if CTOR is default constructible
|
||||
*/
|
||||
template<typename T = CTOR>
|
||||
SoftStateMap(const Runtime& rt, typename std::enable_if<std::is_default_constructible<T>::value>::type* = nullptr) :
|
||||
m_runtime(rt)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct SoftStateMap
|
||||
* \param rt runtime object
|
||||
* \param ctor value creator
|
||||
*/
|
||||
SoftStateMap(const Runtime& rt, creator_type&& ctor) :
|
||||
m_runtime(rt), m_creator(std::move(ctor))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lifetime duration used for new and refreshed entries
|
||||
* \param lifetime entry lieftime
|
||||
*/
|
||||
void set_lifetime(Clock::duration lifetime)
|
||||
{
|
||||
m_lifetime = lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value mapped to key
|
||||
* \param key
|
||||
* \return existing value entry or just created entry
|
||||
*/
|
||||
mapped_type& get_value(const key_type& key)
|
||||
{
|
||||
return get_data(key).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non-expired value pointer mapped to key
|
||||
* \param key
|
||||
* \return pointer to value or nullptr if not existing
|
||||
*/
|
||||
mapped_type* get_value_ptr(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
return data && !is_expired(*data->handle) ? &data->value : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non-expired value pointer mapped to key
|
||||
* \param key
|
||||
* \return pointer to value or nullptr if not existing
|
||||
*/
|
||||
const mapped_type* get_value_ptr(const key_type& key) const
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
return data && !is_expired(*data->handle) ? &data->value : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if non-expired value for given key exists
|
||||
* \param key
|
||||
* \return true if entry exists
|
||||
*/
|
||||
bool has_value(const key_type& key) const
|
||||
{
|
||||
return get_value_ptr(key) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh lifetime of entry associated with given key
|
||||
* \param key
|
||||
* \return associated value (might have been created)
|
||||
*/
|
||||
mapped_type& refresh(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
if (data) {
|
||||
refresh(data->handle);
|
||||
} else {
|
||||
data = &get_data(key);
|
||||
assert(data != nullptr);
|
||||
}
|
||||
return data->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all entries with expired lifetime.
|
||||
* Expired but still stored entries are only hided at retrieval until calling this method.
|
||||
*/
|
||||
void drop_expired()
|
||||
{
|
||||
while (!m_heap.empty() && is_expired(m_heap.top())) {
|
||||
m_map.erase(m_heap.top().key());
|
||||
m_heap.pop();
|
||||
}
|
||||
}
|
||||
|
||||
using map_range = boost::transformed_range<data_transform, const boost::filtered_range<data_filter, data_range>>;
|
||||
|
||||
/**
|
||||
* Create a range of all non-expired entries mimicking STL's map interface
|
||||
*/
|
||||
map_range map()
|
||||
{
|
||||
data_filter filter_fn = [this](const typename map_type::value_type& v) {
|
||||
return !this->is_expired(*v.second.handle);
|
||||
};
|
||||
data_transform transform_fn = [](typename map_type::value_type& v) {
|
||||
return value_type { v.first, v.second.value };
|
||||
};
|
||||
using namespace boost::adaptors;
|
||||
data_range range_all = boost::make_iterator_range(m_map.begin(), m_map.end());
|
||||
return range_all | filtered(filter_fn) | transformed(transform_fn);
|
||||
}
|
||||
|
||||
const map_range map() const
|
||||
{
|
||||
return const_cast<SoftStateMap*>(this)->map();
|
||||
}
|
||||
|
||||
private:
|
||||
ValueWithHandle& get_data(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
if (!data) {
|
||||
auto insertion = m_map.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key), std::forward_as_tuple(m_creator()));
|
||||
data = &insertion.first->second;
|
||||
data->handle = m_heap.push(ExpiryWithKey {key, m_runtime.now() + m_lifetime});
|
||||
} else if (is_expired(*data->handle)) {
|
||||
// resurrect this data element, i.e. pretend it has just been created
|
||||
refresh(data->handle);
|
||||
}
|
||||
return *data;
|
||||
}
|
||||
|
||||
ValueWithHandle* get_data_ptr(const key_type& key)
|
||||
{
|
||||
auto it = m_map.find(key);
|
||||
return it != m_map.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const ValueWithHandle* get_data_ptr(const key_type& key) const
|
||||
{
|
||||
auto it = m_map.find(key);
|
||||
return it != m_map.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool is_expired(const ExpiryWithKey& expiry) const
|
||||
{
|
||||
return m_runtime.now() > expiry;
|
||||
}
|
||||
|
||||
void refresh(typename heap_type::handle_type& handle)
|
||||
{
|
||||
ExpiryWithKey& expiry = *handle;
|
||||
static_cast<Clock::time_point&>(expiry) = m_runtime.now() + m_lifetime;
|
||||
m_heap.update(handle);
|
||||
}
|
||||
|
||||
const Runtime& m_runtime;
|
||||
Clock::duration m_lifetime;
|
||||
creator_type m_creator;
|
||||
heap_type m_heap;
|
||||
map_type m_map;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SOFT_STATE_MAP_HPP_B0MARRWZ */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef STATION_TYPE_HPP_BARYX6ET
|
||||
#define STATION_TYPE_HPP_BARYX6ET
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
enum class StationType {
|
||||
Unknown = 0,
|
||||
Pedestrian = 1,
|
||||
Cyclist = 2,
|
||||
Moped = 3,
|
||||
Motorcycle = 4,
|
||||
Passenger_Car = 5,
|
||||
Bus = 6,
|
||||
Light_Truck = 7,
|
||||
Heavy_Truck = 8,
|
||||
Trailer = 9,
|
||||
Special_Vehicle = 10,
|
||||
Tram = 11,
|
||||
RSU = 15
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* STATION_TYPE_HPP_BARYX6ET */
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
include(UseGTest)
|
||||
configure_gtest_directory(LINK_LIBRARIES geonet)
|
||||
|
||||
add_gtest(Address address.cpp)
|
||||
add_gtest(Areas areas.cpp)
|
||||
add_gtest(BasicHeader basic_header.cpp)
|
||||
add_gtest(CbfPacketBuffer cbf_packet_buffer.cpp)
|
||||
add_gtest(CbrAggregator cbr_aggregator.cpp)
|
||||
add_gtest(CommonHeader common_header.cpp)
|
||||
add_gtest(DataConfirm data_confirm.cpp)
|
||||
add_gtest(DataRequest data_request.cpp)
|
||||
add_gtest(DccMcoField dcc_mco_field.cpp)
|
||||
add_gtest(DuplicatePacketList duplicate_packet_list.cpp)
|
||||
add_gtest(GbcGacHeader gbc_gac_header.cpp)
|
||||
add_gtest(GbcMemory
|
||||
SOURCES gbc_memory.cpp network_topology.cpp)
|
||||
add_gtest(Lifetime lifetime.cpp)
|
||||
add_gtest(LocationTable location_table.cpp)
|
||||
add_gtest(PacketBuffer packet_buffer.cpp)
|
||||
add_gtest(PositionUpdater position_updater.cpp)
|
||||
add_gtest(PositionVector position_vector.cpp)
|
||||
add_gtest(Repeater repeater.cpp)
|
||||
add_gtest(Router router.cpp)
|
||||
add_gtest(RouterIndicate router_indicate.cpp
|
||||
INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/vanetza/security/tests)
|
||||
add_gtest(SequenceNumber sequence_number.cpp)
|
||||
add_gtest(Timestamp timestamp.cpp)
|
||||
add_gtest(TrafficClass traffic_class.cpp)
|
||||
add_gtest(Routing
|
||||
SOURCES network_topology.cpp routing.cpp)
|
||||
add_gtest(RouterRequest router_request.cpp)
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/address.hpp>
|
||||
#include <vanetza/geonet/serialization_buffer.hpp>
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
|
||||
using namespace vanetza;
|
||||
using namespace vanetza::geonet;
|
||||
|
||||
TEST(Address, ctor) {
|
||||
Address a;
|
||||
EXPECT_FALSE(a.is_manually_configured());
|
||||
EXPECT_EQ(a.mid(), MacAddress());
|
||||
EXPECT_EQ(a.station_type(), StationType::Unknown);
|
||||
EXPECT_EQ(a.country_code(), 0);
|
||||
}
|
||||
|
||||
TEST(Address, equality) {
|
||||
Address a;
|
||||
Address b({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
|
||||
EXPECT_NE(a, b);
|
||||
Address c = b;
|
||||
EXPECT_EQ(b, c);
|
||||
c.is_manually_configured(true);
|
||||
EXPECT_NE(b, c);
|
||||
Address d = c;
|
||||
EXPECT_EQ(c, d);
|
||||
d.station_type(StationType::Passenger_Car);
|
||||
EXPECT_NE(c, d);
|
||||
Address e = d;
|
||||
EXPECT_EQ(d, e);
|
||||
e.country_code(8);
|
||||
EXPECT_NE(d, e);
|
||||
a.mid(b.mid());
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
TEST(Address, serialization) {
|
||||
Address a({1, 2, 3, 4, 5, 6});
|
||||
a.is_manually_configured(true);
|
||||
a.station_type(StationType::Tram);
|
||||
a.country_code(0x0333);
|
||||
|
||||
ByteBuffer buffer;
|
||||
serialize_into_buffer(a, buffer);
|
||||
EXPECT_EQ(Address::length_bytes, buffer.size());
|
||||
|
||||
Address b;
|
||||
deserialize_from_buffer(b, buffer);
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/areas.hpp>
|
||||
#include <vanetza/units/length.hpp>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
namespace units = vanetza::units;
|
||||
using units::si::meter;
|
||||
using units::degree;
|
||||
using units::si::square_meter;
|
||||
|
||||
TEST(Areas, cartesian_substraction) {
|
||||
CartesianPosition a(-3.4 * meter , 8.3 * meter);
|
||||
CartesianPosition b(34.8 * meter, -14.8 * meter);
|
||||
CartesianPosition c = a - b;
|
||||
EXPECT_DOUBLE_EQ(c.x / meter, -38.2);
|
||||
EXPECT_DOUBLE_EQ(c.y / meter, 23.1);
|
||||
}
|
||||
|
||||
TEST(Areas, geodetic_distance) {
|
||||
GeodeticPosition a(48.76714 * degree, 11.43263 * degree);
|
||||
GeodeticPosition b(-25.41272 * degree, -49.24815 * degree);
|
||||
units::Length d = distance(a, b);
|
||||
const double expected_m = 10185367.442;
|
||||
// accept less than 0.5% error for this large distance
|
||||
EXPECT_NEAR(d / meter, expected_m, 0.005 * expected_m);
|
||||
EXPECT_DOUBLE_EQ(0.0, distance(a, a).value());
|
||||
}
|
||||
|
||||
TEST(Areas, geometric_function_circle) {
|
||||
Circle c;
|
||||
c.r = 38.4 * meter;
|
||||
CartesianPosition p(0.0 * meter, 0.0 * meter);
|
||||
|
||||
EXPECT_TRUE(at_center_point(c, p));
|
||||
EXPECT_TRUE(inside_shape(c, p));
|
||||
EXPECT_FALSE(outside_shape(c, p));
|
||||
EXPECT_FALSE(at_shape_border(c, p));
|
||||
|
||||
p.x = 15.0 * meter;
|
||||
p.y = 36.0 * meter;
|
||||
EXPECT_TRUE(outside_shape(c, p));
|
||||
}
|
||||
|
||||
TEST(Areas, geometric_function_rectangle) {
|
||||
Rectangle r;
|
||||
r.a = 8.5 * meter;
|
||||
r.b = 3.0 * meter;
|
||||
CartesianPosition p(-3.5 * meter, 2.9 * meter);
|
||||
EXPECT_TRUE(inside_shape(r, p));
|
||||
|
||||
p.x = -8.6 * meter;
|
||||
EXPECT_TRUE(outside_shape(r, p));
|
||||
}
|
||||
|
||||
TEST(Areas, geometric_function_ellipse) {
|
||||
Ellipse e;
|
||||
e.a = 8.4 * meter;
|
||||
e.b = 6.5 * meter;
|
||||
CartesianPosition p(-7.6 * meter, 1.3 * meter);
|
||||
EXPECT_TRUE(inside_shape(e, p));
|
||||
|
||||
p.y = -4.5 * meter;
|
||||
EXPECT_TRUE(outside_shape(e, p));
|
||||
}
|
||||
|
||||
TEST(Areas, local_cartesian) {
|
||||
GeodeticPosition origin(48.76714 * degree, 11.43263 * degree); // THI
|
||||
GeodeticPosition datum(48.7656 * degree, 11.4296 * degree); // ZAF
|
||||
CartesianPosition pos = local_cartesian(origin, datum);
|
||||
EXPECT_NEAR(pos.x / meter, -222.74, 1.0);
|
||||
EXPECT_NEAR(pos.y / meter, -171.25, 1.0);
|
||||
}
|
||||
|
||||
TEST(Areas, canonicalize) {
|
||||
CartesianPosition point(3.0 * meter, 2.0 * meter);
|
||||
units::Angle azimuth = units::Angle(30.0 * degree);
|
||||
CartesianPosition canonical_point = canonicalize(point, azimuth);
|
||||
EXPECT_NEAR(canonical_point.x / meter, 3.23, 0.01);
|
||||
EXPECT_NEAR(canonical_point.y / meter, -1.59, 0.01);
|
||||
}
|
||||
|
||||
TEST(Areas, inside_or_at_border) {
|
||||
Rectangle r;
|
||||
r.a = 300.0 * meter;
|
||||
r.b = 170.0 * meter;
|
||||
Area a;
|
||||
a.shape = r;
|
||||
a.angle = units::Angle(90.0 * degree);
|
||||
a.position = GeodeticPosition(48.7656 * degree, 11.4296 * degree);
|
||||
GeodeticPosition ego(48.76714 * degree, 11.43263 * degree);
|
||||
EXPECT_FALSE(inside_or_at_border(a, ego));
|
||||
a.angle = units::Angle(45.0 * degree);
|
||||
EXPECT_TRUE(inside_or_at_border(a, ego));
|
||||
}
|
||||
|
||||
TEST(Areas, area_size) {
|
||||
Circle c;
|
||||
c.r = 18.3 * meter;
|
||||
Rectangle r;
|
||||
r.a = 393.0 * meter;
|
||||
r.b = 140.8 * meter;
|
||||
Ellipse e;
|
||||
e.a = 393.0 * meter;
|
||||
e.b = 140.8 * meter;
|
||||
|
||||
Area a;
|
||||
a.shape = c;
|
||||
EXPECT_NEAR(area_size(a) / square_meter, 1052.0880, 0.0001);
|
||||
a.shape = r;
|
||||
EXPECT_NEAR(area_size(a) / square_meter, 221337.6000, 0.0001);
|
||||
a.shape = e;
|
||||
EXPECT_NEAR(area_size(a) / square_meter, 173838.1445, 0.0001);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/basic_header.hpp>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/geonet/tests/serialization.hpp>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
using vanetza::units::si::seconds;
|
||||
|
||||
TEST(BasicHeader, ctor) {
|
||||
MIB mib;
|
||||
BasicHeader a(mib);
|
||||
EXPECT_EQ(a.lifetime, mib.itsGnDefaultPacketLifetime);
|
||||
EXPECT_EQ(a.hop_limit, mib.itsGnDefaultHopLimit);
|
||||
|
||||
DataRequest req(mib);
|
||||
req.maximum_lifetime.encode(31.0 * seconds);
|
||||
req.max_hop_limit = 4;
|
||||
BasicHeader b(req, mib);
|
||||
EXPECT_EQ(b.lifetime.decode(), 31.0 * seconds);
|
||||
EXPECT_EQ(b.hop_limit, 4);
|
||||
|
||||
ShbDataRequest shb(mib);
|
||||
BasicHeader c(shb, mib);
|
||||
EXPECT_EQ(c.hop_limit, 1);
|
||||
}
|
||||
|
||||
TEST(BasicHeader, serialization) {
|
||||
BasicHeader a;
|
||||
a.version = 2;
|
||||
a.next_header = NextHeaderBasic::Secured;
|
||||
a.reserved = 0xC3;
|
||||
a.lifetime.raw(0x89);
|
||||
a.hop_limit = 218;
|
||||
|
||||
BasicHeader b = serialize_roundtrip(a);
|
||||
EXPECT_EQ(a.version, b.version);
|
||||
EXPECT_EQ(a.next_header, b.next_header);
|
||||
EXPECT_EQ(a.reserved, b.reserved);
|
||||
EXPECT_EQ(a.lifetime, b.lifetime);
|
||||
EXPECT_EQ(a.hop_limit, b.hop_limit);
|
||||
|
||||
EXPECT_EQ(BasicHeader::length_bytes, serialize_length(a));
|
||||
}
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/common/manual_runtime.hpp>
|
||||
#include <vanetza/geonet/cbf_counter.hpp>
|
||||
#include <vanetza/geonet/cbf_packet_buffer.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <functional>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace vanetza;
|
||||
using namespace vanetza::geonet;
|
||||
|
||||
static const size_t GbcPduLength = BasicHeader::length_bytes +
|
||||
CommonHeader::length_bytes + GeoBroadcastHeader::length_bytes;
|
||||
|
||||
class CbfPacketBufferTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
using PduPtr = std::unique_ptr<GbcPdu>;
|
||||
using PayloadPtr = std::unique_ptr<DownPacket>;
|
||||
using PendingPacketCbf = PendingPacket<GbcPdu>;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
// lifetime of 3 seconds can be stored with 50 ms accuracy
|
||||
mib.itsGnDefaultPacketLifetime.encode(3.0 * units::si::seconds);
|
||||
runtime.reset(Clock::time_point { hours(42) });
|
||||
calls = 0;
|
||||
last_call_length = 0;
|
||||
}
|
||||
|
||||
CbfPacket create_packet(const MacAddress&, SequenceNumber::value_type, std::size_t length = GbcPduLength) const;
|
||||
CbfPacket create_packet(std::size_t length = GbcPduLength) const;
|
||||
CbfPacketBuffer::TimerCallback callback();
|
||||
std::unique_ptr<CbfCounter> counter();
|
||||
|
||||
MIB mib;
|
||||
ManualRuntime runtime;
|
||||
unsigned calls;
|
||||
unsigned last_call_length;
|
||||
};
|
||||
|
||||
CbfPacket CbfPacketBufferTest::create_packet(const MacAddress& mac, SequenceNumber::value_type sn, std::size_t size) const
|
||||
{
|
||||
PduPtr pdu { new CbfPacketBufferTest::PduPtr::element_type(mib) };
|
||||
pdu->extended().source_position.gn_addr.mid(mac);
|
||||
pdu->extended().sequence_number = SequenceNumber { sn };
|
||||
PayloadPtr payload { new CbfPacketBufferTest::PayloadPtr::element_type() };
|
||||
assert(get_length(*pdu) <= size);
|
||||
|
||||
const std::size_t payload_size = size - get_length(*pdu);
|
||||
payload->layer(OsiLayer::Application) = ByteBuffer(payload_size);
|
||||
|
||||
PendingPacketCbf pending { std::make_tuple(std::move(pdu), std::move(payload)), [](PendingPacketCbf::Packet&&) {} };
|
||||
return CbfPacket(std::move(pending), cBroadcastMacAddress);
|
||||
}
|
||||
|
||||
CbfPacket CbfPacketBufferTest::create_packet(std::size_t length) const
|
||||
{
|
||||
static unsigned counter = 0;
|
||||
return create_packet({0, 0, 0, 0, 0, 0}, ++counter, length);
|
||||
}
|
||||
|
||||
CbfPacketBuffer::TimerCallback CbfPacketBufferTest::callback()
|
||||
{
|
||||
return [this](PendingPacketCbf&& data) {
|
||||
++calls;
|
||||
last_call_length = data.length();
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<CbfCounter> CbfPacketBufferTest::counter()
|
||||
{
|
||||
return std::unique_ptr<CbfCounter> { new CbfCounterImmortal() };
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CbfPacketBufferTest, identifier_hash)
|
||||
{
|
||||
std::hash<CbfPacketIdentifier> hasher;
|
||||
CbfPacketIdentifier id1 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(2) };
|
||||
CbfPacketIdentifier id2 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(3) };
|
||||
CbfPacketIdentifier id3 { Address {{ 1, 2, 3, 4, 5, 6}}, SequenceNumber(2) };
|
||||
CbfPacketIdentifier id4 { Address {{ 1, 2, 3, 4, 5, 7}}, SequenceNumber(3) };
|
||||
EXPECT_EQ(id1, id3);
|
||||
EXPECT_EQ(hasher(id1), hasher(id3));
|
||||
EXPECT_NE(id1, id2);
|
||||
EXPECT_NE(hasher(id1), hasher(id2));
|
||||
EXPECT_NE(id2, id4);
|
||||
EXPECT_NE(hasher(id2), hasher(id4));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, packet_identifier)
|
||||
{
|
||||
const MacAddress mac { 1, 3, 5, 7, 9, 11 };
|
||||
CbfPacket packet = create_packet(mac, 8);
|
||||
EXPECT_EQ(mac, packet.source().mid());
|
||||
EXPECT_EQ(SequenceNumber { 8 }, packet.sequence_number());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CbfPacketBufferTest, packet_lifetime)
|
||||
{
|
||||
CbfPacket packet = create_packet({}, 1);
|
||||
|
||||
// check initialization
|
||||
using vanetza::units::clock_cast;
|
||||
EXPECT_EQ(clock_cast(mib.itsGnDefaultPacketLifetime.decode()), packet.reduce_lifetime(Clock::duration::zero()));
|
||||
|
||||
// lifetime has to be modifiable
|
||||
Clock::duration lifetime = packet.reduce_lifetime(Clock::duration::zero());
|
||||
EXPECT_EQ(lifetime - milliseconds(50), packet.reduce_lifetime(milliseconds(50)));
|
||||
// but negative reductions have no effect
|
||||
EXPECT_EQ(lifetime - milliseconds(50), packet.reduce_lifetime(milliseconds(-100)));
|
||||
// and lifetime does not go below zero
|
||||
EXPECT_EQ(Clock::duration::zero(), packet.reduce_lifetime(milliseconds(6000)));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, packet_length)
|
||||
{
|
||||
CbfPacket packet1 = create_packet({0, 1, 2, 3, 4, 5}, 3);
|
||||
EXPECT_EQ(GbcPduLength, packet1.length());
|
||||
|
||||
CbfPacket packet2 = create_packet({0, 1, 2, 3, 4, 5}, 3, 64);
|
||||
EXPECT_EQ(64, packet2.length());
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, find)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
auto found1 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
|
||||
EXPECT_FALSE(found1);
|
||||
|
||||
auto packet1 = create_packet({1, 2, 3, 4, 5, 6}, 3);
|
||||
buffer.add(std::move(packet1), seconds(5));
|
||||
|
||||
auto found2 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(4)));
|
||||
EXPECT_FALSE(found2);
|
||||
auto found3 = buffer.find(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
|
||||
ASSERT_TRUE(found3);
|
||||
EXPECT_EQ(1, buffer.counter(identifier(*found3)));
|
||||
EXPECT_EQ((MacAddress {1, 2, 3, 4, 5, 6}), found3->source().mid());
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, counter)
|
||||
{
|
||||
CbfPacket packet1 = create_packet({3, 8, 3, 8, 3, 8}, 10);
|
||||
CbfPacketIdentifier id1 = identifier(packet1);
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
EXPECT_EQ(0, buffer.counter(id1));
|
||||
|
||||
buffer.add(std::move(packet1), milliseconds(30));
|
||||
EXPECT_EQ(1, buffer.counter(id1));
|
||||
|
||||
buffer.remove(id1);
|
||||
EXPECT_EQ(1, buffer.counter(id1));
|
||||
|
||||
CbfPacket packet2 = create_packet({3, 8, 3, 8, 3, 8}, 11);
|
||||
CbfPacketIdentifier id2 = identifier(packet2);
|
||||
buffer.update(id2, milliseconds(30));
|
||||
EXPECT_EQ(0, buffer.counter(id2));
|
||||
|
||||
buffer.add(std::move(packet2), milliseconds(30));
|
||||
EXPECT_EQ(1, buffer.counter(id2));
|
||||
|
||||
buffer.update(id2, milliseconds(30));
|
||||
EXPECT_EQ(2, buffer.counter(id2));
|
||||
|
||||
EXPECT_EQ(1, buffer.counter(id1));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, fetch)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
auto found1 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
|
||||
EXPECT_FALSE(!!found1);
|
||||
|
||||
auto packet1 = create_packet({1, 2, 3, 4, 5, 6}, 3);
|
||||
buffer.add(std::move(packet1), milliseconds(500));
|
||||
|
||||
auto found2 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(4)));
|
||||
EXPECT_FALSE(!!found2);
|
||||
auto found3 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
|
||||
ASSERT_TRUE(!!found3);
|
||||
EXPECT_EQ((MacAddress {1, 2, 3, 4, 5, 6}), found3->source().mid());
|
||||
|
||||
auto found4 = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(3)));
|
||||
EXPECT_FALSE(!!found4);
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, fetch_reduce_lifetime)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
auto packet = create_packet({1, 2, 3, 4, 5, 6}, 1);
|
||||
buffer.add(std::move(packet), milliseconds(500));
|
||||
runtime.trigger(milliseconds(200));
|
||||
auto found = buffer.fetch(identifier(Address {{1, 2, 3, 4, 5, 6}}, SequenceNumber(1)));
|
||||
ASSERT_TRUE(!!found);
|
||||
EXPECT_EQ(milliseconds(2800), found->reduce_lifetime(Clock::duration::zero()));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, next_timer_expiry)
|
||||
{
|
||||
const Address addr {{1, 2, 3, 4, 5, 6}};
|
||||
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
EXPECT_EQ(Clock::time_point::max(), runtime.next());
|
||||
|
||||
buffer.add(create_packet(), seconds(3));
|
||||
EXPECT_EQ(seconds(3), runtime.next() - runtime.now());
|
||||
|
||||
runtime.trigger(seconds(1));
|
||||
buffer.add(create_packet(addr.mid(), 3), seconds(1));
|
||||
EXPECT_EQ(seconds(1), runtime.next() - runtime.now());
|
||||
|
||||
buffer.add(create_packet(addr.mid(), 2), milliseconds(200));
|
||||
EXPECT_EQ(milliseconds(200), runtime.next() - runtime.now());
|
||||
|
||||
runtime.trigger(milliseconds(100));
|
||||
auto fetch = buffer.fetch(identifier(addr, SequenceNumber(2)));
|
||||
EXPECT_TRUE(!!fetch);
|
||||
EXPECT_EQ(milliseconds(900), runtime.next() - runtime.now());
|
||||
|
||||
bool dropped = buffer.remove(identifier(addr, SequenceNumber(3)));
|
||||
EXPECT_TRUE(dropped);
|
||||
EXPECT_EQ(milliseconds(1900), runtime.next() - runtime.now());
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, remove_sequence_number)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
const auto addr = Address {{1, 1, 1, 1, 1, 1}};
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(3))));
|
||||
|
||||
auto packet = create_packet(addr.mid(), 8);
|
||||
buffer.add(std::move(packet), milliseconds(400));
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(7))));
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(9))));
|
||||
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(8))));
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(8))));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, remove_drop_addr)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
const auto addr1 = Address {{1, 1, 1, 1, 1, 1}};
|
||||
const auto addr2 = Address {{ 2, 2, 2, 2, 2, 2}};
|
||||
|
||||
auto packet = create_packet(addr1.mid(), 8);
|
||||
buffer.add(std::move(packet), milliseconds(400));
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr2, SequenceNumber(8))));
|
||||
EXPECT_TRUE(buffer.remove(identifier(addr1, SequenceNumber(8))));
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr1, SequenceNumber(8))));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, remove_multiple_packets)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 8192);
|
||||
const auto addr = Address {{1, 1, 1, 1, 1, 1}};
|
||||
const auto timeout = milliseconds(400);
|
||||
|
||||
auto packet1 = create_packet(addr.mid(), 8);
|
||||
buffer.add(std::move(packet1), timeout);
|
||||
|
||||
auto packet2 = create_packet(addr.mid(), 10);
|
||||
buffer.add(std::move(packet2), timeout);
|
||||
|
||||
EXPECT_FALSE(buffer.remove(identifier(addr, SequenceNumber(9))));
|
||||
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(10))));
|
||||
EXPECT_TRUE(buffer.remove(identifier(addr, SequenceNumber(8))));
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, capacity)
|
||||
{
|
||||
CbfPacketBuffer buffer(runtime, callback(), counter(), 256);
|
||||
|
||||
buffer.add(create_packet(128), seconds(1));
|
||||
buffer.add(create_packet(128), seconds(1));
|
||||
runtime.trigger(milliseconds(1010));
|
||||
EXPECT_EQ(2, calls);
|
||||
|
||||
buffer.add(create_packet(157), seconds(1));
|
||||
buffer.add(create_packet(100), seconds(1));
|
||||
runtime.trigger(seconds(2));
|
||||
EXPECT_EQ(3, calls);
|
||||
EXPECT_EQ(100, last_call_length);
|
||||
}
|
||||
|
||||
TEST_F(CbfPacketBufferTest, packets_to_send)
|
||||
{
|
||||
std::vector<PendingPacketCbf> packets;
|
||||
auto cb = [&packets](PendingPacketCbf&& data) { packets.emplace_back(std::move(data)); };
|
||||
CbfPacketBuffer buffer(runtime, cb, counter(), 8192);
|
||||
|
||||
runtime.trigger(minutes(42));
|
||||
EXPECT_EQ(0, packets.size());
|
||||
|
||||
buffer.add(create_packet(110), milliseconds(2500));
|
||||
runtime.trigger(seconds(1));
|
||||
EXPECT_EQ(0, packets.size());
|
||||
|
||||
buffer.add(create_packet(120), seconds(1));
|
||||
runtime.trigger(seconds(1));
|
||||
ASSERT_EQ(1, packets.size());
|
||||
EXPECT_EQ(120, packets[0].length());
|
||||
|
||||
runtime.trigger(milliseconds(500));
|
||||
EXPECT_EQ(2, packets.size());
|
||||
|
||||
buffer.add(create_packet(130), seconds(1));
|
||||
buffer.add(create_packet(140), milliseconds(1500));
|
||||
runtime.trigger(seconds(2));
|
||||
ASSERT_EQ(4, packets.size());
|
||||
EXPECT_EQ(130, packets[2].length());
|
||||
EXPECT_EQ(140, packets[3].length());
|
||||
|
||||
// check if lifetime is reduced by queuing time
|
||||
auto packet = create_packet(150);
|
||||
|
||||
EXPECT_EQ(milliseconds(1000), packet.reduce_lifetime(milliseconds(2000)));
|
||||
buffer.add(std::move(packet), milliseconds(72));
|
||||
runtime.trigger(runtime.next());
|
||||
ASSERT_EQ(5, packets.size());
|
||||
// Lifetime can only be encoded in 50ms steps (in best case): 950 ms remaining lifetime
|
||||
EXPECT_EQ((Lifetime {Lifetime::Base::Fifty_Milliseconds, 19}), packets[4].pdu().basic().lifetime);
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/common/manual_runtime.hpp>
|
||||
#include <vanetza/geonet/cbr_aggregator.hpp>
|
||||
#include <vanetza/geonet/location_table.hpp>
|
||||
#include <vanetza/geonet/loctex_g5.hpp>
|
||||
#include <vanetza/geonet/mib.hpp>
|
||||
#include <vanetza/net/mac_address.hpp>
|
||||
|
||||
using namespace vanetza;
|
||||
using namespace vanetza::geonet;
|
||||
using vanetza::dcc::ChannelLoad;
|
||||
using std::chrono::seconds;
|
||||
|
||||
class CbrAggregatorTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
mib.reset(new MIB());
|
||||
runtime.reset(new ManualRuntime(Clock::at("2017-05-20 17:36:00")));
|
||||
location_table.reset(new LocationTable(*mib, *runtime));
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
location_table.reset();
|
||||
runtime.reset();
|
||||
mib.reset();
|
||||
}
|
||||
|
||||
Timestamp timestamp_earlier(std::chrono::milliseconds ms)
|
||||
{
|
||||
Timestamp ts { runtime->now() };
|
||||
ts -= Timestamp::duration_type { ms.count() * Timestamp::millisecond() };
|
||||
return ts;
|
||||
}
|
||||
|
||||
Address address(unsigned i)
|
||||
{
|
||||
Address addr;
|
||||
addr.mid(create_mac_address(i));
|
||||
return addr;
|
||||
}
|
||||
|
||||
std::unique_ptr<LocTEX_G5> loctex_g5(Clock::duration age, double local, double one_hop)
|
||||
{
|
||||
std::unique_ptr<LocTEX_G5> entry { new LocTEX_G5() };
|
||||
entry->local_update = Timestamp { runtime->now() - age };
|
||||
entry->dcc_mco.local_cbr(ChannelLoad (local));
|
||||
entry->dcc_mco.neighbour_cbr(ChannelLoad (one_hop));
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<Runtime> runtime;
|
||||
std::unique_ptr<MIB> mib;
|
||||
std::unique_ptr<LocationTable> location_table;
|
||||
};
|
||||
|
||||
TEST_F(CbrAggregatorTest, init)
|
||||
{
|
||||
CbrAggregator cbra;
|
||||
EXPECT_EQ(ChannelLoad(0.0), cbra.get_local_cbr());
|
||||
EXPECT_EQ(ChannelLoad(0.0), cbra.get_one_hop_cbr());
|
||||
EXPECT_EQ(ChannelLoad(0.0), cbra.get_two_hop_cbr());
|
||||
EXPECT_EQ(ChannelLoad(0.0), cbra.get_global_cbr());
|
||||
}
|
||||
|
||||
TEST_F(CbrAggregatorTest, local_cbr)
|
||||
{
|
||||
CbrAggregator cbra;
|
||||
ChannelLoad cbr_target(0.6);
|
||||
cbra.aggregate(ChannelLoad(0.1), *location_table, timestamp_earlier(seconds(5)), cbr_target);
|
||||
EXPECT_EQ(ChannelLoad(0.1), cbra.get_local_cbr());
|
||||
cbra.aggregate(ChannelLoad(0.2), *location_table, timestamp_earlier(seconds(5)), cbr_target);
|
||||
EXPECT_EQ(ChannelLoad(0.2), cbra.get_local_cbr());
|
||||
|
||||
// no one-hop and two-hop values have been provided: previous local measurement should be maximum
|
||||
EXPECT_EQ(ChannelLoad(0.1), cbra.get_global_cbr());
|
||||
}
|
||||
|
||||
TEST_F(CbrAggregatorTest, shared_cbr)
|
||||
{
|
||||
location_table->get_or_create_entry(address(1)).extensions.insert(loctex_g5(seconds(3), 0.45, 0.5));
|
||||
location_table->get_or_create_entry(address(2)).extensions.insert(loctex_g5(seconds(2), 0.4, 0.2));
|
||||
location_table->get_or_create_entry(address(3)).extensions.insert(loctex_g5(seconds(4), 0.4, 0.45));
|
||||
location_table->get_or_create_entry(address(4)).extensions.insert(loctex_g5(seconds(2), 0.35, 0.25));
|
||||
|
||||
CbrAggregator cbra;
|
||||
cbra.aggregate(ChannelLoad(0.32), *location_table, timestamp_earlier(seconds(10)), ChannelLoad(0.6));
|
||||
EXPECT_DOUBLE_EQ(0.32, cbra.get_local_cbr().value());
|
||||
EXPECT_NEAR(0.4, cbra.get_one_hop_cbr().value(), 0.005); // second largest (one hop average = 0.4 < target = 0.6)
|
||||
EXPECT_NEAR(0.45, cbra.get_two_hop_cbr().value(), 0.005); // second largest (two hop average = 0.35 < target = 0.6)
|
||||
EXPECT_NEAR(0.45, cbra.get_global_cbr().value(), 0.005);
|
||||
|
||||
cbra.aggregate(ChannelLoad(0.34), *location_table, timestamp_earlier(seconds(10)), ChannelLoad(0.3));
|
||||
EXPECT_DOUBLE_EQ(0.34, cbra.get_local_cbr().value());
|
||||
EXPECT_NEAR(0.45, cbra.get_one_hop_cbr().value(), 0.005); // largest (two hop average above target)
|
||||
EXPECT_NEAR(0.5, cbra.get_two_hop_cbr().value(), 0.005); // largest (two hop average above target)
|
||||
EXPECT_NEAR(0.5, cbra.get_global_cbr().value(), 0.005);
|
||||
|
||||
cbra.aggregate(ChannelLoad(0.3), *location_table, timestamp_earlier(seconds(2)), ChannelLoad(0.3));
|
||||
EXPECT_DOUBLE_EQ(0.3, cbra.get_local_cbr().value());
|
||||
EXPECT_NEAR(0.4, cbra.get_one_hop_cbr().value(), 0.005); // average (0.375) above target: largest
|
||||
EXPECT_NEAR(0.2, cbra.get_two_hop_cbr().value(), 0.005); // average (0.225) below target: second largest
|
||||
EXPECT_NEAR(0.4, cbra.get_global_cbr().value(), 0.005);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/common_header.hpp>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/geonet/tests/serialization.hpp>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
|
||||
TEST(CommonHeader, ctor) {
|
||||
MIB mib;
|
||||
CommonHeader a(mib);
|
||||
EXPECT_EQ(a.traffic_class.raw(), mib.itsGnDefaultTrafficClass.raw());
|
||||
EXPECT_EQ(a.maximum_hop_limit, mib.itsGnDefaultHopLimit);
|
||||
EXPECT_EQ(a.payload, 0);
|
||||
|
||||
DataRequest req(mib);
|
||||
req.upper_protocol = UpperProtocol::BTP_B;
|
||||
req.max_hop_limit = 3;
|
||||
req.traffic_class.store_carry_forward(true);
|
||||
CommonHeader b(req, mib);
|
||||
EXPECT_EQ(b.next_header, NextHeaderCommon::BTP_B);
|
||||
EXPECT_EQ(b.maximum_hop_limit, 3);
|
||||
EXPECT_TRUE(b.traffic_class.store_carry_forward());
|
||||
|
||||
ShbDataRequest shb(mib);
|
||||
CommonHeader c(shb, mib);
|
||||
EXPECT_EQ(c.header_type, HeaderType::TSB_Single_Hop);
|
||||
EXPECT_EQ(c.maximum_hop_limit, 1);
|
||||
}
|
||||
|
||||
TEST(CommonHeader, serialization) {
|
||||
CommonHeader a;
|
||||
a.next_header = NextHeaderCommon::IPv6;
|
||||
a.reserved1 = 12;
|
||||
a.header_type = HeaderType::GeoAnycast_Elip;
|
||||
a.traffic_class = TrafficClass(0xAB);
|
||||
a.flags = 0x18;
|
||||
a.payload = 0x1234;
|
||||
a.maximum_hop_limit = 0x78;
|
||||
a.reserved2 = 0x56;
|
||||
|
||||
CommonHeader b = serialize_roundtrip(a);
|
||||
EXPECT_EQ(a.next_header, b.next_header);
|
||||
EXPECT_EQ(a.reserved1, b.reserved1);
|
||||
EXPECT_EQ(a.header_type, b.header_type);
|
||||
EXPECT_EQ(a.traffic_class.raw(), b.traffic_class.raw());
|
||||
EXPECT_EQ(a.flags, b.flags);
|
||||
EXPECT_EQ(a.payload, b.payload);
|
||||
EXPECT_EQ(a.maximum_hop_limit, b.maximum_hop_limit);
|
||||
EXPECT_EQ(a.reserved2, b.reserved2);
|
||||
|
||||
EXPECT_EQ(CommonHeader::length_bytes, serialize_length(a));
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/common/byte_buffer.hpp>
|
||||
#include <vanetza/geonet/data_confirm.hpp>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/geonet/packet.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
using vanetza::units::si::seconds;
|
||||
using vanetza::units::si::meter;
|
||||
|
||||
TEST(DataConfirm, ctor) {
|
||||
DataConfirm a;
|
||||
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Accepted);
|
||||
DataConfirm b(DataConfirm::ResultCode::Rejected_Unspecified);
|
||||
EXPECT_EQ(b.result_code, DataConfirm::ResultCode::Rejected_Unspecified);
|
||||
}
|
||||
|
||||
TEST(DataConfirm, accepted_rejected) {
|
||||
DataConfirm a(DataConfirm::ResultCode::Rejected_Max_Lifetime);
|
||||
EXPECT_TRUE(a.rejected());
|
||||
EXPECT_FALSE(a.accepted());
|
||||
a.result_code = DataConfirm::ResultCode::Accepted;
|
||||
EXPECT_FALSE(a.rejected());
|
||||
EXPECT_TRUE(a.accepted());
|
||||
}
|
||||
|
||||
TEST(DataConfirm, validate_data_request) {
|
||||
MIB mib;
|
||||
DataRequest req(mib);
|
||||
EXPECT_EQ(validate_data_request(req, mib),
|
||||
DataConfirm::ResultCode::Accepted);
|
||||
|
||||
DataRequest req_lt(req);
|
||||
req_lt.maximum_lifetime.encode(mib.itsGnMaxPacketLifetime.decode() + 10.0 * seconds);
|
||||
EXPECT_EQ(validate_data_request(req_lt, mib),
|
||||
DataConfirm::ResultCode::Rejected_Max_Lifetime);
|
||||
|
||||
DataRequest req_rep(req);
|
||||
req_rep.repetition = DataRequest::Repetition();
|
||||
req_rep.repetition->interval = mib.itsGnMinPacketRepetitionInterval - 1 * seconds;
|
||||
EXPECT_EQ(validate_data_request(req_rep, mib),
|
||||
DataConfirm::ResultCode::Rejected_Min_Repetition_Interval);
|
||||
}
|
||||
|
||||
TEST(DataConfirm, validate_data_request_with_area) {
|
||||
MIB mib;
|
||||
DataRequestWithArea req(mib);
|
||||
EXPECT_EQ(validate_data_request(req, mib),
|
||||
DataConfirm::ResultCode::Accepted);
|
||||
|
||||
Circle c;
|
||||
// radius = magnitude of max area size -> circle area is much larger
|
||||
c.r = vanetza::units::Length(mib.itsGnMaxGeoAreaSize / meter); // hack!
|
||||
req.destination.shape = c;
|
||||
EXPECT_EQ(validate_data_request(req, mib),
|
||||
DataConfirm::ResultCode::Rejected_Max_Geo_Area_Size);
|
||||
}
|
||||
|
||||
TEST(DataConfirm, validate_payload) {
|
||||
MIB mib;
|
||||
std::unique_ptr<DownPacket> no_payload;
|
||||
std::unique_ptr<DownPacket> giant_payload(new DownPacket());
|
||||
{
|
||||
vanetza::ByteBuffer giant_buffer;
|
||||
std::fill_n(std::back_inserter(giant_buffer), 2048, 0x0f);
|
||||
(*giant_payload)[vanetza::OsiLayer::Link] = std::move(giant_buffer);
|
||||
}
|
||||
std::unique_ptr<DownPacket> ok_payload(new DownPacket());
|
||||
|
||||
EXPECT_EQ(validate_payload(no_payload, mib),
|
||||
DataConfirm::ResultCode::Rejected_Unspecified);
|
||||
EXPECT_EQ(validate_payload(giant_payload, mib),
|
||||
DataConfirm::ResultCode::Rejected_Max_SDU_Size);
|
||||
EXPECT_EQ(validate_payload(ok_payload, mib),
|
||||
DataConfirm::ResultCode::Accepted);
|
||||
}
|
||||
|
||||
TEST(DataConfirm, xor_op) {
|
||||
DataConfirm a;
|
||||
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Accepted);
|
||||
a ^= DataConfirm::ResultCode::Rejected_Max_Lifetime;
|
||||
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Max_Lifetime);
|
||||
a ^= DataConfirm::ResultCode::Accepted;
|
||||
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Max_Lifetime);
|
||||
a ^= DataConfirm::ResultCode::Rejected_Unspecified;
|
||||
EXPECT_EQ(a.result_code, DataConfirm::ResultCode::Rejected_Unspecified);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/data_request.hpp>
|
||||
#include <vanetza/units/time.hpp>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
using vanetza::units::si::seconds;
|
||||
|
||||
TEST(DataRequest, repetition) {
|
||||
MIB mib;
|
||||
DataRequest r(mib);
|
||||
EXPECT_FALSE(!!r.repetition);
|
||||
|
||||
r.repetition = DataRequest::Repetition();
|
||||
EXPECT_TRUE(!!r.repetition);
|
||||
}
|
||||
|
||||
TEST(DataRequest, has_further_repetition) {
|
||||
DataRequest::Repetition repetition;
|
||||
repetition.interval = 10.0 * seconds;
|
||||
repetition.maximum = 0.0 * seconds;
|
||||
EXPECT_FALSE(has_further_repetition(repetition));
|
||||
|
||||
repetition.interval = 0.0 * seconds;
|
||||
EXPECT_FALSE(has_further_repetition(repetition));
|
||||
|
||||
repetition.maximum = -10.0 * seconds;
|
||||
repetition.interval = -15.0 * seconds;
|
||||
EXPECT_FALSE(has_further_repetition(repetition));
|
||||
|
||||
repetition.maximum = 30.0 * seconds;
|
||||
repetition.interval = 10.0 * seconds;
|
||||
EXPECT_TRUE(has_further_repetition(repetition));
|
||||
|
||||
repetition.interval = 0.0 * seconds;
|
||||
EXPECT_FALSE(has_further_repetition(repetition));
|
||||
}
|
||||
|
||||
TEST(DataRequest, decrement_by_one) {
|
||||
DataRequest::Repetition repetition;
|
||||
repetition.maximum = 30.0 * seconds;
|
||||
repetition.interval = 10.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.interval / seconds, 10.0);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 20.0);
|
||||
|
||||
repetition.interval = 5.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 15.0);
|
||||
|
||||
repetition.interval = 20.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
|
||||
EXPECT_DOUBLE_EQ(repetition.interval / seconds, 20.0);
|
||||
|
||||
repetition.maximum = -30.0 * seconds;
|
||||
repetition.interval = -60.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
|
||||
|
||||
repetition.maximum = -30.0 * seconds;
|
||||
repetition.interval = -10.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
|
||||
|
||||
repetition.maximum = 30.0 * seconds;
|
||||
repetition.interval = -10.0 * seconds;
|
||||
decrement_by_one(repetition);
|
||||
EXPECT_DOUBLE_EQ(repetition.maximum / seconds, 0.0);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <vanetza/geonet/dcc_mco_field.hpp>
|
||||
|
||||
using namespace vanetza::geonet;
|
||||
using vanetza::dcc::ChannelLoad;
|
||||
|
||||
TEST(DccMcoField, ctor)
|
||||
{
|
||||
DccMcoField mco;
|
||||
EXPECT_EQ(ChannelLoad(0.0), mco.local_cbr());
|
||||
EXPECT_EQ(ChannelLoad(0.0), mco.neighbour_cbr());
|
||||
EXPECT_EQ(0, mco.output_power());
|
||||
}
|
||||
|
||||
TEST(DccMcoField, uint32_view)
|
||||
{
|
||||
DccMcoField mco;
|
||||
EXPECT_EQ(0, static_cast<uint32_t>(mco));
|
||||
|
||||
// last 11 bits are reserved for future use: masked zero
|
||||
mco = DccMcoField(0x12345678);
|
||||
EXPECT_EQ(0x12345000, static_cast<uint32_t>(mco));
|
||||
}
|
||||
|
||||
TEST(DccMcoField, channel_load)
|
||||
{
|
||||
DccMcoField mco;
|
||||
mco.local_cbr(ChannelLoad(0.5));
|
||||
EXPECT_NEAR(0.5, mco.local_cbr().value(), 1.0 / 255.0);
|
||||
|
||||
mco.neighbour_cbr(ChannelLoad(0.25));
|
||||
EXPECT_NEAR(0.25, mco.neighbour_cbr().value(), 1.0 / 255.0);
|
||||
}
|
||||
|
||||
TEST(DccMcoField, output_power)
|
||||
{
|
||||
DccMcoField mco;
|
||||
mco.output_power(10);
|
||||
EXPECT_EQ(10, mco.output_power());
|
||||
|
||||
mco.output_power(31);
|
||||
EXPECT_EQ(31, mco.output_power());
|
||||
|
||||
mco.output_power(32);
|
||||
EXPECT_EQ(31, mco.output_power());
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user