Re UniTracker: How in the hell do you *DO* that?

Status
Not open for further replies.

chikin

Member
Joined
Dec 19, 2002
Messages
22
(edit: Whoops, meant to post this in the UniTracker thread... guess my excitement at getting it working has turned my brain to mush. :) )

OK, you gotta share your secret.

I can't possibly fathom how you are able to decode the APCO P25 CQPSK control channel with pretty decent accuracy (I get about 65% average on the notoriously mistuned Austin system). This is not a simple 2-level FSK signal like a Motorola or EDACS control channel signal. Please tell us how you determine the phase shifts in the acquired sound card signal--are you using some kind of time-based algorithm where you look at each sample or is there more advanced DSP involved like FFTs and getting the phase shift from the output of the FFT or something? Do you convert the samples to I/Q and do your work from there? I'm dying to know!

Whatever you're doing, it's incredible. I write device drivers for a living and have even written a couple of sound card Motorola/EDACS decoders in the past, but that is nothing compared to what your software is doing.

Superb job.
 

SCPD

QRT
Joined
Feb 24, 2001
Messages
0
Location
Virginia
Re: How in the hell do you *DO* that?

chikin said:
I can't possibly fathom how you are able to decode the APCO P25 CQPSK control channel with pretty decent accuracy (I get about 65% average on the notoriously mistuned Austin system). This is not a simple 2-level FSK signal like a Motorola or EDACS control channel signal.

If you add a small buffer amp between your radio and the computer, you may be able to get 100% (read the FAQ for details).

This puzzle drove me nuts for almost two years. There were some brute force methods - like using a multiply-accumulate based correlator - or a stoichastic gradient - that were either unreliable or consumed too many cycles. A real APCO P25 subscriber's radio includes a signal processor that does nothing but convert the waveform into a stream of symbols. I wanted to create something that could exist on modest PC based software so I couldn't just build a MATLAB model and use boiler-plate generated code.

Please tell us how you determine the phase shifts in the acquired sound card signal--are you using some kind of time-based algorithm where you look at each sample or is there more advanced DSP involved like FFTs and getting the phase shift from the output of the FFT or something? Do you convert the samples to I/Q and do your work from there? I'm dying to know!

No FFTs, no I/Q conversion. I've been told that IQ samples at rate "X" are the same as I-only (or Q-only) samples at rate 2X.

I took a very low-tech approach. I oversample the data - mostly to cope with producing a 4800 baud symbol rate using the PC standard 44100 sampling rate (48000 samples per second would have been much easier). The rest of the code is a peak detector. If you look at a CQPSK signal (or C4FM for that matter) - you see a stream of pulses - they're very smooth compared to 2-level and 4-level FSK - but they're just pulses. In this case, each pulse is a symbol - one of four possible pulse shapes is transmitted.

For normal two-level signals, you can use the zero-crossing to determine the boundary between one symbol (or bit) and the next. With CQPSK, the zero crossing does not reliably indicate a symbol boundary. You have to use the peaks.

Locating (in time) the peak of each pulse gives us the center of each symbol (and that takes care of clock recovery). The height of the pulse tells you which of the four symbols is being sent. Relative amplitudes for all four symbosl are +1, -1, +3, and -3. Positive peaks above +2 are assumed to be +3's, negative peaks below -2 are assumed to be -3's - otherwise they're +1's and -1's respectively.

The resulting stream of symbols maps to a stream of bit pairs or "dibits". The rest of the code figures out what to do with those bits ...

a. de-interleave
b. trellis decoding (with limited error correction)
c. packet framing

-rick
 

chikin

Member
Joined
Dec 19, 2002
Messages
22
I oversample the data - mostly to cope with producing a 4800 baud symbol rate using the PC standard 44100 sampling rate (48000 samples per second would have been much easier).

Consider that 48kHz (and assuming that you are doing 8-bit single channel wave input) is pretty standard on any modern sound card and would allow you an integer number of samples for many of the protocols that UniTrunker supports. In fact, I've found that a lot of sound cards even support arbitrary sampling rates up to their limit, usually 96 KHz these days. When I wrote my EDACS decoder and was experimenting with RD-LAP (NOTE: I can't decode RD-LAP because I have no specifics on the Trellis state machine beyond that it's rate 3/4, which is not enough to do the decoding), I used this to get 10 samples/bit time at 9600 baud. But you wouldn't of course want to rely on all systems supporting that. I've found that waveInGetDevCaps() sometimes lies too, which is an additional complication if you want to determine at runtime whether you can use certain properties of the sound card.

