- generate M random complex QAM symbols. example: (1+j,1-j,-1-j,1-j....)
- Split my M samples into a N 2048 sized arrays
- Take IFFT of each array individually
- Add cyclic prefix to each array
Exactly! In real-world OFDM systems, not all carriers are used; typically, you leave out one or two DC carriers and leave a few guard carriers at the band edges, so you'd have something like
- split my M samples into 1950-sized chunks, and map them to the elements of a 2048-vector, leaving the center ones and edge ones free
The edge guard carriers make the fifth step unnecessary:
- Make N square shaped signals by oversampling my IFFT arrays
so scratch that, usually.
- Pulse shape the individual signals with raised cosine
Don't do that! That's wrong. This is OFDM, and it's only used when you encounter multipath channels; matched filtering does nothing good here; it only makes things worse by making the channel more complicated and reducing average power.
Remember why you'd do raised cosine with single carrier systems: You want things to be
- nicely pulse-shaped, which simply doesn't make sense in the context of OFDM, and
- you want matched filtering because you want to maximize SNR in an AWGN channel, and OFDM is never used in channels that are purely AWGN, and
- because after convolution with the conjugate pulse shaping filter in the receiver, you might have an easier time recovering timing, but that doesn't apply to OFDM either, because timing recovery must, in the wideband channel scenario, be done using other means (keywords: Schmidl-Cox, autocorrelation timing recovery methods).
- Mix up the signals with frequency separation = 1/symbol_period by multiplying them by $e^{j2\pi ft}$
No; your subcarrier separation is what the IFFT does; you just mix up the baseband signal generated by step 4 to bandpass with your $e^{j2\pi ft}$.
Since you're only simulating things, and we know that bandpass-region channels have an equivalent representation in baseband (in fact, only because we know that works is that we can use OFDM at all): don't do this at all, but work with the complex baseband signal directly.