Coverage Report

Created: 2026-10-28 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ocudu/lib/nrppa/procedures/measurement_procedure.cpp
Line
Count
Source
1
// SPDX-FileCopyrightText: Copyright (C) 2021-2026 Software Radio Systems Limited
2
// SPDX-FileCopyrightText: Copyright (C) 2026 OCUDU contributors
3
// SPDX-License-Identifier: BSD-3-Clause-Open-MPI
4
// Portions of this file may implement 3GPP specifications, which may be subject to additional licensing requirements.
5
6
#include "measurement_procedure.h"
7
#include "../nrppa_asn1_converters.h"
8
#include "../nrppa_helper.h"
9
#include "ocudu/adt/format.h"
10
#include "ocudu/asn1/nrppa/common.h"
11
#include "ocudu/asn1/nrppa/nrppa_pdu_contents.h"
12
#include "ocudu/ran/positioning/measurement_information.h"
13
#include "ocudu/support/async/coroutine.h"
14
#include "fmt/format.h"
15
#include <variant>
16
17
using namespace ocudu;
18
using namespace ocucp;
19
20
measurement_procedure::measurement_procedure(cu_cp_amf_index_t            amf_index_,
21
                                             const measurement_request_t& request_,
22
                                             uint16_t                     transaction_id_,
23
                                             const nrppa_trp_registry&    trp_registry_,
24
                                             nrppa_meas_context_list&     meas_ctxt_list_,
25
                                             nrppa_du_context_list&       du_ctxt_list_,
26
                                             nrppa_cu_cp_notifier&        cu_cp_notifier_,
27
                                             ocudulog::basic_logger&      logger_) :
28
0
  amf_index(amf_index_),
29
0
  meas_request(request_),
30
0
  transaction_id(transaction_id_),
31
0
  trp_registry(trp_registry_),
32
0
  meas_ctxt_list(meas_ctxt_list_),
33
0
  du_ctxt_list(du_ctxt_list_),
34
0
  cu_cp_notifier(cu_cp_notifier_),
35
0
  logger(logger_)
