/* 
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;

}