Thursday 1 November 2018

Homebrew Antenna/Scaler Network Analyser






Final software for the antenna/SNA. It's not perfect, but works for me.


#include <UTFTGLUE.h>
#include <SPI.h>
#include <Wire.h>
#include <DDS.h>
#include <Keypad.h>

// Keypad
char keys[4][3] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[4] = {35, 45, 43, 39};    //connect to the row pinouts of the keypad
byte colPins[3] = {37, 33, 41};           // connect to the column pinouts of the keypad

// AD9850 pins
const int DATA = 47;
const int W_CLK = 49;
const int FQ_UD = 51;
const int RESET = 53;

// SWR bridge pins
const int fwdAntVoltageInput = A15;
const int revAntVoltageInput = A14;

// AD8307 pin
const int SNAVoltageInput = A12;

// Other constants and variables
const int NoBins = 320;
volatile long freq = 7123000;
volatile long Spotfreq = 7123000;
volatile long oldSpotfreq = 7123000;
volatile long UserLowerfreq = 8995000;
volatile long UserUpperfreq = 9005000;
volatile float fwdVoltage = 0;
volatile float revVoltage = 0;
volatile float MaxrevVoltage = 0;
volatile float OldMaxrevVoltage = 0;
volatile float MinrevVoltage = 0;
volatile float OldMinrevVoltage = 0;
volatile float Ripple = 0;
volatile float SWR = 10;
volatile float oldSWR = 0;
volatile long SWRFreq = 0;
volatile float LowestSWR = 10;
volatile int Cal = 0;
volatile long UserFreq = 0;
volatile long oldUserFreq = 0;
volatile boolean EntryComplete = false;
volatile boolean FreqEntered = false;
volatile long UserFreqArray[8] = {0};
volatile float TestArray[NoBins] = {0};
volatile float CalArray[NoBins] = {0};

// Instantiate the Objects
DDS dds(W_CLK, FQ_UD, DATA, RESET);
UTFTGLUE myGLCD(0, A2, A1, A3, A4, A0);
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );

void setup()
{
  Serial.begin(19200);

  pinMode(fwdAntVoltageInput, INPUT);
  pinMode(revAntVoltageInput, INPUT);
  pinMode(SNAVoltageInput, INPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(8, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);

  analogReference(INTERNAL2V56);                              // Built-in 2.56V reference

  //Set up display
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setRotation(3);
  DisplayMainMenu();

  // Initialize the AD9850
  dds.init();
  dds.trim(125000000); // (Optional) trim if your xtal is not at 125MHz...
}

void loop()
{
  char key = keypad.getKey();
  if (key != NO_KEY)
  {
    switch (key)                       //switch used to specify which button
    {
      case '1':
        EntryComplete = false;
        DisplayAntSpotFreqMenu();
        break;
      case '2':
        EntryComplete = false;
        DisplayHFMenu();
        break;
      case '3':
        EntryComplete = false;
        DisplayHamMenu();
        break;
      case '4':
        EntryComplete = false;
        DisplayUserScanMenu();
        break;
      case '5':
        EntryComplete = false;
        DisplaySNAMenu();
        break;
      case '*':
        EntryComplete = false;
        DisplayMainMenu();
        break;
      default:
        ;
    }
  }
}

void DisplayMainMenu()
{
  myGLCD.clrScr();
  myGLCD.setTextSize(2);
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.fillRect(0, 0, 319, 30);
  myGLCD.setColor(255, 255, 0);
  myGLCD.setBackColor(0, 0, 255);
  myGLCD.print("ZL2CTM Ant Analyser/SNA", 20, 8);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(0, 0, 0);
  myGLCD.print("1 Spot Frequency", 30, 60);
  myGLCD.print("2 HF Spectrum Scan", 30, 80);
  myGLCD.print("3 Ham Band Scan", 30, 100);
  myGLCD.print("4 User Scan", 30, 120);
  myGLCD.print("5 Scaler Network", 30, 140);
  myGLCD.print("  Analyser", 30, 160);

  myGLCD.print("Select option...", 30, 200);
}

void DisplayAntSpotFreqMenu()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("1 Enter frequency", 30, 20);
  myGLCD.print("2 Start", 30, 40);
  myGLCD.print("* Main menu", 30, 60);
  myGLCD.print("Select option...", 30, 100);

  myGLCD.setColor(255, 255, 0);
  myGLCD.print("Freq:", 30, 160);
  myGLCD.printNumI(Spotfreq, 110, 160);

  while (EntryComplete == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)                       //switch used to specify which button
      {
        case '1':
          EntryComplete = false;
          Spotfreq = EnterUserFreq();
          DisplayAntSpotFreqMenu();
          break;
        case '2':
          EntryComplete = true;
          AntSpotFreqScan(Spotfreq);
          break;
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
  }
}

