181 TypeId |
81 TypeId |
182 WifiPhy::GetTypeId (void) |
82 WifiPhy::GetTypeId (void) |
183 { |
83 { |
184 static TypeId tid = TypeId ("ns3::WifiPhy") |
84 static TypeId tid = TypeId ("ns3::WifiPhy") |
185 .SetParent<Object> () |
85 .SetParent<Object> () |
186 .AddConstructor<WifiPhy> () |
|
187 .AddAttribute ("EnergyDetectionThreshold", |
|
188 "The energy of a received signal should be higher than " |
|
189 "this threshold (dbm) to allow the PHY layer to detect the signal.", |
|
190 DoubleValue (-140.0), |
|
191 MakeDoubleAccessor (&WifiPhy::SetEdThreshold, |
|
192 &WifiPhy::GetEdThreshold), |
|
193 MakeDoubleChecker<double> ()) |
|
194 .AddAttribute ("TxGain", |
|
195 "Transmission gain (dB).", |
|
196 DoubleValue (1.0), |
|
197 MakeDoubleAccessor (&WifiPhy::SetTxGain, |
|
198 &WifiPhy::GetTxGain), |
|
199 MakeDoubleChecker<double> ()) |
|
200 .AddAttribute ("RxGain", |
|
201 "Reception gain (dB).", |
|
202 DoubleValue (1.0), |
|
203 MakeDoubleAccessor (&WifiPhy::SetRxGain, |
|
204 &WifiPhy::GetRxGain), |
|
205 MakeDoubleChecker<double> ()) |
|
206 .AddAttribute ("TxPowerLevels", |
|
207 "Number of transmission power levels available between " |
|
208 "TxPowerBase and TxPowerEnd included.", |
|
209 UintegerValue (1), |
|
210 MakeUintegerAccessor (&WifiPhy::m_nTxPower), |
|
211 MakeUintegerChecker<uint32_t> ()) |
|
212 .AddAttribute ("TxPowerEnd", |
|
213 "Maximum available transmission level (dbm).", |
|
214 DoubleValue (16.0206), |
|
215 MakeDoubleAccessor (&WifiPhy::SetTxPowerEnd, |
|
216 &WifiPhy::GetTxPowerEnd), |
|
217 MakeDoubleChecker<double> ()) |
|
218 .AddAttribute ("TxPowerStart", |
|
219 "Minimum available transmission level (dbm).", |
|
220 DoubleValue (16.0206), |
|
221 MakeDoubleAccessor (&WifiPhy::SetTxPowerStart, |
|
222 &WifiPhy::GetTxPowerStart), |
|
223 MakeDoubleChecker<double> ()) |
|
224 .AddAttribute ("RxNoise", |
|
225 "Ratio of energy lost by receiver (dB).", |
|
226 DoubleValue (7), |
|
227 MakeDoubleAccessor (&WifiPhy::SetRxNoise, |
|
228 &WifiPhy::GetRxNoise), |
|
229 MakeDoubleChecker<double> ()) |
|
230 .AddAttribute ("Standard", "The standard chosen configures a set of transmission modes" |
|
231 " and some PHY-specific constants.", |
|
232 EnumValue (WIFI_PHY_STANDARD_80211a), |
|
233 MakeEnumAccessor (&WifiPhy::SetStandard), |
|
234 MakeEnumChecker (WIFI_PHY_STANDARD_80211a, "802.11a", |
|
235 WIFI_PHY_STANDARD_holland, "holland")) |
|
236 .AddTraceSource ("State", |
|
237 "The WifiPhy state", |
|
238 MakeTraceSourceAccessor (&WifiPhy::m_stateLogger)) |
|
239 .AddTraceSource ("RxOk", |
|
240 "A packet has been received successfully.", |
|
241 MakeTraceSourceAccessor (&WifiPhy::m_rxOkTrace)) |
|
242 .AddTraceSource ("RxError", |
|
243 "A packet has been received unsuccessfully.", |
|
244 MakeTraceSourceAccessor (&WifiPhy::m_rxErrorTrace)) |
|
245 .AddTraceSource ("Tx", "Packet transmission is starting.", |
|
246 MakeTraceSourceAccessor (&WifiPhy::m_txTrace)) |
|
247 ; |
86 ; |
248 return tid; |
87 return tid; |
249 } |
88 } |
250 |
89 |
251 WifiPhy::WifiPhy () |
90 WifiPhy::WifiPhy () |
252 : m_syncing (false), |
|
253 m_endTx (Seconds (0)), |
|
254 m_endSync (Seconds (0)), |
|
255 m_endCcaBusy (Seconds (0)), |
|
256 m_startTx (Seconds (0)), |
|
257 m_startSync (Seconds (0)), |
|
258 m_startCcaBusy (Seconds (0)), |
|
259 m_previousStateChangeTime (Seconds (0)), |
|
260 m_endSyncEvent (), |
|
261 m_random (0.0, 1.0) |
|
262 { |
91 { |
263 NS_LOG_FUNCTION (this); |
92 NS_LOG_FUNCTION (this); |
264 } |
93 } |
265 |
94 |
266 WifiPhy::~WifiPhy () |
95 WifiPhy::~WifiPhy () |
267 { |
96 { |
268 NS_LOG_FUNCTION (this); |
97 NS_LOG_FUNCTION (this); |
269 } |
98 } |
270 |
99 |
271 void |
|
272 WifiPhy::DoDispose (void) |
|
273 { |
|
274 NS_LOG_FUNCTION (this); |
|
275 m_channel = 0; |
|
276 m_events.clear (); |
|
277 m_modes.clear (); |
|
278 } |
|
279 |
|
280 void |
|
281 WifiPhy::SetStandard (enum WifiPhyStandard standard) |
|
282 { |
|
283 NS_LOG_FUNCTION (this << standard); |
|
284 m_standard = standard; |
|
285 switch (standard) { |
|
286 case WIFI_PHY_STANDARD_80211a: |
|
287 Configure80211a (); |
|
288 break; |
|
289 case WIFI_PHY_STANDARD_holland: |
|
290 ConfigureHolland (); |
|
291 break; |
|
292 default: |
|
293 NS_ASSERT (false); |
|
294 break; |
|
295 } |
|
296 } |
|
297 |
|
298 |
|
299 void |
|
300 WifiPhy::SetRxNoise (double db) |
|
301 { |
|
302 NS_LOG_FUNCTION (this << db); |
|
303 m_rxNoiseRatio = DbToRatio (db); |
|
304 } |
|
305 void |
|
306 WifiPhy::SetTxPowerStart (double start) |
|
307 { |
|
308 NS_LOG_FUNCTION (this << start); |
|
309 m_txPowerBaseDbm = start; |
|
310 } |
|
311 void |
|
312 WifiPhy::SetTxPowerEnd (double end) |
|
313 { |
|
314 NS_LOG_FUNCTION (this << end); |
|
315 m_txPowerEndDbm = end; |
|
316 } |
|
317 void |
|
318 WifiPhy::SetNTxPower (uint32_t n) |
|
319 { |
|
320 NS_LOG_FUNCTION (this << n); |
|
321 m_nTxPower = n; |
|
322 } |
|
323 void |
|
324 WifiPhy::SetTxGain (double gain) |
|
325 { |
|
326 NS_LOG_FUNCTION (this << gain); |
|
327 m_txGainDb = gain; |
|
328 } |
|
329 void |
|
330 WifiPhy::SetRxGain (double gain) |
|
331 { |
|
332 NS_LOG_FUNCTION (this << gain); |
|
333 m_rxGainDb = gain; |
|
334 } |
|
335 void |
|
336 WifiPhy::SetEdThreshold (double threshold) |
|
337 { |
|
338 NS_LOG_FUNCTION (this << threshold); |
|
339 m_edThresholdW = DbmToW (threshold); |
|
340 } |
|
341 double |
|
342 WifiPhy::GetRxNoise (void) const |
|
343 { |
|
344 return RatioToDb (m_rxNoiseRatio); |
|
345 } |
|
346 double |
|
347 WifiPhy::GetTxPowerStart (void) const |
|
348 { |
|
349 return m_txPowerBaseDbm; |
|
350 } |
|
351 double |
|
352 WifiPhy::GetTxPowerEnd (void) const |
|
353 { |
|
354 return m_txPowerEndDbm; |
|
355 } |
|
356 double |
|
357 WifiPhy::GetTxGain (void) const |
|
358 { |
|
359 return m_txGainDb; |
|
360 } |
|
361 double |
|
362 WifiPhy::GetRxGain (void) const |
|
363 { |
|
364 return m_rxGainDb; |
|
365 } |
|
366 |
|
367 double |
|
368 WifiPhy::GetEdThreshold (void) const |
|
369 { |
|
370 return WToDbm (m_edThresholdW); |
|
371 } |
|
372 |
|
373 Ptr<WifiChannel> |
|
374 WifiPhy::GetChannel (void) const |
|
375 { |
|
376 return m_channel; |
|
377 } |
|
378 |
|
379 void |
|
380 WifiPhy::SetChannel (Ptr<WifiChannel> channel) |
|
381 { |
|
382 m_channel = channel; |
|
383 } |
|
384 |
|
385 void |
|
386 WifiPhy::SetReceiveOkCallback (SyncOkCallback callback) |
|
387 { |
|
388 m_syncOkCallback = callback; |
|
389 } |
|
390 void |
|
391 WifiPhy::SetReceiveErrorCallback (SyncErrorCallback callback) |
|
392 { |
|
393 m_syncErrorCallback = callback; |
|
394 } |
|
395 void |
|
396 WifiPhy::StartReceivePacket (Ptr<Packet> packet, |
|
397 double rxPowerDbm, |
|
398 WifiMode txMode, |
|
399 enum WifiPreamble preamble) |
|
400 { |
|
401 NS_LOG_FUNCTION (this << packet << rxPowerDbm << txMode << preamble); |
|
402 rxPowerDbm += m_rxGainDb; |
|
403 double rxPowerW = DbmToW (rxPowerDbm); |
|
404 Time rxDuration = CalculateTxDuration (packet->GetSize (), txMode, preamble); |
|
405 Time endRx = Simulator::Now () + rxDuration; |
|
406 |
|
407 Ptr<RxEvent> event = Create<RxEvent> (packet->GetSize (), |
|
408 txMode, |
|
409 preamble, |
|
410 rxDuration, |
|
411 rxPowerW); |
|
412 AppendEvent (event); |
|
413 |
|
414 switch (GetState ()) { |
|
415 case WifiPhy::SYNC: |
|
416 NS_LOG_DEBUG ("drop packet because already in Sync (power="<< |
|
417 rxPowerW<<"W)"); |
|
418 if (endRx > m_endSync) |
|
419 { |
|
420 goto maybeCcaBusy; |
|
421 } |
|
422 break; |
|
423 case WifiPhy::TX: |
|
424 NS_LOG_DEBUG ("drop packet because already in Tx (power="<< |
|
425 rxPowerW<<"W)"); |
|
426 if (endRx > m_endTx) |
|
427 { |
|
428 goto maybeCcaBusy; |
|
429 } |
|
430 break; |
|
431 case WifiPhy::CCA_BUSY: |
|
432 case WifiPhy::IDLE: |
|
433 if (rxPowerW > m_edThresholdW) |
|
434 { |
|
435 NS_LOG_DEBUG ("sync (power="<<rxPowerW<<"W)"); |
|
436 // sync to signal |
|
437 NotifySyncStart (rxDuration); |
|
438 SwitchToSync (rxDuration); |
|
439 NS_ASSERT (m_endSyncEvent.IsExpired ()); |
|
440 m_endSyncEvent = Simulator::Schedule (rxDuration, &WifiPhy::EndSync, this, |
|
441 packet, |
|
442 event); |
|
443 } |
|
444 else |
|
445 { |
|
446 NS_LOG_DEBUG ("drop packet because signal power too Small ("<< |
|
447 rxPowerW<<"<"<<m_edThresholdW<<")"); |
|
448 goto maybeCcaBusy; |
|
449 } |
|
450 break; |
|
451 } |
|
452 |
|
453 return; |
|
454 |
|
455 maybeCcaBusy: |
|
456 |
|
457 if (rxPowerW > m_edThresholdW) |
|
458 { |
|
459 SwitchMaybeToCcaBusy (rxDuration); |
|
460 NotifyCcaBusyStart (rxDuration); |
|
461 } |
|
462 else |
|
463 { |
|
464 double threshold = m_edThresholdW - rxPowerW; |
|
465 NiChanges ni; |
|
466 CalculateNoiseInterferenceW (event, &ni); |
|
467 double noiseInterferenceW = 0.0; |
|
468 Time end = Simulator::Now (); |
|
469 for (NiChanges::const_iterator i = ni.begin (); i != ni.end (); i++) |
|
470 { |
|
471 noiseInterferenceW += i->GetDelta (); |
|
472 if (noiseInterferenceW < threshold) |
|
473 { |
|
474 break; |
|
475 } |
|
476 end = i->GetTime (); |
|
477 } |
|
478 if (end > Simulator::Now ()) |
|
479 { |
|
480 Time delta = end - Simulator::Now (); |
|
481 SwitchMaybeToCcaBusy (delta); |
|
482 NotifyCcaBusyStart (delta); |
|
483 } |
|
484 } |
|
485 |
|
486 } |
|
487 void |
|
488 WifiPhy::SendPacket (Ptr<const Packet> packet, WifiMode txMode, WifiPreamble preamble, uint8_t txPower) |
|
489 { |
|
490 NS_LOG_FUNCTION (this << packet << txMode << preamble << (uint32_t)txPower); |
|
491 /* Transmission can happen if: |
|
492 * - we are syncing on a packet. It is the responsability of the |
|
493 * MAC layer to avoid doing this but the PHY does nothing to |
|
494 * prevent it. |
|
495 * - we are idle |
|
496 */ |
|
497 NS_ASSERT (!IsStateTx ()); |
|
498 |
|
499 m_txTrace (packet, txMode, preamble, txPower); |
|
500 Time txDuration = CalculateTxDuration (packet->GetSize (), txMode, preamble); |
|
501 NotifyTxStart (txDuration); |
|
502 SwitchToTx (txDuration); |
|
503 m_channel->Send (this, packet, GetPowerDbm (txPower) + m_txGainDb, txMode, preamble); |
|
504 } |
|
505 |
|
506 uint32_t |
|
507 WifiPhy::GetNModes (void) const |
|
508 { |
|
509 return m_modes.size (); |
|
510 } |
|
511 WifiMode |
|
512 WifiPhy::GetMode (uint32_t mode) const |
|
513 { |
|
514 return m_modes[mode]; |
|
515 } |
|
516 uint32_t |
|
517 WifiPhy::GetNTxPower (void) const |
|
518 { |
|
519 return m_nTxPower; |
|
520 } |
|
521 |
|
522 double |
|
523 WifiPhy::CalculateSnr (WifiMode txMode, double ber) const |
|
524 { |
|
525 double low, high, precision; |
|
526 low = 1e-25; |
|
527 high = 1e25; |
|
528 precision = 1e-12; |
|
529 while (high - low > precision) |
|
530 { |
|
531 NS_ASSERT (high >= low); |
|
532 double middle = low + (high - low) / 2; |
|
533 if ((1 - GetChunkSuccessRate (txMode, middle, 1)) > ber) |
|
534 { |
|
535 low = middle; |
|
536 } |
|
537 else |
|
538 { |
|
539 high = middle; |
|
540 } |
|
541 } |
|
542 return low; |
|
543 } |
|
544 |
|
545 void |
|
546 WifiPhy::Configure80211aParameters (void) |
|
547 { |
|
548 NS_LOG_FUNCTION (this); |
|
549 m_plcpLongPreambleDelayUs = 16; |
|
550 m_plcpShortPreambleDelayUs = 16; |
|
551 m_longPlcpHeaderMode = g_6mba; |
|
552 m_shortPlcpHeaderMode = g_6mba; |
|
553 m_plcpHeaderLength = 4 + 1 + 12 + 1 + 6; |
|
554 /* 4095 bytes at a 6Mb/s rate with a 1/2 coding rate. */ |
|
555 m_maxPacketDuration = CalculateTxDuration (4095, g_6mba, WIFI_PREAMBLE_LONG); |
|
556 } |
|
557 |
|
558 void |
|
559 WifiPhy::PrintModes (void) const |
|
560 { |
|
561 #if 0 |
|
562 for (double db = -10; db < 30; db+= 0.5) { |
|
563 double snr = DbToRatio (db); |
|
564 std::cout <<snr<<" "; |
|
565 for (uint8_t i = 0; i < GetNModes (); i++) { |
|
566 WifiMode mode = GetMode (i); |
|
567 double ber = 1-GetChunkSuccessRate (mode,snr, 2000*8); |
|
568 std::cout <<ber<< " "; |
|
569 } |
|
570 std::cout << std::endl; |
|
571 } |
|
572 #endif |
|
573 } |
|
574 |
|
575 void |
|
576 WifiPhy::Configure80211a (void) |
|
577 { |
|
578 NS_LOG_FUNCTION (this); |
|
579 Configure80211aParameters (); |
|
580 m_modes.push_back (g_6mba); |
|
581 m_modes.push_back (g_9mba); |
|
582 m_modes.push_back (g_12mba); |
|
583 m_modes.push_back (g_18mba); |
|
584 m_modes.push_back (g_24mba); |
|
585 m_modes.push_back (g_36mba); |
|
586 m_modes.push_back (g_48mba); |
|
587 m_modes.push_back (g_54mba); |
|
588 |
|
589 PrintModes (); |
|
590 } |
|
591 |
|
592 void |
|
593 WifiPhy::ConfigureHolland (void) |
|
594 { |
|
595 NS_LOG_FUNCTION (this); |
|
596 Configure80211aParameters (); |
|
597 m_modes.push_back (g_6mba); |
|
598 m_modes.push_back (g_12mba); |
|
599 m_modes.push_back (g_18mba); |
|
600 m_modes.push_back (g_36mba); |
|
601 m_modes.push_back (g_54mba); |
|
602 |
|
603 PrintModes (); |
|
604 } |
|
605 |
|
606 void |
|
607 WifiPhy::RegisterListener (WifiPhyListener *listener) |
|
608 { |
|
609 m_listeners.push_back (listener); |
|
610 } |
|
611 |
|
612 bool |
|
613 WifiPhy::IsStateCcaBusy (void) |
|
614 { |
|
615 return GetState () == CCA_BUSY; |
|
616 } |
|
617 |
|
618 bool |
|
619 WifiPhy::IsStateIdle (void) |
|
620 { |
|
621 return (GetState () == IDLE)?true:false; |
|
622 } |
|
623 bool |
|
624 WifiPhy::IsStateBusy (void) |
|
625 { |
|
626 return (GetState () != IDLE)?true:false; |
|
627 } |
|
628 bool |
|
629 WifiPhy::IsStateSync (void) |
|
630 { |
|
631 return (GetState () == SYNC)?true:false; |
|
632 } |
|
633 bool |
|
634 WifiPhy::IsStateTx (void) |
|
635 { |
|
636 return (GetState () == TX)?true:false; |
|
637 } |
|
638 |
|
639 Time |
|
640 WifiPhy::GetStateDuration (void) |
|
641 { |
|
642 return Simulator::Now () - m_previousStateChangeTime; |
|
643 } |
|
644 Time |
|
645 WifiPhy::GetDelayUntilIdle (void) |
|
646 { |
|
647 Time retval; |
|
648 |
|
649 switch (GetState ()) { |
|
650 case SYNC: |
|
651 retval = m_endSync - Simulator::Now (); |
|
652 break; |
|
653 case TX: |
|
654 retval = m_endTx - Simulator::Now (); |
|
655 break; |
|
656 case CCA_BUSY: |
|
657 retval = m_endCcaBusy - Simulator::Now (); |
|
658 break; |
|
659 case IDLE: |
|
660 retval = Seconds (0); |
|
661 break; |
|
662 default: |
|
663 NS_ASSERT (false); |
|
664 // NOTREACHED |
|
665 retval = Seconds (0); |
|
666 break; |
|
667 } |
|
668 retval = Max (retval, Seconds (0)); |
|
669 return retval; |
|
670 } |
|
671 |
|
672 Time |
|
673 WifiPhy::GetLastRxStartTime (void) const |
|
674 { |
|
675 return m_startSync; |
|
676 } |
|
677 |
|
678 |
|
679 Time |
|
680 WifiPhy::CalculateTxDuration (uint32_t size, WifiMode payloadMode, WifiPreamble preamble) const |
|
681 { |
|
682 uint64_t delay = 0; |
|
683 switch (m_standard) { |
|
684 case WIFI_PHY_STANDARD_80211a: |
|
685 case WIFI_PHY_STANDARD_holland: { |
|
686 delay += m_plcpLongPreambleDelayUs; |
|
687 // symbol duration is 4us |
|
688 delay += 4; |
|
689 delay += lrint (ceil ((size * 8.0 + 16.0 + 6.0) / payloadMode.GetDataRate () / 4e-6) * 4); |
|
690 } break; |
|
691 default: |
|
692 // quiet compiler. |
|
693 NS_ASSERT (false); |
|
694 break; |
|
695 } |
|
696 return MicroSeconds (delay); |
|
697 } |
|
698 |
|
699 char const * |
|
700 WifiPhy::StateToString (enum State state) |
|
701 { |
|
702 switch (state) { |
|
703 case TX: |
|
704 return "TX"; |
|
705 break; |
|
706 case CCA_BUSY: |
|
707 return "CCA_BUSY"; |
|
708 break; |
|
709 case IDLE: |
|
710 return "IDLE"; |
|
711 break; |
|
712 case SYNC: |
|
713 return "SYNC"; |
|
714 break; |
|
715 default: |
|
716 NS_ASSERT (false); |
|
717 // quiet compiler |
|
718 return "INVALID"; |
|
719 break; |
|
720 } |
|
721 } |
|
722 enum WifiPhy::State |
|
723 WifiPhy::GetState (void) |
|
724 { |
|
725 if (m_endTx > Simulator::Now ()) |
|
726 { |
|
727 return WifiPhy::TX; |
|
728 } |
|
729 else if (m_syncing) |
|
730 { |
|
731 return WifiPhy::SYNC; |
|
732 } |
|
733 else if (m_endCcaBusy > Simulator::Now ()) |
|
734 { |
|
735 return WifiPhy::CCA_BUSY; |
|
736 } |
|
737 else |
|
738 { |
|
739 return WifiPhy::IDLE; |
|
740 } |
|
741 } |
|
742 |
|
743 double |
|
744 WifiPhy::DbToRatio (double dB) const |
|
745 { |
|
746 double ratio = pow(10.0,dB/10.0); |
|
747 return ratio; |
|
748 } |
|
749 |
|
750 double |
|
751 WifiPhy::DbmToW (double dBm) const |
|
752 { |
|
753 double mW = pow(10.0,dBm/10.0); |
|
754 return mW / 1000.0; |
|
755 } |
|
756 |
|
757 double |
|
758 WifiPhy::WToDbm (double w) const |
|
759 { |
|
760 return 10.0 * log10(w * 1000.0); |
|
761 } |
|
762 |
|
763 double |
|
764 WifiPhy::RatioToDb (double ratio) const |
|
765 { |
|
766 return 10.0 * log10(ratio); |
|
767 } |
|
768 |
|
769 double |
|
770 WifiPhy::GetEdThresholdW (void) const |
|
771 { |
|
772 return m_edThresholdW; |
|
773 } |
|
774 |
|
775 Time |
|
776 WifiPhy::GetMaxPacketDuration (void) const |
|
777 { |
|
778 return m_maxPacketDuration; |
|
779 } |
|
780 |
|
781 double |
|
782 WifiPhy::GetPowerDbm (uint8_t power) const |
|
783 { |
|
784 NS_ASSERT (m_txPowerBaseDbm <= m_txPowerEndDbm); |
|
785 NS_ASSERT (m_nTxPower > 0); |
|
786 double dbm = m_txPowerBaseDbm + power * (m_txPowerEndDbm - m_txPowerBaseDbm) / m_nTxPower; |
|
787 return dbm; |
|
788 } |
|
789 |
|
790 void |
|
791 WifiPhy::NotifyTxStart (Time duration) |
|
792 { |
|
793 for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) { |
|
794 (*i)->NotifyTxStart (duration); |
|
795 } |
|
796 } |
|
797 void |
|
798 WifiPhy::NotifySyncStart (Time duration) |
|
799 { |
|
800 for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) { |
|
801 (*i)->NotifyRxStart (duration); |
|
802 } |
|
803 } |
|
804 void |
|
805 WifiPhy::NotifySyncEndOk (void) |
|
806 { |
|
807 for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) { |
|
808 (*i)->NotifyRxEndOk (); |
|
809 } |
|
810 } |
|
811 void |
|
812 WifiPhy::NotifySyncEndError (void) |
|
813 { |
|
814 for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) { |
|
815 (*i)->NotifyRxEndError (); |
|
816 } |
|
817 } |
|
818 void |
|
819 WifiPhy::NotifyCcaBusyStart (Time duration) |
|
820 { |
|
821 for (Listeners::const_iterator i = m_listeners.begin (); i != m_listeners.end (); i++) { |
|
822 (*i)->NotifyCcaBusyStart (duration); |
|
823 } |
|
824 } |
|
825 |
|
826 void |
|
827 WifiPhy::LogPreviousIdleAndCcaBusyStates (void) |
|
828 { |
|
829 Time now = Simulator::Now (); |
|
830 Time idleStart = Max (m_endCcaBusy, m_endSync); |
|
831 idleStart = Max (idleStart, m_endTx); |
|
832 NS_ASSERT (idleStart <= now); |
|
833 if (m_endCcaBusy > m_endSync && |
|
834 m_endCcaBusy > m_endTx) { |
|
835 Time ccaBusyStart = Max (m_endTx, m_endSync); |
|
836 ccaBusyStart = Max (ccaBusyStart, m_startCcaBusy); |
|
837 m_stateLogger (ccaBusyStart, idleStart - ccaBusyStart, WifiPhy::CCA_BUSY); |
|
838 } |
|
839 m_stateLogger (idleStart, now - idleStart, WifiPhy::IDLE); |
|
840 } |
|
841 |
|
842 void |
|
843 WifiPhy::SwitchToTx (Time txDuration) |
|
844 { |
|
845 Time now = Simulator::Now (); |
|
846 switch (GetState ()) { |
|
847 case WifiPhy::SYNC: |
|
848 /* The packet which is being received as well |
|
849 * as its endSync event are cancelled by the caller. |
|
850 */ |
|
851 m_syncing = false; |
|
852 m_stateLogger (m_startSync, now - m_startSync, WifiPhy::SYNC); |
|
853 m_endSyncEvent.Cancel (); |
|
854 m_endSync = now; |
|
855 break; |
|
856 case WifiPhy::CCA_BUSY: { |
|
857 Time ccaStart = Max (m_endSync, m_endTx); |
|
858 ccaStart = Max (ccaStart, m_startCcaBusy); |
|
859 m_stateLogger (ccaStart, now - ccaStart, WifiPhy::CCA_BUSY); |
|
860 } break; |
|
861 case WifiPhy::IDLE: |
|
862 LogPreviousIdleAndCcaBusyStates (); |
|
863 break; |
|
864 default: |
|
865 NS_ASSERT (false); |
|
866 break; |
|
867 } |
|
868 m_stateLogger (now, txDuration, WifiPhy::TX); |
|
869 m_previousStateChangeTime = now; |
|
870 m_endTx = now + txDuration; |
|
871 m_startTx = now; |
|
872 } |
|
873 void |
|
874 WifiPhy::SwitchToSync (Time rxDuration) |
|
875 { |
|
876 NS_ASSERT (IsStateIdle () || IsStateCcaBusy ()); |
|
877 NS_ASSERT (!m_syncing); |
|
878 Time now = Simulator::Now (); |
|
879 switch (GetState ()) { |
|
880 case WifiPhy::IDLE: |
|
881 LogPreviousIdleAndCcaBusyStates (); |
|
882 break; |
|
883 case WifiPhy::CCA_BUSY: { |
|
884 Time ccaStart = Max (m_endSync, m_endTx); |
|
885 ccaStart = Max (ccaStart, m_startCcaBusy); |
|
886 m_stateLogger (ccaStart, now - ccaStart, WifiPhy::CCA_BUSY); |
|
887 } break; |
|
888 case WifiPhy::SYNC: |
|
889 case WifiPhy::TX: |
|
890 NS_ASSERT (false); |
|
891 break; |
|
892 } |
|
893 m_previousStateChangeTime = now; |
|
894 m_syncing = true; |
|
895 m_startSync = now; |
|
896 m_endSync = now + rxDuration; |
|
897 NS_ASSERT (IsStateSync ()); |
|
898 } |
|
899 void |
|
900 WifiPhy::SwitchFromSync (void) |
|
901 { |
|
902 NS_ASSERT (IsStateSync ()); |
|
903 NS_ASSERT (m_syncing); |
|
904 |
|
905 Time now = Simulator::Now (); |
|
906 m_stateLogger (m_startSync, now - m_startSync, WifiPhy::SYNC); |
|
907 m_previousStateChangeTime = now; |
|
908 m_syncing = false; |
|
909 |
|
910 NS_ASSERT (IsStateIdle () || IsStateCcaBusy ()); |
|
911 } |
|
912 void |
|
913 WifiPhy::SwitchMaybeToCcaBusy (Time duration) |
|
914 { |
|
915 Time now = Simulator::Now (); |
|
916 switch (GetState ()) { |
|
917 case WifiPhy::IDLE: |
|
918 LogPreviousIdleAndCcaBusyStates (); |
|
919 break; |
|
920 case WifiPhy::CCA_BUSY: |
|
921 break; |
|
922 case WifiPhy::SYNC: |
|
923 break; |
|
924 case WifiPhy::TX: |
|
925 break; |
|
926 } |
|
927 m_startCcaBusy = now; |
|
928 m_endCcaBusy = Max (m_endCcaBusy, now + duration); |
|
929 } |
|
930 |
|
931 void |
|
932 WifiPhy::AppendEvent (Ptr<RxEvent> event) |
|
933 { |
|
934 /* attempt to remove the events which are |
|
935 * not useful anymore. |
|
936 * i.e.: all events which end _before_ |
|
937 * now - m_maxPacketDuration |
|
938 */ |
|
939 |
|
940 if (Simulator::Now () > GetMaxPacketDuration ()) |
|
941 { |
|
942 Time end = Simulator::Now () - GetMaxPacketDuration (); |
|
943 Events::iterator i = m_events.begin (); |
|
944 while (i != m_events.end () && |
|
945 (*i)->GetEndTime () <= end) |
|
946 { |
|
947 i++; |
|
948 } |
|
949 m_events.erase (m_events.begin (), i); |
|
950 } |
|
951 m_events.push_back (event); |
|
952 } |
|
953 |
|
954 |
|
955 |
|
956 /** |
|
957 * Stuff specific to the BER model here. |
|
958 */ |
|
959 double |
|
960 WifiPhy::Log2 (double val) const |
|
961 { |
|
962 return log(val) / log(2.0); |
|
963 } |
|
964 double |
|
965 WifiPhy::GetBpskBer (double snr, uint32_t signalSpread, uint32_t phyRate) const |
|
966 { |
|
967 double EbNo = snr * signalSpread / phyRate; |
|
968 double z = sqrt(EbNo); |
|
969 double ber = 0.5 * erfc(z); |
|
970 NS_LOG_INFO ("bpsk snr="<<snr<<" ber="<<ber); |
|
971 return ber; |
|
972 } |
|
973 double |
|
974 WifiPhy::GetQamBer (double snr, unsigned int m, uint32_t signalSpread, uint32_t phyRate) const |
|
975 { |
|
976 double EbNo = snr * signalSpread / phyRate; |
|
977 double z = sqrt ((1.5 * Log2 (m) * EbNo) / (m - 1.0)); |
|
978 double z1 = ((1.0 - 1.0 / sqrt (m)) * erfc (z)) ; |
|
979 double z2 = 1 - pow ((1-z1), 2.0); |
|
980 double ber = z2 / Log2 (m); |
|
981 NS_LOG_INFO ("Qam m="<<m<<" rate=" << phyRate << " snr="<<snr<<" ber="<<ber); |
|
982 return ber; |
|
983 } |
|
984 uint32_t |
|
985 WifiPhy::Factorial (uint32_t k) const |
|
986 { |
|
987 uint32_t fact = 1; |
|
988 while (k > 0) |
|
989 { |
|
990 fact *= k; |
|
991 k--; |
|
992 } |
|
993 return fact; |
|
994 } |
|
995 double |
|
996 WifiPhy::Binomial (uint32_t k, double p, uint32_t n) const |
|
997 { |
|
998 double retval = Factorial (n) / (Factorial (k) * Factorial (n-k)) * pow (p, k) * pow (1-p, n-k); |
|
999 return retval; |
|
1000 } |
|
1001 double |
|
1002 WifiPhy::CalculatePdOdd (double ber, unsigned int d) const |
|
1003 { |
|
1004 NS_ASSERT ((d % 2) == 1); |
|
1005 unsigned int dstart = (d + 1) / 2; |
|
1006 unsigned int dend = d; |
|
1007 double pd = 0; |
|
1008 |
|
1009 for (unsigned int i = dstart; i < dend; i++) |
|
1010 { |
|
1011 pd += Binomial (i, ber, d); |
|
1012 } |
|
1013 return pd; |
|
1014 } |
|
1015 double |
|
1016 WifiPhy::CalculatePdEven (double ber, unsigned int d) const |
|
1017 { |
|
1018 NS_ASSERT ((d % 2) == 0); |
|
1019 unsigned int dstart = d / 2 + 1; |
|
1020 unsigned int dend = d; |
|
1021 double pd = 0; |
|
1022 |
|
1023 for (unsigned int i = dstart; i < dend; i++) |
|
1024 { |
|
1025 pd += Binomial (i, ber, d); |
|
1026 } |
|
1027 pd += 0.5 * Binomial (d / 2, ber, d); |
|
1028 |
|
1029 return pd; |
|
1030 } |
|
1031 |
|
1032 double |
|
1033 WifiPhy::CalculatePd (double ber, unsigned int d) const |
|
1034 { |
|
1035 double pd; |
|
1036 if ((d % 2) == 0) |
|
1037 { |
|
1038 pd = CalculatePdEven (ber, d); |
|
1039 } |
|
1040 else |
|
1041 { |
|
1042 pd = CalculatePdOdd (ber, d); |
|
1043 } |
|
1044 return pd; |
|
1045 } |
|
1046 |
|
1047 double |
|
1048 WifiPhy::GetFecBpskBer (double snr, double nbits, |
|
1049 uint32_t signalSpread, uint32_t phyRate, |
|
1050 uint32_t dFree, uint32_t adFree) const |
|
1051 { |
|
1052 double ber = GetBpskBer (snr, signalSpread, phyRate); |
|
1053 if (ber == 0.0) |
|
1054 { |
|
1055 return 1.0; |
|
1056 } |
|
1057 double pd = CalculatePd (ber, dFree); |
|
1058 double pmu = adFree * pd; |
|
1059 pmu = std::min (pmu, 1.0); |
|
1060 double pms = pow (1 - pmu, nbits); |
|
1061 return pms; |
|
1062 } |
|
1063 |
|
1064 double |
|
1065 WifiPhy::GetFecQamBer (double snr, uint32_t nbits, |
|
1066 uint32_t signalSpread, |
|
1067 uint32_t phyRate, |
|
1068 uint32_t m, uint32_t dFree, |
|
1069 uint32_t adFree, uint32_t adFreePlusOne) const |
|
1070 { |
|
1071 double ber = GetQamBer (snr, m, signalSpread, phyRate); |
|
1072 if (ber == 0.0) |
|
1073 { |
|
1074 return 1.0; |
|
1075 } |
|
1076 /* first term */ |
|
1077 double pd = CalculatePd (ber, dFree); |
|
1078 double pmu = adFree * pd; |
|
1079 /* second term */ |
|
1080 pd = CalculatePd (ber, dFree + 1); |
|
1081 pmu += adFreePlusOne * pd; |
|
1082 pmu = std::min (pmu, 1.0); |
|
1083 double pms = pow (1 - pmu, nbits); |
|
1084 return pms; |
|
1085 } |
|
1086 |
|
1087 double |
|
1088 WifiPhy::GetChunkSuccessRate (WifiMode mode, double snr, uint32_t nbits) const |
|
1089 { |
|
1090 if (mode.GetUid () == g_6mba.GetUid ()) |
|
1091 { |
|
1092 return GetFecBpskBer (snr, |
|
1093 nbits, |
|
1094 mode.GetBandwidth (), // signal spread |
|
1095 mode.GetPhyRate (), // phy rate |
|
1096 10, // dFree |
|
1097 11 // adFree |
|
1098 ); |
|
1099 } |
|
1100 else if (mode.GetUid () == g_9mba.GetUid ()) |
|
1101 { |
|
1102 return GetFecBpskBer (snr, |
|
1103 nbits, |
|
1104 mode.GetBandwidth (), // signal spread |
|
1105 mode.GetPhyRate (), // phy rate |
|
1106 5, // dFree |
|
1107 8 // adFree |
|
1108 ); |
|
1109 } |
|
1110 else if (mode.GetUid () == g_12mba.GetUid ()) |
|
1111 { |
|
1112 return GetFecQamBer (snr, |
|
1113 nbits, |
|
1114 mode.GetBandwidth (), // signal spread |
|
1115 mode.GetPhyRate (), // phy rate |
|
1116 4, // m |
|
1117 10, // dFree |
|
1118 11, // adFree |
|
1119 0 // adFreePlusOne |
|
1120 ); |
|
1121 } |
|
1122 else if (mode.GetUid () == g_18mba.GetUid ()) |
|
1123 { |
|
1124 return GetFecQamBer (snr, |
|
1125 nbits, |
|
1126 mode.GetBandwidth (), // signal spread |
|
1127 mode.GetPhyRate (), // phy rate |
|
1128 4, // m |
|
1129 5, // dFree |
|
1130 8, // adFree |
|
1131 31 // adFreePlusOne |
|
1132 ); |
|
1133 } |
|
1134 else if (mode.GetUid () == g_24mba.GetUid ()) |
|
1135 { |
|
1136 return GetFecQamBer (snr, |
|
1137 nbits, |
|
1138 mode.GetBandwidth (), // signal spread |
|
1139 mode.GetPhyRate (), // phy rate |
|
1140 16, // m |
|
1141 10, // dFree |
|
1142 11, // adFree |
|
1143 0 // adFreePlusOne |
|
1144 ); |
|
1145 } |
|
1146 else if (mode.GetUid () == g_36mba.GetUid ()) |
|
1147 { |
|
1148 return GetFecQamBer (snr, |
|
1149 nbits, |
|
1150 mode.GetBandwidth (), // signal spread |
|
1151 mode.GetPhyRate (), // phy rate |
|
1152 16, // m |
|
1153 5, // dFree |
|
1154 8, // adFree |
|
1155 31 // adFreePlusOne |
|
1156 ); |
|
1157 } |
|
1158 else if (mode.GetUid () == g_48mba.GetUid ()) |
|
1159 { |
|
1160 return GetFecQamBer (snr, |
|
1161 nbits, |
|
1162 mode.GetBandwidth (), // signal spread |
|
1163 mode.GetPhyRate (), // phy rate |
|
1164 64, // m |
|
1165 6, // dFree |
|
1166 1, // adFree |
|
1167 16 // adFreePlusOne |
|
1168 ); |
|
1169 } |
|
1170 else if (mode.GetUid () == g_54mba.GetUid ()) |
|
1171 { |
|
1172 return GetFecQamBer (snr, |
|
1173 nbits, |
|
1174 mode.GetBandwidth (), // signal spread |
|
1175 mode.GetPhyRate (), // phy rate |
|
1176 64, // m |
|
1177 5, // dFree |
|
1178 8, // adFree |
|
1179 31 // adFreePlusOne |
|
1180 ); |
|
1181 } |
|
1182 return 0; |
|
1183 } |
|
1184 |
|
1185 double |
|
1186 WifiPhy::CalculateSnr (double signal, double noiseInterference, WifiMode mode) const |
|
1187 { |
|
1188 // thermal noise at 290K in J/s = W |
|
1189 static const double BOLTZMANN = 1.3803e-23; |
|
1190 double Nt = BOLTZMANN * 290.0 * mode.GetBandwidth (); |
|
1191 // receiver noise Floor (W) |
|
1192 double noiseFloor = m_rxNoiseRatio * Nt; |
|
1193 double noise = noiseFloor + noiseInterference; |
|
1194 double snr = signal / noise; |
|
1195 return snr; |
|
1196 } |
|
1197 |
|
1198 double |
|
1199 WifiPhy::CalculateNoiseInterferenceW (Ptr<RxEvent> event, NiChanges *ni) const |
|
1200 { |
|
1201 Events::const_iterator i = m_events.begin (); |
|
1202 double noiseInterference = 0.0; |
|
1203 while (i != m_events.end ()) |
|
1204 { |
|
1205 if (event == (*i)) |
|
1206 { |
|
1207 i++; |
|
1208 continue; |
|
1209 } |
|
1210 if (event->Overlaps ((*i)->GetStartTime ())) |
|
1211 { |
|
1212 ni->push_back (NiChange ((*i)->GetStartTime (), (*i)->GetRxPowerW ())); |
|
1213 } |
|
1214 if (event->Overlaps ((*i)->GetEndTime ())) |
|
1215 { |
|
1216 ni->push_back (NiChange ((*i)->GetEndTime (), -(*i)->GetRxPowerW ())); |
|
1217 } |
|
1218 if ((*i)->Overlaps (event->GetStartTime ())) |
|
1219 { |
|
1220 noiseInterference += (*i)->GetRxPowerW (); |
|
1221 } |
|
1222 i++; |
|
1223 } |
|
1224 ni->push_back (NiChange (event->GetStartTime (), noiseInterference)); |
|
1225 ni->push_back (NiChange (event->GetEndTime (), 0)); |
|
1226 |
|
1227 /* quicksort vector of NI changes by time. */ |
|
1228 std::sort (ni->begin (), ni->end (), std::less<NiChange> ()); |
|
1229 |
|
1230 return noiseInterference; |
|
1231 } |
|
1232 |
|
1233 double |
|
1234 WifiPhy::CalculateChunkSuccessRate (double snir, Time duration, WifiMode mode) const |
|
1235 { |
|
1236 if (duration == NanoSeconds (0)) { |
|
1237 return 1.0; |
|
1238 } |
|
1239 uint32_t rate = mode.GetPhyRate (); |
|
1240 uint64_t nbits = (uint64_t)(rate * duration.GetSeconds ()); |
|
1241 double csr = GetChunkSuccessRate (mode, snir, (uint32_t)nbits); |
|
1242 return csr; |
|
1243 } |
|
1244 |
|
1245 double |
|
1246 WifiPhy::CalculatePer (Ptr<const RxEvent> event, NiChanges *ni) const |
|
1247 { |
|
1248 double psr = 1.0; /* Packet Success Rate */ |
|
1249 NiChanges::iterator j = ni->begin (); |
|
1250 Time previous = (*j).GetTime (); |
|
1251 uint64_t plcpPreambleDelayUs; |
|
1252 WifiMode payloadMode = event->GetPayloadMode (); |
|
1253 WifiMode headerMode; |
|
1254 switch (event->GetPreambleType ()) { |
|
1255 case WIFI_PREAMBLE_LONG: |
|
1256 plcpPreambleDelayUs = m_plcpLongPreambleDelayUs; |
|
1257 headerMode = m_longPlcpHeaderMode; |
|
1258 break; |
|
1259 case WIFI_PREAMBLE_SHORT: |
|
1260 plcpPreambleDelayUs = m_plcpShortPreambleDelayUs; |
|
1261 headerMode = m_shortPlcpHeaderMode; |
|
1262 break; |
|
1263 default: |
|
1264 NS_ASSERT (false); |
|
1265 // only to quiet compiler. Really stupid. |
|
1266 plcpPreambleDelayUs = 0; |
|
1267 headerMode = m_shortPlcpHeaderMode; |
|
1268 break; |
|
1269 } |
|
1270 Time plcpHeaderStart = (*j).GetTime () + MicroSeconds (plcpPreambleDelayUs); |
|
1271 Time plcpPayloadStart = plcpHeaderStart + |
|
1272 Seconds ((m_plcpHeaderLength + 0.0) / headerMode.GetDataRate ()); |
|
1273 double noiseInterferenceW = (*j).GetDelta (); |
|
1274 double powerW = event->GetRxPowerW (); |
|
1275 |
|
1276 j++; |
|
1277 while (ni->end () != j) |
|
1278 { |
|
1279 Time current = (*j).GetTime (); |
|
1280 NS_ASSERT (current >= previous); |
|
1281 |
|
1282 if (previous >= plcpPayloadStart) |
|
1283 { |
|
1284 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1285 noiseInterferenceW, |
|
1286 payloadMode), |
|
1287 current - previous, |
|
1288 payloadMode); |
|
1289 } |
|
1290 else if (previous >= plcpHeaderStart) |
|
1291 { |
|
1292 if (current >= plcpPayloadStart) |
|
1293 { |
|
1294 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1295 noiseInterferenceW, |
|
1296 headerMode), |
|
1297 plcpPayloadStart - previous, |
|
1298 headerMode); |
|
1299 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1300 noiseInterferenceW, |
|
1301 payloadMode), |
|
1302 current - plcpPayloadStart, |
|
1303 payloadMode); |
|
1304 } |
|
1305 else |
|
1306 { |
|
1307 NS_ASSERT (current >= plcpHeaderStart); |
|
1308 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1309 noiseInterferenceW, |
|
1310 headerMode), |
|
1311 current - previous, |
|
1312 headerMode); |
|
1313 } |
|
1314 } |
|
1315 else |
|
1316 { |
|
1317 if (current >= plcpPayloadStart) |
|
1318 { |
|
1319 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1320 noiseInterferenceW, |
|
1321 headerMode), |
|
1322 plcpPayloadStart - plcpHeaderStart, |
|
1323 headerMode); |
|
1324 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1325 noiseInterferenceW, |
|
1326 payloadMode), |
|
1327 current - plcpPayloadStart, |
|
1328 payloadMode); |
|
1329 } |
|
1330 else if (current >= plcpHeaderStart) |
|
1331 { |
|
1332 psr *= CalculateChunkSuccessRate (CalculateSnr (powerW, |
|
1333 noiseInterferenceW, |
|
1334 headerMode), |
|
1335 current - plcpHeaderStart, |
|
1336 headerMode); |
|
1337 } |
|
1338 } |
|
1339 |
|
1340 noiseInterferenceW += (*j).GetDelta (); |
|
1341 previous = (*j).GetTime (); |
|
1342 j++; |
|
1343 } |
|
1344 |
|
1345 double per = 1 - psr; |
|
1346 return per; |
|
1347 } |
|
1348 |
|
1349 |
|
1350 void |
|
1351 WifiPhy::EndSync (Ptr<Packet> packet, Ptr<RxEvent> event) |
|
1352 { |
|
1353 NS_LOG_FUNCTION (this << packet << event); |
|
1354 NS_ASSERT (IsStateSync ()); |
|
1355 NS_ASSERT (event->GetEndTime () == Simulator::Now ()); |
|
1356 |
|
1357 NiChanges ni; |
|
1358 double noiseInterferenceW = CalculateNoiseInterferenceW (event, &ni); |
|
1359 double snr = CalculateSnr (event->GetRxPowerW (), |
|
1360 noiseInterferenceW, |
|
1361 event->GetPayloadMode ()); |
|
1362 |
|
1363 /* calculate the SNIR at the start of the packet and accumulate |
|
1364 * all SNIR changes in the snir vector. |
|
1365 */ |
|
1366 double per = CalculatePer (event, &ni); |
|
1367 NS_LOG_DEBUG ("mode="<<(event->GetPayloadMode ().GetDataRate ())<< |
|
1368 ", ber="<<(1-GetChunkSuccessRate (event->GetPayloadMode (), snr, 1))<< |
|
1369 ", snr="<<snr<<", per="<<per<<", size="<<packet->GetSize ()); |
|
1370 |
|
1371 if (m_random.GetValue () > per) |
|
1372 { |
|
1373 NotifySyncEndOk (); |
|
1374 SwitchFromSync (); |
|
1375 m_rxOkTrace (packet, snr, event->GetPayloadMode (), event->GetPreambleType ()); |
|
1376 if (!m_syncOkCallback.IsNull ()) |
|
1377 { |
|
1378 m_syncOkCallback (packet, snr, event->GetPayloadMode (), event->GetPreambleType ()); |
|
1379 } |
|
1380 } |
|
1381 else |
|
1382 { |
|
1383 /* failure. */ |
|
1384 NotifySyncEndError (); |
|
1385 SwitchFromSync (); |
|
1386 m_rxErrorTrace (packet, snr); |
|
1387 if (!m_syncErrorCallback.IsNull ()) |
|
1388 { |
|
1389 m_syncErrorCallback (packet, snr); |
|
1390 } |
|
1391 } |
|
1392 } |
|
1393 |
|
1394 |
|
1395 |
|
1396 |
|
1397 } // namespace ns3 |
100 } // namespace ns3 |