Sunday, December 04, 2016

Arduino Scalar Network Analyser - boxed and DDS/power meter mode code.

I jut boxed the Arduino "network analyser" following previous post and also changed the code so it can also double as a standalone power meter and VFO.

Here's measuring the output from the AD9850 in "DDS/meter" mode. DDS out on the left BNC and AD8307 input on the right, labels will be placed!

Still need to calibrate the power meter in the code, it's not correct, the scope was showing more output power than the -52 dBm on display.

Here's the noise floor of the AD8307:
 the value is more in-line with the IC spec's, in any case still needs calibration.
 
Frequency can be change by means of the rotary encoder (red on the right) and the mode between the VNA and DDS/power meter is changed by the black press button. Default mode on start-up is VNA/computer control mode like bellow:


Inside during first stages boxing, AD8307 is inside the screened box:

Code is bellow:
------
// AD9850 and AD8307 VNA
//
// for manual control and computer control with DIY60MHzSNA.py
// direct link for python software here: http://www.changpuak.ch/electronics/Arduino/NWA-DUE/DIY60MHzSNA.zip
// for the ad8307 and buffer amp, here: http://rheslip.blogspot.ie/2015/08/the-simple-scalar-network-analyser.html
//
// with LCD code and dds code from my vlf_receiver with ad9850
//
// From the original code and description bellow:
// http://www.changpuak.ch/electronics/Arduino-Shield-TOBI.php
//
// Ricardo / CT2GQV / 2016


#include   // Comes with Arduino IDE
#include
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// 0x3f the yellow lcd  
  
int AD8307 = A0;  // for the Uno
char inputcmd[100];  // serial data input
int cmdindex=0;

#define W_CLK 8       // Pin 8 - connect to AD9850 module word load clock pin (CLK)
#define FQ_UD 9       // Pin 9 - connect to freq update pin (FQ)
#define DATA 10       // Pin 10 - connect to serial data load pin (DATA)
#define RESET 11      // Pin 11 - connect to reset pin (RST).

#define BUTTON_MODE 7 // digital pin 7 to change from computer control to manual control.
int mode=0; // starts on mode 0, VNA then if mode 1 will be manual control for power meter and dds oscillator
int val=1; // keeps the mode value of the button, starts high due to pull up resistor
int lastmode = 0;

#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

// encoder addon
 // from http://bildr.org/2012/08/rotary-encoder-arduino/
 int encoderPin1 = 3; // D3 nano
 int encoderPin2 = 2; // D2 nano
 int encoderSwitchPin = 4; //push button switch // D4
 volatile int lastEncoded = 0;
 volatile long encoderValue = 0;
 long lastencoderValue = 0;
 int lastMSB = 0;
 int lastLSB = 0;

int station_number = 0; // start station from the stations array... code from vlf receiver
int maxstations = 11;
// pre populated common test frequencies
double stations[11]={ 1000000,   10101000, 9000000 ,  455000,  8000000,     18160000,   77500,      14200000,   4000000,   666000 ,  518000,};
//                    wwv         rtty      9Mhz IF    455 IF    IF vlf        17m         dcf         20m         IF 4Mhz   rdp        navtex
int fstep = 5; // frequency step Hertz
int last_station = station_number;  // last station equals startup station number

 double Freq = 10000000;
 double BFO = 0;       // needs calibration 2.3Khz to be in freq // 249.73 - 252 AM
 double fcomp = 2390; //  2.3 frequency diff for sideband and ad9850 offset from real.
 double lastFreq = 0; // keeps the last freque for not refreshing constantly the display

float dbmvalue=0; // to update on the LCD


 void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //LSB = least significant bit
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
  // update frequency, + or - depending on BFO side + or - 8 Mhz injection so allways increment to the same side // reverse in the last if statement
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) {encoderValue ++; Freq+=fstep;};
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) {encoderValue --; Freq-=fstep;};
  lastEncoded = encoded; //store this value for next time
 
  newfreqtoad9850(); // update the ad9850 with the new frequency 
}

void newfreqtoad9850() // sets new f on ad9850 and add-s remove the compensation for the offset
  {
 // sendFrequency(Freq-fcomp);  // fcomp is the error of the ad9850 and also compensates the filter. can be calculated if removed and beat agains a know freq.
  // here we use the setfrequency of the original code and not the vlf code.
 SetFrequency(Freq-fcomp);
  };