void DisplayHFMenu()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("1 Full HF spectrum", 30, 20);
  myGLCD.print("2 2000-10000 kHz", 30, 40);
  myGLCD.print("3 10000-20000 kHz", 30, 60);
  myGLCD.print("4 20000-30000 kHz", 30, 80);
  myGLCD.print("* Main menu", 30, 100);
  myGLCD.print("Select option...", 30, 140);

  while (EntryComplete == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)                       //switch used to specify which button
      {
        case '1':
          EntryComplete = true;
          SweepAnt(2000000, 30000000);
          break;
        case '2':
          EntryComplete = true;
          SweepAnt(2000000, 10000000);
          break;
        case '3':
          EntryComplete = true;
          SweepAnt(10000000, 20000000);
          break;
        case '4':
          EntryComplete = true;
          SweepAnt(20000000, 30000000);
          break;
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
  }
}

void DisplayHamMenu()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("1 80m (3500-3900)", 30, 20);
  myGLCD.print("2 40m (7000-7300)", 30, 40);
  myGLCD.print("3 30m (10100-10150)", 30, 60);
  myGLCD.print("4 20m (14000-14350)", 30, 80);
  myGLCD.print("5 17m (18068-18168)", 30, 100);
  myGLCD.print("6 15m (21000-21450)", 30, 120);
  myGLCD.print("7 12m (24890-24990)", 30, 140);
  myGLCD.print("8 10m (28000-29700)", 30, 160);
  myGLCD.print("* Main menu", 30, 180);
  myGLCD.print("Select option...", 30, 215);

  while (EntryComplete == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)                       //switch used to specify which button
      {
        case '1':
          EntryComplete = true;
          SweepAnt(3500000, 3900000);
          break;
        case '2':
          EntryComplete = true;
          SweepAnt(7000000, 7300000);
          break;
        case '3':
          EntryComplete = true;
          SweepAnt(10100000, 10150000);
          break;
        case '4':
          EntryComplete = true;
          SweepAnt(14000000, 14500000);
          break;
        case '5':
          EntryComplete = true;
          SweepAnt(18068000, 18168000);
          break;
        case '6':
          EntryComplete = true;
          SweepAnt(21000000, 21450000);
          break;
        case '7':
          EntryComplete = true;
          SweepAnt(24890000, 24990000);
          break;
        case '8':
          EntryComplete = true;
          SweepAnt(28000000, 29700000);
          break;
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
  }
}

void DisplayUserScanMenu()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("1 Enter lower freq", 30, 20);
  myGLCD.print("2 Enter upper freq", 30, 40);
  myGLCD.print("3 Start scan", 30, 60);
  myGLCD.print("* Main menu", 30, 80);
  myGLCD.print("Select option...", 30, 120);

  myGLCD.setColor(255, 255, 0);
  myGLCD.print("Lower:", 30, 160);
  myGLCD.print("Upper:", 30, 180);
  myGLCD.printNumI(UserLowerfreq, 120, 160);
  myGLCD.printNumI(UserUpperfreq, 120, 180);

  while (EntryComplete == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)                       //switch used to specify which button
      {
        case '1':
          EntryComplete = false;
          UserLowerfreq = EnterUserFreq();
          DisplayUserScanMenu();
          break;
        case '2':
          EntryComplete = false;
          UserUpperfreq = EnterUserFreq();
          DisplayUserScanMenu();
          break;
        case '3':
          EntryComplete = true;
          if (UserLowerfreq < UserUpperfreq)
            SweepAnt(UserLowerfreq, UserUpperfreq);
          else
            SweepAnt(UserUpperfreq, UserLowerfreq);
          break;
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
  }
}

