Monday, February 6, 2012

WSPR Encoder for Propeller - Update

I have decided to implement the main interface to the WSPR encoder such that you just pass in a single string containing the WSPR message you desire to encode.  The object will return a pointer to a byte array containing the symbol set for that message.  So the calling sequence will look like this:

OBJ
  WSPR : "ko7mWSPREncode"

VAR
  LONG Symbols

Symbols := WSPR.encodeWSPR(string("KO7M CN87 27"))

I think this will be the most intuitive and least error prone.  The returned value will point to an array of 162 bytes containing the symbol set.

VAR
  BYTE ich

repeat ich from 0 to 161
  sendSymbol(BYTE[Symbols][ich])

You can reference each byte with byte[Symbols][<index>] or you can treat the entire thing as a single string.

  sendSymbols(Symbols)

Things may change again, but I think this is the direction I am going.  I plan to publish the final encoder in the Propeller Object Exchange at the Parallax site.  I am investigating this opportunity now.

Saturday, February 4, 2012

Learned something new about Spin

I did a little study related to my previous post where I was befuddled about parameter passing in Spin for the Propeller.  To review...

Passing parameters to a method looks like this

VAR
  BYTE a
  WORD b
  LONG c

foo(a, b, c)

On the receiving end, we declare foo like this

PUB foo(x, y, z)

This is confusing to me when compared to other languages.  On the calling end, I can declare the size of the data.  I am passing a byte, word and long to foo.  On the receiving end, x, y and z are all longs.  Furthermore, if you declare any local variables, they are longs too.  For example:

PUB FOO(x, y, z) | i, j, k

So, when you pass a parameter, regardless of the size of the data, it is first converted to a long and the called method will receive it as a long.

However, you can also pass an address as a parameter.  Remember, my example was passing my callsign, locator, power and the symbol table to my encodeWSPR method.  This example is working:

VAR
  BYTE sym[162]

 
OBJ
  WSPR : "ko7mWSPREncode"


WSPR.encodeWSPR(string("KO7M "), string("CN87"), 27, @sym)

While this version yielded different results:

DAT
  mycall BYTE "KO7M ", 0
  myloc BYTE "CN87", 0
  mypwr BYTE 27

WSPR.encodeWSPR(@mycall, @myloc, @mypwr, @sym)


The issue here is that I am trying to make this same method handle two different format parameters.  Namely, the third parameter is the WSPR power setting.  In the first example, I am passing the value 27 in the parameter list.  This gets converted to a long and assigned to the third parameter in the call to encodeWSPR.  It then needs to be treated as a long value in the method.  In the second example, I am passing the address of a byte containing the power value.  The address gets converted to a long and assigned to the third parameter.  In the method, I have to then treat it as the address of a byte rather than as a long value.  For example:

encodeWSPR(call, loc, power, sym) | pwr

pwr := BYTE[power]

So, the reason my encodeWSPR was returning different results is that I was using the power parameter in different ways while the method was expecting to receive a long containing the value, not the address of it.  Bonehead...

So, I have to decide which way it is going to be as it cannot be both in the same method.  I can provide other methods that allow either kind of parameter to be passed or separate methods to set each parameter even.  My inclination is to pass the power by value rather than by reference.  Any preferences?

WSPR Encoder for Propeller Beacon Update

Well, after a bit of hair tearing, I have managed to get my WSPR encoder running on the propeller platform.  I am doing some cleanup of the code and will post it as soon as I have completed the changes.  I implemented it as a separate object, so to use it you would do something like this:

VAR
   BYTE sym[162]

OBJ
   WSPR : "ko7mWSPREncode"

This declares a byte array to hold the encoded WSPR symbol set and includes the WSPR encoder in your code.  You would then call this WSPR object to encode your call, locator and power setting and provide the address of the sym BYTE array so it can fill it up with your message.  Typically this would be done in your initialization code.

WSPR.encodeWSPR(string("KO7M  "), string("CN87"), 27, @sym)

Then in you would in your main loop send each symbol returned.

PUB Main | iSym
  doInitialize
  repeat
    repeat iSym from 0 to 161
      sendSymbol(sym[iSym])
    noTone
    Sync := nextSync
    repeat while SecondCnt // Sync
      delay(100)


