Arduino Reed-Solomon Identifier

for Albrecht 2990-handheld.IAlbrecht2.jpg


Purpose for this project is to make Albrecht 2990afs -handheld amateur transceiver able to send some kind of "tactical messages" by pressing ptt-button shortly. These quick messages are then received, used and stored in my home base. Project also includes bluetooth-interface between Albrecht and android-phone or tablet, to be used with AndPskmail-software, but it is another story.

When this Albrecht is closed there is nothing special to see, but if PTT-button is pressed once shortly, rig will transmit first rsid for cw (morse), then my hamcall and number '1' with morse code. (Actually number one is repeated four times). If PTT is pressed shortly twice, number transmitted will be '2' and so on. If PTT is hold down longer than about one second, no special transmissons are send. So it works just normal way on voice conversations.

Morse code transmitted by Albrecht is received with normal transceiver and FLdigi-software. In FLdigi's "Notifications" you can then order FLdigi to for example start speak synthesizer and say something when '1111' is found on received text. FLdigi can start external programs so there is no limit what you can start/do/send/adjust with this system.

Dealing with SSB


Albrecht 2990 is light and cheap transceiver. Same radio is sold under many brands, for example "Magnum 1012". Interestingly it have also SSB capability. It is maybe not best rig for serious digimode-use, it seems to suffer some frequency instability, but it is so fun to play with radio which you can carry anywhere.

Shortly SSB (single sideband) gives significantly longer range for communication than FM or AM modes. But problem is, it is tricky to use. For example when transceivers frequency drifts, also audio received moves to another audio frequency. It is not problem if there is two human operators talking eac others, since they can adjust receiving frequency a little. But if you want to transmit signal to be received automatically, signal drifting have to be handled some way.

One solution for drifting-audio problem is "Reed-Solomon Identifier" which was originally introduced, I believe, on "Multipsk"-program by Patrick Lindecker, F6CTE.

Another solution would be to just use brutal power and decode wide enough range of audio received. I suppose Reverse Beacon Network use this kind of solution, so it obviously can be done.


Reed-Solomon Identifier, rsid, is serial of tones which order receiving program like FLdigi to tune on right frequency and start right software modem for example cw (morse) or some other. There are tens of different digimodes used on amateur radio. Shortly digimode is some way to send message, usually some text, coded in audio tones or some other way.

Rsid is really a short message transmitted on MFSK16-mode. It consist number which defines digimode used in transmission which will become next. Number is coded with powerful Reed-Solomon error correction, here comes confusing name. Even more confusing: when transmitted this code is often called txid, but the name of same code when received is rxid... maybe name changes at halfway between transmitter and receiver?
If you wonder what is MFSK16 here is introduction by Murray Greenman, ZL1BPU.

I didn't actually solve that reed-solomon code. I just transmitted rsid from one instance of FLdigi to another one and checked tone frequencies from waterfall. Other needed knowledge, about timing and so on I found from website of W1HKJ
Lazy me...By the way, rsid produced by my code works but it is not guaranteed that tones are exactly right. It may work even if a bit wrong because of powerful error-correction. If you have more knowledge about that subject please let me know and put some message here. (Or to myhamcall(at)gmail.com) (You will find my hamcall in these pages)

Albrecht2990.JPG
Here is close-up picture.This little beast is build around tiny Arduino Nano clone, brand is "Robox". It just fits in empty space inside Albrecht.(Nano would not fit.) Strangely shield of Albrecht even have hole for reset button of Robox...in just right place! There is also enough room for bluetooth chip from hands-free headset. It can be mounted under Robox. (I had one working, but while this picture was taken it was broken, supposedly by overheating. Most likely I will return to this subject later.)
Arduino Pro-series is even smaller than Robox, but I wanted this thing to have real usb-connector, so I ended up to this.
This hole brick can be pushed inside shield of Albrecht. It is normally insulated with vinyl tape which I taked away for this picture. I used a lot of time to thinking how to fasten Robox inside Albrecht, but after all I find It just do not have space to move...so it is not fastened at all. Just pushed inside.

On hardware side Robox keys Albrecht up to transmit by transistor. Robox inject little base current to transistor which is connected between PTT-line and ground in Albrecht. Reason for transistor is that PTT-line have over 5v voltage. Audio produced by Robox is feeded to mic line (which is in fact same as mentioned PTT-line) through a simple low-pass rc-filter, voltage divider (roughly 1/10 ) and series capasitor. I dont know if that rc-filter is really needed, PCM-audio is produced at 62,5 khz frequency and rig has anyway filtering on audio side. Also 1500hz audio freq used for rsid is choosed to be high enough for 2x harmonic to be cut by rig. I can not see any difference on received audio if rc-filter is used or not, but dont have real measure gear either, only waterfallwiev on SpectrumLab-software.


PCM audio