void DisplaySNAMenu()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("1 Enter lower freq", 30, 20);
  myGLCD.print("2 Enter upper freq", 30, 40);
  myGLCD.print("3 Calibrate", 30, 60);
  myGLCD.print("4 Single Scan", 30, 80);
  myGLCD.print("5 Continuous Scan", 30, 100);
  myGLCD.print("* Main menu", 30, 120);
  myGLCD.print("Select option...", 30, 160);

  myGLCD.setColor(255, 255, 0);
  myGLCD.print("Lower:", 30, 190);
  myGLCD.print("Upper:", 30, 210);
  myGLCD.printNumI(UserLowerfreq, 120, 190);
  myGLCD.printNumI(UserUpperfreq, 120, 210);

  while (EntryComplete == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)                       //switch used to specify which button
      {
        case '1':
          EntryComplete = false;
          UserLowerfreq = EnterUserFreq();
          DisplaySNAMenu();
          break;
        case '2':
          EntryComplete = false;
          UserUpperfreq = EnterUserFreq();
          DisplaySNAMenu();
          break;
        case '3':
          EntryComplete = false;
          if (UserLowerfreq < UserUpperfreq)
            Calibrate(UserLowerfreq, UserUpperfreq);
          else
            Calibrate(UserUpperfreq, UserLowerfreq);
          DisplaySNAMenu();
          break;
        case '4':
          if (Cal == 0)
          {
            EntryComplete = false;
            myGLCD.clrScr();
            myGLCD.setColor(0, 0, 255);
            myGLCD.drawRect(0, 0, 319, 239);
            myGLCD.setTextSize(2);
            myGLCD.setColor(255, 255, 255);
            myGLCD.print("Calibrate first", 60, 100);
            delay(1500);
            DisplaySNAMenu();
          }
          else
          {
            EntryComplete = true;
            if (UserLowerfreq < UserUpperfreq)
              SingleSweepSNA(UserLowerfreq, UserUpperfreq);
            else
              SingleSweepSNA(UserUpperfreq, UserLowerfreq);
          }
          break;
        case '5':
          EntryComplete = true;
          if (UserLowerfreq < UserUpperfreq)
            MultipleSweepSNA(UserLowerfreq, UserUpperfreq);
          else
            MultipleSweepSNA(UserUpperfreq, UserLowerfreq);
          break;
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
  }
}

long EnterUserFreq()
{
  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("Enter freq between", 40, 20);
  myGLCD.print("2MHz and 30MHz", 40, 40);
  myGLCD.print("then press #", 40, 60);

  myGLCD.setColor(105, 105, 105);
  myGLCD.setTextSize(3);
  myGLCD.print("8", 40, 120);
  myGLCD.print("8", 65, 120);
  myGLCD.print("8", 90, 120);
  myGLCD.print("8", 115, 120);
  myGLCD.print("8", 140, 120);
  myGLCD.print("8", 165, 120);
  myGLCD.print("8", 190, 120);
  myGLCD.print("*", 215, 120);

  FreqEntered = false;
  int x = 0;
  for (int y = 0; y <= 7; y++)
    UserFreqArray[y] = 0;

  while (FreqEntered == false)
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      if (key == '#')
      {
        FreqEntered = true;
        if (x == 8)
          UserFreq = ((UserFreqArray[0] * 10000000) + (UserFreqArray[1] * 1000000) + (UserFreqArray[2] * 100000) +
                      (UserFreqArray[3] * 10000) + (UserFreqArray[4] * 1000) + (UserFreqArray[5] * 100) + (UserFreqArray[6] * 10) + UserFreqArray[7]);
        if (x == 7)
          UserFreq = ((UserFreqArray[0] * 1000000) + (UserFreqArray[1] * 100000) + (UserFreqArray[2] * 10000) +
                      (UserFreqArray[3] * 1000) + (UserFreqArray[4] * 100) + (UserFreqArray[5] * 10) + UserFreqArray[6]);

        if ((x < 7) || (UserFreq < 2000000) || (UserFreq > 30000000))
        {
          myGLCD.clrScr();
          myGLCD.setColor(0, 0, 255);
          myGLCD.drawRect(0, 0, 319, 239);
          myGLCD.setTextSize(2);
          myGLCD.setColor(255, 255, 255);
          myGLCD.print("Out of bounds", 80, 100);
          UserFreq = oldUserFreq;
          delay(1500);
          EnterUserFreq();
        }
        oldUserFreq = UserFreq;
      }
      else
      {
        UserFreqArray[x] = (key - 48);
        myGLCD.setColor(0, 255, 0);
        myGLCD.setTextSize(3);
        switch (x)
        {
          case 0:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            break;
          case 1:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            break;
          case 2:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            break;
          case 3:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            myGLCD.printNumI(UserFreqArray[3], 115, 120);
            break;
          case 4:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            myGLCD.printNumI(UserFreqArray[3], 115, 120);
            myGLCD.printNumI(UserFreqArray[4], 140, 120);
            break;
          case 5:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            myGLCD.printNumI(UserFreqArray[3], 115, 120);
            myGLCD.printNumI(UserFreqArray[4], 140, 120);
            myGLCD.printNumI(UserFreqArray[5], 165, 120);
            break;
          case 6:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            myGLCD.printNumI(UserFreqArray[3], 115, 120);
            myGLCD.printNumI(UserFreqArray[4], 140, 120);
            myGLCD.printNumI(UserFreqArray[5], 165, 120);
            myGLCD.printNumI(UserFreqArray[6], 190, 120);
            break;
          case 7:
            myGLCD.printNumI(UserFreqArray[0], 40, 120);
            myGLCD.printNumI(UserFreqArray[1], 65, 120);
            myGLCD.printNumI(UserFreqArray[2], 90, 120);
            myGLCD.printNumI(UserFreqArray[3], 115, 120);
            myGLCD.printNumI(UserFreqArray[4], 140, 120);
            myGLCD.printNumI(UserFreqArray[5], 165, 120);
            myGLCD.printNumI(UserFreqArray[6], 190, 120);
            myGLCD.printNumI(UserFreqArray[7], 215, 120);
            break;
          default:
            ;
        }
        x++;
      }
    }
  }
  return UserFreq;
}

