In a previous blog posting I have demonstrated an extremely simple transmitter / keyer implementation on the propeller platform. The next logical step was to build a receiver to go with it.
What I have implemented is a very simple direct conversion receiver based on the NE602 Gilbert Cell mixer and a LM386 audio amplifier. The circuit is based on the famous sudden receiver design from the Rev George Dobbs G3RJV many years ago and looks something like this:
My implementation removes all the tuned circuit stuff from pins 6 and 7 of the NE612 (NE602 in my circuit) and injects the RF generated by the propeller via a blocking capacitor directly into pin 6 to set the receive frequency.
Here is a photograph of the lash-up with the receiver built manhattan-style on a little 5x7 cm PCB scrap.
Right now I am sitting here listening to a 75 metre net running on 3.98 MHz LSB (The Oregon Emergency Net).
Obviously, I have a lot of work to do on user interface code...
Now as you might well imagine, there are a number of problems with playing at RF frequencies with bits of wire lashing all the bits and pieces together stuck into protoboards. For example, I had to move the RF pin as far from the encoder pins as I could in order to not confuse the encoder code. The frequency kept changing on its own until I turned off the RF. There is a fair amount of "hash" in the receiver audio being generated by the LCD controller chip. But as far as a proof-of-concept impelementation, I could not be happier.
With this success, I plan to put together a complete (simple) all-band transceiver using a propeller as the controller as well as RF generation component for both the transmitter oscillator as well as the local oscillator for the receiver.
Tuesday, February 28, 2012
Monday, February 27, 2012
Rotary Encoders on Propeller
This evening, I have been playing with Eldon's code to handle rotary encoders. Works nicely as long as the encoder inputs to the propeller have pull-up resistors and a capacitor to ground to make a little integrator. Helps with debouncing the encoder output.
So far the LCD driver is working nicely for both I2C displays and parallel displays. Eldon seems to have switched over to using mine.
I will next work up some code to use the encoder for frequency selection. Everyone else is well beyond this point, but I have focused mostly on the WSPR encoder solution whilst everyone has been continuing on with UI and other concerns. I will catch up here soon...
So far the LCD driver is working nicely for both I2C displays and parallel displays. Eldon seems to have switched over to using mine.
I will next work up some code to use the encoder for frequency selection. Everyone else is well beyond this point, but I have focused mostly on the WSPR encoder solution whilst everyone has been continuing on with UI and other concerns. I will catch up here soon...
Sunday, February 26, 2012
Propeller LCD and RTC work (cont.)
In a previous post, I mentioned that I was working on an I2C LCD driver. I have decided to support both I2C and parallel interfaces to common LCD displays that utilize the popular Hitachi HD44780 controller chip.
This work is largely completed at this point although there is some clean-up work to be done. One of the dilemmas I face is the question of how to implement cursor positioning. It is simple enough to envision an API that sets the cursor to some row/column pair, but should those rows and columns be zero-based or one-based values?
I have worked forever on systems wherein all such values are zero based. There does appear to be however some precident for using one-based values. For the moment, I have implemented two APIs, one that is zero-based and the other one-based. Perhaps that is an acceptable, albeit confusing solution.
I welcome any thoughts anyone might have on the topic.
This work is largely completed at this point although there is some clean-up work to be done. One of the dilemmas I face is the question of how to implement cursor positioning. It is simple enough to envision an API that sets the cursor to some row/column pair, but should those rows and columns be zero-based or one-based values?
I have worked forever on systems wherein all such values are zero based. There does appear to be however some precident for using one-based values. For the moment, I have implemented two APIs, one that is zero-based and the other one-based. Perhaps that is an acceptable, albeit confusing solution.
I welcome any thoughts anyone might have on the topic.
Saturday, February 25, 2012
Propeller Beacon on the air on 30 metres
I am waging war on the amount of clutter in my shack this weekend. It has gotten to where I could hardly get in the place. I have put the propeller beacon up on 30 metres on WSPR, QRSS and Opera. It runs on a 10 minute cycle, WSPR first, QRSS then Opera and then it waits for the end of the 10 minute cycle and repeats.
Let me know if you "hear" it on the air.
Let me know if you "hear" it on the air.
Thursday, February 23, 2012
Propeller LCD and RTC work
Sorry for no updates for a while as I have been ill unfortunately. Starting to feel better now however so I am back playing around with propeller. I have a DS1307 real-time clock module (RTC) and the I2C LCD module from my Arduino beacon project that I have been wanting to get working.
The RTC was a complete no-brainer, it just works. I plan to use it to allow atonomous operation of my beacons that need accurate time information such as WSPR.
The LCD however was a bit of a problem as I am not happy with the display drivers that are out there and have not found an acceptable I2C implementation for any display that I care for.
So, I ported the driver I was using for Arduino to spin and have it working now at least at a macro level. I have not tested the functionality fully yet. I am quite pleased with the initial performance.
The plan is to make a rather comprehensive driver that will work with either I2C or parallel mode, though I only will be using I2C. Above you can see it driving my 20 character by 4 line display. The I2C bit is implemented in the driver via bit-banging. This allows the driver to be independent of any other I2C library. The SDA and SCL pins can be specified with the default to share the I2C pins with the EEPROM.
On other fronts I am putting together a low pass filter module that uses six relay selectable low pass filters for the HF Bands 160 - 10 metres. 10 bands are covered with 6 filters. Attenuation in the stop band should exceed -40dB. I anticipate using an 8 bit I2C I/O expander, six bits of which will be used select the appropriate filter for the following bands: 160, 80, 60/40, 30/20, 17/15, 12/10 metres. I anticipate using the remaining bits for transmit/receive switching and antenna auto-tuner control. More to come on this.
The RTC was a complete no-brainer, it just works. I plan to use it to allow atonomous operation of my beacons that need accurate time information such as WSPR.
The LCD however was a bit of a problem as I am not happy with the display drivers that are out there and have not found an acceptable I2C implementation for any display that I care for.
So, I ported the driver I was using for Arduino to spin and have it working now at least at a macro level. I have not tested the functionality fully yet. I am quite pleased with the initial performance.
The plan is to make a rather comprehensive driver that will work with either I2C or parallel mode, though I only will be using I2C. Above you can see it driving my 20 character by 4 line display. The I2C bit is implemented in the driver via bit-banging. This allows the driver to be independent of any other I2C library. The SDA and SCL pins can be specified with the default to share the I2C pins with the EEPROM.
On other fronts I am putting together a low pass filter module that uses six relay selectable low pass filters for the HF Bands 160 - 10 metres. 10 bands are covered with 6 filters. Attenuation in the stop band should exceed -40dB. I anticipate using an 8 bit I2C I/O expander, six bits of which will be used select the appropriate filter for the following bands: 160, 80, 60/40, 30/20, 17/15, 12/10 metres. I anticipate using the remaining bits for transmit/receive switching and antenna auto-tuner control. More to come on this.
Saturday, February 18, 2012
Keyer / Transmitter bugs found by Eldon, WA0UWH
In my previous post about my Propeller keyer / transmitter, my friend Eldon has been kind enough to point out a logic error that I had in my sendData code. The effect of the bug would be felt if you attempted to change the pin numbers that the dit and dah paddles were connected to. Nice catch Eldon!
I have updated the code to fix this error. At the same time, at Eldon's suggestion, I have also updated the code to make it more of a spin object. I have added start and stop methods that allow it to run on a separate cog. I have also made most of the keyer parameters externally readable and writeable by adding get and put methods for each. For example the frequency can be read or written with getFreq/putFreq. Have a look at the code for the entire list of get/put functions. I also renamed the file from keyer.spin to ko7mKeyer.spin in order to try and avoid naming conflicts.
Eldon has implemented a UI in front of this code using a propeller USB prototype board. Here is his implementation. Pretty nice, I think. He can change the frequency and keyer speed through his rotary encoders and display everything on an LCD. Nice little transmitter, Eldon.
I have updated the code to fix this error. At the same time, at Eldon's suggestion, I have also updated the code to make it more of a spin object. I have added start and stop methods that allow it to run on a separate cog. I have also made most of the keyer parameters externally readable and writeable by adding get and put methods for each. For example the frequency can be read or written with getFreq/putFreq. Have a look at the code for the entire list of get/put functions. I also renamed the file from keyer.spin to ko7mKeyer.spin in order to try and avoid naming conflicts.
Eldon has implemented a UI in front of this code using a propeller USB prototype board. Here is his implementation. Pretty nice, I think. He can change the frequency and keyer speed through his rotary encoders and display everything on an LCD. Nice little transmitter, Eldon.
Wednesday, February 15, 2012
FM Receiver with Propeller
Today I received by the evening post a package containing a little break-out board containing an RDA5807SS FM receiver chip. This is a very cute little module that implements a complete FM sterero receiver.
The device sports a standard 3.5mm stereo audio jack on the top.
Hookup is completely trivial with only I2C SDA/SCL pins, VDD/VSS power connections. Four wires and you are done. Could not be simpler, no soldering involved unless you want to.
Fun little project, if you want immedicate success, give it a go! I leave it playing in the background whilst I work on other programming projects. The product information page at the Parallax site is here where you can find all the documentation as well.
The device sports a standard 3.5mm stereo audio jack on the top.
Hookup is completely trivial with only I2C SDA/SCL pins, VDD/VSS power connections. Four wires and you are done. Could not be simpler, no soldering involved unless you want to.
Fun little project, if you want immedicate success, give it a go! I leave it playing in the background whilst I work on other programming projects. The product information page at the Parallax site is here where you can find all the documentation as well.
Iambic Keyer / Transmitter update
I have posted a simple update to the keyer / transmitter project on Github. The change allows you to specify which sideband you wish to use for the transmitter offset. You specify the frequency the receiver will use and how much you want to offset your transmit frequency. The idea is to be able to hear your own transmitter in the receiver as a sidetone. By default I use 600 Hz offset from the receiver frequency. When I drop down below 30 metres, I typically use lower sideband (LSB) and offset 600 Hz below the receiver frequency and on 30 metres or above, I offset 600 Hz above. This can now be changed by setting the sideband value to LSB or USB as appropriate. Previously to achieve the same effect, you would have had to change the toneOffset value to +600 or -600 as appropriate.
Sunday, February 12, 2012
Propeller Iambic Keyer or Transmitter
I have put together a simple keyer or CW transmitter using the Parallax Propeller board. This is sample code that you may find useful for your own experimentation.
To use as a keyer, set Frequency to zero and attach a keyer circuit to the pin defined by keyPin. I suggest using a 2N7000 or VN10 to drive most modern commercial CW transmitters. Something like this should be adequate. The code supports active high or active low keying.
A speaker can be used on RFPin in this mode to allow hearing a sidetone if desired. By default a 600 Hz tone should be heard. A two paddle key is attached between ditPin/dahPin and ground. I use P0, P1 for the paddle, P2 for the keyer output and P27 for RF/Speaker output. Please change to suit your needs.
To use as a transmitter, set Frequency to the transmit frequency. A 600 Hz offset is used by default to allow the transmitter to be heard in the receiver as sidetone. Change the toneFreq constant if you wish to have a different offset. Change ErrorOffset to correct for any transmit frequency error your propeller may experience. This tuning to set the ErrorOffset value should only need be done once by zero beating a frequency standard such as WWV.
A low pass filter for the transmit frequency should be connected to RFPin in order to clean up the square wave output sufficiently to meet regulations about signal purity. I recommend a minimul of a 5 pole filter as described in earlier posts.
Currently only Iambic Mode A is supported. Future versions will include the ability to set the keyer parameters such as keying speed, character speed, frequency and RF pin assignments as well as changing Iambic modes via the paddles.
Here is a photograph of the current lash-up with my Vibroplex paddles hooked to P0 and P1.
The source code can be obtained from https://github.com/ko7m/Keyer. As always comments and suggestions are solicited.
To use as a keyer, set Frequency to zero and attach a keyer circuit to the pin defined by keyPin. I suggest using a 2N7000 or VN10 to drive most modern commercial CW transmitters. Something like this should be adequate. The code supports active high or active low keying.
A speaker can be used on RFPin in this mode to allow hearing a sidetone if desired. By default a 600 Hz tone should be heard. A two paddle key is attached between ditPin/dahPin and ground. I use P0, P1 for the paddle, P2 for the keyer output and P27 for RF/Speaker output. Please change to suit your needs.
To use as a transmitter, set Frequency to the transmit frequency. A 600 Hz offset is used by default to allow the transmitter to be heard in the receiver as sidetone. Change the toneFreq constant if you wish to have a different offset. Change ErrorOffset to correct for any transmit frequency error your propeller may experience. This tuning to set the ErrorOffset value should only need be done once by zero beating a frequency standard such as WWV.
A low pass filter for the transmit frequency should be connected to RFPin in order to clean up the square wave output sufficiently to meet regulations about signal purity. I recommend a minimul of a 5 pole filter as described in earlier posts.
Currently only Iambic Mode A is supported. Future versions will include the ability to set the keyer parameters such as keying speed, character speed, frequency and RF pin assignments as well as changing Iambic modes via the paddles.
Here is a photograph of the current lash-up with my Vibroplex paddles hooked to P0 and P1.
The source code can be obtained from https://github.com/ko7m/Keyer. As always comments and suggestions are solicited.
Thursday, February 9, 2012
A very interesting chip
I have been poking around looking at floating point processors and have come across a very interesting chip that I have bookmarked for future investigation, the Micromega uM-FPU64. So far I have not been able to find anyone with stock on this chip other than the earlier V3.1 32 bit version. For USD25.00 in single unit quantities, this is quite the bargain.
64-bit and 32-bit Floating Point
A comprehensive set of 64-bit and 32-bit floating point operations are provided.
See the uM-FPU64 datasheet for details.
64-bit and 32-bit Integer
A comprehensive set of 64-bit and 32-bit integer operations are provided.
See the uM-FPU64 datasheet for details.
User-defined Functions
User-defined functions can be stored in Flash memory. Flash functions are programmed through the SERIN/SEROUT pins using the uM-FPU64 IDE. A high level language is supported, including control statements and conditional execution.
Matrix Operations
A matrix can be defined as any set of sequential registers. The MOP instruction provides scalar operations, element-wise operations, matrix multiply, inverse, determinant, count, sum, average, min, max, copy and set operations.
FFT Instruction
Provides support for Fast Fourier Transforms. Used as a single instruction for data sets that fit in the available registers, or as a multi-pass instruction for working with larger data sets.
Serial Input / Output
When not required for debugging, the SERIN and SEROUT pins can be used for serial I/O. A second asynchronous serial port, with hardware flow control, is also available as a local device using the DEVIO instruction.
NMEA Sentence Parsing
The serial input can be set to scan for valid NMEA sentences with optional checksum. Multiple sentences can be buffered for further processing.
String Handling
String instructions are provided to insert and append substrings, search for fields and substrings, convert from floating point or long integer to a substring, or convert from a substring to floating point or long integer. For example, the string instructions could be used to parse a GPS NMEA sentence, or format multiple numbers in an output string.
Table Lookup Instructions
Instructions are provided to load 32-bit values from a table or find the index of a floating point or long integer table entry that matches a specified condition.
MAC Instructions
Instructions are provided to support multiply and accumulate and multiply and subtract operations.
A/D Conversion
Multiple 12-bit A/D channels are provided (six on 28-pin device, nine on 44-pin device). The A/D conversion can be triggered manually, through an external input, or from a built-in timer. The A/D values can be read as raw values or automatically scaled to a floating point value. Data rates of up to 10,000 samples per second are supported.
Real-Time Clock
A built-in real-time clock is provided, for scheduling events or creating date/time stamps.
Timers
Timers can be used to trigger the A/D conversion, or to track elapsed time. A microsecond and second timer are provided.
External Input
An external input can be used to trigger an A/D conversion, or to count external events.
Foreground/Background Processing
Event driven foreground/background processing can be used to provide independent monitoring of local peripherals. The microcontroller communicates with the foreground, while background processes can be used to monitor local device activity.
Local Device Support
Local peripheral device support includes: RAM, 1-Wire, I2C, SPI, UART, counter, servo controller, LCD, and VDrive2 devices. The uM-FPU64 can act as a complete subsystem controller for GPS, sensor networks, robotic subsystems, IMUs, and other applications. Local devices are assigned to digital I/O pins at run-time, and controlled with the DEVIO instruction.
Low Power Modes
When the uM-FPU64 chip is not busy it automatically enters a power saving mode. It can also be configured to enter a sleep mode which turns the device off while preserving register contents. In sleep mode the uM-FPU64 chip consumes negligible power.
Firmware Upgrades
When updates become available, the uM-FPU64 firmware can be upgraded in the field using the uM-FPU64 IDE software.
Features
See the uM-FPU64 datasheet for details.
See the uM-FPU64 datasheet for details.
Tuesday, February 7, 2012
WSPR Encoder - Version 1
Updated this posting 8 Feb 2012
I have decided not to release my WSPR encoder for Propeller on the Object Exchange at the Parallax web site and instead I created a Github project repository for it. I am releasing it free of charge under the same MIT License that is used at the Object Exchange however.
You can obtain the files at https://github.com/ko7m/WSPR. Looking at this web page, you will see the following:
By clicking on any of the files you can examine them or you can get it all in one go by clicking on "ZIP" near the top and it will bundle up a zip archive and push it down to you.
As always, I welcome feedback or questions. I hope you find it useful.
I have decided not to release my WSPR encoder for Propeller on the Object Exchange at the Parallax web site and instead I created a Github project repository for it. I am releasing it free of charge under the same MIT License that is used at the Object Exchange however.
You can obtain the files at https://github.com/ko7m/WSPR. Looking at this web page, you will see the following:
By clicking on any of the files you can examine them or you can get it all in one go by clicking on "ZIP" near the top and it will bundle up a zip archive and push it down to you.
As always, I welcome feedback or questions. I hope you find it useful.
Monday, February 6, 2012
Another flying diversion
Last week was a total bust for me mostly due to being sick all week. I think I get to blame my grandson for the cold I caught and the associated sinus infection.
Saturday however was one of those phenomenal clear sky, million-mile visibility days with low wind speeds. I decided that if I had to hire someone to fly me, I was going to take to the skies.
Fortunately, a good buddy of mine has a 1948 NAvion L-17-B and had a shout out to anyone wanting to ride along. Sold! My poor little super cub will have to stay in the hanger a little longer until my head is more clear I am afraid.
We left our home field (Harvey Field - S43) in Snohomish, WA and flew to the Olympic Penninsula and cruised the length of Hood Canal stopping for lunch at Shelton (KSHN) at the cafe in the parachute jump centre.
Hood canal runs against the east side of the Olympic Mountains, which I think are particularly beautiful.
After a nice lunch, we decided to head on out to the Pacific Ocean coast and head north. We flew the length of the Washington coast from about Pacific Beach all the way to Neah Bay.
The wind was a bit brisk onshore as evidenced by the breakers on the shallow beach.
We had to circle a few islands off the coast. Pretty rugged coastline with very few options for an emergency landing.
Here we are rounding the bend around Tatoosh Island off Cape Flattery. This is the most north-western point of the United States. I am actually surprised that any of these pictures turned out because as we made this turn, we were just getting slammed around by the wind. Thousands of miles of Pacific Ocean winds from Canada and the Washington coast converging and trying to stuff through the Straits of Juan de Fuca. We picked up a lot of ground speed as we turned east down the Straits.
Looking back south down the Pacific Coast. Sun is dropping towards the horizon.
Crossing Puget Sound from the West, Mt. Rainier in the distance about 10 miles from Harvey Field. On days like this, there is no finer place to fly than the Pacific Northwest.
If you would like to see all of my pictures of the trip, here is a link. About three hours of flying and endless beauty.
Saturday however was one of those phenomenal clear sky, million-mile visibility days with low wind speeds. I decided that if I had to hire someone to fly me, I was going to take to the skies.
Fortunately, a good buddy of mine has a 1948 NAvion L-17-B and had a shout out to anyone wanting to ride along. Sold! My poor little super cub will have to stay in the hanger a little longer until my head is more clear I am afraid.
We left our home field (Harvey Field - S43) in Snohomish, WA and flew to the Olympic Penninsula and cruised the length of Hood Canal stopping for lunch at Shelton (KSHN) at the cafe in the parachute jump centre.
Hood canal runs against the east side of the Olympic Mountains, which I think are particularly beautiful.
After a nice lunch, we decided to head on out to the Pacific Ocean coast and head north. We flew the length of the Washington coast from about Pacific Beach all the way to Neah Bay.
The wind was a bit brisk onshore as evidenced by the breakers on the shallow beach.
We had to circle a few islands off the coast. Pretty rugged coastline with very few options for an emergency landing.
Here we are rounding the bend around Tatoosh Island off Cape Flattery. This is the most north-western point of the United States. I am actually surprised that any of these pictures turned out because as we made this turn, we were just getting slammed around by the wind. Thousands of miles of Pacific Ocean winds from Canada and the Washington coast converging and trying to stuff through the Straits of Juan de Fuca. We picked up a lot of ground speed as we turned east down the Straits.
Looking back south down the Pacific Coast. Sun is dropping towards the horizon.
Crossing Puget Sound from the West, Mt. Rainier in the distance about 10 miles from Harvey Field. On days like this, there is no finer place to fly than the Pacific Northwest.
If you would like to see all of my pictures of the trip, here is a link. About three hours of flying and endless beauty.
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.
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?
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.
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.
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.
- 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.
- 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.
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:
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.
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:
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:
In reading the specification, I see the following:
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:
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:
Unanswered Questions
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:
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.
<...> 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
<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
<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.
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:
- 28 bits encodes a normal 6 character callsign
- 15 bits encodes a 4 character locator
- 7 bits encodes the power in dBm.
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.
Unanswered Questions
- 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.
- What is the precise encoding of the "/" character?
- Precisely how to calculate the 15 bit hash of the callsign?
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:
- The six digit locator is encoded into the 28 bit callsign field.
- The 15 bit hash is computed of the callsign and sent in the locator 15 bit field.
- 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.
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.
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...
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!
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!
Subscribe to:
Posts (Atom)