Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MIMO Channel Estimation #29

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open

MIMO Channel Estimation #29

wants to merge 22 commits into from

Conversation

MLsmd
Copy link
Member

@MLsmd MLsmd commented Jul 11, 2018

  • Add MIMO channel estimator block in C++.
  • Add qa test in python for channel estimator block.
  • Add doxygen documentation.
  • Add .xml file for use in GRC.

This closes #17.

MLsmd added 21 commits June 7, 2018 11:39
@MLsmd
Copy link
Member Author

MLsmd commented Jul 11, 2018

Please review and merge the V-BLAST branch #28 first, because #28 is already rebased into this branch #29.

Copy link
Member

@marcusmueller marcusmueller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uint16 symbol_length overflow issue is the only bug I could find.

Not quite sure about the break issue, might be an oversight.

The PMT performance issues are not important.

// Iterate over tags in buffer.
for (unsigned int i = 0; i < tags.size(); ++i) {
// Calculate the number of items before the next tag.
if (i < tags.size() - 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply split into a for loop with condition i < tags.size() - 1 and a separate i = tags.size()-1?

That'd enhance readability.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case I would have to repeat the code after the if else statement in the for loop for both cases which would worsen the readability again, or am I overseeing something here?

if (symbol_length < d_training_length){
/* We consume the whole training sequence in the next buffer and don't consume it now
* in order to do the estimation at once. */
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the last iteration of the for loop anyway, and this is the last statement in that iteration. Breaking at this point doesn't do anything, or am I mistaken?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we perhaps missing the case where symbol length is not smaller than training sequence length and we need to consume now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The for loop is not finished after the if else statement. If the symbol length is smaller than the training length, we cannot process the received training sequence right now. So we are breaking the loop (we are on the last iteration, but there would still be the copy and tag assignment operations in this iteration to be done) to skip the rest of the code in the for loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I'll have to talk to you personally about this, but this is the "else" belonging to the "if(not last iteration)" clause, and that means this else is only executed on the last iteration, and right after the closing bracket of the "else" clause comes the closing bracket of the "for" clause. I'm clearly missing something here!

uint16_t d_N; /*!< Number of receiving antennas. */
std::vector<std::vector<gr_complex> > d_training_sequence;
/*!< Training matrix: Each subvector/row is sent through one of the M transmit antennas. */
uint16_t d_training_length; /*!< Length of the training sequence. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just out of interest (it's unlikely we find a training sequence longer than 2¹⁶ - 1): Why the uint16_t here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has no specific reason. I am changing it to uint32_t.

// Collect all tags of the input buffer in the vector 'tags'.
get_tags_in_window(tags, 0, 0, noutput_items);

uint16_t symbol_length; // Number of items in the current symbol.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure our symbols are limited to 2¹⁶ - 1 items?

if (tags[0].offset - nitems_read(0) > 0){
/* There are items in the input buffer, before the first tag arrives,
* which belong to the previous symbol. */
symbol_length = (tags[0].offset - nitems_read(0))*d_num_inputs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or rather, limited to (2¹⁶-1)/num_inputs? Sounds a bit of a dangerous thing, considering let's say 8×8 DAB MIMO ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to fix these issues in the according branch 'vblast'.

symbol_length = noutput_items - (tags[i].offset - nitems_read(0))*d_num_inputs;
}
// Check the key of the tag.
if (pmt::symbol_to_string(tags[i].key).compare("csi") == 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole idea of PMTs having symbols is that a symbol representing the same string is unique. So, instead of converting the symbol to the string and comparing the string you should just have a static pmt_t csi_key here which get initialized once; then you can compare that to the tag key with == directly.

}
// Calculate the weighting vector for the next symbol with the received CSI.
update_mimo_equalizer();
} else if (pmt::symbol_to_string(tags[i].key).compare("snr") == 0 && d_equalizer_type.compare("MMSE") == 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with comparing the string really is that PMT is horrific. The symbol_to_string function does a dynamic cast of the pmt_base to a pmt_symbol. That's an awful lot of indirection just to compare. Also, in a lot of cases (I think this might be one), this requires a copy of the std::string contained in that PMT to be made (did I ever mention how bad an idea that design is? Like, have "polymorphic type", but it not be properly polymorphic, and it also just inefficiently wrap std types, for communication within the same process?).

std::string copies are relatively expensive and you try to avoid them in the signal processing part, because they require allocation and deallocation of memory.

gr_complex *out = (gr_complex *) output_items[i];
// Write output data to current output port.
for (int j = 0; j < noutput_items; ++j) {
out[j] = in[j*d_num_outputs + i];
Copy link
Member

@marcusmueller marcusmueller Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need this, or would one of the standard deinterleaver blocks work, too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we swap the i loop with the j loop, it becomes evident that this might be solvable more efficiently with a memcpy

'''
2 tests validating the correct estimation of the channel coefficients with a random channel
and 1xN MIMO scheme. '''
def test_001_t (self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but for your own sanity in the future: test methods need to start with test_ and can take names like test_heavier_than_duck.

pmt.make_f32vector(num_inputs, 1e8),
pmt.from_long(0))))

for i in range(0, num_tags):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's simply range(num_tags) ;)

@marcusmueller
Copy link
Member

I get merge conflicts. Can you rebase?

@MLsmd
Copy link
Member Author

MLsmd commented Aug 8, 2018

Thanks for the review! As discussed, I opened a new branch with the merged master. I am going to request a new pull and this pull request gets obsolete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MIMO CSI estimation
2 participants