Pretty simple and it works quite well.  I have a couple of caveats that are important in the current implementation.

  1. Only standard WSPR messages are supported currently.  No six digit locators or calls with prefixes or suffixes such as KO7M/5 or JA2/KO7M.  I have more work to do to support these special message formats.
  2. Call signs must be exactly 6 characters and the third character must be a numeric.  This is according to Joe Taylor's specifications.  The point of this is that my routine does not do any error checking to be sure you passed in the call sign with the numeric aligned in the third character and that it is padded with spaces to make it exactly 6 characters.  You will need to attend to this in the parameter you pass to encodeWSPR or the results will not be predictable.  For example, my call is passed as KO7M<space><space>.  If you had a call like one of the special even calls of "K7S", it would need to be passed in as <space>K7S<space><space>.  I may attend to this in future versions if I get inspired to do the work.
I also ran into a caveat that I cannot yet explain.  I am looking for insights into why this is happening.

If I pass the parameters like so:

DAT
  mycall BYTE "KO7M  ", 0
  myloc  BYTE "CN87", 0
  mypwr  BYTE 27

WSPR.encodeWSPR(@mycall, @myloc, @mypwr, @sym)

The results are not encoded correctly.  Changing to use the "string("...") format rather than BYTE data block constants with exactly the same data shown above works correctly.  So, you will have to stick to this format until I figure out why there is any difference.  Any thoughts on this from any of you propeller hackers out there appreciated.

Friday, February 3, 2012

WSPR Encoder for Propeller Beacon

In my previous post, I was thinking out loud about my WSPR encoder for the propeller-based beacon project.  Dave, G4FRE posted a couple of comments, one of which was quite useful entitled WSPR Coding Process and was instrumental in enabling me to fix my WSPR encoder implementation.  I was in fact encoding the locator incorrectly.  This document gives the following information for locators:



Locator
Designating the four locator characters as [Loc 1] to [Loc 4], the first two can each take on the 18 values ‘A’ to ‘R’ only so are allocated numbers from 0 – 17. The second pair can take only the values 0 – 9.

Another integer M is formed from:

M1 = (179 - 10 * [Loc 1] - [Loc 3] ) * 180 + 10 * [Loc 2] + [Loc 4]

Which gives a range of values from ‘AA00’ = 32220 to ‘RR99’ = 179, which comfortably fit into a 15 bit representation (2^15 = 32768), leaving spare codes for further enhancements.

The useful bit here was the information that AA00 = 32220 and RR99 = 179.  The algorithm above is not quite correct however.  The corrected form should be:
 
 
M1 = (179 - 10 * ([Loc 1]-10) - [Loc 3] ) * 180 + 10 * ([Loc 2]-10) + [Loc 4]

This appears to give me the correct results for these end cases, so assuming that useful bit is accurate, I should be good to go.  I will be testing against the WSPR application and posting the final code bits for those interested very soon.

Thursday, February 2, 2012

WSPR Encoder - "Thinking Out Loud"

I have been pondering why my WSPR encoder routine could be giving me incorrect results.  When I encode my message "KO7M CN87 27" and let the WSPR 2.11 application decode it, I get the following:

<...> KO7M 27

In reading the specification, I see the following:

Special Message Formats
Normal WSPR messages consist of a callsign, 4-digit grid locator, and power level in dBm. These messages are always preferred when appropriate. However, compound callsigns (i.e., callsigns with add-on prefix or suffix) cannot fit into the 28 bits allocated in a standard message. Similarly, 6-digit locators cannot fit into 15 bits. Messages using these components are therefore sent using a two-transmission sequence. For example, if the callsign is PJ4/K1ABC, the 6-digit grid locator is FK52UD, and the power level 37 dBm, the following messages will be sent in alternating transmissions:
PJ4/K1ABC 37
<PJ4/K1ABC> FK52UD 37

If you have special need to use a 6-digit locator with a normal callsign, check the box
Force transmission of 6-digit locator. If the callsign is K1ABC, the 6-digit grid locator FN42AX, and the power level 37 dBm, the following messages will then be sent in alternating transmissions:

K1ABC FN42 37
<K1ABC> FN42AX 37

Callsigns enclosed in angle brackets are actually sent as 15-bit hash codes. If such a code is received by another station before the full callsign has been received, it will be displayed as

<...>

on the decoded text line. Once the full callsign has been received, the decoder will thereafter recognize the hash code and fill in the blanks. Two very different callsigns might have the same hash code, but the 15-bit hash-code length ensures that in practice such collisions will be rare.

Please note that messages with compound callsigns or 6-digit locators will not be properly decoded by WSPR versions earlier than 2.0. Further details on message formats can be found in Appendix B, and in the WSPR source code.

Hmmm...  So, if I am reading this correctly, in a normal format message, I am supposed to encode the message as follows:

  1. 28 bits encodes a normal 6 character callsign
  2. 15 bits encodes a 4 character locator
  3. 7 bits encodes the power in dBm.
