WSPR-Transceiver mit Arduino
Der vorhandene CW-QRP-Transceiver auf der Basis des
SDR-Arduino-Shields soll nun erweitert werden, sodass er selbständig
WSPR-Signale senden kann. Der Raspberry-Sender wird dann überflüssig.
Außerdem soll erreicht werden, dass der Empfange und das Senden
koordiniert ablaufen, wie es vom WSPR-Decoder vorgesehen ist. Der PC
muss dazu den Sender zur richtigen Zeit starten können.
Das Projekt wurde möglich, als ich das WSPR-Projekt von Jason Milldrum in Github entdeckt habe: https://github.com/etherkit/JTEncode Derselbe Autor hat auch schon die Bibliothek <si5351.h> geschrieben, die im Gerät schon verwendet wird.
// Simple JT65/JT9/WSPR/FSQ beacon for Arduino, with the Etherkit
// Si5351A Breakout Board, by Jason Milldrum NT7S.
Die
Umsetzung ist mit dem in Github beiliegenden Beispiel einfach. Weil das
WSPR-Signal eine Frequenzumtastung mit vier nahe beieinanderliegenden
Frequenzen verwendet, ist der SI5351 bestens geeignet, und auch der
Sendeverstärker ist ideal für die Aufgabe. Im ersten Versuch
wurden nur minimale Änderungen am Beispiel durchgeführt und der Ausgang
CLK2 verwendet. Bei der späteren Integration in den CW-Sender
zeigte sich ein Problem: Der Encoder verträgt sich nicht mit der
LCD-Library. Die Lösung besteht darin, die nötigen Daten zunächst ohne
das LCD zu kodieren und dann als Konstanten zu verwenden. Entsprechende
Print-Befehle wurden in die Sende-Funktion eingebaut. Im Terminal
empfängt man dann 255 mit Kommas getrennte Zahlen 0...3, die für die
vier FSK-Frequenzen stehen. Diese Zahlenfolge muss dann in das Programm
eingefügt werden.
Download: Si5351WSPR1.zip
// Global variables
unsigned long freq;
char message[] = "CALL1 XX00";
char call[] = "CALL1"; //das Rufzeichen
char loc[] = "XX00"; //der Lacotor wie z.B. JO31";
uint8_t dbm = 10; //10 mW, 30 für 1W, 37 für 5 W
uint8_t tx_buffer[255];
enum mode cur_mode = DEFAULT_MODE;
uint8_t symbol_count;
uint16_t ctc, tone_spacing;
uint8_t tx_buffer2[255] = {1,3,2,0,2,0,2,0,3,0,0,2,3,3,3,0,2,2,3,0,0,1,0,3,3,3,3,2,0,0,2,
2,2,0,3,2,0,1,2,1,0,0,2,2,2,0,1,0,3,1,0,2,1,3,2,3,2,2,2,3,1,2,3,0,2,0,0,1,1,2,3,2,3,0,1,2
,3,0,0,1,0,2,1,2,3,1,2,0,2,1,3,2,1,2,1,2,2,0,3,0,2,2,2,0,1,2,2,1,0,2,3,3,3,2,3,3,0,0,3,3,2
,1,2,2,0,3,1,1,0,2,0,2,0,3,0,3,2,2,3,3,0,0,0,2,0,0,0,1,1,2,1,0,1,1,2,0,2,3,3,2,0,0);
...
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
//tx_buffer = tx_buffer2;
// Reset the tone to the base frequency and turn on the output
si5351.output_enable(SI5351_CLK2, 1);
digitalWrite(LED_PIN, HIGH);
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((freq * 100) + (tx_buffer2[i] * tone_spacing), SI5351_PLL_FIXED, SI5351_CLK2);
Serial.print (tx_buffer[i]); Serial.print (",");
proceed = false;
while(!proceed);
}
Das
Beispiel enthält ein verändertes Rufzeichen und einen veränderten
Locator, damit nicht versehentlich jemand mein Rufzeichen in die Luft
sendet. Man beachte, dass der Betrieb auf den
Amateurfunkfrequenzen nur für lizensierte Funkamateure erlaubt ist. Mit
dem Fake-Rufzeichen kann man aber immerhin schon Versuche ohne Antenne
direkt neben einem Empfänger durchführen. Denkbar ist auch, dass man
mit kleiner Leistung bis 100 mW auf 13,56 MHz oder 27,12 MHz
experimentiert. Ob das mit Eigenbaugeräten legal ist, müsste noch
genauer untersucht werden. Eine weitere interessante ISM-Frequenz ist
6,78 MHz.
In der endgültigen Software für CW und WSPR ist
jtencode.wspr_encode(call, loc, dbm, tx_buffer) auskommentiert.
Stattdessen wird die vordefinierte WSPR-Nachricht in tx_buffer2[255]
verwendet. Deshalb kann das LCD-Shield verwendet werden um die aktuelle
Frequenz anzuzeigen. Die Taste S1 wählt wie bisher die WSPR-Frequenzen
auf den einzelnen Bändern aus. Die Taste S2 startet einen
WSPR-Sendelauf von zwei Minuten. Beim Senden müssen das Antennenrelais
und die Träger-Tastung eingeschaltet werden.
Man kann den
Sender manuell mit der Taste S2 starten und braucht dazu nur eine Uhr,
damit man genau zu einer geraden Minute beginnt. Das Startsignal kann
aber auch von der WSPR-Software kommen. Dazu dient ein keines Interface
mit einem Transistor zwischen der seriellen Schnittstelle und dem
Eingang A2, an dem auch die Taste S2 liegt.
Unter
den WSPR-Einstellungen habe ich festgelegt, dass die DTR-Leitung an
COM1 den WSPR-Sender starten soll. In der Software kann man dann z.B.
einstellen, dass der Sender 20% der Zeit laufen soll, also viermal
empfangen und einmal senden. So erreiche ich endlich den vorgesehenen
Modus, bei dem man auf der gleichen Frequenz auch hört. Interessant
sind nämlich besonders die Fälle, wo eine Station sowohl erreicht aus
auch empfangen werden kann. In dem Fall ist es spannend, Leistungen und
Empfangsergebnisse zu vergleichen.
Mit
dem Gerät verwende ich jetzt meist eine Leistung von 33 dBm (2 W). Man
kann schon sehen, dass das besser geht als die bisherigen 100 mW. Im
WSPR-Betrieb verwende ich getrennte Antennen für Senden und Empfang.
Die Dipolantenne im Garten bringt auf allen Bändern den besseren
Störabstand, während die Vertikalantenne sich besser als Sendeantenne
anpassen lässt. Das funktioniert jetzt sehr gut zwischen 80 m und 20 m.
Im Empfangsverlauf sieht man die Sende-Phasen als hellgrüne Striche, in
denen nicht empfangen wird. Das Beispiel zeigt etwa eine Stunde
Sende-und Empfangsbetrieb im 80m-Band am Abend.
Download: Si5351vfo10WSPR2.zip
// Loop through the string, transmitting one character at a time.
void encode()
{
uint8_t i;
// Set up Timer1 for interrupts every symbol period.
noInterrupts(); // Turn off interrupts.
TCCR1A = 0; // Set entire TCCR1A register to 0; disconnects
// interrupt output pins, sets normal waveform
// mode. We're just using Timer1 as a counter.
TCNT1 = 0; // Initialize counter value to 0.
TCCR1B = (1 << CS12) | // Set CS12 and CS10 bit to set prescale
(1 << CS10) | // to /1024
(1 << WGM12); // turn on CTC
// which gives, 64 us ticks
TIMSK1 = (1 << OCIE1A); // Enable timer compare interrupt.
OCR1A = ctc; // Set up interrupt trigger count;
interrupts(); // Re-enable interrupts.
digitalWrite (txOn,1); //Relais
digitalWrite (keyOut,1); //keyOut
delay (20);
// Reset the tone to the base frequency and turn on the output
si5351.output_enable(SI5351_CLK2, 1);
// Now transmit the channel symbols
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq(((freqHz+1460) * 100) + (tx_buffer2[i] * tone_spacing), SI5351_PLL_FIXED, SI5351_CLK2);
proceed = false;
while(!proceed);
}
// Turn off the output
si5351.output_enable(SI5351_CLK2, 0);
digitalWrite (keyOut,0); //keyOut
digitalWrite (txOn,0); //Relais
delay(5000);
}
void morseKey(void)
{
timeTx = 100;
digitalWrite (txOn,1); //Relais
delay (20);
si5351.output_enable(SI5351_CLK2, 1);
while (timeTx > 0){
if (digitalRead(key) == 0) {
timeTx = 100;
digitalWrite (keyOut,1); //keyOut
analogWrite(buz, 2);
delay (5);
}
if (digitalRead(key) == 1) {
digitalWrite (keyOut,0); //keyOut
analogWrite(buz, 0);
delay (5);
timeTx--;
}
}
digitalWrite (txOn,0); //Relais
}
Mit 2 W im 30m-Band