jks-prv/Beagle_SDR_GPS

Real AM Stereo/C-QUAM Stereo

MaPePeR opened this issue · 8 comments

Now that we have the synchronous AM and the SAS/ pseudo-stereo mode the question arises if there is a possibility for real AM stereo using C-QUAM?

I think this would require a synchronous IQ mode, though?

Some time ago I wrote this small script, that adds an "AM Stereo" button to the Audio tab of the kiwisdr.
To use it, add it between <script type="text/javascript"> and </script> into the "Additional HTML/Javascript" Textbox on the Webpage Admin Tab.
The button switches to IQ mode and uses a PLL and low pass filtering to synchronize the IQ signal and then output the difference signals on the left and right channel accordingly to achieve C-QUAM stereo support.
The injection into the audio output chain of the kiwi and the adding of the button can only be described as a dirty hack, though.

The low pass filter has to be selected based on the samplerate/mode of the kiwi. (See lines 14 to 19)

Wow, wow, wow. That's super cool. I made a few UI improvements here: kiwisdr.com/files/C-QUAM.js I'm really sorry you had to go through all the current Kiwi code to figure that out. A lot of things are a complete mess including audio.js and the UI stuff.

But my file also has some debugging code included because I'm confused about a few things:

I can hear a stereo effect, I think, but I also hear the usual off-frequency flutter in IQ mode which to me says that the PLL isn't really locking. This is odd for a couple of reasons. The displayed PLL value always converges to zero. So there's that.

But also the audio processing routine is really being called at the browser/OS audio output rate, not the Kiwi network IQ transmission rate (12 or 20.25 kHz). This usually means 44.1 kHz but I've also seen 48 and 22.050 on occasion. It's host dependent. You say your use of audio_input_rate was for setting the LPF. But is it also used for setting PLL parameters? I couldn't quite tell from the code. All the PLLs I've ever seen need to know the sample rate, but I'm no expert about that topic.

The displayed PLL value gets to zero whether I tried using audio_input_rate or audio_output_rate to set phi_hat_diff_factor. I still heard the fluttering in both cases. The file above displays the measured sample rate of the audio processing routine.

What Kiwis/stations do you test with? I found a strong station on an Indiana Kiwi and one in Japan. Neither had GPS lock which further added to the signals being off-frequency.

In the long run there is really no reason not to implement this on the Beagle side. You could run it at 12/20 kHz and the waterfall would slow as necessary due to the process scheduling.

But it's really amazing that you figured out how to do this all from a js plugin!

Stations I tried: (my version of the code is installed)
1390 WZZB, Seymour, IN, USA
1314 JOUF, Osaka, Japan

Well, this gets more interesting. I found the original Motorola C-QUAM doc online and think I understand the process a bit better. It's really much simpler than I thought. If you have the synchronized IQ carrier it's just your two lines of code to recover the L & R channels:

            output_l[sample] = corrected_i / 2 + corrected_q / 2;
            output_r[sample] = corrected_i / 2 - corrected_q / 2;

So what I've done is add another mode to the Kiwi SAM/SAL/SAU/SAS mode button at the end called QAM. This is running now on the two Kiwis mentioned earlier. Currently it's daytime in Japan and the stereo from JOUF sounds pretty good! I should be able to check against WZZB later today.

There is definitely actual stereo separation with QAM rather than the pseudo-stereo SAS mode gives. There is some level or equalization problem with the QAM mode right now. It seems to be weaker than SAS or SAM mode. I don't understand why.

I have pushed the changes to Github but not as a new release version. So to rebuild with the QAM changes use the "force software build / build now" button on the admin page, update tab.

The code in rx/wdsp/SAM_demod.cpp now looks like:

            case MODE_SAS:
                audio  = (ai_ps + bi_ps) - (aq_ps - bq_ps); // lsb
                audiou = (ai_ps - bi_ps) + (aq_ps + bq_ps); // usb
                break;

            case MODE_QAM:  // C-QUAM
                audio  = corr[I]/2 + corr[Q]/2; // L
                audiou = corr[I]/2 - corr[Q]/2; // R
                break;

I can hear a stereo effect, I think, but I also hear the usual off-frequency flutter in IQ mode which to me says that the PLL isn't really locking. This is odd for a couple of reasons. The displayed PLL value always converges to zero. So there's that.