void AntSpotFreqScan(long userfrequency)
{
  EntryComplete = false;

  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  dds.setFrequency(userfrequency);
  delay(5);

  myGLCD.setTextSize(1);
  myGLCD.setColor(0, 255, 0);
  myGLCD.fillRect(45, 35, 73, 40);
  myGLCD.setColor(255, 255, 0);
  myGLCD.fillRect(74, 35, 160, 40);
  myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(161, 35, 275, 40);
  myGLCD.setColor(255, 255, 255);
  myGLCD.drawRect(45, 34, 275, 200);

  myGLCD.print("1", 45, 20);
  myGLCD.print("1.5", 73, 20);
  myGLCD.print("3", 160, 20);
  myGLCD.print("5", 192, 20);
  myGLCD.print("10", 265, 20);

  myGLCD.setTextSize(2);

  while (EntryComplete == false)
  {
    fwdVoltage = analogRead(fwdAntVoltageInput);
    revVoltage = analogRead(revAntVoltageInput);

    if (revVoltage >= fwdVoltage)
      revVoltage = fwdVoltage - 1;

    if (revVoltage < 1)
      revVoltage = 1;

    SWR = (fwdVoltage + revVoltage) / (fwdVoltage - revVoltage);

    myGLCD.setColor(0, 0, 0);
    if (oldSWR <= 1)
      myGLCD.drawLine(160, 200, 46, 41);
    if (oldSWR == 3)
      myGLCD.drawLine(160, 200, 160, 41);
    if (oldSWR >= 10)
      myGLCD.drawLine(160, 200, 274, 41);
    if ((oldSWR < 3) && (oldSWR > 1))
      myGLCD.drawLine(160, 200, (45 + ((oldSWR - 1) * 57.5)), 41);
    if ((oldSWR > 3) && (oldSWR < 10))
      myGLCD.drawLine(160, 200, (160 + (oldSWR - 3) / 7 * 115), 41);

    myGLCD.printNumF(oldSWR, 2, 125, 212);     // Print Floating Number   2 decimal places

    if (SWR <= 1.5)
      myGLCD.setColor(0, 255, 0);
    if ((SWR > 1.5) && (SWR <= 3))
      myGLCD.setColor(255, 255, 0);
    if (SWR > 3)
      myGLCD.setColor(255, 0, 0);

    if (SWR <= 1)
      myGLCD.drawLine(160, 200, 46, 41);
    if (SWR == 3)
      myGLCD.drawLine(160, 200, 160, 41);
    if (SWR >= 10)
      myGLCD.drawLine(160, 200, 274, 41);
    if ((SWR < 3) && (SWR > 1))
      myGLCD.drawLine(160, 200, (45 + ((SWR - 1) * 57.5)), 41);
    if ((SWR > 3) && (SWR < 10))
      myGLCD.drawLine(160, 200, (160 + (SWR - 3) / 7 * 115), 41);

    myGLCD.printNumF(SWR, 2, 125, 212);       // Print Floating Number   2 decimal places

    oldSWR = SWR;

    char key = keypad.getKey();
    if (key != NO_KEY) {
      switch (key)                       //switch used to specify which button
      {
        case '*':
          EntryComplete = true;
          DisplayMainMenu();
          break;
        default:
          ;
      }
    }
    delay(30);
  }
  dds.init();
  dds.trim(125000000);
}