36
0
{
37
0
}
38
39
void measurement_procedure::operator()(coro_context<async_task<void>>& ctx)
40
0
{
41
0
  CORO_BEGIN(ctx);
42
43
0
  logger.info("\"{}\" started...", name());
44
45
  // NOTE: create_measurement_context() must run first, as it allocates the RAN Meas ID that
46
  // prepare_du_measurement_information_requests() stamps onto every per-DU request.
47
0
  if (!create_measurement_context() or !prepare_du_measurement_information_requests()) {
48
0
    logger.warning("\"{}\" failed", name());
49
    // Send failure to CU-CP.
50
0
    send_ul_nrppa_pdu(logger,
51
0
                      cu_cp_notifier,
52
0
                      create_measurement_failure(nrppa_cause_protocol_t::msg_not_compatible_with_receiver_state),
53
0
                      "MeasResponse",
54
0
                      "MeasFailure",
55
0
                      amf_index);
56
0
    CORO_EARLY_RETURN();
57
0
  }
58
59
  // TODO: use when_all.
60
0
  for (du_request_it = du_meas_requests.begin(); du_request_it != du_meas_requests.end(); du_request_it++) {
61
0
    sub_procedure_du_index     = du_request_it->first;
62
0
    sub_procedure_meas_request = du_request_it->second;
63
    // Send measurement request to DU.
64
0
    CORO_AWAIT_VALUE(
65
0
        du_meas_outcome,
66
0
        du_ctxt_list[sub_procedure_du_index].f1ap->on_measurement_information_request(sub_procedure_meas_request));
67
68
0
    handle_du_measurement_information_outcome(sub_procedure_du_index);
69
0
  }
70
71
0
  handle_procedure_outcome();
72
73
0
  logger.info("\"{}\" finished successfully", name());
74
75
0
  CORO_RETURN();
76
0
}
77
78
bool measurement_procedure::create_measurement_context()
79
0
{
80
  // Allocate RAN MEAS ID.
81
0
  expected<ran_meas_id_t, std::string> ret = meas_ctxt_list.allocate_ran_meas_id();
82
0
  if (!ret.has_value()) {
83
0
    logger.warning("lmf_meas_id={}: Couldn't allocate RAN meas ID. Cause: {}",
84
0
                   fmt::underlying(meas_request.lmf_meas_id),
85
0
                   ret.error());
86
0
    return false;
87
0
  }
88
89
0
  ran_meas_id = ret.value();
90
91
0
  std::vector<trp_id_t> trp_list;
92
0
  trp_list.reserve(meas_request.trp_meas_request_list.size());
93
0
  for (const auto& trp_meas_req_item : meas_request.trp_meas_request_list) {
94
0
    trp_list.push_back(trp_meas_req_item.trp_id);
95
0
  }
96
97
  // Create measurement context and store it.
98
0
  meas_ctxt_list.add_measurement(amf_index, ran_meas_id, meas_request.lmf_meas_id, trp_list);
99
100
0
  return true;
101
0
}
102
103
bool measurement_procedure::prepare_du_measurement_information_requests()
104
0
{
105
  // A TRP is addressed by its ID, which only the TRP Information Exchange procedure maps to a DU.
106
0
  if (!trp_registry.has_trp_information()) {
107
0
    logger.warning("lmf_meas_id={}: No TRP information available", fmt::underlying(meas_request.lmf_meas_id));
108
0
    return false;
109
0
  }
110
111
0
  for (const auto& trp_meas_req_item : meas_request.trp_meas_request_list) {
112
0
    trp_id_t trp_id = trp_meas_req_item.trp_id;
113
0
    if (std::optional<cu_cp_du_index_t> du_idx = trp_registry.find_du(trp_id); du_idx.has_value()) {
114
0
      cu_cp_du_index_t du_index = du_idx.value();
115
      // If the DU index is not found in the du_meas_requests map, create it.
116
0
      if (du_meas_requests.find(du_index) == du_meas_requests.end()) {
117
0
        measurement_request_t du_meas_request;
118
0
        du_meas_request.lmf_meas_id                      = meas_request.lmf_meas_id;
119
0
        du_meas_request.ran_meas_id                      = ran_meas_id;
120
0
        du_meas_request.report_characteristics           = meas_request.report_characteristics;
121
0
        du_meas_request.meas_periodicity                 = meas_request.meas_periodicity;
122
0
        du_meas_request.trp_meas_quantities              = meas_request.trp_meas_quantities;
123
0
        du_meas_request.sfn_initialization_time          = meas_request.sfn_initialization_time;
124
0
        du_meas_request.srs_config                       = meas_request.srs_config;
125
0
        du_meas_request.meas_beam_info_request           = meas_request.meas_beam_info_request;
126
0
        du_meas_request.sys_frame_num                    = meas_request.sys_frame_num;
127
0
        du_meas_request.slot_num                         = meas_request.slot_num;
128
0
        du_meas_request.meas_periodicity_extended        = meas_request.meas_periodicity_extended;
129
0
        du_meas_request.resp_time                        = meas_request.resp_time;
130
0
        du_meas_request.meas_characteristics_request_ind = meas_request.meas_characteristics_request_ind;
131
0
        du_meas_request.meas_time_occasion               = meas_request.meas_time_occasion;
132
0
        du_meas_request.meas_amount                      = meas_request.meas_amount;
133
134
0
        du_meas_requests.emplace(du_index, du_meas_request);
135
0
      }
136
137
0
      du_meas_requests.at(du_index).trp_meas_request_list.push_back(trp_meas_req_item);
138
0
    }
139
0
  }
140
141
0
  if (du_meas_requests.empty()) {
142
0
    logger.warning("lmf_meas_id={}: No DU for TRP IDs found", fmt::underlying(meas_request.lmf_meas_id));
143
0
    return false;
144
0
  }
145
146
0
  return true;
147
0
}
148
149
void measurement_procedure::handle_du_measurement_information_outcome(cu_cp_du_index_t du_index)
150
0
{
151
0
  if (du_meas_outcome.has_value()) {
152
0
    if (procedure_outcome.has_value()) {
153
      // Append result to the response message.
154
0
      for (const auto& trp_meas_resp_item : du_meas_outcome.value().trp_meas_resp_list) {
155
0
        procedure_outcome->trp_meas_resp_list.push_back(trp_meas_resp_item);
156
0
      }
157
0
    } else {
158
0
      procedure_outcome = du_meas_outcome.value();
159
0
    }
160
0
  } else {
161
0
    logger.warning("Measurement Information Transfer Procedure failed for DU index={}", du_index);
162
0
  }
163
0
}
164
165
void measurement_procedure::handle_procedure_outcome()
166
0
{
167
0
  if (!procedure_outcome.has_value()) {
168
0
    meas_outcome = create_measurement_failure(nrppa_cause_radio_network_t::requested_item_not_supported);
169
0
    logger.warning("\"{}\" failed", name());
170
0
  } else {
171
0
    meas_outcome = create_measurement_response();
172
0
    logger.info("\"{}\" finished successfully", name());
173
0
  }
174
175
  // The measurement context only exists to correlate the DU response(s) with this one-shot query; release it now
176
  // that the procedure has completed, freeing up its RAN Meas ID for reuse.
177
0
  meas_ctxt_list.remove_measurement_context(meas_request.lmf_meas_id);
178
179
  // Send response to CU-CP.
180
0
  send_ul_nrppa_pdu(logger, cu_cp_notifier, meas_outcome, "MeasResponse", "MeasFailure", amf_index);
181
0
}
182
183
asn1::nrppa::nr_ppa_pdu_c measurement_procedure::create_measurement_failure(nrppa_cause_t cause) const
184
0
{
185
0
  asn1::nrppa::nr_ppa_pdu_c asn1_fail;
186
187
0
  asn1_fail.set_unsuccessful_outcome().load_info_obj(ASN1_NRPPA_ID_MEAS);
188
0
  asn1_fail.unsuccessful_outcome().nrppatransaction_id = transaction_id;
189
0
  asn1::nrppa::meas_fail_s& meas_init_fail             = asn1_fail.unsuccessful_outcome().value.meas_fail();
190
191
0
  meas_init_fail->lmf_meas_id = to_underlying(meas_request.lmf_meas_id);
192
0
  meas_init_fail->cause       = cause_to_asn1(cause);
193
194
0
  return asn1_fail;
195
0
}
196
197
static inline asn1::nrppa::relative_path_delay_c
198
relative_path_delay_to_asn1(const relative_path_delay_t& relative_path_delay)
199
0
{
200
0
  asn1::nrppa::relative_path_delay_c asn1_relative_path_delay;
201
202
0
  if (relative_path_delay.type == relative_path_delay_t::type_t::k0) {
203
0
    asn1_relative_path_delay.set_k0() = relative_path_delay.value;
204
0
  } else if (relative_path_delay.type == relative_path_delay_t::type_t::k1) {
205
0
    asn1_relative_path_delay.set_k1() = relative_path_delay.value;
206
0
  } else if (relative_path_delay.type == relative_path_delay_t::type_t::k2) {
207
0
    asn1_relative_path_delay.set_k2() = relative_path_delay.value;
208
0
  } else if (relative_path_delay.type == relative_path_delay_t::type_t::k3) {
209
0
    asn1_relative_path_delay.set_k3() = relative_path_delay.value;
210
0
  } else if (relative_path_delay.type == relative_path_delay_t::type_t::k4) {
211
0
    asn1_relative_path_delay.set_k4() = relative_path_delay.value;
212
0
  } else {
213
0
    asn1_relative_path_delay.set_k5() = relative_path_delay.value;
214
0
  }
215
216
0
  return asn1_relative_path_delay;
217
0
}
218
219
static inline asn1::nrppa::trp_meas_quality_c trp_meas_qualitiy_to_asn1(const trp_meas_quality_t& trp_meas_quality)
220
0
{
221
0
  asn1::nrppa::trp_meas_quality_c asn1_trp_meas_quality;
222
223
0
  if (std::holds_alternative<trp_meas_timing_quality_t>(trp_meas_quality)) {
224
0
    const trp_meas_timing_quality_t& trp_meas_timing_quality = std::get<trp_meas_timing_quality_t>(trp_meas_quality);
225
0
    asn1::nrppa::trp_meas_timing_quality_s& asn1_trp_meas_timing_quality =
226
0
        asn1_trp_meas_quality.set_timing_meas_quality();
227
228
0
    asn1_trp_meas_timing_quality.meas_quality = trp_meas_timing_quality.meas_quality;
229
0
    asn1_trp_meas_timing_quality.resolution   = asn1::nrppa::trp_meas_timing_quality_s::resolution_opts::options(
230
0
        (uint8_t)trp_meas_timing_quality.resolution.value);
231
0
  } else {
232
0
    const trp_meas_angle_quality_t& trp_meas_angle_quality = std::get<trp_meas_angle_quality_t>(trp_meas_quality);
233
0
    asn1::nrppa::trp_meas_angle_quality_s& asn1_trp_meas_angle_quality = asn1_trp_meas_quality.set_angle_meas_quality();
234
235
0
    asn1_trp_meas_angle_quality.azimuth_quality = trp_meas_angle_quality.azimuth_quality;
236
0
    if (trp_meas_angle_quality.zenith_quality.has_value()) {
237
0
      asn1_trp_meas_angle_quality.zenith_quality_present = true;
238
0
      asn1_trp_meas_angle_quality.zenith_quality         = trp_meas_angle_quality.zenith_quality.value();
239
0
    }
240
0
    asn1_trp_meas_angle_quality.resolution = asn1::nrppa::trp_meas_angle_quality_s::resolution_opts::options(
241
0
        (uint8_t)trp_meas_angle_quality.resolution.value);
242
0
  }
243
244
0
  return asn1_trp_meas_quality;
245
0
}
246
247
static asn1::nrppa::lcs_to_gcs_translation_s
248
lcs_to_gcs_translation_to_asn1(const lcs_to_gcs_translation_t& lcs_to_gcs_translation)
249
0
{
250
0
  asn1::nrppa::lcs_to_gcs_translation_s asn1_lcs_to_gcs_translation;
251
0
  asn1_lcs_to_gcs_translation.alpha = lcs_to_gcs_translation.alpha;
252
0
  asn1_lcs_to_gcs_translation.beta  = lcs_to_gcs_translation.beta;
253
0
  asn1_lcs_to_gcs_translation.gamma = lcs_to_gcs_translation.gamma;
254
0
  return asn1_lcs_to_gcs_translation;
255
0
}
256
257
static inline asn1::nrppa::multiple_ul_ao_a_item_c
258
multiple_ul_aoa_item_to_asn1(const multiple_ul_aoa_item_t& multiple_ul_aoa_item)
259
0
{
260
0
  asn1::nrppa::multiple_ul_ao_a_item_c asn1_multiple_ul_aoa_item;
261
0
  if (std::holds_alternative<ul_angle_of_arrival_t>(multiple_ul_aoa_item)) {
262
0
    const ul_angle_of_arrival_t& aoa      = std::get<ul_angle_of_arrival_t>(multiple_ul_aoa_item);
263
0
    asn1::nrppa::ul_ao_a_s&      asn1_aoa = asn1_multiple_ul_aoa_item.set_ul_ao_a();
264
0
    asn1_aoa.azimuth_ao_a                 = aoa.azimuth_aoa;
265
0
    if (aoa.zenith_aoa.has_value()) {
266
0
      asn1_aoa.zenith_ao_a_present = true;
267
0
      asn1_aoa.zenith_ao_a         = aoa.zenith_aoa.value();
268
0
    }
269
0
    if (aoa.lcs_to_gcs_translation.has_value()) {
270
0
      asn1_aoa.lcs_to_gcs_translation_present = true;
271
0
      asn1_aoa.lcs_to_gcs_translation.alpha   = aoa.lcs_to_gcs_translation->alpha;
272
0
      asn1_aoa.lcs_to_gcs_translation.beta    = aoa.lcs_to_gcs_translation->beta;
273
0
      asn1_aoa.lcs_to_gcs_translation.gamma   = aoa.lcs_to_gcs_translation->gamma;
274
0
    }
275
0
  } else {
276
0
    const zoa_t&         zoa      = std::get<zoa_t>(multiple_ul_aoa_item);
277
0
    asn1::nrppa::zo_a_s& asn1_zoa = asn1_multiple_ul_aoa_item.set_ul_zo_a();
278
0
    asn1_zoa.zenith_ao_a          = zoa.zenith_aoa;
279
0
    if (zoa.lcs_to_gcs_translation.has_value()) {
280
0
      asn1_zoa.lcs_to_gcs_translation_present = true;
281
0
      asn1_zoa.lcs_to_gcs_translation.alpha   = zoa.lcs_to_gcs_translation->alpha;
282
0
      asn1_zoa.lcs_to_gcs_translation.beta    = zoa.lcs_to_gcs_translation->beta;
283
0
      asn1_zoa.lcs_to_gcs_translation.gamma   = zoa.lcs_to_gcs_translation->gamma;
284
0
    }
285
0
  }
286
287
0
  return asn1_multiple_ul_aoa_item;
288
0
}
289
290
static inline asn1::nrppa::add_path_list_item_s
291
add_path_list_item_to_asn1(const add_path_list_item_t& add_path_list_item)
292
0
{
293
0
  asn1::nrppa::add_path_list_item_s asn1_add_path_list_item;
294
  // Fill relative time of path.
295
0
  asn1_add_path_list_item.relative_time_of_path = relative_path_delay_to_asn1(add_path_list_item.relative_time_of_path);
296
297
  // Fill path quality.
298
0
  if (add_path_list_item.path_quality.has_value()) {
299
0
    asn1_add_path_list_item.path_quality_present = true;
300
0
    asn1_add_path_list_item.path_quality         = trp_meas_qualitiy_to_asn1(add_path_list_item.path_quality.value());
301
0
  }
302
303
  // Fill IE exts.
304
0
  if (!add_path_list_item.multiple_ul_aoa.empty()) {
305
0
    asn1_add_path_list_item.ie_exts_present                  = true;
306
0
    asn1_add_path_list_item.ie_exts.multiple_ul_ao_a_present = true;
307
0
    for (const auto multiple_ul_aoa_item : add_path_list_item.multiple_ul_aoa) {
308
0
      asn1_add_path_list_item.ie_exts.multiple_ul_ao_a.multiple_ul_ao_a.push_back(
309
0
          multiple_ul_aoa_item_to_asn1(multiple_ul_aoa_item));
310
0
    }
311
0
  }
312
313
0
  if (add_path_list_item.path_pwr.has_value()) {
314
0
    asn1_add_path_list_item.ie_exts_present                    = true;
315
0
    asn1_add_path_list_item.ie_exts.path_pwr_present           = true;
316
0
    asn1_add_path_list_item.ie_exts.path_pwr.first_path_rsrp_p = add_path_list_item.path_pwr->first_path_rsrp_pwr;
317
0
  }
318
319
0
  return asn1_add_path_list_item;
320
0
}
321
322
static inline asn1::nrppa::extended_add_path_list_item_s
323
extended_add_path_list_item_to_asn1(const add_path_list_item_t& extended_add_path_list_item)
324
0
{
325
0
  asn1::nrppa::extended_add_path_list_item_s asn1_extended_add_path_list_item;
326
  // Fill relative time of path.
327
0
  asn1_extended_add_path_list_item.relative_time_of_path =
328
0
      relative_path_delay_to_asn1(extended_add_path_list_item.relative_time_of_path);
329
330
  // Fill path quality.
331
0
  if (extended_add_path_list_item.path_quality.has_value()) {
332
0
    asn1_extended_add_path_list_item.path_quality_present = true;
333
0
    asn1_extended_add_path_list_item.path_quality =
334
0
        trp_meas_qualitiy_to_asn1(extended_add_path_list_item.path_quality.value());
335
0
  }
336
337
  // Fill Multiple UL AOA.
338
0
  if (!extended_add_path_list_item.multiple_ul_aoa.empty()) {
339
0
    asn1_extended_add_path_list_item.multiple_ul_ao_a_present = true;
340
0
    for (const auto multiple_ul_aoa_item : extended_add_path_list_item.multiple_ul_aoa) {
341
0
      asn1_extended_add_path_list_item.multiple_ul_ao_a.multiple_ul_ao_a.push_back(
342
0
          multiple_ul_aoa_item_to_asn1(multiple_ul_aoa_item));
343
0
    }
344
0
  }
345
346
0
  if (extended_add_path_list_item.path_pwr.has_value()) {
347
0
    asn1_extended_add_path_list_item.path_pwr_present = true;
348
0
    asn1_extended_add_path_list_item.path_pwr.first_path_rsrp_p =
349
0
        extended_add_path_list_item.path_pwr->first_path_rsrp_pwr;
350
0
  }
351
352
0
  return asn1_extended_add_path_list_item;
353
0
}
354
355
asn1::nrppa::nr_ppa_pdu_c measurement_procedure::create_measurement_response()
356
0
{
357
0
  asn1::nrppa::nr_ppa_pdu_c asn1_resp;
358
359
0
  asn1_resp.set_successful_outcome().load_info_obj(ASN1_NRPPA_ID_MEAS);
360
0
  asn1_resp.successful_outcome().nrppatransaction_id = transaction_id;
361
362
0
  asn1::nrppa::meas_resp_s& asn1_meas_resp = asn1_resp.successful_outcome().value.meas_resp();
363
364
0
  asn1_meas_resp->lmf_meas_id = to_underlying(procedure_outcome->lmf_meas_id);
365
0
  asn1_meas_resp->ran_meas_id = to_underlying(ran_meas_id);
366
367
0
  if (!procedure_outcome->trp_meas_resp_list.empty()) {
368
0
    asn1_meas_resp->trp_meas_resp_list_present = true;
369
370
0
    for (const auto& trp_measurement_response_item : procedure_outcome->trp_meas_resp_list) {
371
0
      asn1::nrppa::trp_meas_resp_item_s asn1_trp_meas_resp_item;
372
0
      asn1_trp_meas_resp_item.trp_id = to_underlying(trp_measurement_response_item.trp_id);
373
0
      for (const auto& trp_meas_result_item : trp_measurement_response_item.meas_result) {
374
0
        asn1::nrppa::trp_meas_result_item_s asn1_trp_meas_result_item;
375
376
        // Fill measured results value.
377
0
        if (std::holds_alternative<ul_angle_of_arrival_t>(trp_meas_result_item.measured_results_value)) {
378
0
          ul_angle_of_arrival_t   ul_aoa = std::get<ul_angle_of_arrival_t>(trp_meas_result_item.measured_results_value);
379
0
          asn1::nrppa::ul_ao_a_s& asn1_ul_aoa =
380
0
              asn1_trp_meas_result_item.measured_results_value.set_ul_angle_of_arrival();
381
0
          asn1_ul_aoa.azimuth_ao_a = ul_aoa.azimuth_aoa;
382
0
          if (ul_aoa.zenith_aoa.has_value()) {
383
0
            asn1_ul_aoa.zenith_ao_a_present = true;
384
0
            asn1_ul_aoa.zenith_ao_a         = ul_aoa.zenith_aoa.value();
385
0
          }
386
0
          if (ul_aoa.lcs_to_gcs_translation.has_value()) {
387
0
            asn1_ul_aoa.lcs_to_gcs_translation_present = true;
388
0
            asn1_ul_aoa.lcs_to_gcs_translation = lcs_to_gcs_translation_to_asn1(ul_aoa.lcs_to_gcs_translation.value());
389
0
          }
390
0
        } else if (const auto* zoa = std::get_if<zoa_t>(&trp_meas_result_item.measured_results_value)) {
391
0
          auto& asn1_ext = asn1_trp_meas_result_item.measured_results_value.set_choice_ext();
392
0
          asn1_ext->set(asn1::nrppa::trp_measured_results_value_ext_ies_o::value_c::types_opts::zo_a);
393
0
          asn1::nrppa::zo_a_s& asn1_zoa = asn1_ext->zo_a();
394
0
          asn1_zoa.zenith_ao_a          = zoa->zenith_aoa;
395
0
          if (zoa->lcs_to_gcs_translation.has_value()) {
396
0
            asn1_zoa.lcs_to_gcs_translation_present = true;
397
0
            asn1_zoa.lcs_to_gcs_translation = lcs_to_gcs_translation_to_asn1(zoa->lcs_to_gcs_translation.value());
398
0
          }
399
0
        } else if (std::holds_alternative<uint8_t>(trp_meas_result_item.measured_results_value)) {
400
0
          asn1_trp_meas_result_item.measured_results_value.set_ul_srs_rsrp() =
401
0
              std::get<uint8_t>(trp_meas_result_item.measured_results_value);
402
0
        } else if (std::holds_alternative<ul_rtoa_t>(trp_meas_result_item.measured_results_value)) {
403
0
          ul_rtoa_t ul_rtoa = std::get<ul_rtoa_t>(trp_meas_result_item.measured_results_value);
404
0
          asn1::nrppa::ul_rtoa_measurement_s& asn1_ul_rtoa =
405
0
              asn1_trp_meas_result_item.measured_results_value.set_ul_rtoa();
406
407
          // Fill UL RTOA meas.
408
0
          if (ul_rtoa.ul_rtoa_meas.type == ul_rtoa_meas_t::type_t::k0) {
409
0
            asn1_ul_rtoa.ul_rto_ameas.set_k0() = ul_rtoa.ul_rtoa_meas.value;
410
0
          } else if (ul_rtoa.ul_rtoa_meas.type == ul_rtoa_meas_t::type_t::k1) {
411
0
            asn1_ul_rtoa.ul_rto_ameas.set_k1() = ul_rtoa.ul_rtoa_meas.value;
412
0
          } else if (ul_rtoa.ul_rtoa_meas.type == ul_rtoa_meas_t::type_t::k2) {
413
0
            asn1_ul_rtoa.ul_rto_ameas.set_k2() = ul_rtoa.ul_rtoa_meas.value;
414
0
          } else if (ul_rtoa.ul_rtoa_meas.type == ul_rtoa_meas_t::type_t::k3) {
415
0
            asn1_ul_rtoa.ul_rto_ameas.set_k3() = ul_rtoa.ul_rtoa_meas.value;
416
0
          } else if (ul_rtoa.ul_rtoa_meas.type == ul_rtoa_meas_t::type_t::k4) {
417
0
            asn1_ul_rtoa.ul_rto_ameas.set_k4() = ul_rtoa.ul_rtoa_meas.value;
418
0
          } else {
419
0
            asn1_ul_rtoa.ul_rto_ameas.set_k5() = (uint16_t)ul_rtoa.ul_rtoa_meas.value;
420
0
          }
421
          // Fill add path list.
422
0
          for (const auto& add_path_list_item : ul_rtoa.add_path_list) {
423
0
            asn1_ul_rtoa.add_path_list.push_back(add_path_list_item_to_asn1(add_path_list_item));
424
0
          }
425
          // Fill extended add path list.
426
0
          if (!ul_rtoa.extended_add_path_list.empty()) {
427
0
            asn1_ul_rtoa.ie_exts_present                        = true;
428
0
            asn1_ul_rtoa.ie_exts.extended_add_path_list_present = true;
429
0
            for (const auto& extended_add_path_list_item : ul_rtoa.extended_add_path_list) {
430
0
              asn1_ul_rtoa.ie_exts.extended_add_path_list.push_back(
431
0
                  extended_add_path_list_item_to_asn1(extended_add_path_list_item));
432
0
            }
433
0
          }
434
          // Fill TRP RX reg info.
435
0
          if (ul_rtoa.trp_rx_teg_info.has_value()) {
436
0
            asn1_ul_rtoa.ie_exts_present                       = true;
437
0
            asn1_ul_rtoa.ie_exts.trp_rx_teg_info_present       = true;
438
0
            asn1_ul_rtoa.ie_exts.trp_rx_teg_info.trp_rx_teg_id = ul_rtoa.trp_rx_teg_info->trp_rx_teg_id;
439
0
            asn1::number_to_enum(asn1_ul_rtoa.ie_exts.trp_rx_teg_info.trp_rx_timing_error_margin,
440
0
                                 ul_rtoa.trp_rx_teg_info->trp_rx_timing_error_margin);
441
0
          }
442
0
        } else {
443
0
          gnb_rx_tx_time_diff_t rx_tx_time_diff =
444
0
              std::get<gnb_rx_tx_time_diff_t>(trp_meas_result_item.measured_results_value);
445
0
          asn1::nrppa::gnb_rx_tx_time_diff_s& asn1_rx_tx_time_diff =
446
0
              asn1_trp_meas_result_item.measured_results_value.set_gnb_rx_tx_time_diff();
447
448
          // Fill RX TX time diff.
449
0
          if (rx_tx_time_diff.rx_tx_time_diff.type == gnb_rx_tx_time_diff_meas_t::type_t::k0) {
450
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k0() = rx_tx_time_diff.rx_tx_time_diff.value;
451
0
          } else if (rx_tx_time_diff.rx_tx_time_diff.type == gnb_rx_tx_time_diff_meas_t::type_t::k1) {
452
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k1() = rx_tx_time_diff.rx_tx_time_diff.value;
453
0
          } else if (rx_tx_time_diff.rx_tx_time_diff.type == gnb_rx_tx_time_diff_meas_t::type_t::k2) {
454
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k2() = rx_tx_time_diff.rx_tx_time_diff.value;
455
0
          } else if (rx_tx_time_diff.rx_tx_time_diff.type == gnb_rx_tx_time_diff_meas_t::type_t::k3) {
456
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k3() = rx_tx_time_diff.rx_tx_time_diff.value;
457
0
          } else if (rx_tx_time_diff.rx_tx_time_diff.type == gnb_rx_tx_time_diff_meas_t::type_t::k4) {
458
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k4() = rx_tx_time_diff.rx_tx_time_diff.value;
459
0
          } else {
460
0
            asn1_rx_tx_time_diff.rx_tx_time_diff.set_k5() = (uint16_t)rx_tx_time_diff.rx_tx_time_diff.value;
461
0
          }
462
          // Fill add path list.
463
0
          for (const auto& add_path_list_item : rx_tx_time_diff.add_path_list) {
464
0
            asn1_rx_tx_time_diff.add_path_list.push_back(add_path_list_item_to_asn1(add_path_list_item));
465
0
          }
466
          // Fill extended add path list.
467
0
          if (!rx_tx_time_diff.extended_add_path_list.empty()) {
468
0
            asn1_rx_tx_time_diff.ie_exts_present                        = true;
469
0
            asn1_rx_tx_time_diff.ie_exts.extended_add_path_list_present = true;
470
0
            for (const auto& extended_add_path_list_item : rx_tx_time_diff.extended_add_path_list) {
471
0
              asn1_rx_tx_time_diff.ie_exts.extended_add_path_list.push_back(
472
0
                  extended_add_path_list_item_to_asn1(extended_add_path_list_item));
473
0
            }
474
0
          }
475
          // Fill TRP TEG info.
476
0
          if (rx_tx_time_diff.trp_teg_info.has_value()) {
477
0
            asn1_rx_tx_time_diff.ie_exts_present             = true;
478
0
            asn1_rx_tx_time_diff.ie_exts.trpteg_info_present = true;
479
0
            if (std::holds_alternative<rx_tx_teg_t>(rx_tx_time_diff.trp_teg_info.value())) {
480
0
              const rx_tx_teg_t&        rx_tx_teg      = std::get<rx_tx_teg_t>(rx_tx_time_diff.trp_teg_info.value());
481
0
              asn1::nrppa::rx_tx_teg_s& asn1_rx_tx_teg = asn1_rx_tx_time_diff.ie_exts.trpteg_info.set_rx_tx_teg();
482
0
              asn1_rx_tx_teg.trp_rx_tx_teg_info.trp_rx_tx_teg_id = rx_tx_teg.trp_rx_tx_teg_info.trp_rx_tx_teg_id;
483
0
              asn1_rx_tx_teg.trp_rx_tx_teg_info.trp_rx_tx_timing_error_margin =
484
0
                  asn1::nrppa::rx_tx_timing_error_margin_opts::options(
485
0
                      (uint8_t)rx_tx_teg.trp_rx_tx_teg_info.trp_rx_tx_timing_error_margin.value);
486
0
              if (rx_tx_teg.trp_tx_teg_info.has_value()) {
487
0
                asn1_rx_tx_teg.trp_tx_teg_info_present       = true;
488
0
                asn1_rx_tx_teg.trp_tx_teg_info.trp_tx_teg_id = rx_tx_teg.trp_tx_teg_info->trp_tx_teg_id;
489
0
                asn1::number_to_enum(asn1_rx_tx_teg.trp_tx_teg_info.trp_tx_timing_error_margin,
490
0
                                     rx_tx_teg.trp_tx_teg_info->trp_tx_timing_error_margin);
491
0
              }
492
0
            } else {
493
0
              const rx_teg_t&        rx_teg             = std::get<rx_teg_t>(rx_tx_time_diff.trp_teg_info.value());
494
0
              asn1::nrppa::rx_teg_s& asn1_rx_teg        = asn1_rx_tx_time_diff.ie_exts.trpteg_info.set_rx_teg();
495
0
              asn1_rx_teg.trp_rx_teg_info.trp_rx_teg_id = rx_teg.trp_rx_teg_info.trp_rx_teg_id;
496
0
              asn1::number_to_enum(asn1_rx_teg.trp_rx_teg_info.trp_rx_timing_error_margin,
497
0
                                   rx_teg.trp_rx_teg_info.trp_rx_timing_error_margin);
498
0
              asn1_rx_teg.trp_tx_teg_info.trp_tx_teg_id = rx_teg.trp_tx_teg_info.trp_tx_teg_id;
499
0
              asn1::number_to_enum(asn1_rx_teg.trp_tx_teg_info.trp_tx_timing_error_margin,
500
0
                                   rx_teg.trp_tx_teg_info.trp_tx_timing_error_margin);
501
0
            }
502
0
          }
503
0
        }
504
505
        // Fill time stamp.
506
0
        asn1_trp_meas_result_item.time_stamp.sys_frame_num = trp_meas_result_item.time_stamp.sys_frame_num;
507
0
        if (trp_meas_result_item.time_stamp.slot_idx.type == time_stamp_slot_idx_t::idx_type::scs15) {
508
0
          asn1_trp_meas_result_item.time_stamp.slot_idx.set_scs_15() = trp_meas_result_item.time_stamp.slot_idx.value;
509
0
        } else if (trp_meas_result_item.time_stamp.slot_idx.type == time_stamp_slot_idx_t::idx_type::scs30) {
510
0
          asn1_trp_meas_result_item.time_stamp.slot_idx.set_scs_30() = trp_meas_result_item.time_stamp.slot_idx.value;
511
0
        } else if (trp_meas_result_item.time_stamp.slot_idx.type == time_stamp_slot_idx_t::idx_type::scs60) {
512
0
          asn1_trp_meas_result_item.time_stamp.slot_idx.set_scs_60() = trp_meas_result_item.time_stamp.slot_idx.value;
513
0
        } else {
514
0
          asn1_trp_meas_result_item.time_stamp.slot_idx.set_scs_120() = trp_meas_result_item.time_stamp.slot_idx.value;
515
0
        }
516
0
        if (trp_meas_result_item.time_stamp.meas_time.has_value()) {
517
0
          asn1_trp_meas_result_item.time_stamp.meas_time_present = true;
518
0
          asn1_trp_meas_result_item.time_stamp.meas_time.from_number(trp_meas_result_item.time_stamp.meas_time.value());
519
0
        }
520
521
        // Fill meas quality.
522
0
        if (trp_meas_result_item.meas_quality.has_value()) {
523
0
          asn1_trp_meas_result_item.meas_quality_present = true;
524
0
          asn1_trp_meas_result_item.meas_quality = trp_meas_qualitiy_to_asn1(trp_meas_result_item.meas_quality.value());
525
0
        }
526
527
        // Fill meas beam info.
528
0
        if (trp_meas_result_item.meas_beam_info.has_value()) {
529
0
          asn1_trp_meas_result_item.meas_beam_info_present = true;
530
0
          if (trp_meas_result_item.meas_beam_info->prs_res_id.has_value()) {
531
0
            asn1_trp_meas_result_item.meas_beam_info.prs_res_id_present = true;
532
0
            asn1_trp_meas_result_item.meas_beam_info.prs_res_id =
533
0
                trp_meas_result_item.meas_beam_info->prs_res_id.value();
534
0
          }
535
0
          if (trp_meas_result_item.meas_beam_info->prs_res_set_id.has_value()) {
536
0
            asn1_trp_meas_result_item.meas_beam_info.prs_res_set_id_present = true;
537
0
            asn1_trp_meas_result_item.meas_beam_info.prs_res_set_id =
538
0
                trp_meas_result_item.meas_beam_info->prs_res_set_id.value();
539
0
          }
540
0
          if (trp_meas_result_item.meas_beam_info->ssb_idx.has_value()) {
541
0
            asn1_trp_meas_result_item.meas_beam_info.ssb_idx_present = true;
542
0
            asn1_trp_meas_result_item.meas_beam_info.ssb_idx = trp_meas_result_item.meas_beam_info->ssb_idx.value();
543
0
          }
544
0
        }
545
546
0
        asn1_trp_meas_resp_item.meas_result.push_back(asn1_trp_meas_result_item);
547
0
      }
548
549
0
      if (trp_measurement_response_item.cgi_nr.has_value()) {
550
0
        asn1::protocol_ext_field_s<asn1::nrppa::trp_meas_resp_item_ext_ies_o> asn1_ie_exts_container;
551
0
        asn1_ie_exts_container->cell_id().nr_cell_id.from_number(trp_measurement_response_item.cgi_nr->nci.value());
552
0
        asn1_ie_exts_container->cell_id().plmn_id = trp_measurement_response_item.cgi_nr->plmn_id.to_bytes();
553
0
        asn1_trp_meas_resp_item.ie_exts.push_back(asn1_ie_exts_container);
554
0
      }
555
556
0
      asn1_meas_resp->trp_meas_resp_list.push_back(asn1_trp_meas_resp_item);
557
0
    }
558
0
  }
559
560
0
  return asn1_resp;
561
0
}