Loading content...
We've examined NAV as the core timer for virtual carrier sensing. However, virtual sensing in IEEE 802.11 encompasses more than just NAV—it's a complete coordination system that includes interframe spacing (IFS), error recovery mechanisms (EIFS), and protocol-level integration that together enable reliable channel access in complex wireless environments.
This page synthesizes all virtual sensing components into a unified understanding, demonstrating how they work together to solve the hidden terminal problem while maintaining efficiency and fairness.
Virtual carrier sensing is the process of determining channel busy state from information contained in frames, rather than from physical RF energy detection. It encompasses: (1) NAV - the central timer updated from Duration fields, (2) EIFS - extended interframe spacing after frame errors, (3) Protocol-level integration - how RTS/CTS/ACK/Data exchanges coordinate virtual sensing, and (4) Recovery procedures - handling failures in the virtual sensing system.
The Grand Purpose:
Virtual sensing exists to solve a fundamental problem: stations cannot always detect each other's transmissions physically. Whether due to distance, obstacles, or power asymmetries, hidden terminals create collision risks. Virtual sensing propagates channel reservation information beyond direct RF detection range.
Think of it as a gossip protocol for channel state: 'I heard from Station B that it's about to receive data from Station A, so I'll stay quiet even though I can't hear A directly.'
The IEEE 802.11 carrier sense mechanism combines multiple signals to determine channel state. Understanding the complete architecture reveals how robustly the standard handles diverse scenarios.
1. Physical Carrier Sense (CS/CCA)
2. Virtual Carrier Sense (NAV)
3. State from Interframe Spacing
Formal Decision Rules:
Channel_State =
if (CCA == BUSY) then BUSY
else if (NAV > 0) then BUSY
else if (EIFS_active) then BUSY
else IDLE
Priority: Any BUSY signal prevails. All three conditions must be clear for IDLE.
Practical Implication:
A station might observe:
Or conversely:
The redundancy ensures coverage of all interference scenarios.
Physical sensing handles: nearby transmissions, non-802.11 interference, and real-time detection. Virtual sensing handles: hidden terminals, channel reservations, and predictive protection. Neither alone is sufficient—together they provide comprehensive coverage.
Interframe spacing (IFS) values create timing windows that coordinate transmissions. While not strictly 'carrier sensing,' IFS interacts with virtual sensing to create the complete channel access control system.
SIFS (Short Interframe Space):
PIFS (PCF Interframe Space):
DIFS (DCF Interframe Space):
EIFS (Extended Interframe Space):
| IFS Type | Duration | When Used | Relation to Virtual Sensing |
|---|---|---|---|
| SIFS | 10 μs | RTS→CTS, CTS→Data, Data→ACK | NAV Duration includes SIFS gaps |
| Slot Time | 9 μs | Backoff counter unit | Backoff during virtual idle |
| PIFS | 19 μs (SIFS + 1 slot) | PCF polling | May interrupt NAV for PC |
| DIFS | 28 μs (SIFS + 2 slots) | DCF transmission initiation | Wait DIFS after NAV = 0 |
| AIFS[AC] | SIFS + AIFSN×slot | EDCA priorities | Per-AC virtual wait after NAV |
| EIFS | SIFS + ACK + DIFS ≈ 94 μs | After FCS error | Extended recovery, NAV may expire |
Purpose of EIFS:
When a station receives a frame with an FCS error, the Duration field may be corrupted. The station cannot trust it to set NAV correctly. However, a hidden terminal exchange might be in progress—the error could be a collision symptom.
EIFS Logic:
EIFS_duration = SIFS + ACK_time + DIFS
Why this formula?
EIFS Activation:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
"""IFS and Virtual Sensing IntegrationComplete channel access timing model""" from enum import Enum, autofrom dataclasses import dataclassfrom typing import Optional # 802.11g timing constants (microseconds)SLOT_TIME = 9SIFS = 10PIFS = SIFS + SLOT_TIME # 19 μsDIFS = SIFS + 2 * SLOT_TIME # 28 μsACK_TIME = 44 # At 6 MbpsEIFS = SIFS + ACK_TIME + DIFS # 82 μs class ChannelAccessState(Enum): """Station's channel access state machine.""" IDLE = auto() BACKOFF = auto() WAIT_DIFS = auto() WAIT_EIFS = auto() WAIT_NAV = auto() TRANSMITTING = auto() RECEIVING = auto() @dataclassclass ChannelAccessController: """Complete channel access controller with IFS and virtual sensing.""" # Carrier sense states cca_busy: bool = False nav_remaining_us: int = 0 # IFS states difs_timer_us: int = 0 eifs_active: bool = False eifs_timer_us: int = 0 # Backoff state backoff_remaining_slots: int = 0 contention_window: int = 15 # CWmin for 802.11g # State machine state: ChannelAccessState = ChannelAccessState.IDLE def receive_frame_start(self): """Called when frame preamble detected.""" self.cca_busy = True self.state = ChannelAccessState.RECEIVING def receive_frame_end(self, fcs_valid: bool, duration_us: int = 0, is_for_me: bool = False): """ Called when frame reception completes. Args: fcs_valid: True if FCS check passed duration_us: Duration field value (if FCS valid) is_for_me: True if this station is the intended recipient """ self.cca_busy = False if not fcs_valid: # FCS error - enter EIFS recovery self.eifs_active = True self.eifs_timer_us = EIFS self.state = ChannelAccessState.WAIT_EIFS print(f" FCS error → Enter EIFS ({EIFS} μs)") return # Valid frame self.eifs_active = False if not is_for_me and duration_us > 0: # Update NAV from other stations' frames if duration_us > self.nav_remaining_us: self.nav_remaining_us = duration_us print(f" NAV updated to {duration_us} μs") self.update_state() def update_state(self): """Update state machine based on current conditions.""" if self.cca_busy: self.state = ChannelAccessState.RECEIVING return if self.eifs_active and self.eifs_timer_us > 0: self.state = ChannelAccessState.WAIT_EIFS return if self.nav_remaining_us > 0: self.state = ChannelAccessState.WAIT_NAV return if self.difs_timer_us > 0: self.state = ChannelAccessState.WAIT_DIFS return if self.backoff_remaining_slots > 0: self.state = ChannelAccessState.BACKOFF return self.state = ChannelAccessState.IDLE def advance_time(self, delta_us: int): """Advance all timers by delta_us microseconds.""" if self.nav_remaining_us > 0: self.nav_remaining_us = max(0, self.nav_remaining_us - delta_us) if self.eifs_timer_us > 0: self.eifs_timer_us = max(0, self.eifs_timer_us - delta_us) if self.eifs_timer_us == 0: self.eifs_active = False if self.difs_timer_us > 0: self.difs_timer_us = max(0, self.difs_timer_us - delta_us) # Backoff decrements only when channel idle if (not self.cca_busy and self.nav_remaining_us == 0 and not self.eifs_active and self.backoff_remaining_slots > 0): # Decrement by number of slot times in delta slots_elapsed = delta_us // SLOT_TIME self.backoff_remaining_slots = max(0, self.backoff_remaining_slots - slots_elapsed) self.update_state() def can_transmit(self) -> bool: """Check if station can transmit now.""" return (self.state == ChannelAccessState.IDLE and self.backoff_remaining_slots == 0 and not self.cca_busy and self.nav_remaining_us == 0 and not self.eifs_active) def initiate_transmission(self): """Begin transmission process.""" if self.can_transmit(): # Need to wait DIFS first self.difs_timer_us = DIFS self.state = ChannelAccessState.WAIT_DIFS print(f" Starting DIFS wait ({DIFS} μs)") def get_status(self) -> str: """Get current status string.""" return (f"State: {self.state.name}, CCA: {'BUSY' if self.cca_busy else 'idle'}, " f"NAV: {self.nav_remaining_us} μs, EIFS: {self.eifs_timer_us} μs, " f"Backoff: {self.backoff_remaining_slots} slots") def demonstrate_ifs_interaction(): """Demonstrate IFS and virtual sensing interaction.""" print("=== IFS and Virtual Sensing Demonstration ===") print() controller = ChannelAccessController() print("Initial state:", controller.get_status()) print() # Scenario 1: Normal NAV from CTS print("--- Scenario 1: Receive valid CTS (not for us) ---") controller.receive_frame_start() print(" Receiving CTS...") controller.receive_frame_end(fcs_valid=True, duration_us=1000, is_for_me=False) print(" Status:", controller.get_status()) print() # Time passes, NAV decrements print("--- 500 μs later ---") controller.advance_time(500) print(" Status:", controller.get_status()) print() # Scenario 2: FCS error triggers EIFS print("--- Scenario 2: Receive corrupted frame ---") controller.receive_frame_start() print(" Receiving frame...") controller.receive_frame_end(fcs_valid=False) print(" Status:", controller.get_status()) print() # EIFS runs in parallel with remaining NAV print("--- 100 μs later ---") controller.advance_time(100) print(" Status:", controller.get_status()) print() # Everything clears print("--- 1500 μs later (all timers expire) ---") controller.advance_time(1500) print(" Status:", controller.get_status()) print(f" Can transmit: {controller.can_transmit()}") demonstrate_ifs_interaction()When a hidden terminal collision occurs, receiving stations may get a garbled frame (FCS error). EIFS ensures they defer conservatively while the collision recovery (retransmission) resolves. Without EIFS, stations might immediately try to transmit after the error, potentially causing another collision.
Let's trace through complete protocol exchanges, showing how virtual sensing protects against hidden terminals at each step.
Participants:
T = 0 μs: Station A sends RTS
| Station | Can Hear? | Action |
|---|---|---|
| A | Transmitting | Sends RTS, Duration = 2000 μs |
| B | Yes | Prepares CTS |
| C | No (hidden) | Unaware |
| D | Yes | Sets NAV = 2000 μs |
T = 62 μs: RTS ends, SIFS begins
RTS transmission time (~52 μs at 6 Mbps) + setup.
| Station | NAV | CCA | Channel State |
|---|---|---|---|
| A | 0 (own exchange) | Idle | Waiting for CTS |
| B | 0 (will respond) | Idle | SIFS countdown |
| C | 0 | Idle | Potentially transmitting! |
| D | 1938 μs | Idle | Busy (NAV) |
Critical Window: Station C might transmit during SIFS! This is the vulnerability that CTS addresses.
T = 72 μs: Station B sends CTS
| Station | Can Hear? | Action |
|---|---|---|
| A | Yes | CTS received, proceed to Data |
| B | Transmitting | Sends CTS, Duration = 1890 μs |
| C | Yes | Sets NAV = 1890 μs ← Protected! |
| D | Yes | NAV unchanged (1928 > 1890) |
T = 130 μs: Station A sends Data
| Station | NAV (approx) | CCA | Protected? |
|---|---|---|---|
| A | 0 | Transmitting | N/A |
| B | 0 | Receiving | N/A |
| C | 1820 μs | Busy (CCA) or Idle | Yes (NAV) |
| D | 1808 μs | Busy (CCA) | Yes (both) |
Station C's Perspective:
T = ~1400 μs: Data completes, ACK follows
| Station | NAV | Action |
|---|---|---|
| A | 0 | Waiting for ACK |
| B | 0 | Sends ACK |
| C | ~450 μs | Still waiting |
| D | ~540 μs | Still waiting |
T = ~2000 μs: Exchange complete
| Station | NAV | CCA | Can Transmit? |
|---|---|---|---|
| All | 0 | Idle | Yes (after DIFS + backoff) |
Between RTS and CTS (during SIFS), a hidden terminal that wasn't protected might start transmitting. If this happens, CTS and the hidden terminal's frame collide at Station C and/or interference reaches B. This is why hidden terminals can still cause problems even with RTS/CTS—if they start transmitting before CTS reaches them. The CTS timing is critical.
Virtual sensing isn't perfect. Several edge cases and failure modes can compromise protection.
Scenario: Station A sends RTS; AP responds with CTS; but CTS is lost (interference, weak signal, etc.)
Problem:
Result:
Scenario: Station E is hidden from A AND far from B (can barely hear B's CTS).
Problem:
Result:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
"""Virtual Sensing Failure Mode AnalysisDemonstrates scenarios where virtual sensing provides incomplete protection""" from dataclasses import dataclassfrom typing import List, Tuple @dataclassclass TransmissionEvent: """Represents a transmission attempt and its outcome.""" station: str start_time_us: int duration_us: int target: str success: bool failure_reason: str = "" def analyze_sifs_race_condition(): """ Analyze the SIFS boundary race condition. Scenario: Hidden terminal starts transmitting during SIFS between RTS and CTS. """ print("=== SIFS Race Condition Analysis ===") print() # Timeline RTS_END = 52 # RTS transmission ends SIFS = 10 # SIFS duration CTS_START = RTS_END + SIFS # = 62 μs # Hidden terminal C's transmission attempt # C checks channel during SIFS (RTS already ended, CTS not started) C_CCA_CHECK = 55 # During SIFS C_TX_START = 55 # If C decides to transmit print(f"RTS ends at: T = {RTS_END} μs") print(f"SIFS period: T = {RTS_END} to {CTS_START} μs") print(f"CTS starts at: T = {CTS_START} μs") print() print(f"Hidden terminal C checks channel at: T = {C_CCA_CHECK} μs") print(f" → CCA result: IDLE (RTS ended, CTS not started)") print(f" → NAV at C: 0 (C never heard RTS)") print(f" → C's decision: TRANSMIT") print() if C_TX_START < CTS_START: print(f"🔴 COLLISION: C starts at {C_TX_START} μs, CTS at {CTS_START} μs") print(f" Both C's frame and CTS collide at stations that hear both") print(f" Hidden terminal protection FAILED for this case") else: print(f"🟢 No collision: C would start after CTS") print() return C_TX_START < CTS_START def analyze_distant_hidden_terminal(): """ Analyze distant hidden terminal that barely hears CTS. """ print("=== Distant Hidden Terminal Analysis ===") print() # Signal strength calculations (simplified) CTS_TX_POWER_DBM = 20 PATH_LOSS_NEAR = 70 # dB at normal distance PATH_LOSS_FAR = 90 # dB at far distance RX_SENSITIVITY = -82 # dBm, minimum for valid decode near_rx_power = CTS_TX_POWER_DBM - PATH_LOSS_NEAR # -50 dBm far_rx_power = CTS_TX_POWER_DBM - PATH_LOSS_FAR # -70 dBm print(f"CTS transmit power: {CTS_TX_POWER_DBM} dBm") print(f"Receiver sensitivity: {RX_SENSITIVITY} dBm") print() print(f"Near hidden terminal (70 dB loss):") print(f" Received power: {near_rx_power} dBm") print(f" Can decode CTS: {'YES ✓' if near_rx_power > RX_SENSITIVITY else 'NO ✗'}") print(f" NAV protection: {'YES' if near_rx_power > RX_SENSITIVITY else 'NO (EIFS only)'}") print() print(f"Far hidden terminal (90 dB loss):") print(f" Received power: {far_rx_power} dBm") print(f" Can decode CTS: {'YES ✓' if far_rx_power > RX_SENSITIVITY else 'NO ✗'}") if far_rx_power > RX_SENSITIVITY: print(f" NAV protection: YES") else: # Check if energy detection triggers ED_THRESHOLD = -62 # dBm for energy detection if far_rx_power > ED_THRESHOLD: print(f" Energy detected: YES → defers based on CCA") else: print(f" Energy detected: NO") print(f" NAV protection: NO (frame not received)") print(f" → Distant hidden terminal may transmit!") print() return far_rx_power < RX_SENSITIVITY def analyze_nav_attack(): """ Analyze NAV-based denial of service attack. """ print("=== NAV Attack Analysis ===") print() MAX_DURATION = 32767 # Maximum Duration field value (μs) ATTACK_FRAME_RATE = 30 # Frames per second # Attacker sends one frame with max Duration every 33ms attack_cycle_ms = 1000 / ATTACK_FRAME_RATE # 33.3 ms denial_per_cycle_ms = MAX_DURATION / 1000 # 32.7 ms print(f"Attack parameters:") print(f" Max Duration value: {MAX_DURATION} μs ({MAX_DURATION/1000:.1f} ms)") print(f" Attack frame rate: {ATTACK_FRAME_RATE} frames/second") print(f" Attack cycle: {attack_cycle_ms:.1f} ms") print() print(f"Impact per cycle:") print(f" Channel denied: {denial_per_cycle_ms:.1f} ms") print(f" Channel available: {attack_cycle_ms - denial_per_cycle_ms:.1f} ms") print() effective_throughput_ratio = (attack_cycle_ms - denial_per_cycle_ms) / attack_cycle_ms print(f"Effective channel capacity: {effective_throughput_ratio * 100:.1f}%") print(f"Denial of service severity: {(1 - effective_throughput_ratio) * 100:.1f}%") print() if effective_throughput_ratio < 0.1: print("🔴 SEVERE: Network essentially unusable") elif effective_throughput_ratio < 0.5: print("🟡 MODERATE: Significant performance degradation") else: print("🟢 MILD: Some throughput loss") # Run analysesprint("="*60)analyze_sifs_race_condition()print("="*60)analyze_distant_hidden_terminal()print("="*60)analyze_nav_attack()Practical networks mitigate these edge cases through: (1) AP transmit power ensuring CTS coverage extends beyond hidden terminal range, (2) Retransmission mechanisms catching occasional collisions, (3) NAV timeout limits (vendor-implemented) preventing indefinite denial, (4) 802.11ax OFDMA which schedules access explicitly rather than relying solely on virtual sensing.
How effective is virtual sensing in practice? Research and simulation provide quantitative answers.
Without Virtual Sensing (RTS/CTS disabled):
With Virtual Sensing (RTS/CTS enabled):
Virtual sensing improves net throughput when:
Collision_reduction × Data_frame_time > RTS/CTS_overhead
Break-even analysis for 1500-byte frame at 54 Mbps:
| Scenario | Collision Rate (No RTS/CTS) | Collision Rate (With RTS/CTS) | Net Throughput Change |
|---|---|---|---|
| Low density, no hidden terminals | 2% | ~0% | -25% (overhead exceeds benefit) |
| Medium density, some hidden terminals | 15% | 2% | +5 to +15% (balanced) |
| High density, many hidden terminals | 40% | 5% | +30 to +50% (significant improvement) |
| Linear mesh (severe hidden terminals) | 60%+ | 10% | +100%+ (transforms usability) |
The effectiveness of virtual sensing depends on geometric coverage:
CTS Coverage Zone: All stations that can decode CTS from the receiver will set NAV correctly. This zone defines the protection radius.
Hidden Terminal Zone: Stations that can interfere with the receiver but cannot decode CTS. These are the 'unprotected' hidden terminals.
Exposed Node Zone: Stations that hear RTS/CTS but could transmit without causing interference. Virtual sensing unnecessarily blocks them.
For optimal protection:
Studies by Bianchi (2000) and subsequent researchers show that RTS/CTS provides significant throughput improvement when hidden terminal probability exceeds 20-30%, with benefits increasing in dense deployments and mesh networks. The mechanism is less beneficial in sparse infrastructure networks where hidden terminals are rare.
802.11 has evolved significantly since the original virtual sensing design. Modern standards add new mechanisms that complement or partially supersede traditional NAV-based protection.
Frame Aggregation Implications:
Block ACK:
OFDMA Changes Virtual Sensing Paradigm:
Multi-Link Operation:
Enhanced Scheduling:
Backward Compatibility:
While scheduled access in 802.11ax/be reduces reliance on virtual sensing for coordinated traffic, virtual sensing remains essential for: (1) Legacy device support, (2) Non-scheduled random access, (3) Multi-BSS coordination, (4) Error recovery scenarios. NAV isn't going away—it's being supplemented by smarter scheduling.
Understanding virtual sensing implementation details helps in network design and troubleshooting.
NAV Timer:
Duration Field Processing:
State Machine:
Symptoms of Virtual Sensing Issues:
High NAV utilization, low throughput:
Collisions despite RTS/CTS enabled:
Variable performance by client location:
Monitoring Tools:
For optimal virtual sensing: (1) Measure hidden terminal severity before enabling RTS/CTS, (2) Use RTS threshold to protect only large frames, (3) Ensure AP coverage is symmetric (high AP power relative to clients), (4) Consider 802.11ax scheduled access for critical traffic, (5) Monitor continuously and adjust based on observed collisions and throughput.
Congratulations! You've completed a comprehensive exploration of the hidden terminal problem and its solutions. Let's consolidate everything into a unified understanding.
| Concept | Definition | Key Mechanism |
|---|---|---|
| Hidden Terminal | Stations that can't sense each other but share a receiver | Solved by RTS/CTS (receiver confirms) |
| Exposed Node | Stations that defer unnecessarily | Worsened by RTS/CTS; mitigated by 802.11ax |
| RTS | Request-to-Send control frame | Reserves channel, starts NAV at visible stations |
| CTS | Clear-to-Send response frame | Confirms reservation, sets NAV at hidden stations |
| NAV | Network Allocation Vector | Countdown timer for virtual carrier sensing |
| Duration Field | Time until exchange completion | Sets NAV in receiving stations |
| Virtual Sensing | Channel state from frame content | NAV + IFS + protocol integration |
| EIFS | Extended IFS after errors | Conservative recovery without valid NAV |
You now possess comprehensive knowledge of the hidden terminal problem: from its fundamental causes through mathematical analysis, practical solutions (RTS/CTS, NAV), and modern enhancements. This understanding is essential for wireless network design, troubleshooting, and excelling in GATE/competitive examinations. The next module will explore WiFi frame formats in detail.