I was confused for a while after reading your post, because I was under the impression that CQPSK was significantly different from C4FM and that you would have to determine phase shift information. I did a bit of research and found this:

the "C" is for "compatible" - the parameters of the QPSK modulation were chosen so that at symbol time the signal is identical to C4FM

So it makes sense to me now that you just need the peaks and their amplitude to do the decoding, regardless of the modulation of the control channel.

Two more questions:
1. Do you do any FIR/IIR filtering on the input signal? If not, consider an RRC filter, which I did when extracting RD-LAP frame syncs. It had a significant effect on the number of correct bits I was able to match in the frame syncs.
2. I'm interesting in how you do the Trellis decoding, because the BAAA-A document describes the encoding process but not vice-versa. I wonder if you are using the Viterbi algorithm or whether you coded something else that does the same thing.
 

SCPD

QRT
Joined
Feb 24, 2001
Messages
0
Location
Virginia
chikin said:
Consider that 48kHz (and assuming that you are doing 8-bit single channel wave input) is pretty standard on any modern sound card and would allow you an integer number of samples for many of the protocols that UniTrunker supports. In fact, I've found that a lot of sound cards even support arbitrary sampling rates up to their limit, usually 96 KHz these days.

I use 16 bit samples. Most of the wave input code I'm using was written when Windows 98 was standard issue on most new PCs. Everything supports CD quality audio so this was one feature I could count on. I should look in to support 48khz sampling - just because of the savings in cycles. The scary thing about requesting audio at an arbitrary sample rate is you may end up offloading resampling (an expensive operation) to the wave audio drivers. You take a performance hit either way.

When I wrote my EDACS decoder and was experimenting with RD-LAP (NOTE: I can't decode RD-LAP because I have no specifics on the Trellis state machine beyond that it's rate 3/4, which is not enough to do the decoding), I used this to get 10 samples/bit time at 9600 baud. But you wouldn't of course want to rely on all systems supporting that.

For clock recovery, the PLL I use needs something a little finer than 10 samples per symbol. Otherwise, you get a full 10% change in phase for the tiniest correction. There are some tricks to oversampling in situations where you don't actually need ALL of the samples to save on compute cycles.

For folks out there not familiar with trellis coding - here's an example.

A half-rate trellis can accept two bits of real data to produce four bits of trellis coded data (you lose half your bandwidth to the trellis code - that's why it's called half-rate trellis).

A 3/4 rate trellis code produces four bits of trellis code for every three bits of real data.

The "magic" behind a trellis code is that for any given trellis code, there are only four other possible valid trellis codes (for the half-rate example) that may follow it in a error-free bitstream. There are a number of techniques for dealing with a pair of trellis codes that don't match.

I've found that waveInGetDevCaps() sometimes lies too, which is an additional complication if you want to determine at runtime whether you can use certain properties of the sound card.

You can always call waveInOpen with the desired settings and check the return code to see if Windows likes it or not.

1. Do you do any FIR/IIR filtering on the input signal? If not, consider an RRC filter, which I did when extracting RD-LAP frame syncs. It had a significant effect on the number of correct bits I was able to match in the frame syncs.

I tried inserting a raised cosine filter. I saw an improvement of bits-per-thousands (on other words, a fraction of a percent). It consumed too many CPU cycles for such a dismal return. I'll plug "RRC" into google and see what comes back.

2. I'm interesting in how you do the Trellis decoding, because the BAAA-A document describes the encoding process but not vice-versa. I wonder if you are using the Viterbi algorithm or whether you coded something else that does the same thing.

I built a state machine (a trellis code is just bits taken from a state machine - with some of the bits dropped). For deterministic bit sequences the incoming and outgoing states match. For ambiguous (or corrupted) bits that don't match I substitute a different symbol (favoring symbols that differ by only one bit) so that the states do match.

-rick
 

ECPD279

Member
Premium Subscriber
Joined
Jul 9, 2002
Messages
815
Location
Bay Area, CA
I prefer to use a Furnortner Rod and Kanifilator Bypass Spring to de-parenthisize the chocolate parfait. God, my head hurts after reading all that ^
 

RolnCode3

Member
Joined
Jun 7, 2004
Messages
2,255
Location
Sacramento/Bay Area, CA
Unavailable for comment (and then back to the thread :lol: ):

1955_doc_a.jpg
 
Status
Not open for further replies.
Top