CARLA
Loading...
Searching...
No Matches
CustomV2XSensor.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8
9#include "Carla.h"
14#include "V2X/PathLossModel.h"
15
16#include <algorithm>
17#include <cstring>
18#include <string>
19
23
24ACustomV2XSensor::ACustomV2XSensor(const FObjectInitializer &ObjectInitializer)
25 : Super(ObjectInitializer)
26{
27 PrimaryActorTick.bCanEverTick = true;
28 RandomEngine = CreateDefaultSubobject<URandomEngine>(TEXT("RandomEngine"));
29
30 // Init path loss model
31 PathLossModelObj = std::make_unique<PathLossModel>(RandomEngine, this);
32}
33
35{
36 Super::SetOwner(Owner);
37
38 if(Owner != nullptr) {
39 UCarlaEpisode* CarlaEpisode = UCarlaStatics::GetCurrentEpisode(GetWorld());
40 FCarlaActor* CarlaActor = CarlaEpisode->FindCarlaActor(Owner);
41 if (CarlaActor != nullptr) {
42 mStationId = static_cast<long>(CarlaActor->GetActorId());
43 }
44 }
45}
46
47void ACustomV2XSensor::EndPlay(const EEndPlayReason::Type EndPlayReason)
48{
49 // Drop our queued messages from the shared static maps so a destroyed
50 // sensor cannot leave a dangling AActor* key for the surviving sensors.
51 {
52 std::lock_guard<std::mutex> lock(v2xDataLock);
55 }
56
57 // PathLossModelObj is a std::unique_ptr released automatically in the
58 // destructor; not reset here so teardown paths never see a freed object.
59 Super::EndPlay(EndPlayReason);
60}
61
63{
64 if ( mStationId == 0) {
65 UCarlaEpisode* CarlaEpisode = UCarlaStatics::GetCurrentEpisode(GetWorld());
66 FCarlaActor* CarlaActor = CarlaEpisode->FindCarlaActor(this);
67 if (CarlaActor != nullptr) {
68 mStationId = static_cast<long>(CarlaActor->GetActorId());
69 }
70 }
71}
72
77
78/* Function to add configurable parameters*/
79void ACustomV2XSensor::Set(const FActorDescription &ActorDescription)
80{
81 Super::Set(ActorDescription);
83
84 auto Channel = ActorDescription.Variations.Find("channel_id");
85 if (Channel != nullptr)
86 {
87 mChannelId = TCHAR_TO_UTF8(*Channel->Value);
88 }
89}
90
91void ACustomV2XSensor::SetPropagationParams(const float TransmitPower,
92 const float ReceiverSensitivity,
93 const float Frequency,
94 const float combined_antenna_gain,
95 const float path_loss_exponent,
96 const float reference_distance_fspl,
97 const float filter_distance,
98 const bool use_etsi_fading,
99 const float custom_fading_stddev)
100{
101 // forward parameters to PathLossModel Obj
102 PathLossModelObj->SetParams(TransmitPower, ReceiverSensitivity, Frequency, combined_antenna_gain, path_loss_exponent, reference_distance_fspl, filter_distance, use_etsi_fading, custom_fading_stddev);
103}
104
106 PathLossModelObj->SetPathLossModel(path_loss_model);
107}
108
110{
111 PathLossModelObj->SetScenario(scenario);
112}
113
114/*
115 * Function stores the actor details in to the static list.
116 * Calls the CaService object to generate CAM message
117 * Stores the message in static map
118 */
119void ACustomV2XSensor::PrePhysTick(float DeltaSeconds)
120{
121 Super::PrePhysTick(DeltaSeconds);
122 if (GetOwner())
123 {
124 // Swap the messages with the ones sent out since last sim cycle
125 std::lock_guard<std::mutex> lock(v2xDataLock);
126 auto LastMessagesResult = ACustomV2XSensor::gActorV2XDataMap.find(GetSenderId());
127 auto NextMessagesResult = ACustomV2XSensor::gActorV2XDataMapNextFrame.find(GetSenderId());
128 if ( LastMessagesResult != ACustomV2XSensor::gActorV2XDataMap.end() ) {
129 if ( NextMessagesResult != ACustomV2XSensor::gActorV2XDataMapNextFrame.end() ) {
130 // swap the next frame messages to current frame
131 LastMessagesResult->second.swap(NextMessagesResult->second);
132 ACustomV2XSensor::gActorV2XDataMapNextFrame.erase(NextMessagesResult);
133 } else {
134 // no new messages, just clear
135 ACustomV2XSensor::gActorV2XDataMap.erase(LastMessagesResult);
136 }
137 }
138 else if ( NextMessagesResult != ACustomV2XSensor::gActorV2XDataMapNextFrame.end() ) {
139 // first time, just move next to current
140 ACustomV2XSensor::gActorV2XDataMap.insert({GetSenderId(), V2XDataList()}).first->second.swap(NextMessagesResult->second);
141 ACustomV2XSensor::gActorV2XDataMapNextFrame.erase(NextMessagesResult);
142 }
143 else {
144 // no messages at all
145 }
146 }
147}
148
149
150/*
151 * Function takes care of sending messages to the current actor.
152 * First simulates the communication by calling LOSComm object.
153 * If there is a list present then messages from those list are sent to the current actor
154 */
155void ACustomV2XSensor::PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaTime)
156{
157 // tracing after we get the lock
158 TRACE_CPUPROFILER_EVENT_SCOPE(ACustomV2XSensor::PostPhysTick);
159 // Step 1: Create an actor list which has messages to send targeting this v2x sensor instance
160 std::vector<ActorPowerPair> ActorPowerList;
161 {
162 std::lock_guard<std::mutex> lock(v2xDataLock);
163 for (const auto &pair : gActorV2XDataMap)
164 {
165 // only different sensors with the same ChannelId talk to each other
166 if ((pair.first.Actor != this) && (mChannelId == pair.first.ChannelId))
167 {
168 // Skip senders with no queued message this tick; front() on an
169 // empty list is undefined behaviour.
170 if (pair.second.empty())
171 {
172 continue;
173 }
174 ActorPowerPair actor_power_pair;
175 actor_power_pair.first = pair.first.Actor;
176 // actor sending with transmit power (identical for all messages of this tick)
177 actor_power_pair.second = pair.second.front().Power;
178 ActorPowerList.push_back(actor_power_pair);
179 }
180 }
181 }
182
183 // Step 2: Simulate the communication for the actors in actor list to current actor.
184 FV2XData v2XData;
185 if (!ActorPowerList.empty())
186 {
187 UCarlaEpisode *carla_episode = UCarlaStatics::GetCurrentEpisode(GetWorld());
188 PathLossModelObj->Simulate(ActorPowerList, carla_episode, GetWorld());
189 // Step 3: Get the list of actors who can send message to current actor, and the receive power of their messages.
190 ActorPowerMap actor_receivepower_map = PathLossModelObj->GetReceiveActorPowerList();
191 // Step 4: Retrieve the messages of the actors that are received
192 {
193 std::lock_guard<std::mutex> lock(v2xDataLock);
194 for (const auto &pair : actor_receivepower_map)
195 {
196 // find + skip-on-miss: at() would throw std::out_of_range if the
197 // sender's queue was cleared between the two locked sections.
198 const auto sender_it = gActorV2XDataMap.find({pair.first, mChannelId});
199 if (sender_it == gActorV2XDataMap.end())
200 {
201 continue;
202 }
203 for ( const auto & send_msg_and_pw: sender_it->second)
204 {
205 carla::sensor::data::CustomV2XData received_msg_and_pw;
206 // sent CAM
207 received_msg_and_pw.Message = send_msg_and_pw.Message;
208 // receive power
209 received_msg_and_pw.Power = pair.second;
210
211 v2XData.WriteMessage(received_msg_and_pw);
212 }
213 }
214 }
215 }
216 // Step 5: Send message
217 if (v2XData.GetMessageCount() > 0)
218 {
219 auto DataStream = GetDataStream(*this);
220 DataStream.SerializeAndSend(*this, v2XData, DataStream.PopBufferFromPool());
221 }
222}
223
225{
226 // We have to queue the data immediately otherwhise only one single message can be processed per frame!
227 std::lock_guard<std::mutex> lock(v2xDataLock);
228
230
231 // make a pair of message and sending power
232 // if different v2x sensors send with different power, we need to store that
235 message_pw.Message.header.messageID = mMessageId;
236 message_pw.Message.header.stationID = mStationId;
237 message_pw.Message.data = data;
238 message_pw.Power = PathLossModelObj->GetTransmitPower();
239 gActorV2XDataMapNextFrame.insert({GetSenderId(), V2XDataList()}).first->second.push_back(message_pw);
240}
241
long long first
Definition LaneGraph.cpp:34
std::pair< AActor *, float > ActorPowerPair
std::map< AActor *, float > ActorPowerMap
EScenario
EPathLossModel
const long mMessageId
SenderId GetSenderId()
std::list< carla::sensor::data::CustomV2XData > V2XDataList
void Set(const FActorDescription &ActorDescription) override
void SetOwner(AActor *Owner) override
void SetPathLossModel(const EPathLossModel path_loss_model)
std::map< SenderId, V2XDataList > ActorV2XDataMap
void SetPropagationParams(const float TransmitPower, const float ReceiverSensitivity, const float Frequency, const float combined_antenna_gain, const float path_loss_exponent, const float reference_distance_fspl, const float filter_distance, const bool use_etsi_fading, const float custom_fading_stddev)
static std::mutex v2xDataLock
virtual void PrePhysTick(float DeltaSeconds) override
void SetScenario(EScenario scenario)
static ActorV2XDataMap gActorV2XDataMapNextFrame
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
static FActorDefinition GetSensorDefinition()
void Send(const carla::rpc::CustomV2XBytes &data)
std::string mChannelId
const long mProtocolVersion
virtual void PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaTime) override
std::unique_ptr< PathLossModel > PathLossModelObj
ACustomV2XSensor(const FObjectInitializer &ObjectInitializer)
static ActorV2XDataMap gActorV2XDataMap
FAsyncDataStream GetDataStream(SensorType &&Self)
Return the FDataStream associated with this sensor.
URandomEngine * RandomEngine
Random Engine used to provide noise for sensor output.
A view over an actor and its properties.
Definition CarlaActor.h:25
IdType GetActorId() const
Definition CarlaActor.h:81
static void SetCustomV2X(const FActorDescription &Description, ACustomV2XSensor *V2X)
A simulation episode.
FCarlaActor * FindCarlaActor(FCarlaActor::IdType ActorId)
Find a Carla actor by id.
static UCarlaEpisode * GetCurrentEpisode(const UObject *WorldContextObject)
Fixed-size binary payload carried by the custom V2X sensor (sensor.other.v2x_custom).
void WriteMessage(const CustomV2XData &message)
Adds a new message.
Definition V2XData.h:79
size_t GetMessageCount() const
Returns the number of currently received messages.
Definition V2XData.h:69
ITSContainer::ItsPduHeader_t header
Definition LibITS.h:783
carla::rpc::CustomV2XBytes data
Definition LibITS.h:784
A definition of a Carla Actor with all the variation and attributes.
A description of a Carla Actor with all its variation.
TMap< FString, FActorAttribute > Variations
User selected variations of the actor.