I’m wondering if spreading a bitstream (XOR-ing each bit with
SPREAD_FACTOR bits of sequence) before QPSK modulation is equivalent to
operating on the modulated stream, i.e., multiplying the complex output
with -1’s and 1’s from the sequence. So far I can’t get the math to work
out. Any suggestions?

In general you’re right, but it depends on the details. Its usually
quite
straightforward, but a common mistake is to use the wrong bit-to-symbol
mapping. 0 -> +1 and 1 -> -1 means that an XOR applied to bits is
equivalent to multiplication applied to symbols.

I second Peter. As long as you make sure in your gray-coded QPSK
constellation, 0 is mapped to +1 and 1 is mapped to -1 then XOR is
equivalent to multiplication.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.