...let me offer my own redneck point of view. I'm not a graduated EE techie and the more arcane parts of the math are lost on me. I'm trying to use the tools like I would use a shovel (or a third-party programming library, whom I trust to do its job).
I've met this phenomenon of negative spectra while trying to put KissFFT to work as part of my proggie called the RtlSDR Skyline.
FFT is a transform that works upon a buffer of complex samples. The number of samples, AKA the Window, should preferably be a power of two. Such as, 2048 samples. Each sample is a complex number, encoding two orthogonal components of the signal, or amplitude and phase if you prefer to look at it that way. So the transform takes a buffer of CPX samplex of the time-domain signal, and returns a buffer of the same size, only containing complex samples of frequency spectrum (spectral lines with amplitude and phase). Within the context of some "assumptions out of band" about infinities etc, the input buffer and the output buffer carry the same amount of information. The number of time-domain samples is equal to the number of frequency-domain samples. And, apologies for skipping some math, this whole algorithm implies, that the frequency data buffer contains twice the bandwidth, that you would normally expect by applying the popular Nyquist limit: i.e., that the maximum frequency component represented in the time-domain signal is at half the sampling rate. I.e., looking at the spectral output buffer, the lower half is what you would expect, and the upper half are mirror frequencies, a result of aliasing, wrapped around the nyquist frequency. Or, maybe there's a better way to look at this "aliased half": consider them the negative frequencies :-) = wrapped around the DC bin. And, they are just the same worth as the positive half of the spectrum. An important point: does this mean, that the two half-spectra are necessarily mirror images of each other? No, they are not! :-)
Let me quote from the KissFFT docs: "Note: frequency-domain data is stored from dc up to 2pi. so cx_out[0] is the dc bin of the FFT and cx_out[nfft/2] is the Nyquist bin (if exists)". In my program, the first thing I do with the spectral data is: I swap the two halves in the buffer, so that I get a continuous spectrum of -(bandwidth/2) .. +(bandwidth/2)
, with the DC bin in the middle.
At this point, let me cut to a side plot:
Why complex numbers? The explanation in pure physics might be, that a vibrating system, especially if this is a resonant sinusoidal vibration, actually contains two forms of energy, that keep rotating, circulating, spilling from one form to the other and back again. A metastable system containing a constant amount of energy. Kinetic energy to positional potential energy. Current to Voltage and vice versa. Electric vs. magnetic field. Or some such. (In an environment with some characteristic impedance, which is a condition for physical "work" and "power" to arise - but let's not tighten the math too far.)
In a purely voltage-coupled signal path, the complex number may carry information about phase of the "pure voltage" signal - which raises the question how the phase was arrived at, if there's no current associated :-) What does the phase encode?
Cutting back to my FFT-based band-scanning software:
The complex I+Q samples that you get out of the demodulator, those are a product of mixing. At the input of the quadrature demodulator, you have a narrow-band intermediate frequency signal, which itself is already a product of down-mix/downconversion from some carrier frequency. The quadrature demodulator takes the arbitrary input signal, whose spectrum is centered around the nominal
IF frequency (shaped by a passive band-pass filter that "selects" a band of frequencies around the nominal IF) and multiplies that signal by a pure sinewave of the IF frequency. The quadrature demodulator actually uses two reference sinewaves, shifted 90* (or sine and cosine) - so you get two multiplication products, called I and Q. These are still time-domain samples, but each sampled "timeslot" contains a complex number.
Now... suppose that the signal coming down the IF path is actually a pure sinewave exactly at the IF frequency, identical to the local oscillator. In that case, on the output of the demodulator, you get a constant complex number, containing an amplitude of the received IF signal, and a phasor = phase angle of the received sinewave, as compared to the local oscillator's reference signal. The phase angle is decoded using the two reference waves, phase-shifted by 90*.
Next step: suppose that the sinewave, coming down the IF path, is a little slower than the local oscillator. What happens? The complex phasor, coming out of the demodulator in the form of I+Q samples, will rotate.
Next: suppose the sinewave, coming down the IF path, is a little faster than the local osc. What happens? Again the complex I+Q phasor will rotate, but in an opposite direction.
So: here we have a time series of complex I+Q samples (phasors sampled in discrete time instants). What happens if we run this through FFT? That's right, for the three examples above, we get a DC component, a negative and a positive frequency line in the FFT output.
Note: you need to consider the broader superhet system at hand. The "positive and negative" is relative to the IF channel's center frequency. As soon as I plot the partial band just decoded into the overview spectrum, it will sit on the spectral scale at absolute frequencies that are definitely positive (after I've added the "tuned carrier center frequency").
So if I tune my RTLSDR dongle into say 100 MHz carrier frequency, and the sampling rate is 2 MSps, as a net result on the screen I can get a channel worth of 2 MHz of spectrum, centered around 100 MHz. That's 99 to 101 MHz. If there's a radio transmitter at 99,7 MHz, I should see a peak around -300 kHz in my raw FFT output.
You may ask: that's all fine, but how does FFT come up with a spectrum of a real-world IF signal, that looks mostly like noise, centered around your local oscillator frequency? The demodulator will keep spewing I+Q samples with random absolute values and phases. What's the point? You cannot tell a frequency out of a single random sample, or a sequence of very random samples, right? Well, wrong :-) A single sample indeed doesn't contain frequency information. But a time series, that's a whole different matter. For some nice results appealing to the human eye, such as a peak in the otherwise noisy spectrum, the time series of CPX samples must contain some repetitive pattern. It may not be well visible to the naked eye in the time domain waveform, but the time series buffer is a statistical dataset, over which you can decode individual frequency components by a simple statistical operation called the convolution. You multiply samples of a time series (time-domain signal) by samples of a "probe waveform" and accumulate the per-sample products. If the accumulated result is large, it means that your probe wavelet "rings a bell" :-)
("Ooohhh waiwaiwait, how come that modems or TV transponders produce such a nicely shaped brick of noise? Allegedly modulated by QPSK/QAM/OFDM. How does that carry some clean data?" Answer: the signal is scrambled by a pseudo-random sequence to be noisy, or at least coded by 10/8 or some such to be free of a DC component, forward error correction is added, on duplex channels the media can be trained and the signal pre-equalized, etc. That's where my brain is already squirting out my ears = stop asking.)
So what the Discrete Fourier Transform does: it meticulously convolves the time-series buffer with every "frequency bin" of the output, and places the accumulated product into the output bin. Fast Fourier Transform is a version of DFT that's heavily optimized for compute efficiency - an explanation is outside of scope here (and beyond my grasp anyway).
So... how do negative frequencies come out of this meticulous convolution algorithm? My take: apparently the "frequency probe waveform" per spectral bin is a complex time series too, and can be made to rotate clockwise or counter-clockwise? And, I seem to recall something about complex conjugates and correlation between the positive and negative spectrum being used to calculate (and post-compensate) I/Q imbalance,
but that's where I'm getting lost :-)
Note that the raw spectral samples coming out of FFT are not only positive/negative (explained above) but on top of that, they are complex too... which in this case means, that apart from amplitude of each particular spectral line (FFT output bin), we also know its phase angle within the FFT window. For practical purposes in a simple spectrum analyzer, the phase is not really interesting - therefore, for spectral display on the screen, the only sensible preprocessing operation is to take the absolute value (amplitude) of each complex frequency bin = turn the complex phasor into a scalar amplitude.