void SweepAnt(long StartFreq, long StopFreq)
{
  LowestSWR = 100;
  SWRFreq = 0;

  myGLCD.clrScr();
  myGLCD.setColor(0, 0, 255);
  myGLCD.drawRect(0, 0, 319, 239);
  myGLCD.setTextSize(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("Analysing...", 80, 100);

  int x = 0;
  for (freq = StartFreq; freq <= StopFreq; freq = freq + (((StopFreq - StartFreq) / NoBins)))
  {
    dds.setFrequency(freq);
    delay(10);                                      // Let DDS settle then take SWR readings

    fwdVoltage = analogRead(fwdAntVoltageInput);
    revVoltage = analogRead(revAntVoltageInput);

    if (revVoltage >= fwdVoltage)
      revVoltage = fwdVoltage - 1;

    if (revVoltage < 1)
      revVoltage = 1;

    SWR = (fwdVoltage + revVoltage) / (fwdVoltage - revVoltage);

    TestArray[x] = SWR;

    if (SWR < LowestSWR)
    {
      LowestSWR = SWR;
      SWRFreq = freq;
    }
    x++;
  }
  PlotSWR(StartFreq, StopFreq);
}

void Calibrate(long StartFreq, long StopFreq)
{
  int x = 0;
  float MaxCalVoltage = 0;
  long freq = 0;

  for (x = 0; x < NoBins; x++)
    CalArray[x] = 0;
  x = 0;

  for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
  {
    dds.setFrequency(freq);
    CalArray[x] = analogRead(SNAVoltageInput);
    x++;
  }

  for (x = 20; x < NoBins; x++)                                // Start at 20 to skip initial high errors
    if (CalArray[x] > MaxCalVoltage)                              // Determine max calibration voltage
      MaxCalVoltage = CalArray[x];

  for (x = 0; x < NoBins; x++)
    CalArray[x] = MaxCalVoltage - CalArray[x];                    // Populate cal array with error offset values

  Cal = 1;
}

void SingleSweepSNA(long StartFreq, long StopFreq)
{
  int x = 0;
  float MinTestdB = 0;
  long freq = 0;

  for (x = 0; x < NoBins; x++)
    TestArray[x] = 0;

  x = 0;

  for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
  {
    dds.setFrequency(freq);
    TestArray[x] = analogRead(SNAVoltageInput) + CalArray[x];          // test array has corrected raw analog read voltages from 0 to 1024 (0 to 2.56 volts)
    x++;
  }

  for (x = 0; x < NoBins; x++)                                // 2.56V / 25mV/dB = 102.4dB range.
    TestArray[x] = 102.4 - (TestArray[x] * 102.4 / 1024);     // test array now has dB levels from 0dB (2.56V = 1024) to 102dB (0V = 0)

  PlotSNA();
}

void MultipleSweepSNA(long StartFreq, long StopFreq)
{
  int x = 0;
  float TestSNAReading = 0;

  EntryComplete = false;

  myGLCD.clrScr();
  myGLCD.setColor(255, 255, 255);

  while (EntryComplete == false)
  {
    for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
    {
      dds.setFrequency(freq);
      TestSNAReading = analogRead(SNAVoltageInput);
      TestSNAReading = 102.4 - (TestSNAReading * 102.4 / 1024);
      myGLCD.drawPixel(x, TestSNAReading * (240 / 102));

      char key = keypad.getKey();
      if (key != NO_KEY)
      {
        switch (key)                       //switch used to specify which button
        {
          case '*':
            EntryComplete = true;
            break;
          default:
            ;
        }
      }
      x++;
    }
    x = 0;
    myGLCD.clrScr();
  }
  dds.init();
  dds.trim(125000000);
  DisplayMainMenu();
}

void PlotSWR(long StartFreq, long StopFreq)
{
  int ax = 0;
  int bx = 0;
  float ay = 10;
  float by = 10;

  dds.init();
  dds.trim(125000000);
  myGLCD.clrScr();

  ay = TestArray[0];
  if (by > 10)
    ay = 10;
  ax = 0;


  for (bx = 0; bx < NoBins; bx++)
  {
    by = TestArray[bx];
    if (by > 10)
      by = 10;

    if (by > 3)
      myGLCD.setColor(255, 0, 0);     // red
    else if (by <= 1.5)
      myGLCD.setColor(0, 255, 0);     // green
    else
      myGLCD.setColor(255, 255, 0);   // yellow

    myGLCD.drawLine(ax, 240 - (ay * 24), bx, 240 - (by * 24));
    ax = bx;
    ay = by;
  }

  myGLCD.setFont(1);
  myGLCD.setColor(255, 255, 255);
  myGLCD.printNumI(StartFreq / 1000, 5, 220);
  myGLCD.printNumI(StopFreq / 1000, 278, 220);
  myGLCD.printNumI((StopFreq + StartFreq) / 2000, 138, 220);

  myGLCD.setFont(2);
  myGLCD.setColor(255, 255, 255);
  myGLCD.print("Freq:", 120, 20);
  myGLCD.print(" SWR:", 120, 40);
  myGLCD.printNumI(SWRFreq, 210, 20);
  myGLCD.printNumF(LowestSWR, 2, 210, 40);
}

void PlotSNA()
{
  int x = 0;
  myGLCD.clrScr();
  myGLCD.setColor(255, 255, 255);

  for (x = 0; x < NoBins; x++)
    myGLCD.drawPixel(x, TestArray[x] * (240 / 102));

  myGLCD.setTextSize(1);
  myGLCD.drawLine(0, 39, 10, 39);
  myGLCD.print("20dB", 15, 35);
  myGLCD.drawLine(0, 79, 10, 79);
  myGLCD.print("40dB", 15, 75);
  myGLCD.drawLine(0, 119, 10, 119);
  myGLCD.print("60dB", 15, 115);
  myGLCD.drawLine(0, 159, 10, 159);
  myGLCD.print("80dB", 15, 155);
  myGLCD.drawLine(0, 199, 10, 199);
  myGLCD.print("100dB", 15, 195);

  myGLCD.printNumI(UserLowerfreq / 1000, 5, 230);
  myGLCD.printNumI(UserUpperfreq / 1000, 278, 230);
  myGLCD.printNumI((UserUpperfreq + UserLowerfreq) / 2000, 138, 230);

}

Friday 3 August 2018

Homebrew 80/40m SSB/CW Base Rig

Audio Amplifier


Another audio amplifier option.



Double Balanced Mixers





Plessey Bidirectional IF Amplifiers


Mic Amp



RF Power Amp



Low Pass Filters




#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
#include <si5351.h>

const uint32_t band80mStart = 3500000;    // start of 80m
const uint32_t band80mEnd = 3900000;      // end of 80m
const uint32_t band40mStart = 7000000;    // start of 40m
const uint32_t band40mEnd = 7300000;      // end of 40m

const uint32_t bandInit =  3700000;       // where to initially set the frequency
volatile long oldfreq = 0;
volatile long old80mfreq = 3700000;
volatile long old40mfreq = 7100000;
volatile long currentfreq = 0;
volatile int updatedisplay = 0;
volatile int TX = 0;
volatile int oldTX = 0;
volatile int mode = 0;
volatile int oldmode = 0;
volatile int band = 0;
volatile int oldband = 0;

volatile uint32_t freq = bandInit ;     // this is a variable (changes) - set it to the beginning of the band
volatile uint32_t radix = 1000;         // how much to change the frequency by, clicking the rotary encoder will change this.

volatile uint32_t SSB_BFO_freq = 8999700;   // 8998050 (filter centre) + 2700/2 + 300
//volatile uint32_t SSB_BFO_freq = 9000300;   // 8998050 (filter centre) + 2700/2 + 300
volatile uint32_t CW_BFO_freq = 8997336;    // 8996750 (filter centre) + 700 - 114 (from test)
static const uint32_t CW_Filter_CentreFreq = 8996750;

// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int pushPin = 4;

// Mode switches
static const int BandPin = 5;
static const int ModePin = 6;

// Discrete Outputs
static const int CrystalFilterSelectPin = 10;
static const int PTTPin = 11;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;

// Instantiate the Objects
LiquidCrystal_I2C lcd(0x27, 16, 2);
Si5351 si5351;

void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);
  pinMode(ModePin, INPUT);
  pinMode(BandPin, INPUT);
  pinMode(PTTPin, INPUT);
  pinMode(CrystalFilterSelectPin, OUTPUT);

  // Set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);
  digitalWrite(ModePin, HIGH);
  digitalWrite(BandPin, HIGH);
  digitalWrite(PTTPin, HIGH);
  digitalWrite(CrystalFilterSelectPin, LOW);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Initialize the display
  lcd.begin();
  lcd.backlight();
  lcd.cursor();
  UpdateDisplay();
  delay(1000);

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_correction(35980);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
//  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_6MA);
//  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_6MA);  si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}