The special message format from the description above encodes in two messages, the content depends on the format of the data being encoded and whether or not the "Force transmissions of 6-digit locator" checkbox is  set to "checked".

Case 1:
Callsign with slash character in it
In this case, the normal (up to) 6 character callsign is augmented with a slash character (front or back) and up to 3 additional characters.  For example WA0FOO/7, JA2/KJ7ABC, etc.  The 28 bit callsign field and the 15 bit locator are large enough to contain this data while the power is sent in its own normal 7 bit field.  Here is my hypothesis:

  • When encoding WSPR messages, a the callsign field is required to have no more than 6 characters and is required to have a numeric in the third character position.  If the callsign does not match these specifications, spaces are added as neeed.  For example, "K7XX" is encoded as "<space>K7XX<space>".  This I have previously proven before the special formats where encorporated into WSPR.
  • When encoding a special format callsign, I believe that the "/" character and up to three additional characters must be encoded in the 15 bit locator field while the normal callsign is encoded in the callsign field padded with spaces as necessary.
  • The power level goes in the normal 7 bit field.
  • The first message contains the special callsign and power level only.
  • The second message packs up to 6 digits of a locator, space padded as needed into the normal 28 bit callsign field.  A 15 bit hash of the callsign is computed and sent in the 15 bit locator field.  The power is sent normally in its 7 bit field.
This case should allow for adorned callsigns as well as six character locators to be sent in two messages max.

Unanswered Questions
  1. Can the application can detect when decoding whether the callsign adornment belongs on the left or right side of the call?  For example: JA2/WA0AAA vs. WA0AAA/JA2.  I think this could be stored in the encoding of the "/" character.
  2. What is the precise encoding of the "/" character?
  3. Precisely how to calculate the 15 bit hash of the callsign?
Case 2:
Normal unadorned callsign (no slash) and 6 character locator
In this case the first message is a normal message with the extra 2 characters of the locator truncated as indicated in the documentation reference above.  My hypothesis about the remainder of the message is:
  1. The six digit locator is encoded into the 28 bit callsign field.
  2. The 15 bit hash is computed of the callsign and sent in the locator 15 bit field.
  3. The 7 bit power field contains the power level in dBm.


My code to encode the WSPR message doesn't know anything about special formats (yet) but is generating a symbol set that is decoding as if it was the second message a two message set, very much as would be found in case 2 above.  Joe Taylor's spec says that any callsign enclosed in <...> characters is sent as a 15 bit hash.  This points to the locator being incorrectly encoded.  The message I am sending is being decoded as:

<...> KO7M 27

This tells me that my call sign and power are both being encoded correctly because WSPR can decode them.  Since the encoding between the callsign and the locator fields is a different algorithm, the callsign must be encoded correctly and placed in the correct 28 bit callsign field of the WSPR message.  What appears to be wrong is the locator encoding.

The results would match case 1 above, if WSPR thought the locator field was a 15 bit hash, not a locator and that it has not yet received the first message of the two message set.  WSPR would then display precisely what I am seeing.

So, this little exercise has served to narrow my focus to the encoding of the locator field.  I must have some sort of logic error in this part of the code.

More to follow.

Much better WSPR map

After letting the new power amplifier run on the Propeller WSPR beacon today, I see a much more lively WSPR map:


I expect as the sun as set here that activity will wane during the evening ours, but I plan to let it run overnight to see how we get on.

Propeller WSPR Beacon Power Amp Results

This morning I finished up my beacon power amp by putting in the low pass filter.  I had a bit of a struggle with that however, as I had no capacitors that would work for the 5 pole filter in hand.  I ended up with some rather significant changes in values in my substituted capacitors.  The filter modeling still showed that I would barely meet a 40 dB supression of the harmonics, so some additional work is needed here before I can call this complete.

The final breadboard looks like this:


The Propeller Beacon with it's outboard low pass filter is putting in a nice clean signal to the input.


Checking the output with the spectrum analyzer, I can see that the harmonics are 40 dB down or better (but just barely).


So, hooking this up to the matched antenna and measuring the power output, I got found that I was putting out a solid 2 watts.


This is pushing things quite a bit and the transistor finals were getting quite warm.  I backed off the bias on the driver until the power dropped to about 500-600 mW.


It seems happy at this level and the entire amplifier draws about 160 mA at this setting at 12 V.

So, we let a WSPR transmission happen on the next even minute and were immediately rewarded:


So, I am quite pleased with the initial results.  I have (subsequently) changed my power setting reported to correctly reflect my new power level.  More work to be done to clean up the harmonic supression to have some more margin.  I am seeing some fluctuation in SWR that appears to be related to the effects of harmonics, so more work to do, but for now we are on the air at around 27 dBm.

Wednesday, February 1, 2012

Update on Power Amplifier

Um...  Ok...  I lied...  It is just plain ugly construction...


But, hey...  We are trying to prove a concept, right?  :)  Well, time to see if I can let the magic smoke out of it...  I have not built the output 5 pole filter yet, there will be time for that once things check out a bit.

I hooked up the 12V current limited supply and with the current limit just above the minimum current shutoff, slowly turned up the voltage to 12 VDC and there was no current draw.  So far so good.

Next I hooked up a dummy load, scope to the output and my beacon on the input.  Here is the lashup:


I had set the bias pot all the way down.  With the beacon supplying about 1.2 V of signal to the input, I slowly adjusted the bias until the finals kicked in.  There is a noticeable threshold effect from the unbiased output stage, but it kicked in at about 2.1 VDC of bias.  I ran it up to around 2.8 VDC and the current limiter kicked in (remember it is set to turn off at a very low current value.  For the moment I have bias set a 2.75 VDC and am seeing this on the output.  Remember there are no low pass filters yet in place and the bias is just rough set.  Pretty ratty looking, rather terrible really but about 5.5 V P-P into 50 ohms.  There is no skipping a low pass filter on this device.


This is would represent about 75 mW, given the unfiltered output.  There is quite a bit of energy going to  harmonics with this waveform and we really have not explored the bias setting or allowed the entire amp to consume more than about 6 mA, so the finals are not even getting slightly warm yet.  More testing to come.

So, it's time to build a low pass filter and do some more adjustments and performance tuning.

Note to self:  Gorilla glue sucks...  Don't use it for breadboarding circuits...

More power for Propeller Beacon

While I have enjoyed the few WSPR spots I have received:


I have received no QRSS or Opera spots at all.  There are times when I think (wishfully) that I see my 5 mW QRSS trace on remote grabbers, I have not been able to definatively say that I see my signal.  So, I think it is time to bring the power level up to a more respectable 10-20 dBm.

I have been scouting around for some interesting designs that I might actually have parts for.  I have a boatload of 2N7000 devices, so my focus has been in this area.  After some searching I have decided to try out the following design taken from the Wee Willy DSB rig described over on the "popcorn" site.


Don't know how well this is going to work out, but you never know until you try it, so off I go.  I am going to build this dead-bug style with a little Manhatten style thrown in for good measure.  I even went out and picked up a fresh super-glue tube.  Unfortunately, I got it home only to discover that it is a solid mass.  Rats...

So, I had to resort to Gorilla glue, unfortunately.  It will elongate my building time a bit as it takes a while to dry.  But we are building again after a long hiatus.


So, I will post the build progress and performance results as I go.  Should be fun!

Tuesday, January 31, 2012

Propeller Beacon Update

I have taken the baseline beacons that I have created for QRSS, WSPR and Opera and put them together into a single beacon.

WSPR is on a 10 minute cycle (TX Percent = 20%).  QRSS follows and just sends my call sign.  Opera then follows and the remainder of the 10 minute cycle is idle.  WSPR goes first because it has the even minute starting time requirement.

I have created each of the beacons as separate objects that the main beacon code can instantiate and call.  The main beacon code looks like this:

CON
  _CLKMODE = XTAL1 + PLL16X
  _XINFREQ = 5_000_000

  WMin     =       381        'WAITCNT-expression-overhead Minimum

OBJ
  WSPR  : "ko7mBeaconWSPR"
  QRSS  : "ko7mBeaconQRSS"
  Opera : "ko7mBeaconOpera"
  Clock : "ko7mClock"
  Freq  : "Synth"
 
VAR
  LONG Sync


PUB Main
  doInitialize
  repeat
    WSPR.doBeacon               ' 2 minutes (110.6 seconds) for WSPR
    delay(10000)                ' Delay 10 seconds before doing QRSS
    QRSS.doBeacon               ' QRSS is about 3 minutes
    delay(10000)                ' Delay 10 seconds before doing Opera
    Opera.doBeacon              ' Opera is about 3 minutes
    repeat while Clock.getSeconds // Sync
      delay(100)  


PUB doInitialize
  Sync := 10 * 60               ' Beacons cycle every 10 minutes
  Clock.Start


PUB delay(Duration)
  waitcnt(((clkfreq / 1_000 * Duration - 3932) #> WMin) + cnt)


The beacon is up and running as of now.  I would love to have any reports if anyone is able to spot any of my three beacons.