mirror of
https://github.com/JeffersGlass/DDS_VFO.git
synced 2024-11-26 18:27:36 -06:00
Add WSPR and CQ code to VFO.
This commit is contained in:
parent
e2c313f8c6
commit
f7349de7a8
|
@ -43,7 +43,7 @@ char* stepNames[][10] = {
|
|||
{" 10MHz", " 5MHz", " 1MHz", "500Khz", "100KHz", " 10KHz", " 1KHz", " 100Hz", " 10Hz", " 1 Hz"}, //basic
|
||||
{" 10KHz", " 1KHz", " 100 Hz", " 10 Hz"}, //basic
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //polyakov
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //BFO
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //BFO
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //IF
|
||||
};
|
||||
|
||||
|
@ -208,7 +208,7 @@ void loop(){
|
|||
}
|
||||
}
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
}s
|
||||
}
|
||||
|
||||
void displayInfo(){
|
||||
|
@ -321,7 +321,7 @@ void setFrequency_5351(long newFreq){
|
|||
si5351.set_freq((newFreq + ifFreq) * 100UL, 0ULL, SI5351_CLK0); //VFO+IF
|
||||
//VFO-IF
|
||||
//IF-VFO
|
||||
beak;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <LiquidCrystal.h>
|
||||
#include <si5351.h>
|
||||
|
||||
|
||||
//-----------Variables & Declarations---------------
|
||||
/*
|
||||
* The current and desired LISTENING FREQUENCY, which is not always the frequency being output by the Si5351.
|
||||
|
@ -15,45 +14,80 @@
|
|||
|
||||
long currFreq = 1800000;
|
||||
|
||||
//FOR CQ MODE:
|
||||
char CQ[] = "-.-. --.-";
|
||||
char DE[] = "-.. .";
|
||||
char morseCallsign[] = "-.- -.- ----. .--- . ..-.";
|
||||
int morseCallsignLength = 25;
|
||||
long morseElementLength = 70; //ms
|
||||
|
||||
//FOR WSPR MODE
|
||||
|
||||
int correctionFactor = 0; //adjusts the offset of the Si5351
|
||||
int WSPR_TRANSMISSION_DATA[] = { //KK9JEF EN61 30
|
||||
3,3,2,0,0,0,0,0,3,0,0,2,1,1,1,0,0,2,1,2,2,1,0,3,1,3,1,2,2,0,
|
||||
0,0,2,2,1,0,2,3,0,1,2,0,2,2,2,2,3,2,3,1,2,0,1,3,2,3,2,0,2,1,
|
||||
1,0,1,0,0,2,2,1,1,0,1,0,3,0,1,2,1,0,2,3,0,0,1,0,1,1,2,2,2,3,
|
||||
1,0,1,2,3,2,2,0,1,2,2,0,2,0,1,2,2,3,0,2,1,1,1,0,1,3,2,2,3,1,
|
||||
0,1,0,2,2,1,1,1,2,0,0,0,0,3,0,1,0,2,3,1,2,2,2,2,0,2,2,3,1,0,
|
||||
1,2,1,3,2,0,2,3,3,2,0,2};
|
||||
|
||||
/*
|
||||
int WSPR_TRANSMISSION_DATA[] = { //KK9JEF EN61 27
|
||||
3,3,2,0,0,2,0,2,3,0,0,2,1,3,1,2,0,2,1,0,2,3,0,1,1,1,1,0,2,2,
|
||||
0,0,2,2,1,2,2,3,0,1,2,2,2,2,2,0,3,0,3,1,2,0,1,3,2,1,2,0,2,3,
|
||||
1,0,1,2,0,2,2,1,1,0,1,0,3,2,1,0,1,2,2,3,0,0,1,0,1,1,2,2,2,3,
|
||||
1,2,1,2,3,0,2,0,1,0,2,0,2,0,1,2,2,3,0,0,1,1,1,0,1,1,2,0,3,3,
|
||||
0,3,0,0,2,1,1,3,2,0,0,2,0,1,0,3,0,2,3,1,2,2,2,0,0,2,2,3,1,0,
|
||||
1,0,1,1,2,0,2,1,3,0,0,2};
|
||||
*/
|
||||
|
||||
//-----Enumerations of frequency steps and their labels for each mode----//
|
||||
|
||||
enum modes{mode_testing = 0, mode_basic, mode_polyakov, mode_bfo};
|
||||
const int NUM_MODES = 4;
|
||||
enum modes{mode_testing = 0, mode_basic, mode_polyakov, mode_bfo, mode_WSPR, mode_CQ};
|
||||
const int NUM_MODES = 6;
|
||||
int currMode = mode_basic;
|
||||
|
||||
char* modeNames[NUM_MODES] = {"TEST", "VFO", "POLYA", "BFO"};
|
||||
char* modeNames[NUM_MODES] = {"TEST", "VFO", "POLYA", "BFO", "WSPR", "CQ"};
|
||||
|
||||
long steps[][10] = { //don't forget to update the NUM_STEP_OPTIONS array below
|
||||
{10000000, 5000000, 1000000, 500000, 100000, 10000, 1000, 10, 1}, //testing
|
||||
{10000, 1000, 100, 10}, //basic
|
||||
{1000, 100, 10, 1}, //polyakov
|
||||
{1000, 100, 10, 1} //bfo
|
||||
{1000, 100, 10, 1}, //bfo
|
||||
{5}, //WSPR
|
||||
{500} //CQ
|
||||
};
|
||||
|
||||
const int NUM_STEP_OPTIONS[NUM_MODES] = {
|
||||
10, //testing
|
||||
4, //basic
|
||||
4, //polyakov
|
||||
4 //bfo
|
||||
4, //bfo
|
||||
1, //wspr
|
||||
1 //cq
|
||||
};
|
||||
char* stepNames[][10] = {
|
||||
{" 10MHz", " 5MHz", " 1MHz", "500Khz", "100KHz", " 10KHz", " 1KHz", " 100Hz", " 10Hz", " 1 Hz"}, //basic
|
||||
{" 10KHz", " 1KHz", " 100Hz", " 10 Hz"}, //basic
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //polyakov
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //BFO
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //BFO
|
||||
{" 5 Hz"}, //WSPR
|
||||
{" 500Hz"} //CQ
|
||||
};
|
||||
|
||||
int stepIndex = 0; // holds the index of the currently selected step value
|
||||
|
||||
//-----AMATEUR BAND DEFININTIONS----------------//
|
||||
//See function "getCurrentBand" below as well
|
||||
const int NUM_BANDS = 9;
|
||||
char* bandNames[NUM_BANDS] = {"160m", "80m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
|
||||
const int NUM_BANDS = 10;
|
||||
char* bandNames[NUM_BANDS] = {"160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
|
||||
char* OUT_OF_BAND_LABEL = "OOB";
|
||||
|
||||
long bandEdges[NUM_BANDS][2] = {
|
||||
{1800000, 2000000}, //160m
|
||||
{3500000, 4000000}, //80m
|
||||
{5288600, 5288800},
|
||||
{7000000, 7300000}, //40m
|
||||
{10100000, 10150000}, //30m
|
||||
{14000000, 14350000}, //20m
|
||||
|
@ -63,6 +97,19 @@ long bandEdges[NUM_BANDS][2] = {
|
|||
{28000000, 29700000} //10m
|
||||
};
|
||||
|
||||
long WSPRbandEdges[NUM_BANDS][2] = {
|
||||
{1838000, 1838200}, //160m
|
||||
{3594000, 3594200}, //80m
|
||||
{5288600, 5288800}, //60m
|
||||
{7040000, 7040200}, //40m
|
||||
{10140100, 10140300}, //30m
|
||||
{14097000, 14097200}, //20m
|
||||
{18106000, 18106200}, //17m
|
||||
{21096000, 21096200}, //15m
|
||||
{24926000, 24926200}, //12m
|
||||
{28126000, 28126200}, //10m
|
||||
};
|
||||
|
||||
/*
|
||||
* Holds the last-seen frequency within each band. The list below is also the default location at bootup.
|
||||
* This array is updated when the BAND button is used to change between bands.
|
||||
|
@ -73,6 +120,7 @@ long bandEdges[NUM_BANDS][2] = {
|
|||
long lastBandFreq[NUM_BANDS] = {
|
||||
1800000, //160m
|
||||
3500000, //80m
|
||||
5288600, //60m
|
||||
7000000, //40m
|
||||
10100000, //30m
|
||||
14000000, //20m
|
||||
|
@ -97,6 +145,9 @@ boolean displayNeedsUpdate;
|
|||
const long MIN_FREQ = 8500;
|
||||
const long MAX_FREQ = 150000000;
|
||||
|
||||
//Onboard LED Steup
|
||||
const int PIN_LED = 13;
|
||||
|
||||
//---------LCD SETUP-------//
|
||||
int PIN_RS = 7;
|
||||
int PIN_EN = 8;
|
||||
|
@ -136,14 +187,16 @@ void setup(){
|
|||
|
||||
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
|
||||
si5351.set_freq(currFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
si5351.output_enable(SI5351_CLK0, 1);
|
||||
enableOutput();
|
||||
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
||||
|
||||
si5351.output_enable(SI5351_CLK1, 0);
|
||||
si5351.output_enable(SI5351_CLK2, 0);
|
||||
delay(750);
|
||||
|
||||
//knob.write(0);
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
|
||||
pinMode(PIN_BUTTON_ENCODER, INPUT);
|
||||
digitalWrite(PIN_BUTTON_ENCODER, HIGH);
|
||||
|
||||
|
@ -173,17 +226,52 @@ void loop(){
|
|||
|
||||
displayNeedsUpdate = false;
|
||||
|
||||
//step up or down or change step size, for either button presses or encoder turns
|
||||
//step up or down or change step size, for encoder turns
|
||||
if ((encoderChange > 0)){currFreq += steps[currMode][stepIndex]; currFreq = min(currFreq, MAX_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
||||
if ((encoderChange < 0)){currFreq -= steps[currMode][stepIndex]; currFreq = max(currFreq, MIN_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
||||
|
||||
//pressing the encoder button increments through the possible step sizes for each mode
|
||||
if (checkButtonPress(PIN_BUTTON_ENCODER)){stepIndex = (stepIndex + 1) % (NUM_STEP_OPTIONS[currMode]); displayNeedsUpdate = true;}
|
||||
//pressing the encoder button increments through the possible step sizes for each mode;
|
||||
//in WSPR or CQ modes, the encoder button triggers the transmission of WSPR or a CQ, respectively.
|
||||
if (checkButtonPress(PIN_BUTTON_ENCODER)){
|
||||
if (currMode == mode_testing || currMode == mode_basic || currMode == mode_polyakov || currMode == mode_bfo) {
|
||||
stepIndex = (stepIndex + 1) % (NUM_STEP_OPTIONS[currMode]);
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
else if (currMode == mode_WSPR){
|
||||
transmitWSPR();
|
||||
}
|
||||
else if (currMode == mode_CQ){
|
||||
transmitMorseWord(CQ);
|
||||
transmitSpace();
|
||||
transmitMorseWord(CQ);
|
||||
transmitSpace();
|
||||
transmitMorseWord(DE);
|
||||
transmitSpace();
|
||||
transmitMorseWord(morseCallsign);
|
||||
transmitSpace();
|
||||
transmitMorseWord(morseCallsign);
|
||||
}
|
||||
}
|
||||
|
||||
//pressing the mode button cycles through the available modes
|
||||
if (checkButtonPress(PIN_BUTTON_MODE)){currMode = (currMode+1) % NUM_MODES; stepIndex = 0; setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
||||
if (checkButtonPress(PIN_BUTTON_MODE)){
|
||||
currMode = (currMode+1) % NUM_MODES;
|
||||
stepIndex = 0;
|
||||
if (currMode == mode_WSPR){ //If entering WSPR mode, set the current freqency to the bottom of the WSPR band slice
|
||||
currFreq = findWSPRBand();
|
||||
}
|
||||
|
||||
/*The mode button: if currFreq is inside an amateur band, save that frequency as the one to return to when
|
||||
if (currMode == mode_WSPR || currMode == mode_CQ){
|
||||
disableOutput(); //In WSPR or CQ mode, the transmitter should be off until manually triggered
|
||||
}
|
||||
else{
|
||||
enableOutput(); //In all other modes, the output of the VFO is on by default
|
||||
}
|
||||
setFrequency_5351(currFreq);
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
|
||||
/*The band button: if currFreq is inside an amateur band, save that frequency as the one to return to when
|
||||
* the user returns to this band, and jump to the return frequency for the next higher band. Otherwise,
|
||||
* just jump to the next higher band
|
||||
*/
|
||||
|
@ -203,6 +291,10 @@ void loop(){
|
|||
if (currFreq < lastBandFreq[i]){currFreq = lastBandFreq[i]; setFrequency_5351(currFreq); break;}
|
||||
}
|
||||
}
|
||||
if (currMode == mode_WSPR){ //WSPR mode behaves differntly from other modes
|
||||
currFreq = WSPRbandEdges[getCurrentBand()][0];
|
||||
setFrequency_5351(currFreq);
|
||||
}
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
@ -302,7 +394,7 @@ boolean checkButtonPress(int pin){
|
|||
void setFrequency_5351(long newFreq){
|
||||
switch (currMode){
|
||||
case mode_testing:
|
||||
si5351.set_freq(newFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
si5351.set_freq((newFreq + correctionFactor) * 100ULL, 0ULL, SI5351_CLK0);
|
||||
break;
|
||||
case mode_basic:
|
||||
si5351.set_freq(newFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
|
@ -316,6 +408,16 @@ void setFrequency_5351(long newFreq){
|
|||
}
|
||||
}
|
||||
|
||||
void enableOutput(){
|
||||
si5351.output_enable(SI5351_CLK0, 1);
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
}
|
||||
|
||||
void disableOutput(){
|
||||
si5351.output_enable(SI5351_CLK0, 0);
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
}
|
||||
|
||||
//Returns the index of the current amateur radio band based on currFreq. Does not include the 60m band
|
||||
//Returns -1 if out of band, but within the HF amateur turning range
|
||||
//returns -2 if out of band and lower than the lowest defined band
|
||||
|
@ -374,3 +476,112 @@ char getPermission(){
|
|||
return 'X';
|
||||
}
|
||||
|
||||
void transmitWSPR(){
|
||||
long startTime = millis();
|
||||
enableOutput();
|
||||
for (int dataFrame = 0; dataFrame < 162; dataFrame++){
|
||||
si5351.set_freq((currFreq + correctionFactor) * 100ULL + (146*WSPR_TRANSMISSION_DATA[dataFrame]*1ULL), SI5351_PLL_FIXED, SI5351_CLK0);
|
||||
displayWSPR(dataFrame);
|
||||
while (millis() < startTime + 683*(dataFrame+1)){
|
||||
if (checkButtonPress(PIN_BUTTON_ENCODER) || dataFrame > 162){
|
||||
goto escape;
|
||||
}
|
||||
}
|
||||
}
|
||||
escape:
|
||||
disableOutput();
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void displayWSPR(int frame){
|
||||
lcd.clear();
|
||||
|
||||
lcd.setCursor(0,0);
|
||||
long printFreq = currFreq * 100ULL + (146*WSPR_TRANSMISSION_DATA[frame]*1ULL);
|
||||
lcd.print(printFreq);
|
||||
|
||||
//current frame and data are printed on the 3rd line
|
||||
lcd.setCursor(0, 2);
|
||||
lcd.print("FRAME:");
|
||||
lcd.setCursor(6, 2);
|
||||
lcd.print(frame);
|
||||
lcd.setCursor(10, 2);
|
||||
lcd.print("DATA:");
|
||||
lcd.setCursor(15, 2);
|
||||
lcd.print(WSPR_TRANSMISSION_DATA[frame]);
|
||||
|
||||
//The current amateur band is printed in the top-right corner
|
||||
int currBand = getCurrentBand();
|
||||
if (currBand >= 0){
|
||||
char* currBandName = bandNames[currBand];
|
||||
lcd.setCursor(20-strlen(currBandName), 0);
|
||||
lcd.print(currBandName);
|
||||
}
|
||||
else{
|
||||
lcd.setCursor(20-strlen(OUT_OF_BAND_LABEL), 0);
|
||||
lcd.print(OUT_OF_BAND_LABEL);
|
||||
}
|
||||
|
||||
//Callsign is printed at the beginning of the 4th line
|
||||
lcd.setCursor(0, 3);
|
||||
lcd.print("KK9JEF");
|
||||
|
||||
//The mode is printed on the 4th line with no label
|
||||
//lcd.setCursor(6, 3);
|
||||
lcd.setCursor(16, 3);
|
||||
lcd.print("WSPR");
|
||||
}
|
||||
|
||||
//When switching into WSPR mode, the VFO jumps to the WSPR portion of the appropriate band; if not inside a band when switching to WSPR mode,
|
||||
//This fucntion determines where to jump to.
|
||||
//Currently always resets to the bottom of the lowest band
|
||||
long findWSPRBand(){
|
||||
/*switch (getCurrentBand()){
|
||||
case -2: //Below the lowest defined band
|
||||
currFreq = WSPRbandEdges[0][0]; //set frequency to the bottom edge of lowest band
|
||||
break;
|
||||
case -3: //Above the highest defined band
|
||||
currFreq = WSPRbandEdges[NUM_BANDS-1][0]; //Set frequency to bottom edge of highest band
|
||||
break;
|
||||
case -1: //in between bands
|
||||
break;
|
||||
default:
|
||||
currFreq = currFreq = (currFreq % 5); //round to the nearest multiple of 5 hz, for readability in WSPR mode
|
||||
break;
|
||||
}
|
||||
*/
|
||||
return WSPRbandEdges[0][0];
|
||||
}
|
||||
|
||||
void transmitMorseWord(char singleWord[]){
|
||||
for (int i = 0; i < strlen(singleWord); i++){
|
||||
if (singleWord[i] == '-') transmitDash();
|
||||
else if (singleWord[i] == '.') transmitDot();
|
||||
else transmitIntracharacter();
|
||||
}
|
||||
}
|
||||
|
||||
void transmitDash(){
|
||||
enableOutput();
|
||||
delay(morseElementLength * 3);
|
||||
disableOutput();
|
||||
delay(morseElementLength);
|
||||
}
|
||||
|
||||
void transmitDot(){
|
||||
enableOutput();
|
||||
delay(morseElementLength);
|
||||
disableOutput();
|
||||
delay(morseElementLength);
|
||||
}
|
||||
|
||||
void transmitIntracharacter(){
|
||||
disableOutput();
|
||||
delay(morseElementLength*2); //each element naturally has a one-dot space built in
|
||||
}
|
||||
|
||||
void transmitSpace(){
|
||||
disableOutput();
|
||||
delay(morseElementLength*6); //each element naturally has a one-dot space built in that follows it.
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user