void loop()
{
  currentfreq = getfreq();                    // Interrupt safe method to get the current frequency

  if (currentfreq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = currentfreq;
  }

  if (digitalRead(pushPin) == LOW)
  {
    delay(10);
    while (digitalRead(pushPin) == LOW)
    {
      if (updatedisplay == 1)
      {
        UpdateDisplay();
        updatedisplay = 0;
      }
    }
    delay(50);
  }

  if (digitalRead(PTTPin) == HIGH)
    TX = 1;
  else
    TX = 0;

  if (digitalRead(ModePin) == LOW)
  {
    mode = 1;                                       // 0 = LSB, 1 = CW
    digitalWrite(CrystalFilterSelectPin, HIGH);     // Select CW filter
  }
  else
  {
    mode = 0;
    digitalWrite(CrystalFilterSelectPin, LOW);      // Select SSB filter
  }

  if (digitalRead(BandPin) == LOW)
    band = 1;                                       // 0 = 80m, 1 = 40m
  else
    band = 0;

  if (TX != oldTX)                              // Only update the display on mode change
  {
    //UpdateMode();
    UpdateDisplay();
    oldTX = TX;
  }

  if (mode != oldmode)                              // Only update the display on mode change
  {
    //UpdateMode();
    UpdateDisplay();
    SendFrequency();
    oldmode = mode;
  }

  if (band != oldband)                          // Only update the display on band change
  {
    // 0 = 80m, 1 = 40m
    if (band == 0)  // now 80m was 40m
    {
      old40mfreq = freq;
      freq = old80mfreq;
    }
    if (band == 1)  // now 40m was 80m
    {
      old80mfreq = freq;
      freq = old40mfreq;
    }
    UpdateDisplay();
    oldband = band;
  }
}