There is better explanations of PCM-audio on internet as I can write. Shortly basic Arduino can not produce real audio signal. With tone() and many other beeping audio codes like turning pin high and low frequently Arduino pruduces square wawe. This works fine if you want to produce some tone beeps for loudspeaker. But for radio transmissions this kind of audio is not really suitable. They produce a lot of harmonics, bad signal quality and spreading radiosignal to wider frequency than wanted. If square waves are used, heavy filtering is needed.
PCM-audio is made by running pin fast (as fast as possible) in pulse width modulation mode, and audio is produced by chancing pulse width ( on/off relationship) continuously with analogWrite().

Also when proper MFSK-signal is wanted, it is just not putting right tones continuing each others, transmission changes should happen without sudden changes in phase or amplitude. This can be done by this PCM-audio code. It is based on Max Pierson's work. (There is also good explanation of PCM.) Main differences on Max's and my code is that timer 1 prescaler is set off instead of 8, and sine wavetable have only 18 entries. Reason for both is to get enough frequency possibilities. Now difference of OCR1A values (for example) 500 and 501 is roughly audio 1776hz and 1772,5 hz. So you are able to choose frequency in steps of 3-4hz. In mfsk16-mode smallest difference between tones is 10,766 hz. I dont know 3-4 hz is precice enough, we could made better with shorter sinewave table, but at cost of little lower audio quality. Maybie you can try it :) Or made some real digimode-transmitter also. MFSK or some other mode :D It would need some character-coding logic.

One note about audio frequency. At code there is formula 888000/OCR1A=audio frequency. When I first time tried code, I just butted randomly 444 to OCR1A value and noted that it produces about exactly 2000hz tone. So this formula is not calculated, it is not based on anything...OCR1A values on code are then calculated by this formula to produce rsid at center freq of 1500hz. In my hardware FLdigi says freq is 1496 hz, so it is near enough for me. (In Albrecht you can choose transmitting frequency at steps of 500 hz and my one seems to be 300 hz off frequency on 10 meter band...)

Morse library used here is excellent Raron's Morse EnDecoder. Morse code produced by code below is actually "two-tone morse", continous tone where another frequency is "morse" and another one is produced only to keep amplitude and phase somewhat continous. It works better this way on my Albrecht 2990 on SSB.

So code below produces rsid for cw around 1500hz, and some morse characters with pulse code modulated audio. I ripped most of hardware-specific code out from this code. But of course something had to left. I suppose if you load this code to arduino uno or similar and call cwRsid() somehow, you should get PCM-modulated rsid from pin 3.

One more note! Code I use on my Albrecht puts Robox to sleep while nothing is happening. It wakes only wen ptt is pressed or code is produsing transmission. Running Arduino inside Albrecht produce hard interference on some frequencies, this can be avoided with sleepmode while receiving.

All the best!

Tarmo, OH6ECF

/*
From OH6ECF, Tarmo Huttunen
This code introduce use of pcm-audio
to generate reed-solomon identifier (rsid) for cw and some morse-signals.
Use as you like, make it better and please share.
Pcm-audio on this code is based heavily on Max Pierson's work. (blog.wingedvictorydesign.com)
Morselibrary morsEnDecoder is work of "Raron" (https://code.google.com/p/morse-endecoder/
and  http://raronoff.wordpress.com/2010/12/16/morse-endecoder/)
*/
 
#include <MorseEnDecoder.h>
#include <avr/pgmspace.h>
morseEncoder morseOutput(4);//Pin4=morse output
 
 //a sine wave with 18 samples
char sine[] = {0, 44, 82, 111, 126, 126, 111, 82, 44, 0, -44, -82, -111, -126, -126, -111, -82, -44,};
 
 
 
byte speakerpin = 3;  //audio playback on pin 3.  This can also be set to pin 11.
 
volatile byte waveindex = 0; //index variable for position in waveform array Sine[]
volatile byte currentvalue = 0;
unsigned long txStart;
unsigned long Stop;
unsigned long txTime;
boolean txFlag = false;
boolean rsidFlag = false;
int morseMessage = 0;
void setup() {
 
 
 
//PWMconfiguration
 
cli(); //disable interrupts while registers are configured
 
bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)
bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);
// set prescaler to /1 (no prescaling).  The timer will overflow every
 
sei(); //enable interrupts now that registers have been set
 
//Timer 1 configuration
 
cli(); //disable interrupts while registers are configured
 
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, WGM10);
bitClear(TCCR1A, WGM11);
bitSet(TCCR1B, WGM12);
bitClear(TCCR1B, WGM13);
 
//the interrupt by writing new values to OCR1A.
bitSet(TCCR1B, CS10);
bitClear(TCCR1B, CS11);
bitClear(TCCR1B, CS12);
//set the clock prescaler to off, for enough frequency resolution.
 
 
 
bitClear(TCCR1C, FOC1A);
bitClear(TCCR1C, FOC1B);
 
 
OCR1A = 1000;
//Initializes Output Compare Register A. If you forget that(like I did), millis() wont work.
 
