Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qchannel.cc
Go to the documentation of this file.
1/*-----------------------------------------------------------------------------
2 * Q2NS - Quantum Network Simulator
3 * Copyright (c) 2026 quantuminternet.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *---------------------------------------------------------------------------*/
9/**
10 * @file q2ns-qchannel.cc
11 * @brief Defines q2ns::QChannel.
12 */
13
14#include "ns3/q2ns-qchannel.h"
15
16#include "ns3/q2ns-qmap.h"
17#include "ns3/q2ns-qnet-device.h"
18#include "ns3/q2ns-qubit.h"
19
20#include "ns3/attribute.h"
21#include "ns3/log.h"
22#include "ns3/net-device.h"
23#include "ns3/nstime.h"
24#include "ns3/pointer.h"
25#include "ns3/simulator.h"
26
27namespace q2ns {
28
29NS_LOG_COMPONENT_DEFINE("QChannel");
30NS_OBJECT_ENSURE_REGISTERED(QChannel);
31
32ns3::TypeId QChannel::GetTypeId(void) {
33 static ns3::TypeId tid =
34 ns3::TypeId("q2ns::QChannel")
35 .SetParent<ns3::Channel>()
36 .SetGroupName("q2ns")
37 .AddConstructor<QChannel>()
38 .AddAttribute("Delay", "Symmetric propagation delay", ns3::TimeValue(ns3::Seconds(0)),
39 MakeTimeAccessor(&QChannel::SetDelay, &QChannel::GetDelay),
40 ns3::MakeTimeChecker())
41 .AddAttribute("DelayAB", "One-way propagation delay A->B",
42 ns3::TimeValue(ns3::Seconds(0)),
43 MakeTimeAccessor(&QChannel::SetDelayAB, &QChannel::GetDelayAB),
44 ns3::MakeTimeChecker())
45 .AddAttribute("DelayBA", "One-way propagation delay B->A",
46 ns3::TimeValue(ns3::Seconds(0)),
47 MakeTimeAccessor(&QChannel::SetDelayBA, &QChannel::GetDelayBA),
48 ns3::MakeTimeChecker())
49 .AddAttribute("Jitter",
50 "Symmetric jitter. A uniform random jitter in [-jitter, +jitter] is added "
51 "to the propagation delay on each send, with a minimum value of 0.",
52 ns3::TimeValue(ns3::Seconds(0)),
53 MakeTimeAccessor(&QChannel::SetJitter, &QChannel::GetJitter),
54 ns3::MakeTimeChecker())
55 .AddAttribute(
56 "JitterAB",
57 "One-way jitter A->B. A uniform random jitter in [-jitter, +jitter] is added "
58 "to the propagation delay on each send, with a minimum value of 0.",
59 ns3::TimeValue(ns3::Seconds(0)),
61 ns3::MakeTimeChecker())
62 .AddAttribute(
63 "JitterBA",
64 "One-way jitter B->A. A uniform random jitter in [-jitter, +jitter] is added "
65 "to the propagation delay on each send, with a minimum value of 0.",
66 ns3::TimeValue(ns3::Seconds(0)),
68 ns3::MakeTimeChecker())
69 .AddAttribute(
70 "QMap", "Symmetric channel map applied to both directions unless overridden.",
71 ns3::PointerValue(), ns3::MakePointerAccessor(&QChannel::SetQMap, &QChannel::GetQMap),
72 ns3::MakePointerChecker<QMap>())
73 .AddAttribute("QMapAB", "One-way channel map for A->B.", ns3::PointerValue(),
74 ns3::MakePointerAccessor(&QChannel::SetQMapAB, &QChannel::GetQMapAB),
75 ns3::MakePointerChecker<QMap>())
76 .AddAttribute("QMapBA", "One-way channel map for B->A.", ns3::PointerValue(),
77 ns3::MakePointerAccessor(&QChannel::SetQMapBA, &QChannel::GetQMapBA),
78 ns3::MakePointerChecker<QMap>());
79 return tid;
80}
81
82
83
85 jitterRv_ = ns3::CreateObject<ns3::UniformRandomVariable>();
86 mapRv_ = ns3::CreateObject<ns3::UniformRandomVariable>();
87}
88
89
90
91void QChannel::SetDelay(ns3::Time d) {
92 if (d < ns3::Seconds(0)) {
93 NS_LOG_WARN("SetDelay rejected: negative delay.");
94 return;
95 }
96
97 delayAB_ = d;
98 delayBA_ = d;
99}
100
101
102
103ns3::Time QChannel::GetDelay() const {
104 if (delayAB_ != delayBA_) {
105 NS_LOG_WARN("GetDelay returned A->B delay because the channel is asymmetric.");
106 }
107
108 return delayAB_;
109}
110
111
112
113void QChannel::SetJitter(ns3::Time j) {
114 if (j < ns3::Seconds(0)) {
115 NS_LOG_WARN("SetJitter rejected: negative jitter.");
116 return;
117 }
118
119 jitterAB_ = j;
120 jitterBA_ = j;
121}
122
123
124
125ns3::Time QChannel::GetJitter() const {
126 if (jitterAB_ != jitterBA_) {
127 NS_LOG_WARN("GetJitter returned A->B jitter because the channel is asymmetric.");
128 }
129
130 return jitterAB_;
131}
132
133
134
135void QChannel::SetQMap(ns3::Ptr<QMap> m) {
136 if (!m) {
137 NS_LOG_WARN("SetQMap received a null map; transmissions will use the identity map.");
138 }
139
140 qmapAB_ = m;
141 qmapBA_ = m;
142}
143
144
145
146ns3::Ptr<QMap> QChannel::GetQMap() const {
147 if (qmapAB_ != qmapBA_) {
148 NS_LOG_WARN("GetQMap returned A->B map because the channel is asymmetric.");
149 }
150
151 return qmapAB_;
152}
153
154
155
156void QChannel::SetDelayAB(ns3::Time d) {
157 if (d < ns3::Seconds(0)) {
158 NS_LOG_WARN("SetDelayAB rejected: negative delay.");
159 return;
160 }
161
162 delayAB_ = d;
163}
164
165
166
167ns3::Time QChannel::GetDelayAB() const {
168 return delayAB_;
169}
170
171
172
173void QChannel::SetDelayBA(ns3::Time d) {
174 if (d < ns3::Seconds(0)) {
175 NS_LOG_WARN("SetDelayBA rejected: negative delay.");
176 return;
177 }
178
179 delayBA_ = d;
180}
181
182
183
184ns3::Time QChannel::GetDelayBA() const {
185 return delayBA_;
186}
187
188
189
190void QChannel::SetJitterAB(ns3::Time j) {
191 if (j < ns3::Seconds(0)) {
192 NS_LOG_WARN("SetJitterAB rejected: negative jitter.");
193 return;
194 }
195
196 jitterAB_ = j;
197}
198
199
200
201ns3::Time QChannel::GetJitterAB() const {
202 return jitterAB_;
203}
204
205
206
207void QChannel::SetJitterBA(ns3::Time j) {
208 if (j < ns3::Seconds(0)) {
209 NS_LOG_WARN("SetJitterBA rejected: negative jitter.");
210 return;
211 }
212
213 jitterBA_ = j;
214}
215
216
217
218ns3::Time QChannel::GetJitterBA() const {
219 return jitterBA_;
220}
221
222
223
224void QChannel::SetQMapAB(ns3::Ptr<QMap> m) {
225 if (!m) {
226 NS_LOG_WARN("SetQMapAB received a null map; transmissions will use the identity map.");
227 }
228
229 qmapAB_ = m;
230}
231
232
233
234ns3::Ptr<QMap> QChannel::GetQMapAB() const {
235 return qmapAB_;
236}
237
238
239
240void QChannel::SetQMapBA(ns3::Ptr<QMap> m) {
241 if (!m) {
242 NS_LOG_WARN("SetQMapBA received a null map; transmissions will use the identity map.");
243 }
244
245 qmapBA_ = m;
246}
247
248
249
250ns3::Ptr<QMap> QChannel::GetQMapBA() const {
251 return qmapBA_;
252}
253
254
255
256void QChannel::Connect(ns3::Ptr<QNetDevice> a, ns3::Ptr<QNetDevice> b) {
257 if (!a || !b) {
258 NS_LOG_WARN("Connect received a null endpoint device.");
259 }
260
261 if (a == b && a != nullptr) {
262 NS_LOG_WARN("Connect received the same device for both endpoints.");
263 }
264
265 aDev_ = a;
266 bDev_ = b;
267
268 if (aDev_) {
269 aDev_->AttachChannel(ns3::Ptr<QChannel>(this));
270 }
271 if (bDev_) {
272 bDev_->AttachChannel(ns3::Ptr<QChannel>(this));
273 }
274}
275
276
277
278bool QChannel::SendFrom(QNetDevice* src, std::shared_ptr<Qubit> q) {
279 if (!src) {
280 NS_LOG_WARN("SendFrom rejected: null source device.");
281 return false;
282 }
283
284 if (!q) {
285 NS_LOG_WARN("SendFrom rejected: null qubit.");
286 return false;
287 }
288
289 const auto loc = q->GetLocation();
290 if (loc.type == LocationType::Lost) {
291 NS_LOG_WARN("SendFrom rejected: qubit " << q->GetQubitId() << " is lost.");
292 return false;
293 }
294
295 ns3::Ptr<QNetDevice> dst;
296 ns3::Time dly;
297 ns3::Time jtr;
298 ns3::Ptr<QMap> qmap;
299
300 if (aDev_ && src == ns3::PeekPointer(aDev_)) {
301 dst = bDev_;
302 dly = delayAB_;
303 jtr = jitterAB_;
304 qmap = qmapAB_;
305 } else if (bDev_ && src == ns3::PeekPointer(bDev_)) {
306 dst = aDev_;
307 dly = delayBA_;
308 jtr = jitterBA_;
309 qmap = qmapBA_;
310 } else {
311 NS_LOG_WARN("SendFrom rejected: source device is not attached to this channel.");
312 return false;
313 }
314
315 if (!dst) {
316 NS_LOG_WARN("SendFrom rejected: destination device is null.");
317 return false;
318 }
319
320 // Sample per-send jitter and clamp the resulting propagation delay to zero.
321 if (jtr > ns3::Time(0)) {
322 const double j = jitterRv_->GetValue(-jtr.GetSeconds(), jtr.GetSeconds());
323 dly += ns3::Seconds(j);
324 if (dly < ns3::Time(0)) {
325 dly = ns3::Time(0);
326 }
327 }
328
329 // Sample the configured direction-specific QMap once for this transmission.
330 QMapInstance mapInstance;
331 if (qmap) {
332 QMapContext ctx;
333 ctx.elapsedTime = dly;
334 mapInstance = qmap->Sample(mapRv_, ctx);
335 }
336
337 ns3::Simulator::Schedule(
338 dly, [dst, q, mapInstance]() mutable { dst->ReceiveFromChannel(q, mapInstance); });
339
340 return true;
341}
342
343
344
345std::size_t QChannel::GetNDevices() const {
346 std::size_t n = 0;
347 if (aDev_) {
348 ++n;
349 }
350 if (bDev_) {
351 ++n;
352 }
353 return n;
354}
355
356
357
358ns3::Ptr<ns3::NetDevice> QChannel::GetDevice(std::size_t i) const {
359 if (i == 0) {
360 return ns3::Ptr<ns3::NetDevice>(aDev_);
361 }
362 if (i == 1) {
363 return ns3::Ptr<ns3::NetDevice>(bDev_);
364 }
365 return nullptr;
366}
367
368
369
370int64_t QChannel::AssignStreams(int64_t stream) {
371 int64_t used = 0;
372
373 if (jitterRv_) {
374 jitterRv_->SetStream(stream + used++);
375 }
376 if (mapRv_) {
377 mapRv_->SetStream(stream + used++);
378 }
379
380 return used;
381}
382
383
384
385} // namespace q2ns
Duplex quantum channel with configurable delay, jitter, and transit maps.
ns3::Ptr< ns3::NetDevice > GetDevice(std::size_t i) const override
Return an attached device by endpoint index.
void SetJitter(ns3::Time j)
Set the same jitter in both directions.
void SetJitterBA(ns3::Time j)
Set the one-way jitter from B to A.
std::size_t GetNDevices() const override
Return the number of attached devices.
ns3::Time delayAB_
Propagation delay from A to B.
ns3::Time delayBA_
Propagation delay from B to A.
ns3::Ptr< QMap > GetQMap() const
Get the A->B transit QMap.
bool SendFrom(QNetDevice *src, std::shared_ptr< Qubit > q)
Send a qubit from one endpoint to the opposite endpoint.
void SetDelay(ns3::Time d)
Set the same propagation delay in both directions.
void SetQMap(ns3::Ptr< QMap > m)
Set the same transit QMap in both directions.
ns3::Ptr< ns3::UniformRandomVariable > jitterRv_
Jitter sampler.
void SetDelayAB(ns3::Time d)
Set the one-way delay from A to B.
void SetJitterAB(ns3::Time j)
Set the one-way jitter from A to B.
ns3::Ptr< QMap > qmapAB_
Transit QMap from A to B. Null means identity.
ns3::Ptr< QNetDevice > aDev_
Device attached to endpoint A.
ns3::Ptr< QNetDevice > bDev_
Device attached to endpoint B.
void SetQMapAB(ns3::Ptr< QMap > m)
Set the one-way transit QMap from A to B.
int64_t AssignStreams(int64_t stream)
Assign RNG streams used by this channel.
void SetQMapBA(ns3::Ptr< QMap > m)
Set the one-way transit QMap from B to A.
ns3::Time GetJitterAB() const
Get the one-way jitter from A to B.
void SetDelayBA(ns3::Time d)
Set the one-way delay from B to A.
ns3::Ptr< QMap > GetQMapBA() const
Get the one-way transit QMap from B to A.
static ns3::TypeId GetTypeId(void)
Get the ns-3 TypeId.
ns3::Time GetDelay() const
Get the A->B propagation delay.
void Connect(ns3::Ptr< QNetDevice > a, ns3::Ptr< QNetDevice > b)
Connect two QNetDevice endpoints to this channel.
ns3::Time GetDelayAB() const
Get the one-way delay from A to B.
ns3::Time jitterBA_
Jitter magnitude from B to A.
ns3::Ptr< QMap > qmapBA_
Transit QMap from B to A. Null means identity.
ns3::Time GetJitter() const
Get the A->B jitter.
ns3::Time jitterAB_
Jitter magnitude from A to B.
ns3::Time GetJitterBA() const
Get the one-way jitter from B to A.
ns3::Time GetDelayBA() const
Get the one-way delay from B to A.
ns3::Ptr< ns3::UniformRandomVariable > mapRv_
QMap sampler.
QChannel()
Default constructor.
ns3::Ptr< QMap > GetQMapAB() const
Get the one-way transit QMap from A to B.
Minimal quantum net device that bridges a QChannel and a QNetworker.
std::function< void(QNode &, std::shared_ptr< Qubit > &)> QMapInstance
Per-transmission quantum map callable applied to a received qubit.
Definition q2ns-types.h:71
@ Lost
Qubit was lost and is no longer accessible.
Optional per-sample context passed to QMaps.
Definition q2ns-qmap.h:41
ns3::Time elapsedTime
Elapsed time that this map is applied over.
Definition q2ns-qmap.h:42