351 lines
9.2 KiB
C++
351 lines
9.2 KiB
C++
/*
|
|
* Copyright (C) 2016 Bastian Bloessl <bloessl@ccs-labs.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "frame_equalizer_impl.h"
|
|
#include "equalizer/base.h"
|
|
#include "equalizer/comb.h"
|
|
#include "equalizer/lms.h"
|
|
#include "equalizer/ls.h"
|
|
#include "equalizer/sta.h"
|
|
#include "utils.h"
|
|
#include <gnuradio/io_signature.h>
|
|
|
|
namespace gr {
|
|
namespace ieee802_11 {
|
|
|
|
frame_equalizer::sptr
|
|
frame_equalizer::make(Equalizer algo, double freq, double bw, bool log, bool debug) {
|
|
return gnuradio::get_initial_sptr
|
|
(new frame_equalizer_impl(algo, freq, bw, log, debug));
|
|
}
|
|
|
|
|
|
frame_equalizer_impl::frame_equalizer_impl(Equalizer algo, double freq, double bw, bool log, bool debug) :
|
|
gr::block("frame_equalizer",
|
|
gr::io_signature::make(1, 1, 64 * sizeof(gr_complex)),
|
|
gr::io_signature::make(1, 1, 48)),
|
|
d_current_symbol(0), d_log(log), d_debug(debug), d_equalizer(NULL),
|
|
d_freq(freq), d_bw(bw), d_frame_bytes(0), d_frame_symbols(0),
|
|
d_freq_offset_from_synclong(0.0) {
|
|
|
|
message_port_register_out(pmt::mp("symbols"));
|
|
|
|
d_bpsk = constellation_bpsk::make();
|
|
d_qpsk = constellation_qpsk::make();
|
|
d_16qam = constellation_16qam::make();
|
|
d_64qam = constellation_64qam::make();
|
|
|
|
d_frame_mod = d_bpsk;
|
|
|
|
set_tag_propagation_policy(block::TPP_DONT);
|
|
set_algorithm(algo);
|
|
}
|
|
|
|
frame_equalizer_impl::~frame_equalizer_impl() {
|
|
}
|
|
|
|
|
|
void
|
|
frame_equalizer_impl::set_algorithm(Equalizer algo) {
|
|
gr::thread::scoped_lock lock(d_mutex);
|
|
delete d_equalizer;
|
|
|
|
switch(algo) {
|
|
|
|
case COMB:
|
|
dout << "Comb" << std::endl;
|
|
d_equalizer = new equalizer::comb();
|
|
break;
|
|
case LS:
|
|
dout << "LS" << std::endl;
|
|
d_equalizer = new equalizer::ls();
|
|
break;
|
|
case LMS:
|
|
dout << "LMS" << std::endl;
|
|
d_equalizer = new equalizer::lms();
|
|
break;
|
|
case STA:
|
|
dout << "STA" << std::endl;
|
|
d_equalizer = new equalizer::sta();
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Algorithm not implemented");
|
|
}
|
|
}
|
|
|
|
void
|
|
frame_equalizer_impl::set_bandwidth(double bw) {
|
|
gr::thread::scoped_lock lock(d_mutex);
|
|
d_bw = bw;
|
|
}
|
|
|
|
void
|
|
frame_equalizer_impl::set_frequency(double freq) {
|
|
gr::thread::scoped_lock lock(d_mutex);
|
|
d_freq = freq;
|
|
}
|
|
|
|
void
|
|
frame_equalizer_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) {
|
|
ninput_items_required[0] = noutput_items;
|
|
}
|
|
|
|
int
|
|
frame_equalizer_impl::general_work (int noutput_items,
|
|
gr_vector_int &ninput_items,
|
|
gr_vector_const_void_star &input_items,
|
|
gr_vector_void_star &output_items) {
|
|
|
|
gr::thread::scoped_lock lock(d_mutex);
|
|
|
|
const gr_complex *in = (const gr_complex *) input_items[0];
|
|
uint8_t *out = (uint8_t *) output_items[0];
|
|
|
|
int i = 0;
|
|
int o = 0;
|
|
gr_complex symbols[48];
|
|
gr_complex current_symbol[64];
|
|
|
|
dout << "FRAME EQUALIZER: input " << ninput_items[0] << " output " << noutput_items << std::endl;
|
|
|
|
while((i < ninput_items[0]) && (o < noutput_items)) {
|
|
|
|
get_tags_in_window(tags, 0, i, i + 1, pmt::string_to_symbol("wifi_start"));
|
|
|
|
// new frame
|
|
if(tags.size()) {
|
|
d_current_symbol = 0;
|
|
d_frame_symbols = 0;
|
|
d_frame_mod = d_bpsk;
|
|
|
|
d_freq_offset_from_synclong = pmt::to_double(tags.front().value) * d_bw / (2 * M_PI);
|
|
d_epsilon0 = pmt::to_double(tags.front().value) * d_bw / (2 * M_PI * d_freq);
|
|
d_er = 0;
|
|
|
|
dout << "epsilon: " << d_epsilon0 << std::endl;
|
|
}
|
|
|
|
// not interesting -> skip
|
|
if(d_current_symbol > (d_frame_symbols + 2)) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
std::memcpy(current_symbol, in + i*64, 64*sizeof(gr_complex));
|
|
|
|
// compensate sampling offset
|
|
for(int i = 0; i < 64; i++) {
|
|
current_symbol[i] *= exp(gr_complex(0, 2*M_PI*d_current_symbol*80*(d_epsilon0 + d_er)*(i-32)/64));
|
|
}
|
|
|
|
gr_complex p = equalizer::base::POLARITY[(d_current_symbol - 2) % 127];
|
|
gr_complex sum =
|
|
(current_symbol[11] * p) +
|
|
(current_symbol[25] * p) +
|
|
(current_symbol[39] * p) +
|
|
(current_symbol[53] * -p);
|
|
|
|
double beta;
|
|
if(d_current_symbol < 2) {
|
|
beta = arg(
|
|
current_symbol[11] -
|
|
current_symbol[25] +
|
|
current_symbol[39] +
|
|
current_symbol[53]);
|
|
|
|
} else {
|
|
beta = arg(
|
|
(current_symbol[11] * p) +
|
|
(current_symbol[39] * p) +
|
|
(current_symbol[25] * p) +
|
|
(current_symbol[53] * -p));
|
|
}
|
|
|
|
double er = arg(
|
|
(conj(d_prev_pilots[0]) * current_symbol[11] * p) +
|
|
(conj(d_prev_pilots[1]) * current_symbol[25] * p) +
|
|
(conj(d_prev_pilots[2]) * current_symbol[39] * p) +
|
|
(conj(d_prev_pilots[3]) * current_symbol[53] * -p));
|
|
|
|
er *= d_bw / (2 * M_PI * d_freq * 80);
|
|
|
|
d_prev_pilots[0] = current_symbol[11] * p;
|
|
d_prev_pilots[1] = current_symbol[25] * p;
|
|
d_prev_pilots[2] = current_symbol[39] * p;
|
|
d_prev_pilots[3] = current_symbol[53] * -p;
|
|
|
|
// compensate residual frequency offset
|
|
for(int i = 0; i < 64; i++) {
|
|
current_symbol[i] *= exp(gr_complex(0, -beta));
|
|
}
|
|
|
|
// update estimate of residual frequency offset
|
|
if(d_current_symbol >= 2) {
|
|
|
|
double alpha = 0.1;
|
|
d_er = (1-alpha) * d_er + alpha * er;
|
|
}
|
|
|
|
// do equalization
|
|
d_equalizer->equalize(current_symbol, d_current_symbol,
|
|
symbols, out + o * 48, d_frame_mod);
|
|
|
|
// signal field
|
|
if(d_current_symbol == 2) {
|
|
|
|
if(decode_signal_field(out + o * 48)) {
|
|
|
|
pmt::pmt_t dict = pmt::make_dict();
|
|
dict = pmt::dict_add(dict, pmt::mp("frame_bytes"), pmt::from_uint64(d_frame_bytes));
|
|
dict = pmt::dict_add(dict, pmt::mp("encoding"), pmt::from_uint64(d_frame_encoding));
|
|
dict = pmt::dict_add(dict, pmt::mp("snr"), pmt::from_double(d_equalizer->get_snr()));
|
|
dict = pmt::dict_add(dict, pmt::mp("freq"), pmt::from_double(d_freq));
|
|
dict = pmt::dict_add(dict, pmt::mp("freq_offset"), pmt::from_double(d_freq_offset_from_synclong));
|
|
add_item_tag(0, nitems_written(0) + o,
|
|
pmt::string_to_symbol("wifi_start"),
|
|
dict,
|
|
pmt::string_to_symbol(alias()));
|
|
}
|
|
}
|
|
|
|
if(d_current_symbol > 2) {
|
|
o++;
|
|
pmt::pmt_t pdu = pmt::make_dict();
|
|
message_port_pub(pmt::mp("symbols"), pmt::cons(pmt::make_dict(), pmt::init_c32vector(48, symbols)));
|
|
}
|
|
|
|
i++;
|
|
d_current_symbol++;
|
|
}
|
|
|
|
consume(0, i);
|
|
return o;
|
|
}
|
|
|
|
bool
|
|
frame_equalizer_impl::decode_signal_field(uint8_t *rx_bits) {
|
|
|
|
static ofdm_param ofdm(BPSK_1_2);
|
|
static frame_param frame(ofdm, 0);
|
|
|
|
deinterleave(rx_bits);
|
|
uint8_t *decoded_bits = d_decoder.decode(&ofdm, &frame, d_deinterleaved);
|
|
|
|
return parse_signal(decoded_bits);
|
|
}
|
|
|
|
void
|
|
frame_equalizer_impl::deinterleave(uint8_t *rx_bits) {
|
|
for(int i = 0; i < 48; i++) {
|
|
d_deinterleaved[i] = rx_bits[interleaver_pattern[i]];
|
|
}
|
|
}
|
|
|
|
bool
|
|
frame_equalizer_impl::parse_signal(uint8_t *decoded_bits) {
|
|
|
|
int r = 0;
|
|
d_frame_bytes = 0;
|
|
bool parity = false;
|
|
for(int i = 0; i < 17; i++) {
|
|
parity ^= decoded_bits[i];
|
|
|
|
if((i < 4) && decoded_bits[i]) {
|
|
r = r | (1 << i);
|
|
}
|
|
|
|
if(decoded_bits[i] && (i > 4) && (i < 17)) {
|
|
d_frame_bytes = d_frame_bytes | (1 << (i-5));
|
|
}
|
|
}
|
|
|
|
if(parity != decoded_bits[17]) {
|
|
dout << "SIGNAL: wrong parity" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
switch(r) {
|
|
case 11:
|
|
d_frame_encoding = 0;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 24);
|
|
d_frame_mod = d_bpsk;
|
|
dout << "Encoding: 3 Mbit/s ";
|
|
break;
|
|
case 15:
|
|
d_frame_encoding = 1;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 36);
|
|
d_frame_mod = d_bpsk;
|
|
dout << "Encoding: 4.5 Mbit/s ";
|
|
break;
|
|
case 10:
|
|
d_frame_encoding = 2;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 48);
|
|
d_frame_mod = d_qpsk;
|
|
dout << "Encoding: 6 Mbit/s ";
|
|
break;
|
|
case 14:
|
|
d_frame_encoding = 3;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 72);
|
|
d_frame_mod = d_qpsk;
|
|
dout << "Encoding: 9 Mbit/s ";
|
|
break;
|
|
case 9:
|
|
d_frame_encoding = 4;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 96);
|
|
d_frame_mod = d_16qam;
|
|
dout << "Encoding: 12 Mbit/s ";
|
|
break;
|
|
case 13:
|
|
d_frame_encoding = 5;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 144);
|
|
d_frame_mod = d_16qam;
|
|
dout << "Encoding: 18 Mbit/s ";
|
|
break;
|
|
case 8:
|
|
d_frame_encoding = 6;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 192);
|
|
d_frame_mod = d_64qam;
|
|
dout << "Encoding: 24 Mbit/s ";
|
|
break;
|
|
case 12:
|
|
d_frame_encoding = 7;
|
|
d_frame_symbols = (int) ceil((16 + 8 * d_frame_bytes + 6) / (double) 216);
|
|
d_frame_mod = d_64qam;
|
|
dout << "Encoding: 27 Mbit/s ";
|
|
break;
|
|
default:
|
|
dout << "unknown encoding" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
mylog(boost::format("encoding: %1% - length: %2% - symbols: %3%")
|
|
% d_frame_encoding % d_frame_bytes % d_frame_symbols);
|
|
return true;
|
|
}
|
|
|
|
const int
|
|
frame_equalizer_impl::interleaver_pattern[48] = {
|
|
0, 3, 6, 9,12,15,18,21,
|
|
24,27,30,33,36,39,42,45,
|
|
1, 4, 7,10,13,16,19,22,
|
|
25,28,31,34,37,40,43,46,
|
|
2, 5, 8,11,14,17,20,23,
|
|
26,29,32,35,38,41,44,47};
|
|
|
|
} /* namespace ieee802_11 */
|
|
} /* namespace gr */
|