bitClear(TIMSK1, ICIE1); //disable input capture interrupt
bitClear(TIMSK1, OCIE1B); //disable Output Compare B Match Interrupt
bitSet(TIMSK1, OCIE1A); //enable Output Compare A Match Interrupt
bitClear(TIMSK1, TOIE1); //disable Overflow Interrupt Enable
 
sei(); //enable interrupts now that registers have been set
 
 
 
}//end setup()
 
ISR(TIMER1_COMPA_vect) {
/* timer1 ISR.  Every time it is called it sets
*  speakerpin to the next value in Sine[].  frequency modulation is done by changing
*  the timing between successive calls of this function, e.g. for a 1KHz tone,
*  set the timing so that it runs through Sine[] 1000 times a second. */
if (rsidFlag==true){
  analogWrite(speakerpin, sine[waveindex] + 128);
  waveindex++;
    if (waveindex > 17) { //reset waveindex if it has reached the end of the array
    waveindex = 0;
    }
}
else {analogWrite(speakerpin,0);waveindex = 0;}
}
 
 
 
void loop()
{
 
 
if (digitalRead(11)==LOW && txFlag == false) { //iff pin 11 is low, ptt-button have been pushed down
     delay(100);//ptt debounce
     txStart=millis();
     txFlag=true;
}
 
  if (digitalRead(11)==HIGH && txFlag == true){
    delay(100);//ptt debounce
    Stop=millis();
    txFlag=false;
    txTime=Stop-txStart;
    if (txTime < 1000) {morseMessage++; //Count how many clicks is given with ptt-button
      if (morseMessage > 9) {morseMessage = 9;}
    }
    if (txTime >= 1000) {morseMessage=0;} //If ptt is pushed long (normal voice operation), no rsid
  }
 
 
if ((millis()-Stop)>2000 && digitalRead(11)==HIGH){ //rsid/morse transmission starts 2 seconds after last ptt-press
if (morseMessage > 0) {cwRsid();}}
 
 
 
 
}//end loop()
 
void cwRsid(){
 
morseOutput.encode(); //If  morseOutput.encode(); is not called before getting cwRsid(), morseOutput.available() may never be true !
digitalWrite(10,HIGH); //Starts transmitter via transistor (hardware-specific).
rsidFlag=true;// Instruction for ISR() -function to produce tone.
OCR1A =500;//Pre-tone frecuency(hardware-specific)
delay (1000);//Pre-tone gives a little time for transmitter to set up (hardware-specific).
 
int cwrsid[] = {623,588,572,614,596,580,614,605,596,588,564,564,572,623,580}; //sequence of OCR1A values, giving right tones to produce rsid on audio freq
//  Tone frequency in hz  OCR1A 888 = 1000hz, 444=2000hz,  888000/OCR1A = audio freq,  OCR1A = 888000/freq
unsigned long rsidTime = micros();
 
for( int x = 0; x<15;){
    if (micros()-rsidTime >= 92880){ //Timing for invidual tones in rsid-tonesequence
      rsidTime = micros();
      x++;
      OCR1A = cwrsid[x];
    }
}
 //MORSE ID or something else
char morseCall[] = {'D','E',' ','N','O','C','A','L','L'};//Put your hamcall or some other message here
 
  for (int x = 0; x<9;){//Change x<9 to fit your message. This one is for 9-lettered message/call.
    if (morseOutput.available()==true){ morseOutput.write(morseCall[x]);x++;}
   //If  morseOutput.encode(); is not called before getting cwRsid(), morseOutput.available() will never be true !
    morseOutput.encode();
    if (digitalRead(4)==HIGH){rsidFlag=true;OCR1A=594;} //brutal way to produce morsetone in pcm...hihi..pin4 is defined for output of morseEnDecoder
    else {OCR1A=494;} //for my handheld it works better in ssb if "two-tone" morse is used. This produce alternartive frequensy.
  }
//Morse "message-character" is sent 4 times
 char morseTx[] = {'X',' ','A','B','4','5','6','7','8','9'};
  if (morseMessage > 0){
    for (int x = 0; x<5;){
      if (morseOutput.available()==true){ morseOutput.write(morseTx[morseMessage]);x++;}
     //If  morseOutput.encode(); is not called before getting cwRsid(), morseOutput.available() will never be true !
      morseOutput.encode();
        if (digitalRead(4)==HIGH){rsidFlag=true;OCR1A=594;} //brutal way to produce morsetone in pcm...hihi..pin4 is defined for output of morseEnDecoder
        else {OCR1A=494;} //for my handheld it works better in ssb if "two-tone" morse is used. This produce alternartive frequensy.
   }
  }
 
  rsidFlag=false; // this is instruction for ISR() -function to stop tone
  digitalWrite(10,LOW); //this pin stops transmitter when setted low(hardware-specific). Not needed if VOX used
  morseMessage=0;
  delay(500);//This is to prevent transmission to start another transmission(hardware-specific)
}
 
 
 

comments powered by Disqus