void dBm_power()
{
  // needs calibration.....
  float dBm=0;
  int i;
  for (i=0;i<20 analogread="" average="" br="" dbm="" float="" i="" nbsp="">  dBm=dBm/i;
  // dBm = dBm - 869; // original code
  dBm = dBm - 869;
  dBm = ( dBm * 0.1014342 ) - 6.6 ;   
  Serial.print(" "); // may help Python parser
  Serial.print(dBm);
  Serial.println(" "); // may help Python parser
  dbmvalue = dBm; // we need to pass this value to an outside variable to update lcd when mode = 1
}
void raw_power()
{
  Serial.print(" "); // may help Python parser
  Serial.print(analogRead(AD8307));
  Serial.println(" "); // may help Python parser
}
  // transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8 data="" i="">>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }


// frequency calc from datasheet page 8 = * /2^32
// my original code was senfrequency();
void SetFrequency(double frequency) {
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850
  for (int b=0; b<4 b="" freq="">>=8) {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
}


void setup()
{
 Serial.begin(9600);  
 pinMode(encoderPin1, INPUT);
 pinMode(encoderPin2, INPUT);
 pinMode(encoderSwitchPin, INPUT);
 digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
 digitalWrite(encoderPin2, HIGH); //turn pullup resistor on
 digitalWrite(encoderSwitchPin, HIGH); //turn pullup resistor on  
 attachInterrupt(0, updateEncoder, CHANGE);
 attachInterrupt(1, updateEncoder, CHANGE);
 pinMode(FQ_UD, OUTPUT);
 pinMode(W_CLK, OUTPUT);
 pinMode(DATA, OUTPUT);
 pinMode(RESET, OUTPUT);
 pinMode(BUTTON_MODE, INPUT); // mode button connected to digital 7
 pulseHigh(RESET);
 pulseHigh(W_CLK);
 pulseHigh(FQ_UD);  // this pulse enables serial mode - Datasheet page 12 figure 10
 SetFrequency(stations[station_number]);
 //SetFrequency(1000000); // 1 MHz default using the original code, wen mode is dds it will start on 10Mhz
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("VNA    CT2GQV");
lcd.setCursor(0,1);
lcd.print("USB    2016");
}

void loop()   // Arduino superloop - where everything gets done
{
  char ch;
long int temp;
// VNA computer control
if (mode == 0) {
   if (lastmode==0) { // we need to print mode again on lcd
   lcd.begin(16,2); lcd.setCursor(0,0);lcd.print("VNA    CT2GQV");
   lcd.setCursor(0,1);  lcd.print("USB    2016"); }
   lastmode=1;  // no need to update the previous info on display
// serial command interpreter
// enter a number to set the frequency, anything else shows power
  while (Serial.available()) {
    ch=(char)Serial.read();
    if (((ch >= '0') && (ch <= '9')) || ((ch >= 'A') &&
            (ch <= 'Z'))) inputcmd[cmdindex++]=ch;
    if (ch == '\n') {    // parse command if its a newline
      inputcmd[cmdindex]=0; // terminate the string
      if ((temp=atol(inputcmd)) > 0) SetFrequency(temp);
      else dBm_power(); 
      //else raw_power();  // python has trouble with floats
      cmdindex=0; // reset command line     
    }
  } // end while
}; // end mode 0 VNA
// end VNA computer control

// read the digital button for going down (to change mode)
  val = digitalRead(BUTTON_MODE);  // read input value
  if (val == LOW) { // button press so lets change mode
   temp = mode; // just dummy variable that keeps mode so we can change it..
   lastmode = 0; // keeps the last mode just to help if something needs to be updated on the lcd to save cycles.
    if (mode == 1) { temp=0; };  // we switch temp so then we can switch mode to be equal to mode, if not next line will reverse.
    if (mode == 0) { temp=1; };
    mode = temp; // now we can switch mode safely
    if (mode == 1) {  Serial.println(" -60 "); } // this will give a -60 spike on the DIYVNA display..no need since the lcd will show also
    if (mode == 0) {  Serial.println(" -40 "); }
    delay(250); // software debounce...
   }

// DDS control and power meter
if (mode == 1) { // read the power and print on lcd and serial
  delay(90); // let's not refresh to fast...
  dBm_power(); // outputs to serial the ad8307 meter and keeps dbmvalue variable

  //if frequency "channel"  is called
  if(digitalRead(encoderSwitchPin)){
    //button is not being pushed
  }else{
    station_number++; if (station_number > maxstations)station_number=0;
    Freq=stations[station_number];
    SetFrequency(stations[station_number]);
    delay(250); // software debounce...
  }
 
 // if mode change on last iteraction then print the following, if not, no need.
 if (lastmode == 0) {    // neans last mode was VNA so we need to put everything in lcd
   lcd.setCursor(0,0);
   lcd.print("f:              "); lcd.setCursor(3,0); lcd.print((Freq/1000),3);// change latter to show the frequency
   lcd.setCursor(0,1);
   lcd.print("dBm: ");
  } // end if
 // this what we need allways to print if frequency changed
 if (lastFreq != Freq) {   lcd.setCursor(3,0); lcd.print((Freq/1000),3); lastFreq=Freq; }
 // we can print dbm value all the time, flicker is not noticable
  lcd.setCursor(5,1); lcd.print(dbmvalue);
  lastmode = 1; // now that previously we cleared the display no need to do again on next time.
}
// end DDS control and power meter


} // end main loop... that's it!

-----
LCD connected as bellow:




Have a nice week!





No comments: