diff --git a/src/band.c b/src/band.c index c5908d24..e4d1cbb8 100644 --- a/src/band.c +++ b/src/band.c @@ -20,9 +20,98 @@ * */ +#include +#include + +#include + #include "band.h" void band_free(struct band *band) { l_free(band); } + +/* + * Base RSSI values for 20MHz (both HT and VHT) channel. These values can be + * used to calculate the minimum RSSI values for all other channel widths. HT + * MCS indexes are grouped into ranges of 8 (per spatial stream) where VHT are + * grouped in chunks of 10. This just means HT will not use the last two + * index's of this array. + */ +static const int32_t ht_vht_base_rssi[] = { + -82, -79, -77, -74, -70, -66, -65, -64, -59, -57 +}; + +/* + * Data Rate for HT/VHT is obtained according to this formula: + * Nsd * Nbpscs * R * Nss / (Tdft + Tgi) + * + * Where Nsd is [52, 108, 234, 468] for 20/40/80/160 Mhz respectively + * Nbpscs is [1, 2, 4, 6, 8] for BPSK/QPSK/16QAM/64QAM/256QAM + * R is [1/2, 2/3, 3/4, 5/6] depending on the MCS index + * Nss is the number of spatial streams + * Tdft = 3.2 us + * Tgi = Long/Short GI of 0.8/0.4 us + * + * Short GI rate can be easily obtained by multiplying by (10 / 9) + * + * The table was pre-computed using the following python snippet: + * rfactors = [ 1/2, 1/2, 3/4, 1/2, 3/4, 2/3, 3/4, 5/6, 3/4, 5/6 ] + * nbpscs = [1, 2, 2, 4, 4, 6, 6, 6, 8, 8 ] + * nsds = [52, 108, 234, 468] + * + * for nsd in nsds: + * rates = [] + * for i in xrange(0, 10): + * data_rate = (nsd * rfactors[i] * nbpscs[i]) / 0.004 + * rates.append(int(data_rate) * 1000) + * print('rates for nsd: ' + nsd + ': ' + rates) + */ + +static const uint64_t ht_vht_rates[4][10] = { + [OFDM_CHANNEL_WIDTH_20MHZ] = { + 6500000ULL, 13000000ULL, 19500000ULL, 26000000ULL, + 39000000ULL, 52000000ULL, 58500000ULL, 65000000ULL, + 78000000ULL, 86666000ULL }, + [OFDM_CHANNEL_WIDTH_40MHZ] = { + 13500000ULL, 27000000ULL, 40500000ULL, 54000000ULL, + 81000000ULL, 108000000ULL, 121500000ULL, 135000000ULL, + 162000000ULL, 180000000ULL, }, + [OFDM_CHANNEL_WIDTH_80MHZ] = { + 29250000ULL, 58500000ULL, 87750000ULL, 117000000ULL, + 175500000ULL, 234000000ULL, 263250000ULL, 292500000ULL, + 351000000ULL, 390000000ULL, }, + [OFDM_CHANNEL_WIDTH_160MHZ] = { + 58500000ULL, 117000000ULL, 175500000ULL, 234000000ULL, + 351000000ULL, 468000000ULL, 526500000ULL, 585000000ULL, + 702000000ULL, 780000000ULL, + } +}; + +/* + * Both HT and VHT rates are calculated in the same fashion. The only difference + * is a relative MCS index is used for HT since, for each NSS, the formula + * is the same with relative index's. This is why this is called with index % 8 + * for HT, but not VHT. + */ +bool band_ofdm_rate(uint8_t index, enum ofdm_channel_width width, + int32_t rssi, uint8_t nss, bool sgi, + uint64_t *data_rate) +{ + uint64_t rate; + int32_t width_adjust = width * 3; + + if (rssi < ht_vht_base_rssi[index] + width_adjust) + return false; + + rate = ht_vht_rates[width][index]; + + if (sgi) + rate = rate / 9 * 10; + + rate *= nss; + + *data_rate = rate; + return true; +} diff --git a/src/band.h b/src/band.h index e3b11c91..d0e86e17 100644 --- a/src/band.h +++ b/src/band.h @@ -20,6 +20,13 @@ * */ +enum ofdm_channel_width { + OFDM_CHANNEL_WIDTH_20MHZ = 0, + OFDM_CHANNEL_WIDTH_40MHZ, + OFDM_CHANNEL_WIDTH_80MHZ, + OFDM_CHANNEL_WIDTH_160MHZ, +}; + struct band { uint8_t vht_mcs_set[8]; uint8_t vht_capabilities[4]; @@ -32,3 +39,7 @@ struct band { }; void band_free(struct band *band); + +bool band_ofdm_rate(uint8_t index, enum ofdm_channel_width width, + int32_t rssi, uint8_t nss, bool sgi, + uint64_t *data_rate); diff --git a/src/ie.c b/src/ie.c index 737ae0a6..2e196fbb 100644 --- a/src/ie.c +++ b/src/ie.c @@ -31,6 +31,7 @@ #include "ell/useful.h" #include "src/util.h" #include "src/crypto.h" +#include "src/band.h" #include "src/ie.h" const unsigned char ieee_oui[3] = { 0x00, 0x0f, 0xac }; @@ -1726,98 +1727,6 @@ static int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie, return 0; } -enum ht_vht_channel_width { - HT_VHT_CHANNEL_WIDTH_20MHZ = 0, - HT_VHT_CHANNEL_WIDTH_40MHZ, - HT_VHT_CHANNEL_WIDTH_80MHZ, - HT_VHT_CHANNEL_WIDTH_160MHZ, -}; - -/* - * Base RSSI values for 20MHz (both HT and VHT) channel. These values can be - * used to calculate the minimum RSSI values for all other channel widths. HT - * MCS indexes are grouped into ranges of 8 (per spatial stream) where VHT are - * grouped in chunks of 10. This just means HT will not use the last two - * index's of this array. - */ -static const int32_t ht_vht_base_rssi[] = { - -82, -79, -77, -74, -70, -66, -65, -64, -59, -57 -}; - -/* - * Data Rate for HT/VHT is obtained according to this formula: - * Nsd * Nbpscs * R * Nss / (Tdft + Tgi) - * - * Where Nsd is [52, 108, 234, 468] for 20/40/80/160 Mhz respectively - * Nbpscs is [1, 2, 4, 6, 8] for BPSK/QPSK/16QAM/64QAM/256QAM - * R is [1/2, 2/3, 3/4, 5/6] depending on the MCS index - * Nss is the number of spatial streams - * Tdft = 3.2 us - * Tgi = Long/Short GI of 0.8/0.4 us - * - * Short GI rate can be easily obtained by multiplying by (10 / 9) - * - * The table was pre-computed using the following python snippet: - * rfactors = [ 1/2, 1/2, 3/4, 1/2, 3/4, 2/3, 3/4, 5/6, 3/4, 5/6 ] - * nbpscs = [1, 2, 2, 4, 4, 6, 6, 6, 8, 8 ] - * nsds = [52, 108, 234, 468] - * - * for nsd in nsds: - * rates = [] - * for i in xrange(0, 10): - * data_rate = (nsd * rfactors[i] * nbpscs[i]) / 0.004 - * rates.append(int(data_rate) * 1000) - * print('rates for nsd: ' + nsd + ': ' + rates) - */ - -static const uint64_t ht_vht_rates[4][10] = { - [HT_VHT_CHANNEL_WIDTH_20MHZ] = { - 6500000ULL, 13000000ULL, 19500000ULL, 26000000ULL, - 39000000ULL, 52000000ULL, 58500000ULL, 65000000ULL, - 78000000ULL, 86666000ULL }, - [HT_VHT_CHANNEL_WIDTH_40MHZ] = { - 13500000ULL, 27000000ULL, 40500000ULL, 54000000ULL, - 81000000ULL, 108000000ULL, 121500000ULL, 135000000ULL, - 162000000ULL, 180000000ULL, }, - [HT_VHT_CHANNEL_WIDTH_80MHZ] = { - 29250000ULL, 58500000ULL, 87750000ULL, 117000000ULL, - 175500000ULL, 234000000ULL, 263250000ULL, 292500000ULL, - 351000000ULL, 390000000ULL, }, - [HT_VHT_CHANNEL_WIDTH_160MHZ] = { - 58500000ULL, 117000000ULL, 175500000ULL, 234000000ULL, - 351000000ULL, 468000000ULL, 526500000ULL, 585000000ULL, - 702000000ULL, 780000000ULL, - } -}; - -/* - * Both HT and VHT rates are calculated in the same fashion. The only difference - * is a relative MCS index is used for HT since, for each NSS, the formula - * is the same with relative index's. This is why this is called with index % 8 - * for HT, but not VHT. - */ -static bool calculate_ht_vht_data_rate(uint8_t index, - enum ht_vht_channel_width width, - int32_t rssi, uint8_t nss, bool sgi, - uint64_t *data_rate) -{ - uint64_t rate; - int32_t width_adjust = width * 3; - - if (rssi < ht_vht_base_rssi[index] + width_adjust) - return false; - - rate = ht_vht_rates[width][index]; - - if (sgi) - rate = rate / 9 * 10; - - rate *= nss; - - *data_rate = rate; - return true; -} - static int ie_parse_ht_capability(struct ie_tlv_iter *iter, int32_t rssi, uint64_t *data_rate) { @@ -1867,19 +1776,15 @@ static int ie_parse_ht_capability(struct ie_tlv_iter *iter, int32_t rssi, if (!support_40mhz) goto check_20; - if (calculate_ht_vht_data_rate(i % 8, - HT_VHT_CHANNEL_WIDTH_40MHZ, - rssi, (i / 8) + 1, - short_gi_40mhz, &drate)) { + if (band_ofdm_rate(i % 8, OFDM_CHANNEL_WIDTH_40MHZ, + rssi, (i / 8) + 1, short_gi_40mhz, &drate)) { *data_rate = drate; return 0; } check_20: - if (!calculate_ht_vht_data_rate(i % 8, - HT_VHT_CHANNEL_WIDTH_20MHZ, - rssi, (i / 8) + 1, - short_gi_20mhz, &drate)) + if (!band_ofdm_rate(i % 8, OFDM_CHANNEL_WIDTH_20MHZ, + rssi, (i / 8) + 1, short_gi_20mhz, &drate)) continue; if (!support_40mhz) { @@ -2046,31 +1951,31 @@ static int ie_parse_vht_capability(struct ie_tlv_iter *vht_iter, * on the channel width for this iteration. */ switch (width) { - case HT_VHT_CHANNEL_WIDTH_20MHZ: + case OFDM_CHANNEL_WIDTH_20MHZ: sgi = short_gi_20mhz; break; - case HT_VHT_CHANNEL_WIDTH_40MHZ: + case OFDM_CHANNEL_WIDTH_40MHZ: sgi = short_gi_40mhz; break; - case HT_VHT_CHANNEL_WIDTH_80MHZ: + case OFDM_CHANNEL_WIDTH_80MHZ: sgi = short_gi_80mhz; break; - case HT_VHT_CHANNEL_WIDTH_160MHZ: + case OFDM_CHANNEL_WIDTH_160MHZ: sgi = short_gi_160mhz; break; } for (nss = minsize(rx_nss, tx_nss); nss > 0; nss--) { /* NSS > 4 does not apply to 20/40MHz */ - if (width <= HT_VHT_CHANNEL_WIDTH_40MHZ && nss > 4) + if (width <= OFDM_CHANNEL_WIDTH_40MHZ && nss > 4) continue; for (mcs = minsize(max_rx_mcs, max_tx_mcs); mcs >= 0; mcs--) { uint64_t drate; - if (!calculate_ht_vht_data_rate(mcs, width, - rssi, nss, sgi, &drate)) + if (!band_ofdm_rate(mcs, width, rssi, + nss, sgi, &drate)) continue; if (drate > highest_rate)