/*
29sep2022: Copy out WSPR stuff from ESP_2_4_orig.ino
Line number where copy starts from shown as "// line 125"
Search on "defaults" to set WSPRdata defaults
This compiles and loads on Duemilanove with ATmega168 using Tools > Programmer AVRisp mkII
- 16 kB flash memory
- 1. kB SRAM
- 512 bytes EEPROM
Note: "// ***" is a line that was commented out by JA:
30sep2022 add if (DEBUG) print() statements
7oct2022: Details of ESP8266
*/
bool DEBUG = true;
// line 125 // JA:: comment out all headers
// #include <ESP8266WiFi.h>
// #include <EEPROM.h> // JA:: Arduino library that supports all processors
// #include <SoftwareSerial.h>
// #include <NMEAGPS.h> //NeoGps by SlashDevin"
// #include "Wire.h" // JA:: for I2C
// line 159
enum E_Mode
{
WSPRBeacon ,
SignalGen,
Idle
};
// line 166
enum E_LocatorOption {
Manual,
GPS
};
enum E_PowerOption {
Normal,
Altitude
};
// line 176
enum E_SufixPreFixOption {
Sufix,
Prefix,
None
};
struct S_WSPRData {
char CallSign[7]; //Radio amateur Call Sign, zero terminated string can be four to six char in length + zero termination
E_SufixPreFixOption SuPreFixOption; //If a suffix or Prefix to the Callsign is used or not
char Prefix[4]; // Prefix three chars max and a zero termination
uint8_t Sufix; // Sufix code in WSPR format, e.g single digit is 0-9, single char (A to Z) is 10-35, double digits (10-99) is 36-125
E_LocatorOption LocatorOption; //If transmitted Maidenhead locator is based of GPS location or if it is using MaidneHead4 variable.
uint8_t LocationPrecision; //Determines if a second Type 3 transmission will be sent to increase the postiton precision.
char MaidenHead4[5]; //Maidenhead locator with four characters and a zero termination
char MaidenHead6[7]; //Maidenhead locator with six characters and a zero termination
E_PowerOption PowerOption; //If transmitted Power is based on TXPowerdBm field or is calculated from GPS Altitude.
uint8_t TXPowerdBm; //Power data in dBm min=0 max=60
uint8_t TimeSlotCode; //Determine on what time slot a tranmsission will be made. If TimeslotCode is 0 to 4 a ten minute scheduled transmission will be used.
};
struct S_GadgetData {
char Name[40]; //Optional Name of the device.
E_Mode StartMode; //What mode the Gadget should go to after boot.
S_WSPRData WSPRData; //Data needed to transmit a WSPR packet.
bool TXOnBand[16]; //Arraycount corresponds to the Enum E_Band, True =Transmitt Enabled, False = Transmitt disabled on this band
unsigned long TXPause; //Number of seconds to pause after having transmitted on all enabled bands.
uint64_t GeneratorFreq; //Frequency for when in signal Generator mode. Freq in centiHertz.
};
// line 252
#define WSPR_SYMBOL_COUNT 162
// line 305
//Global Variables
S_GadgetData GadgetData; //Create a datastructure that holds all relevant data for a WSPR Beacon
// *** S_FactoryData FactoryData; //Create a datastructure that holds information of the hardware
E_Mode CurrentMode; //What mode are we in, WSPR, signal generator or nothing
// line 310
//from Jason Mildrums JTEncode class
char callsign[7]; // JA: Initialized by memcpy(callsign, call, 6) in wspr_messge_check()
char locator[5];
uint8_t power;
// line 327
uint8_t symbolSequence[WSPR_SYMBOL_COUNT];
uint8_t tx_buffer[WSPR_SYMBOL_COUNT];
uint64_t dummy; // JA: dummy variable for the loop to increment
uint32_t n = 4294967295; // JA: for debug
// line 351
void setup() {
Serial.begin(230400);
while(!Serial); // JA: Wait for Serial port to be ready.
/* Test Serial.print()
Serial.println("Print check. The following line should be: 15 F 16 10 OK.");
Serial.print (" ");
int jk;
for (jk=15; jk<17; jk++) {
delay(100);
Serial.print(jk, DEC);
Serial.print(" ");
Serial.print(jk, HEX);
Serial.print(" ");
}
Serial.println("OK.");
Serial.println("");
delay(200);
Serial.print("sizeof(n): ");
Serial.println(sizeof(n));
Serial.print("n (hex): ");
Serial.println(n, HEX);
*/
// WiFi.mode( WIFI_OFF );
// WiFi.forceSleepBegin();
// delay( 1 );
// EEPROM.begin(512);
// int i; // JA: Why int???
// Wire.begin();
// PCConnected = false;
// line 451
// if (LoadFromEPROM(UserSpace)) //Read all UserSpace data from EEPROM at position 0
// {
// CurrentMode = GadgetData.StartMode;
// GadgetData.WSPRData.CallSign[6] = 0;//make sure Call sign is null terminated in case of incomplete data saved
// GadgetData.WSPRData.MaidenHead4[4] = 0; //make sure Maidenhead locator is null terminated in case of incomplete data saved
// GadgetData.WSPRData.MaidenHead6[6] = 0; //make sure Maidenhead locator is null terminated in case of incomplete data saved
// }
// else //No user data was found in EEPROM, set some defaults
// {
CurrentMode = WSPRBeacon; // JA: was set to SignalGen
GadgetData.Name[0] = 'W';
GadgetData.Name[1] = 'S';
GadgetData.Name[2] = 'P';
GadgetData.Name[3] = 'R';
GadgetData.Name[4] = ' ';
GadgetData.Name[5] = 'T';
GadgetData.Name[6] = 'X';
GadgetData.Name[7] = 0;
GadgetData.StartMode = WSPRBeacon; // JA: was IDLE
GadgetData.WSPRData.CallSign[0] = 'W';
GadgetData.WSPRData.CallSign[1] = '6';
GadgetData.WSPRData.CallSign[2] = 'H';
GadgetData.WSPRData.CallSign[3] = 'W';
GadgetData.WSPRData.CallSign[4] = '/';
GadgetData.WSPRData.CallSign[5] = '?';
GadgetData.WSPRData.CallSign[6] = 0;
// GadgetData.WSPRData.CallSign[0] = ' '; // JA: pre-pad is ok
// GadgetData.WSPRData.CallSign[1] = 'W';
// GadgetData.WSPRData.CallSign[2] = '6';
// GadgetData.WSPRData.CallSign[3] = 'H';
// GadgetData.WSPRData.CallSign[4] = 'W';
// GadgetData.WSPRData.CallSign[5] = ' ';
// GadgetData.WSPRData.CallSign[6] = 0;
GadgetData.WSPRData.LocatorOption = Manual; // JA: was GPS
GadgetData.WSPRData.MaidenHead4[0] = 'C';
GadgetData.WSPRData.MaidenHead4[1] = 'M';
GadgetData.WSPRData.MaidenHead4[2] = '9';
GadgetData.WSPRData.MaidenHead4[3] = '7';
GadgetData.WSPRData.MaidenHead4[4] = 0; //Null termination
GadgetData.WSPRData.MaidenHead6[0] = 'C';
GadgetData.WSPRData.MaidenHead6[1] = 'M';
GadgetData.WSPRData.MaidenHead6[2] = '9';
GadgetData.WSPRData.MaidenHead6[3] = '7';
GadgetData.WSPRData.MaidenHead6[4] = 'C';
GadgetData.WSPRData.MaidenHead6[5] = 'E';
GadgetData.WSPRData.MaidenHead6[6] = 0; //Null termination
GadgetData.WSPRData.LocationPrecision = 4;
GadgetData.WSPRData.PowerOption = Normal; //Use the Power encoding for normal power reporting
GadgetData.WSPRData.TXPowerdBm = 23; //Set deafult power to 0.2W
GadgetData.WSPRData.TimeSlotCode = 16; //TX on any even minute (no scheduling)
GadgetData.WSPRData.SuPreFixOption = None;
GadgetData.WSPRData.Prefix[0] = ' '; // Prefix three chars max and a zero termination
GadgetData.WSPRData.Prefix[1] = ' ';
GadgetData.WSPRData.Prefix[2] = ' ';
GadgetData.WSPRData.Prefix[3] = 0; //Null termination
GadgetData.WSPRData.Sufix = 1;
///////////////////////////////////////////////////////////////////////////
if (CurrentMode == WSPRBeacon) DoWSPR(); // JA for now, just DoWSPR() once.
// JA: from wspr_encode()
// n = wspr_code(' ');
// Serial.println(n, DEC);
// n = n * 36 + wspr_code('W');
// Serial.println(n, DEC);
// n = n * 10 + wspr_code('6');
// Serial.println(n, DEC);
// n = n * 27 + (wspr_code('H') - 10);
// Serial.println(n, DEC);
// n = n * 27 + (wspr_code('W') - 10);
// Serial.println(n, DEC);
// n = n * 27 + (wspr_code(' ') - 10);
// Serial.print("n (final): ");
// Serial.println(n);
}
// line 617
void loop() {
delay(200); // JA: Give the loop something to do
dummy++;
// if (Serial.available()) { //Handle Serial API request from the PC
// DoSerialHandling();
// }
//if (CurrentMode == WSPRBeacon) DoWSPR(); //If in WSPR beacon mode but it broke out of beacon loop to handle a Serial data from the PC then go back to the WSPR routine
// while (gps.available( GPSSerial )) { //Handle Serial data from the GPS as they arrive
}
// line 1325
void DoWSPR() {
uint8_t WSPRMessageTypeToUse; // JA: type was int. Can only be 1 or 2. 3 is hardcoded in SendWSPRMessage(3) if LocaionPrecision == 6
if (GadgetData.WSPRData.SuPreFixOption == None) //if standard Call Sign with no Sufix then send a Standards Type 1 message, else Send a Type 2 Message to include the Sufix
{
WSPRMessageTypeToUse = 1;
} else {
WSPRMessageTypeToUse = 2;
}
// line 1409 // JA:: Send msg and return errcode. errcode=1 if Serial.available() > 0. Serial.available returns number of bytes in receive buffer[64]
if (SendWSPRMessage(WSPRMessageTypeToUse) != 0) //Send a WSPR Type 1 or Type 2 message for 1 minute and 50 seconds
{
// there was a serial command that interrupted the WSPR Block so go and handle it
return;
}
if (GadgetData.WSPRData.LocationPrecision == 6) //If higher position precision is set then start a new WSPR tranmission of Type 3
{
// *** delay(9000); //wait 9 seconds so we are at the top of an even minute again
// if (GadgetData.WSPRData.PowerOption == Altitude) // If Power field should be used for Altitude coding
// {
// GadgetData.WSPRData.TXPowerdBm = pwr2;
// }
if (SendWSPRMessage(3) != 0) //Send a WSPR Type 3 message for 1 minute and 50 seconds
{
// there was a serial command that interrupted the WSPR Block so go and handle it
return;
}
}
}
// line 1485: JA: comment out all of SendWSPRMessage() except wspr_encode()
// Transmitt a WSPR message for 1 minute 50 seconds on frequency freq
int SendWSPRMessage(uint8_t WSPRMessageType) // JA: Here WSPRMessageType is unint8_t. Why not return unit8_t???
{
// uint8_t i;
// uint8_t Indicator;
// uint8_t BlinkCount;
// unsigned long startmillis; // JA: long is 32 bits. Doc: "If doiing math wiht integers at lease one of the values must be long"
// unsigned long endmillis;
// boolean TXEnabled = true;
// int errcode; // JA: why int???
// errcode = 0; // JA: why not initialize on line above???
// boolean blinked;
memset(tx_buffer, 0, sizeof(tx_buffer));//clear WSPR symbol buffer
// JA: why .Maidenhead4 ???
wspr_encode(GadgetData.WSPRData.CallSign, GadgetData.WSPRData.MaidenHead4, GadgetData.WSPRData.TXPowerdBm, tx_buffer, WSPRMessageType); //Send a WSPR message for 2 minutes
// //PrintBuffer ('B');
// //Send WSPR for two minutes
// digitalWrite(StatusLED, LOW);
// digitalWrite(TransmitLED, HIGH);
// startmillis = millis();
// for (i = 0; i < 162; i++) //162 WSPR symbols to transmit
// {
// blinked = false;
// endmillis = startmillis + ((i + 1) * (unsigned long) 683) ; // intersymbol delay in WSPR is 682.687 milliseconds (1.4648 baud)
// uint64_t tonefreq; // JA: Why not put this at the beginning of SendWSPRMessage()???
// tonefreq = freq + ((tx_buffer[i] * 146)); //146 centiHz (Tone spacing is 1.4648Hz in WSPR)
// if (TXEnabled) si5351aSetFrequency(tonefreq);
// //wait untill tone is transmitted for the correct amount of time
// while ((millis() < endmillis)) && (!Serial.available())) ;//Until time is up or there is serial data received on the control Serial port
// {
// if (!blinked) { //do pulsing blinks on Status LED every WSPR symbol to indicate WSPR Beacon transmission
// //Send Status updates to the PC
// Indicator = i;
// // Serial.print (F("{TWS} "));
// // if (CurrentBand < 10) SerialPrintZero();
// // Serial.print (CurrentBand);
// // Serial.print (" ");
// if (GadgetData.WSPRData.LocationPrecision == 6) Indicator = Indicator / 2; //If four minutes TX time then halve the indicator value so it will be full after four minutes instead of 2 minutes
// if (WSPRMessageType == 3) Indicator = Indicator + 81; //If this is the second 2 minute transmission then start to from 50%
// if (Indicator < 10) SerialPrintZero();
// if (Indicator < 100) SerialPrintZero();
// Serial.println (Indicator);
// for (int BlinkCount = 0; BlinkCount < 6; BlinkCount++)
// {
// digitalWrite(StatusLED, LOW);
// delay (5);
// digitalWrite(StatusLED, HIGH);
// delay (50);
// }
// blinked = true;
// }
// }
// if (Serial.available()) // If serialdata was received on Control port then abort and handle command
// {
// errcode = 1;
// break;
// }
// }
// // Switches off Si5351a output
// si5351aOutputOff(SI_CLK0_CONTROL);
// digitalWrite(StatusLED, HIGH);
// digitalWrite(TransmitLED, LOW);
// return errcode;
}
// line 2611
// JA: wspr_code() gives same ouput as EncodeChar() except wspr_code('/') returns 255
//Converts a letter (A-Z) or digit (0-9)to a special format used in the encoding of WSPR messages
uint8_t EncodeChar(char Character) {
uint8_t ConvertedNumber;
if (Character == ' ') {
ConvertedNumber = 36;
} else {
if (isdigit(Character)) {
ConvertedNumber = Character - '0';
} else {
ConvertedNumber = 10 + (Character - 'A');
}
}
return ConvertedNumber;
}
// line 2633
void wspr_encode(const char * call, const char * loc, const uint8_t dbm, uint8_t * symbols, uint8_t WSPRMessageType)
{
char call_[7];
char loc_[5];
uint8_t dbm_ = dbm;
strcpy(call_, call); // JA: strcpy seems unnecessary since in wspr_message_prep() mempcy(callsign, call, 6)
strcpy(loc_, loc);
uint32_t n, m;
Serial.println("In wspr_encode()");
Serial.print("WSPRMessageType: ");
Serial.println(WSPRMessageType);
Serial.print("GadgetData.WSPRData.CallSign: ");
Serial.println(GadgetData.WSPRData.CallSign);
// Ensure that the message text conforms to standards
// --------------------------------------------------
wspr_message_prep(call_, loc_, dbm_);
// Bit packing
// -----------
uint8_t c[11];
switch (WSPRMessageType) {
case 1: //Normal coding with callsign, 4letter Maidenhead postion and power
n = wspr_code(callsign[0]);
n = n * 36 + wspr_code(callsign[1]);
//Serial.println(n, DEC);
n = n * 10 + wspr_code(callsign[2]);
n = n * 27 + (wspr_code(callsign[3]) - 10);
n = n * 27 + (wspr_code(callsign[4]) - 10);
n = n * 27 + (wspr_code(callsign[5]) - 10);
Serial.print("n (final) in wspr_encode: ");
Serial.println(n, DEC);
m = ((179 - 10 * (locator[0] - 'A') - (locator[2] - '0')) * 180) +
(10 * (locator[1] - 'A')) + (locator[3] - '0');
m = (m * 128) + power + 64;
break;
case 2: //Call sign and Prefix or suffix for it and power, no Maidenhead position
n = wspr_code(callsign[0]);
n = n * 36 + wspr_code(callsign[1]);
n = n * 10 + wspr_code(callsign[2]);
n = n * 27 + (wspr_code(callsign[3]) - 10);
n = n * 27 + (wspr_code(callsign[4]) - 10);
n = n * 27 + (wspr_code(callsign[5]) - 10);
if (GadgetData.WSPRData.SuPreFixOption == Sufix)
{
// Single number or letter suffix from 0 to 35, 0-9= 0-9. 10-35=A-Z.
// Or double number suffix from 36 to 125, 36-125=10-99
m = (27232 + GadgetData.WSPRData.Sufix);
m = (m * 128) + power + 2 + 64;
}
else
{
//Three character prefix. Numbers, letters or space
//0 to 9=0-9, A to Z=10-35, space=36
m = EncodeChar(GadgetData.WSPRData.Prefix[0]); //Left Character
m = 37 * m + EncodeChar(GadgetData.WSPRData.Prefix[1]); //Mid character
m = 37 * m + EncodeChar(GadgetData.WSPRData.Prefix[2]); //Right character
//m = (m * 128) + power +1+ 64;
if (m > 32767)
{
m = m - 32768;
m = (m * 128) + power + 66;
}
else
{
m = (m * 128) + power + 65;
}
}
break;
case 3: //Hashed Callsign, six letter maidenhead position and power
//encode the six letter Maidenhear postion in to n that is usually used for callsign coding, reshuffle the character order to conform to the callsign rules
n = wspr_code(GadgetData.WSPRData.MaidenHead6[1]);
n = n * 36 + wspr_code(GadgetData.WSPRData.MaidenHead6[2]);
n = n * 10 + wspr_code(GadgetData.WSPRData.MaidenHead6[3]);
n = n * 27 + (wspr_code(GadgetData.WSPRData.MaidenHead6[4]) - 10);
n = n * 27 + (wspr_code(GadgetData.WSPRData.MaidenHead6[5]) - 10);
n = n * 27 + (wspr_code(GadgetData.WSPRData.MaidenHead6[0]) - 10);
m = 128 * WSPRCallHash(call) - power - 1 + 64;
break;
}//switch
// Callsign is 28 bits, locator/power is 22 bits.
// A little less work to start with the least-significant bits
c[3] = (uint8_t)((n & 0x0f) << 4);
n = n >> 4;
c[2] = (uint8_t)(n & 0xff);
n = n >> 8;
c[1] = (uint8_t)(n & 0xff);
n = n >> 8;
c[0] = (uint8_t)(n & 0xff);
c[6] = (uint8_t)((m & 0x03) << 6);
m = m >> 2;
c[5] = (uint8_t)(m & 0xff);
m = m >> 8;
c[4] = (uint8_t)(m & 0xff);
m = m >> 8;
c[3] |= (uint8_t)(m & 0x0f);
c[7] = 0;
c[8] = 0;
c[9] = 0;
c[10] = 0;
// Convolutional Encoding
// ---------------------
uint8_t s[WSPR_SYMBOL_COUNT];
convolve(c, s, 11, WSPR_SYMBOL_COUNT);
// Interleaving
// ------------
wspr_interleave(s);
// Merge with sync vector
// ----------------------
wspr_merge_sync_vector(s, symbols);
}
void wspr_message_prep(char * call, char * loc, uint8_t dbm)
{
// PrintCallSign ('2');
// If only the 2nd character is a digit, then pad with a space.
// If this happens, then the callsign will be truncated if it is
// longer than 6 characters.
if (isdigit(call[1]) && isupper(call[2])) // JA: What if call[2] is not upper???
{
call[5] = call[4];
call[4] = call[3];
call[3] = call[2];
call[2] = call[1];
call[1] = call[0];
call[0] = ' ';
}
// Ensure that the only allowed characters are digits and uppercase letters
uint8_t i;
for (i = 0; i < 6; i++)
{
call[i] = toupper(call[i]);
if (!(isdigit(call[i]) || isupper(call[i])))
{
call[i] = ' '; // JA: So cannot enter call as W6HW/P -> must use SuffixPrefix
if (i == 4)
{
call[5] = ' '; //If char 4 is a space then also set the last character to a space
}
}
}
memcpy(callsign, call, 6); // JA: memcpy(*dest, *src, number_of_bytes)
// Grid locator validation
for (i = 0; i < 4; i++)
{
loc[i] = toupper(loc[i]);
if (!(isdigit(loc[i]) || (loc[i] >= 'A' && loc[i] <= 'R')))
{
memcpy(loc, "AA00", 5); //loc = "AA00";
}
}
memcpy(locator, loc, 4);
power = dbm; // JA: ValiddBmValue (dbm);
}
// line 2820
void convolve(uint8_t * c, uint8_t * s, uint8_t message_size, uint8_t bit_size)
{
uint32_t reg_0 = 0;
uint32_t reg_1 = 0;
uint32_t reg_temp = 0;
uint8_t input_bit, parity_bit;
uint8_t bit_count = 0;
uint8_t i, j, k;
for (i = 0; i < message_size; i++)
{
for (j = 0; j < 8; j++)
{
// Set input bit according the MSB of current element
input_bit = (((c[i] << j) & 0x80) == 0x80) ? 1 : 0;
// Shift both registers and put in the new input bit
reg_0 = reg_0 << 1;
reg_1 = reg_1 << 1;
reg_0 |= (uint32_t)input_bit;
reg_1 |= (uint32_t)input_bit;
// AND Register 0 with feedback taps, calculate parity
reg_temp = reg_0 & 0xf2d05351;
parity_bit = 0;
for (k = 0; k < 32; k++)
{
parity_bit = parity_bit ^ (reg_temp & 0x01);
reg_temp = reg_temp >> 1;
}
s[bit_count] = parity_bit;
bit_count++;
// AND Register 1 with feedback taps, calculate parity
reg_temp = reg_1 & 0xe4613c47;
parity_bit = 0;
for (k = 0; k < 32; k++)
{
parity_bit = parity_bit ^ (reg_temp & 0x01);
reg_temp = reg_temp >> 1;
}
s[bit_count] = parity_bit;
bit_count++;
if (bit_count >= bit_size)
{
break;
}
}
}
}
void wspr_interleave(uint8_t * s)
{
uint8_t d[WSPR_SYMBOL_COUNT];
uint8_t rev, index_temp, i, j, k;
i = 0;
for (j = 0; j < 255; j++)
{
// Bit reverse the index
index_temp = j;
rev = 0;
for (k = 0; k < 8; k++)
{
if (index_temp & 0x01)
{
rev = rev | (1 << (7 - k));
}
index_temp = index_temp >> 1;
}
if (rev < WSPR_SYMBOL_COUNT)
{
d[rev] = s[i];
i++;
}
if (i >= WSPR_SYMBOL_COUNT)
{
break;
}
}
memcpy(s, d, WSPR_SYMBOL_COUNT);
}
void wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols)
{
uint8_t i;
const uint8_t sync_vector[WSPR_SYMBOL_COUNT] =
{ 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0,
1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,
1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0
};
for (i = 0; i < WSPR_SYMBOL_COUNT; i++)
{
symbols[i] = sync_vector[i] + (2 * g[i]);
}
}
// JA: function Encode_Char does a similar function, except it doesn't return 255 if bad char
uint8_t wspr_code(char c)
{
// Validate the input then return the proper integer code.
// Return 255 as an error code if the char is not allowed.
// JA: wspr_message_prep only outputs 0-9 A-Z ' '
if (isdigit(c))
{
return (uint8_t)(c - 48); // JA: decimal 48 is char '0'. Is conversion to unit8_t necessary???
}
else if (c == ' ') // JA: The space char ' ' is actually decimal 32
{
return 36;
}
else if (c >= 'A' && c <= 'Z')
{
return (uint8_t)(c - 55); // JA: 'A' is decimal 65 so wspr_code('A') returns 10 = 0x0A
}
else
{
return 255; // JA: Apparently no calling functions check if wspr_code returns 255. wspr_message_prep() does validataion.
}
}
// line 2974
//Type 3 call sign hash by RFZero www.rfzero.net modified by SM7PNV
uint32_t WSPRCallHash(const char * call)
{
#define rot(x, k) ((x << k) | (x >> (32 - k)))
uint32_t a, b, c;
char CallWithSuPrefix [11];
uint8_t Length = strlen(call);
uint8_t TenDigit = 0;
uint8_t Number;
uint8_t CharLoop;
// Serial.print("Length ");
// Serial.print(Length);
strcpy(CallWithSuPrefix, call);
if (GadgetData.WSPRData.SuPreFixOption == Sufix)
{
CallWithSuPrefix[Length] = '/'; //Add slash at the end
if (GadgetData.WSPRData.Sufix < 36) //Single digit or letter
{
CallWithSuPrefix[Length + 2] = 0; //Zero terminate
if (GadgetData.WSPRData.Sufix < 10)
{
CallWithSuPrefix[Length + 1] = '0' + GadgetData.WSPRData.Sufix; //Add a single digit
}
else
{
CallWithSuPrefix[Length + 1] = 'A' + (GadgetData.WSPRData.Sufix - 10); //Add a single letter
}
}
else //Suffix is double digits
{
/* Seems the Type 3 decodes are not correct in case of two suffix numbers so this code is commented out for now as it will not be used by the Configurtion software
Number=GadgetData.WSPRData.Sufix-36;
while (Number>9)
{
++TenDigit;
Number -= 10;
}
CallWithSuPrefix[Length+1]='0'+TenDigit; //Add the Ten Digit
CallWithSuPrefix[Length+2]='0'+Number; //Add the One Digit
CallWithSuPrefix[Length+3]=0; //Zero terminate
*/
}
}//if Sufix
else if (GadgetData.WSPRData.SuPreFixOption == Prefix)
{
CallWithSuPrefix[0] = GadgetData.WSPRData.Prefix[0];
CallWithSuPrefix[1] = GadgetData.WSPRData.Prefix[1];
CallWithSuPrefix[2] = GadgetData.WSPRData.Prefix[2];
CallWithSuPrefix[3] = '/';
for (CharLoop = 0; CharLoop < Length; CharLoop++)
{
CallWithSuPrefix[CharLoop + 4] = call[CharLoop];
}
}//else if Prefix
Length = strlen(CallWithSuPrefix);
// Serial.print(" : ");
//Serial.println(Length);
//Serial.print("{MIN} Call with Sufix=");
//Serial.println(CallWithSuPrefix);
a = b = c = 0xdeadbeef + Length + 146;
const uint32_t *k = (const uint32_t *)CallWithSuPrefix;
switch (Length) // Length 3-10 chars, thus 0, 1, 2, 11 and 12 omitted
{
case 10: c += k[2] & 0xffff; b += k[1]; a += k[0]; break;
case 9: c += k[2] & 0xff; b += k[1]; a += k[0]; break;
case 8: b += k[1]; a += k[0]; break;
case 7: b += k[1] & 0xffffff; a += k[0]; break;
case 6: b += k[1] & 0xffff; a += k[0]; break;
case 5: b += k[1] & 0xff; a += k[0]; break;
case 4: a += k[0]; break;
case 3: a += k[0] & 0xffffff; break;
}
c ^= b; c -= rot(b, 14);
a ^= c; a -= rot(c, 11);
b ^= a; b -= rot(a, 25);
c ^= b; c -= rot(b, 16);
a ^= c; a -= rot(c, 4);
b ^= a; b -= rot(a, 14);
c ^= b; c -= rot(b, 24);
c &= 0xFFFF; // 15 bits mask
return c;
}