You might have run into some case, where the PLL code is running, but you are still listening to the bare IQ signal? I had some problems with that.

But also the audio processing routine is really being called at the browser/OS audio output rate, not the Kiwi network IQ transmission rate (12 or 20.25 kHz). This usually means 44.1 kHz but I've also seen 48 and 22.050 on occasion. It's host dependent.

I assumed the audio processing routine runs before the upsampling for the device output sample rate happens. The rate of which it is called is also not important, because it can control for sample rate with the amount of samples in the AudioFrame data structure. But that might be wrong. I probably never checked, because "works for me".

You say your use of audio_input_rate was for setting the LPF. But is it also used for setting PLL parameters? I couldn't quite tell from the code. All the PLLs I've ever seen need to know the sample rate, but I'm no expert about that topic.
I'm really no expert on PLL as well. That's the first one I've ever written and its mostly copy pasted from here I think I just used the PLL parameters from this tutorial as well as they seemed to work for my examples.

What Kiwis/stations do you test with? I found a strong station on an Indiana Kiwi and one in Japan. Neither had GPS lock which further added to the signals being off-frequency.

Sadly my testing kiwi and station is not available at the moment.

In the long run there is really no reason not to implement this on the Beagle side. You could run it at 12/20 kHz and the waterfall would slow as necessary due to the process scheduling.

I forgot to mention, that the code also slows down the waterfall, when enabling am stereo, because the Kiwi I used struggled with bandwidth a lot. Having two uncompressed channels + fast waterfall caused problems. But that is probably not needed on other devices with better internet connectivity.
And that long run turned out to be a very short run 🥳 😅 ?

Well, this gets more interesting. I found the original Motorola C-QUAM doc online and think I understand the process a bit better. It's really much simpler than I thought. If you have the synchronized IQ carrier it's just your two lines of code to recover the L & R channels:

output_l[sample] = corrected_i / 2 + corrected_q / 2;
output_r[sample] = corrected_i / 2 - corrected_q / 2;

Yes, you are right! Most of the processing code is to get a synchronized IQ carrier.

So what I've done is add another mode to the Kiwi SAM/SAL/SAU/SAS mode button at the end called QAM. This is running now on the two Kiwis mentioned earlier. Currently it's daytime in Japan and the stereo from JOUF sounds pretty good! I should be able to check against WZZB later today.

This is amazing, that you could integrate that into the beagle side of things so fast! I tried Japan earlier and it sounded good to me.

At the moment there seem to be some artifacts, that i can hear when listening and also appear, when i record the output through Audacity:
image
Interestingly, these don't appear, when I record the output using the kiwi build in record function, even though I could hear them during the recording. (So probably caused by internet connection speed issues?)

Also there is a DC offset for which I don't know how to deal with. I think the size of the offset was also relative to the frequency offset, but that might be a wrong assumption? I think the PLL removed the frequency offset of the carrier signal, but the amplitude of the carrier frequency offset is still left in the signal, somehow?

The only reason I was able to get it onto the Beagle side quickly was because the existing SAM/SAS code from Warren Pratt's wdsp package already has this nice IQ PLL which works well.

I think the issue I've seen with low output and your observation of DC offset is because with Beagle QAM I don't have the Butterworth 50 Hz HPF filter that your code does. So the DC offset and the C-QUAM 25 Hz pilot tone get through. I'm going to try adding a filter today.

I think my code had the same problem of low output and dc offset. I didn't high pass filter the output. I low pass filtered the input of the PLL, because I had problems getting it to hook otherwise. I then applied the PLL correction to the not low pass filtered iq signal and outputted that.
I could have probably achieved the same by tuning the PLL parameters, but I'm clueless how.

Okay, I was misinterpreting your code as a pilot tone HPF. But I guess the point of using a 25 Hz pilot is that you don't really need a filter at that frequency since the rest of the audio chain will likely filter out a tone that low.

v1.439 has the QAM change added. I hope to release it in another day or so.

Except for the Windows 10 audio "popping" problem, which has pretty conclusively been demonstrated to be unrelated to SAM/QAM, I think we're good on this. Thanks very much for the development work allowing this feature. There have been many positive comments.