Final Checkpoint!

Timing, Commands, Output, etc.

Due Wednesday, December 6th by 11:59pm
Not accepted after Friday, December 8th (2% late penalty per day after Wednesday)


Overview

This is it — the final submission! You'll add BEACON frames and clock synchronization, determine timeout values, deal with checksums, and add diagnostic output, along with a variety of other small details.

Specifics

The following items spell out the requirements for this checkpoint in more detail:
Control Interface
You should implement the dot11_command interface routine so that it supports at least the following command options. Be sure that your implementation uses the command numbers specified below — passing 3 5 to dot11_command should set the delay between BEACON frames to 5 seconds, for example.
  1. Print current settings to the output stream for diagnostic level, collision window slot choice, and beacon intervals. If you've chosen to support additional commands, report their settings here as well (as appropriate). Along with the settings, display the command number used to set them. (The command value is ignored for this command.)
  2. Set the diagnostic level according to the command value. This can be as simple as turning diagnostic output on or off, or you can choose to selectively enable output about certain categories of LLC/MAC layer activities as discussed in class.
  3. Select whether slot selection is random or fixed. If it's random, your implementation should select a value within the current collision window randomly when deciding how long to wait after DIFS has elapsed. Otherwise, you should select the maximum value each time. For example, if the collision window allows delays between 0 and 15 slots, you'd choose 15. If the window doubled, and now allowed delays between 0 and 31, you'd select 31, etc.
  4. Set the delay, in seconds, between the transmission of BEACON frames. For example, if the user specifies a five-second delay, you should attempt to send a beacon every five seconds. (Do not interpret this as the time between the end of the last beacon and the start of the next — you should start a beacon every N seconds if possible.)
  5. A delay of -1 must disable beacons.

Status
You must implement the dot11_status interface routine. Your implementation should set a status code upon the completion of each interface routine (SUCCESS if all went well, or the appropriate error code if not). You should also set the status code for each attempted packet transmission — TX_DELIVERED if the packet was acknowleged, TX_FAILED otherwise. The dot11_status routine simply returns the current value of the status code.

Output
When diagnostic output is enabled (see command #1 above), you should print details of your link layer's operation to the output stream. This output should contain information about the state your implementation is in, activities like transmitting or receiving packets, and the local time at which these activities take place. You should include information about the size of the collision window and slot counts as well.

Limited Buffering
Enforce limits on the number of packets queued for transmission or receipt: You should not allow more than four unacknowleged packets to be queued for transmission. If the layer above tries calls dot11_send when this limit has been reached, return 0 so the client knows no data was sent. Limit buffered incoming packets to four as well. If four packets are queued and awaiting delivery to the layer above, additional incoming data packets should be ignored (not acknowleged). Note that you must still process incoming beacon frames even if the four data packets are awaiting delivery.

Timing Issues
For the previous checkpoint, you were allowed to guess at an ACK timeout value that was large enough to allow a transmitted DATA packet to reach its destination and for the destination to transmit an acknowlegement. For full credit, you must determine timeout values more precisely for the final checkpoint: Run tests to determine how long it actually takes to get an ACK back after transmitting data, and use the results of these tests to determine your timeout value. Follow the guidelines in the IEEE specification — it tells you how much of a safety margin to build in once you've determined the actual round-trip time. (Hint: it's in terms of slot times.)

Also, to ensure that all stations are in close synchronization, don't simply wait DIFS after a previous transmission has ended. Round up to the nearest 50ms boundary. For example, if you're waiting to send and notice the channel has become idle at time 12431, your DIFS wait should end at 12950 instead of 12931 (assuming DIFS is 500ms). Slot begin and end times should all be aligned at 50ms boundaries as well.

Clock Synchronization
There are three components to clock synchronization: Handling BEACON frames, experimentally determining the time offset to be added to your local clock when creating the timestamps for outgoing beacons, and experimentally determining the time offset that should be added to incoming beacon timestamps to reflect the time required to process incoming packets.

Handling beacon frames requires both that you send beacon frames at the appropriate intervals, and that you inspect incoming beacons and adjust your local clock as necessary. The IEEE specification says that you should send beacon frames at regular intervals, even if there are outgoing data frames waiting to be sent. (If you're in the middle of transmitting a data frame when it comes time to send the next beacon, you finish the data frame first before sending a beacon.) Beacon frames are broadcast to all hosts, and contain an 8-byte timestamp. The timestamp is your local clock value plus the amount of time it takes you to create and transmit a beacon frame. (In other words, it's the time you expect the last bit of your beacon to arrive at the destination hosts.) When handling incoming beacon frames, retrieve the timestamp from the packet and add to it the amount of time it takes you to detect and process incoming frames. If the this adjusted timestamp is larger than your current clock's value, adjust your current clock to match the incoming value.

Determining the time required to create and transmit outgoing beacons is fairly straightforward: Add some test code to your implementation that repeatedly sends beacon frames. Measure the time it takes to send 10 or more beacons on an empty channel and calculate the average time per beacon. (The propagation delay in our simulated RF layer is dwarfed by the time taken to construct and transmit a frame, so you can neglect it.) I'll let you think about how to determine the time your implementation takes to receive and handle incoming beacons. (Note that you should implement CRCs first, before measuring time values, since it takes some time to calculate CRCs.)

Note: The time constants you measure could be influenced to a small degree by the speed of the computer on which your code runs. Please tell me which system you used when doing your timings when you submit.

CRCs
Prior to this checkpoint you could get away with setting the CRC field to -1 (all 1s), but for full credit you should calculate the actual checksum for your packet and send that in the CRC field. This is pretty simple if you use the CRC32 class.

Testing

Make sure you test your implementation thoroughly before submitting! Test your implementation against mine, against yours, and against other students' implementations. Use the jam and nuke features of the monitor to ensure that your layer behaves as it should in response to collisions and busy channels. Verify that your clock synchronization works, and that the limits on the number of incoming and outgoing packets are being enforced properly.

Submitting

Submit this checkpoint as before: Zip up your entire project folder and submit it via Canvas. Each member of the team should then send me an individual email summarizing the team dynamics and describing their specific contributions to the project.


Brad Richards, 2023