long getfreq()
{
  long temp_freq;
  cli();
  temp_freq = freq;
  sei();
  return temp_freq;
}


// Interrupt routines
void ISRrotAChange()
{
  if (digitalRead(rotAPin))
  {
    rotAval = 1;
    UpdateRot();
  }
  else
  {
    rotAval = 0;
    UpdateRot();
  }
}


void ISRrotBChange()
{
  if (digitalRead(rotBPin))
  {
    rotBval = 1;
    UpdateRot();
  }
  else
  {
    rotBval = 0;
    UpdateRot();
  }
}


void UpdateRot()
{
  switch (rotState)
  {

    case 0:                                         // Idle state, look for direction
      if (!rotBval)
        rotState = 1;                               // CW 1
      if (!rotAval)
        rotState = 11;                              // CCW 1
      break;

    case 1:                                         // CW, wait for A low while B is low
      if (!rotBval)
      {
        if (!rotAval)
        {
          // either increment radixindex or freq
          if (digitalRead(pushPin) == LOW)
          {
            updatedisplay = 1;
            if (radix == 1000000)
              radix = 100000;
            else if (radix == 100000)
              radix = 10000;
            else if (radix == 10000)
              radix = 1000;
            else if (radix == 1000)
              radix = 100;
            else if (radix == 100)
              radix = 10;
            else if (radix == 10)
              radix = 1;
            else
              radix = 1000000;
          }
          else
          {
            freq = (freq + radix);
            if (band == 0)          // 80m
              if (freq > band80mEnd)
                freq = band80mEnd;
            if (band == 1)          // 80m
              if (freq > band40mEnd)
                freq = band40mEnd;
          }
          rotState = 2;                             // CW 2
        }
      }
      else if (rotAval)
        rotState = 0;                               // It was just a glitch on B, go back to start
      break;

    case 2:                                         // CW, wait for B high
      if (rotBval)
        rotState = 3;                               // CW 3
      break;

    case 3:                                         // CW, wait for A high
      if (rotAval)
        rotState = 0;                               // back to idle (detent) state
      break;

    case 11:                                        // CCW, wait for B low while A is low
      if (!rotAval)
      {
        if (!rotBval)
        {
          // either decrement radixindex or freq
          if (digitalRead(pushPin) == LOW)
          {
            updatedisplay = 1;
            if (radix == 1)
              radix = 10;
            else if (radix == 10)
              radix = 100;
            else if (radix == 100)
              radix = 1000;
            else if (radix == 1000)
              radix = 10000;
            else if (radix == 10000)
              radix = 100000;
            else if (radix == 100000)
              radix = 1000000;
            else
              radix = 1;
          }
          else
          {
            freq = (freq - radix);
            if (band == 0)          // 80m
              if (freq < band80mStart)
                freq = band80mStart;
            if (band == 1)          // 80m
              if (freq < band40mStart)
                freq = band40mStart;
          }
          rotState = 12;                            // CCW 2
        }
      }
      else if (rotBval)
        rotState = 0;                               // It was just a glitch on A, go back to start
      break;

    case 12:                                        // CCW, wait for A high
      if (rotAval)
        rotState = 13;                              // CCW 3
      break;

    case 13:                                        // CCW, wait for B high
      if (rotBval)
        rotState = 0;                               // back to idle (detent) state
      break;
  }
}


