Friday, February 8, 2019

New SNA Power Meter function update 2/17/19

Finally got around to working on the hardware and software for the new SNA.  I decided to build the hardware in stages and test each as I went along.  After getting the ESP32 module and display working, I started on the AD8307 power meter circuit.  One of the differences between my version and the UR5FFR circuit, is that I use a separate AD8307 instead of switching between inputs.  This made the board layout a lot easier, and I already had several AD8307s on hand. 

 I partially populated the input portion of the circuit board, and started working on the software.  I want to be able to use this for broadband measurements of low power signals in dBm., and with a 40 dB. tap  to measure levels up to around 100 Watts.  There will be four ranges   +15dBm with direct input, and 1, 10,  100 Watts using an external 40 dB tap.  Range selection is made by moving the Joystick in a up or down direction.  I decided I wanted to measure  Peak value and the Average power during a user selectable time window.  I currently have time windows of  0.1, 1, and 2.5 seconds. The time window is selected by moving the Joystick to the right or left.  The use of the Joystick for the input device has made the changing of parameters much easier than it had been when I was using a rotary encoder and switches. With the Joystick, I can easily control two different parameters without having to create a sub-menu.  I still have the ability to look for either a Short or Long press on the Joystick pushbutton .    A short press will mostly be used to toggle a function or operation on or off.  In all cases I have decided that a Long press will be used to get out of whatever modality or setting screen  you are in and return to the main or upper menu.

The power meter screen will use my standard simulated analog meter with the scale changing with the range selection.  The Peak reading during the time window will be displayed, along with the Average power over the entire time period.  I also display the current range and time window.

The software for this is a basic While loop.  It first uses the millis() function to get the start time then checks current time  before each time through the loop.   It exits the loop when the selected window time is reached. During each pass through the loop it checks for a higher peak reading and updates a sum and counter that are used to compute the average, I also store the last reading.  I compare the current reading with the previous to determine if I should redraw the needle and print current value.  This helps a lot with eliminating a flicker on the meter.

UPDATE 2/17/19

  tft.setTextColor( BLACK, WHITE);
  while ((millis() - startTime) < pmWindowTimes[pmWindow]) {  // sample for window 
    adc = analogRead(pmADCpin);     // get reading
    average = average + adc;    // add readings to compute average
    if (adc > peak)  peak = adc;   // check if a new peak reading
    count++;     // increment count for computing average
    pmDB = ADC2dBm(adc); // calculate dB value times 10
    if ((abs(lastAdc - adc)) > 4)  {  //  limit change size  to reduce jitter
      // erase old needle and draw in new position
    tft.fillRect(DB_MTR_X + 2, DB_MTR_Y + 30, DB_MTR_W - 4, DB_MTR_H - 40,  WHITE);
      if (pmRange == 0) {        // dBm  direct input
        plotNeedle(DB_MTR_X, DB_MTR_Y, DB_MTR_W, DB_MTR_H, adc, 3300, BLACK);
        lastAdc = adc;   // save last reading for comparrison
      else {             // Watts  using 40 dB tap
        pmWatt = dBm2Watt( pmDB);     // convert dbm reading to watts
        pmWatt = int( pmWatt * (1000 / pmRangeVal[pmRange]));    // prescale
        if (pmWatt > 1000)pmWatt = 1050;      // keep needle on meter face
        plotNeedle(DB_MTR_X, DB_MTR_Y, DB_MTR_W, DB_MTR_H, pmWatt , 1080, BLACK);
        lastWatt =   pmWatt;   // save last reading for comparrison
      tft.setCursor(120, 150);
      printPMValues( pmDB);
  average = average / count;       // compute average  and print
  pmAverage = ADC2dBm(average);    // convert adc readings and store in global 

  pmPeak = ADC2dBm(peak);

Made some changes to the loop that updates the display to reduce some of the jitter I was seeing.  Also took care of some minor issues, such as keeping the needle display on the  meter if  value goes over range.  After a lot of playing around, I was able to come up with a single function to print the power value correctly scaled for whatever range is selected.

void printPMValues( float value) {
  switch (pmRange) {
    case 0:                 // value directly in dBm.
      tft.print ( value / 10, 1);
      tft.println (" dBm.   ");
    case 1:                 // value in mWatt
      value = dBm2Watt( value );         // convert from dBm to watts
      tft.print (value * 1000, 0);       //  scale up for mw display
      tft.println (" mWatt   ");
    case 2:                 // value in Watts  2 decimal places
      value = dBm2Watt( value );         // convert from dBm to watts
      tft.print (value , 2);
      tft.println (" Watt   ");
    case 3:                  // value in Watts only one decimal place
      value = dBm2Watt( value );            // convert from dBm to watts
      tft.print ( value , 1);
      tft.println (" Watt   ");


 This function is used for the value  displayed on the meter, and the Peak and Average values.

I did a manual calibration using a signal source and a fixed attenuator to determine the ADC counts per dB.  Later I will add a calibration function and have the  value stored in eeprom.

Next I have decided to reuse some of these functions and add a modulation monitor.  This will display the power output on a simulated oscilloscope screen.  I think I will have both a Rectified and a RF envelope display. This should help in setting Mic. gain levels to prevent distortion.