void UpdateDisplay()
{
  lcd.setCursor(0, 0);
  lcd.print("        ");
  lcd.setCursor(0, 0);
  lcd.print(freq);
  lcd.setCursor(10, 0);
  lcd.print("ZL2CTM");

  lcd.setCursor(0, 1);
  lcd.print("   ");
  lcd.setCursor(0, 1);

  lcd.setCursor(0, 1);
  if (mode == 0)
    lcd.print("LSB");
  if (mode == 1)
    lcd.print("CW ");

  lcd.setCursor(5, 1);
  lcd.print("  ");
  lcd.setCursor(5, 1);
  if (TX == 1)
    lcd.print("TX");

  if (freq > 9999999)
  {
    if (radix == 1)
      lcd.setCursor(7, 0);
    if (radix == 10)
      lcd.setCursor(6, 0);
    if (radix == 100)
      lcd.setCursor(5, 0);
    if (radix == 1000)
      lcd.setCursor(4, 0);
    if (radix == 10000)
      lcd.setCursor(3, 0);
    if (radix == 100000)
      lcd.setCursor(2, 0);
    if (radix == 1000000)
      lcd.setCursor(1, 0);
  }
  if (freq <= 9999999)
  {
    if (radix == 1)
      lcd.setCursor(6, 0);
    if (radix == 10)
      lcd.setCursor(5, 0);
    if (radix == 100)
      lcd.setCursor(4, 0);
    if (radix == 1000)
      lcd.setCursor(3, 0);
    if (radix == 10000)
      lcd.setCursor(2, 0);
    if (radix == 100000)
      lcd.setCursor(1, 0);
    if (radix == 1000000)
      lcd.setCursor(0, 0);
  }
}


void SendFrequency()
{
  // CW_BFO_freq = 8997336;    // 8996750 (filter centre) + 700 - 114 (from test)
  // SSB_BFO_freq = 8999700;   // 8998050 (filter centre) + 2700/2 + 300
  // CW_Filter_CentreFreq = 8996750;

  if (mode == 1)      // CW
  {
    si5351.set_freq(((CW_Filter_CentreFreq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    si5351.set_freq((CW_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
  else                // SSB
  {
    si5351.set_freq(((SSB_BFO_freq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
}