<?php
/*!*******************************************************************************
*! FILE NAME  : diag_utils.php
*! DESCRIPTION: Provides development/diagnostic data
*! Copyright (C) 2008 Elphel, Inc
*! -----------------------------------------------------------------------------**
*!
*!  This program is free software: you can redistribute it and/or modify
*!  it under the terms of the GNU General Public License as published by
*!  the Free Software Foundation, either version 3 of the License, or
*!  (at your option) any later version.
*!
*!  This program is distributed in the hope that it will be useful,
*!  but WITHOUT ANY WARRANTY; without even the implied warranty of
*!  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*!  GNU General Public License for more details.
*!
*!  You should have received a copy of the GNU General Public License
*!  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*!  $Log: diag_utils.php,v $
*!  Revision 1.5  2010/04/30 21:09:00  elphel
*!  added forced synchronization on/off links, parsedit.php link
*!
*!  Revision 1.4  2008/11/30 21:56:39  elphel
*!  Added enforcing limit on the overall gains in the color channels, storage of exposure and gains in the histograms cache (to be used with autoexposure/white balance)
*!
*!  Revision 1.3  2008/11/30 06:41:43  elphel
*!  changed default IP back to 192.168.0.9 (temporarily was 192.168.0.7)
*!
*!  Revision 1.2  2008/11/30 05:02:27  elphel
*!  wrong links
*!
*!  Revision 1.1.1.1  2008/11/27 20:04:03  elphel
*!
*!
*!  Revision 1.1  2008/11/27 09:29:22  elphel
*!  new file - diag_utils.php that includes somewhat cleaned-up version of utilities from framepars.php. That file installation  is moved to attic
*!
*!
*/

    $imgsrv="http://".$_SERVER['SERVER_ADDR'].":8081";
    if (count($_GET)==0) {
       showUsage();
       exit (0);
    }
//$_SERVER["SERVER_ADDR"] . ":8081
    $elp_const=get_defined_constants(true);
    $elp_const=$elp_const["elphel"];
    foreach($_GET as $key=>$value)  switch($key) {
       case "profile":           profileShow(myval($value));                          break;
       case "histogram_direct":
       case "histogram_reverse":
       case "gamma_direct":
       case "gamma_reverse":     showHistGamma ($key,floatval($value));               break;
       case "jpegheader":        showJpegHeader();                                    break;
       case "constants":         echo "<pre>\n";print_r($elp_const); echo "</pre>\n"; break;
       case "frame":             printRawFrame(myval($value));                        break;
       case "hist_raw":          printRawHistogram(0xfff,myval($value));              break;
       case "hist":
       case "histogram":         printHistogram(myval($value));                       break;
       case "gamma":             printGammaStructure();                               break;
       case "gamma_page":        printRawGamma(myval($value));                        break;
       default:                  showUsage();
     }
exit (0);
///==========================================================================================================
function showUsage() {
global $imgsrv;
         echo <<<USAGE
   <p>This is a collection of tools for the 8.0 software development/testing, you can provide parameters in HTTP GET request.</p>
   <p>Here are some of the current options</p>
   <ul>
    <li><a href="?constants">constants</a> - list all defined PHP constants related to Elphel camera</li>
    <li><a href="?profile=50">profile=50</a> - show last 50 (up to ELPHEL_CONST_PASTPARS_SAVE_ENTRIES=2048) profile entries - recorded if profiling is enabled</li>
    <li><a href="?histogram_direct=0.5">histogram_direct=0.5</a> - show part of the pixels below specified output value (on 0..1.0 scale - 0.5 in this example) for each color channel </li>
    <li><a href="?histogram_reverse=0.5">histogram_reverse=0.5</a> - show pixel output value (on 0..1.0 scale) so that specified part of all pixels have output (after gamma conversion) valuebelow it, for each color channel </li>
    <li><a href="?gamma_direct=0.5">gamma_direct=0.5</a> - show output pixel value (on 0..1.0 scale) for the specified (0..1.0 scale) sensor pixel value, for each color channel </li>
    <li><a href="?gamma_reverse=0.5">gamma_reverse=0.5</a> - show what sensor pixel value would produce specified output (after gamma conversion) value (on 0..1.0 scale), for each color channel </li>
    <li><a href="?jpegheader">jpegheader</a> - show data in the current JPEG header</li>
    <li><a href="?frame=0">frame=0</a> - show raw data for the frame parameters internal data. There are 8 parameter pages used in sequence, so only the 3 least significant bits of the frame number are used</li>
    <li><a href="?histogram=prev">histogram=prev</a> - Show histogram data for the specified frame - in addition to the absolute frame number you may specify prev1...prev6. Giving future or too long ago frame will result in an (uncaught) error</li>
    <li><a href="?hist_raw=prev">hist_raw=prev</a> - Show raw histogram data for the specified frame - in addition to the absolute frame number you may specify prev1...prev6. Giving future or too long ago frame will result in an (uncaught) error. It shows the same data as <a href="?histogram=prev">histogram=prev</a> - just uses different access to the histogram arrays.</li>
    <li><a href="?gamma">gamma</a> - show structure of the gamma tables cache. Among othe data it provides links to the individual gamma tables pages. This command takes morfe than 10 seconds to complete.</li>
    <li><a href="?gamma_page=255">gamma_page=255</a> - contents of the gamma tables cache page (0 - index page, 1..255 - cache pages). Individual pages links are provided by the <a href="?gamma">?gamma</a>  command. Some of the tables in the cache (reverse, FPGA) may be missing if they were never requested.</li> 
  </ul>
   <p>There are additional development pages links available:</p>
   <ul>
    <li><a href="parsedit.php">parsedit.php</a> - this page includes links to multiple useful parameter controls as well as explanation how to create custom control pages by just modifying the URL line</li>
    <li><a href="/tuneseq.php">tuneseq.php</a> - run-time tuning of sequencer latencies;</li>
    <li><a href="$imgsrv">imgsrv</a> getting images from the camera internal buffer (you have to acquire them there first)</li>
    <li><i>forced synchronization mode</i>: <a href="/fpga.php?a=0x4e&d=0x60">ON</a> and <a href="/fpga.php?a=0x4e&d=0x40">OFF</a> (default). When this mode is activated the camera will force re-synchronization between the sensor and the FPGA compressor at each frame start. Normally it is not needed and loosing synchronization is usually an indication of a software/fpga bug (there are some rare conditions when this is normal), this is why this mode is disabled by default so the problems would reveal themselves.</li>
    <li><a href="/var/klog.txt">var/klog.txt</a> - read kernel messages (you first need to telnet to the camera and run "printk_mod &amp;" and enable some of the DEBUG bits)</li>
   </ul>
   <h4>Make sure to run "printk_mod &amp;" before enabling debug bits - some are from interrupt service routine, and default printk() output to console can really mess up things and make the camera &quot;freeze&quot;.</h4>


USAGE;
}

function showJpegHeader() {
    $xml = new SimpleXMLElement("<?xml version='1.0'?><framepars/>");
    $circbuf_file=fopen("/dev/circbuf","r");
    fseek($circbuf_file,ELPHEL_LSEEK_CIRC_LAST,SEEK_END);
    $jpeg_start=ftell($circbuf_file);
    $xml->addChild ('circbuf_pointer',sprintf("0x%x (0x%x)",$jpeg_start,$jpeg_start>>2));
    fclose($circbuf_file);
    $header_file=fopen("/dev/jpeghead","r");
/// Now select right frame (different frames may have different header sizes)
    fseek($header_file,$jpeg_start+1,SEEK_END); /// selects frame, creates header
    fseek($header_file,0,SEEK_END);   /// positions to the end
    $header_size=ftell($header_file); ///
    $xml->addChild ('header_size',$header_size);
    fseek($header_file,0,SEEK_SET);   /// positions to the beginning
    $header=fread ($header_file,8192);
    $xml->addChild ('header_read_length',strlen($header));
    fclose($header_file);
    $aheader=unpack('C*',$header);
    for ($i=0; $i<count($aheader) ;$i+=16){
      $d="";
      for ($j=$i; ($j<$i+16) && ($j<count($aheader)); $j++)  $d.=sprintf(" %02x",$aheader[$j+1]);
      $xml->addChild (sprintf('header%03x',$i),$d);
    }
    $rslt=$xml->asXML();
    header("Content-Type: text/xml");
    header("Content-Length: ".strlen($rslt)."\n");
    header("Pragma: no-cache\n");
    printf($rslt);
}

function showHistGamma ($key,$value) {
          $xml = new SimpleXMLElement("<?xml version='1.0'?><framepars/>");
          switch($key) {
            case "histogram_direct":
              $xml->addChild ('histogram_direct_r', elphel_histogram(0,$value));
              $xml->addChild ('histogram_direct_g', elphel_histogram(1,$value));
              $xml->addChild ('histogram_direct_gb',elphel_histogram(2,$value));
              $xml->addChild ('histogram_direct_b', elphel_histogram(3,$value));
              break;
            case "histogram_reverse":
              $xml->addChild ('histogram_reverse_r', elphel_reverse_histogram(0,$value));
              $xml->addChild ('histogram_reverse_g', elphel_reverse_histogram(1,$value));
              $xml->addChild ('histogram_reverse_gb',elphel_reverse_histogram(2,$value));
              $xml->addChild ('histogram_reverse_b', elphel_reverse_histogram(3,$value));
              break;
            case "gamma_direct":
              $xml->addChild ('gamma_direct_r', elphel_gamma(0,$value));
              $xml->addChild ('gamma_direct_g', elphel_gamma(1,$value));
              $xml->addChild ('gamma_direct_gb',elphel_gamma(2,$value));
              $xml->addChild ('gamma_direct_b', elphel_gamma(3,$value));
              break;
            case "gamma_reverse":
              $xml->addChild ('gamma_reverse_r', elphel_reverse_gamma(0,$value));
              $xml->addChild ('gamma_reverse_g', elphel_reverse_gamma(1,$value));
              $xml->addChild ('gamma_reverse_gb',elphel_reverse_gamma(2,$value));
              $xml->addChild ('gamma_reverse_b', elphel_reverse_gamma(3,$value));
              break;
          }
         $rslt=$xml->asXML();
         header("Content-Type: text/xml");
         header("Content-Length: ".strlen($rslt)."\n");
         header("Pragma: no-cache\n");
         printf($rslt);
}

function profileShow($num_entries) {
//echo "<pre>";
//http://192.168.0.9/parsedit.php?PROFILING_EN=1
          $prof_template=array(
                 "PROFILE00"=>0,"PROFILE01"=>0,
                 "PROFILE02"=>0,"PROFILE03"=>0,
                 "PROFILE04"=>0,"PROFILE05"=>0,
                 "PROFILE06"=>0,"PROFILE07"=>0,
                 "PROFILE08"=>0,"PROFILE09"=>0,
                 "PROFILE10"=>0,"PROFILE11"=>0,
                 "PROFILE12"=>0,"PROFILE13"=>0,
                 "PROFILE14"=>0,"PROFILE15"=>0
          );
          $now=elphel_get_frame()-2; /// data is available 2 frames behind
          $time_start=elphel_get_fpga_time();
          $prof_raw=array();
          for ($i=$now-$num_entries-1;$i<=$now;$i++) {
            $prof_raw[$i]=elphel_get_P_arr($prof_template,$i);
          }
          $time_end=elphel_get_fpga_time();
          $prof=array();
          for ($i=$now-$num_entries;$i<=$now;$i++) {
            $prof[$i]=array ( "dt0"=>($prof_raw[$i]["PROFILE00"]-$prof_raw[$i-1]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE01"]-$prof_raw[$i-1]["PROFILE01"]),
                              "dt1"=>($prof_raw[$i]["PROFILE02"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE03"]-$prof_raw[$i]["PROFILE01"]),
                              "dt2"=>($prof_raw[$i]["PROFILE04"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE05"]-$prof_raw[$i]["PROFILE01"]),
                              "dt3"=>($prof_raw[$i]["PROFILE06"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE07"]-$prof_raw[$i]["PROFILE01"]),
                              "dt4"=>($prof_raw[$i]["PROFILE08"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE09"]-$prof_raw[$i]["PROFILE01"]),
                              "dt5"=>($prof_raw[$i]["PROFILE10"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE11"]-$prof_raw[$i]["PROFILE01"]),
                              "dt6"=>($prof_raw[$i]["PROFILE12"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE13"]-$prof_raw[$i]["PROFILE01"]),
                              "dt7"=>($prof_raw[$i]["PROFILE14"]-$prof_raw[$i]["PROFILE00"])*1000000+($prof_raw[$i]["PROFILE15"]-$prof_raw[$i]["PROFILE01"]));
           foreach ($prof[$i] as $key=>$value) if ($prof[$i][$key]<0)$prof[$i][$key]="";
         }
 if (!elphel_get_P_value(ELPHEL_PROFILING_EN)) {
echo <<<PROFILE_ENABLE
   <p><i>Interrupt service/tasklet profiling is currently disabled. You may enable it by following this link:
   <a href="/parsedit.php?PROFILING_EN=1">/parsedit.php?PROFILING_EN=1</a>
   </i></p>
PROFILE_ENABLE;
 }
echo <<<CAPTION
<p>reading profile time start=$time_start </p>
<p>reading profile time end=$time_end </p>
<ol>Profiling interrupt/tasklet execution time in microseconds, starting from the start of the frame
  <li>after updating frame pointers, Exif, parameters structures (IRQ service)</li>
  <li>start of the tasklet</li>
  <li>after Y histogram (G1) load from the FPGA (if enabled)</li>
  <li>after processing parameters (actions triggered by the parameter changes), </li>
  <li>after C histograms (R,G2,B) load from the FPGA (if enabled)</li>
  <li>When parameters are started to be written by appliaction(s) - overwritten if several calls take place during the same frame</li>
  <li>When parameters are finished to be written by appliaction(s) (may be  overwritten)</li>
</oul>
<br/><br/>
CAPTION;

 printf ("<table border='1'><tr><td>Frame</td><td>(hex)</td><td>Period</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td></tr>\n");
 for ($i=$now-$num_entries;$i<=$now;$i++) {
   printf("<tr style='align:right'><td>%d</td><td>%08x</td><td>%d</td>",$i,$i,$prof[$i]["dt0"]);
   for ($j=1;$j<8;$j++) {
     if ($prof[$i]["dt".$j]) printf("<td>%d</td>",$prof[$i]["dt".$j]);
     else                    printf("<td>&nbsp;</td>");
   }
   printf("</tr>\n");
 }
 printf("</table>");
//echo "<pre>";print_r($prof_raw);echo"</pre>\n";

}




function printGammaStructure() {
  $gammaStructure=getGammaStructure();
   printf("<table \"border=1\">\n");
   printf(       "<tr><td>oldest_non_scaled</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>newest_non_scaled</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>oldest_all</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>newest_all</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>non_scaled_length</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>num_locked</td><td><b>%d</b></td></tr>\n"
                ."<tr><td>locked_col 0</td><td><b>%d</b></td>\n"
                ."<tr><td>locked_col 1</td><td><b>%d</b></td>\n"
                ."<tr><td>locked_col 2</td><td><b>%d</b></td>\n"
                ."<tr><td>locked_col 3</td><td><b>%d</b></td>\n"
                ."</table>\n",
                $gammaStructure["oldest_non_scaled"],
                $gammaStructure["newest_non_scaled"],
                $gammaStructure["oldest_all"],
                $gammaStructure["newest_all"],
                $gammaStructure["non_scaled_length"],
                $gammaStructure["num_locked"],
                $gammaStructure["locked_col"][0],
                $gammaStructure["locked_col"][1],
                $gammaStructure["locked_col"][2],
                $gammaStructure["locked_col"][3]
);
    printf("<br/><br/>\n");

    printf("<table \"border=1\">\n");
//    printf("<tr><td>index           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["index"]);printf("</tr>\n");
    printf("<tr><td>index           </td>\n"); foreach ($gammaStructure["entries"] as $entry)
       printf ("<td><a href='?gamma_page=%d'><b>%d</b></a></td>",$entry["index"],$entry["index"]);printf("</tr>\n");

    printf("<tr><td>hash32           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%08x</b></td>",$entry["hash32"]);printf("</tr>\n");
    printf("<tr><td>scale           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%01.3f</b></td>",$entry["scale"]);printf("</tr>\n");
    printf("<tr><td>gamma           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%01.3f</b></td>",$entry["gamma"]);printf("</tr>\n");
    printf("<tr><td>black           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["black"]);printf("</tr>\n");
    printf("<tr><td>valid           </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>0x%x</b></td>",$entry["valid"]);printf("</tr>\n");
    printf("<tr><td>locked          </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>0x%8x</b></td>",$entry["locked"]);printf("</tr>\n");
    printf("<tr><td>this_non_scaled </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["this_non_scaled"]);printf("</tr>\n");
    printf("<tr><td>newer_non_scaled</td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["newer_non_scaled"]);printf("</tr>\n");
    printf("<tr><td>older_non_scaled</td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["older_non_scaled"]);printf("</tr>\n");
    printf("<tr><td>newer_all       </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["newer_all"]);printf("</tr>\n");
    printf("<tr><td>older_all       </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["older_all"]);printf("</tr>\n");
    printf("<tr><td>oldest_scaled   </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["oldest_scaled"]);printf("</tr>\n");
    printf("<tr><td>newest_scaled   </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["newest_scaled"]);printf("</tr>\n");
    printf("<tr><td>newer_scaled    </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["newer_scaled"]);printf("</tr>\n");
    printf("<tr><td>older_scaled    </td>\n"); foreach ($gammaStructure["entries"] as $entry) printf ("<td><b>%d</b></td>",$entry["older_scaled"]);printf("</tr>\n");
    printf("</table>\n");
  }

function getGammaStructure() {
        $gammas_file=fopen("/dev/gamma_cache","r");
        fseek($gammas_file,0,SEEK_END);
        $numberOfEntries=ftell($gammas_file);
        fclose($gammas_file);
        $gammaStructure=array();
        $g_raw=elphel_gamma_get_raw(0);
        $g_raw_ul=unpack('V*',$g_raw);
        $gammaStructure["oldest_non_scaled"]=$g_raw_ul[5];
        $gammaStructure["newest_non_scaled"]=$g_raw_ul[6];
        $gammaStructure["oldest_all"]=       $g_raw_ul[7];
        $gammaStructure["newest_all"]=       $g_raw_ul[8];
        $gammaStructure["non_scaled_length"]=$g_raw_ul[9];
        $gammaStructure["num_locked"]=       $g_raw_ul[10];
        $gammaStructure["locked_col"]=  array ($g_raw_ul[11],$g_raw_ul[12],$g_raw_ul[13],$g_raw_ul[14]);
        $gammaStructure["entries"]=  array ();
        for ($i=1; $i<$numberOfEntries; $i++) {
          $g_raw=elphel_gamma_get_raw($i);
          $g_raw_ul=unpack('V*',$g_raw);
          if ($g_raw_ul[ 4]>=0) { /// >=0 if ever used. This field seems to do nothing in the code.
            $hash32= $g_raw_ul[1];
            $gammaStructure["entries"][$i]=  array (
              "index" =>                 $i,
              "hash32"=>                 $hash32,
              "scale" =>                 ($hash32 & 0xffff)/1024.0,
              "gamma" =>                 (($hash32 >> 16) & 0xff)/100.0,
              "black" =>                 (($hash32 >> 24) & 0xff),
              "valid" =>                 $g_raw_ul[ 2], /// 0 - table invalid, 1 - table valid +2 for table locked (until sent to FPGA)
              "locked" =>                $g_raw_ul[ 3], /// bit frame+ (color<<3) locked for color/frame
              "this_non_scaled" =>       $g_raw_ul[ 4], /// 0 for non-scaled, others - (for scaled) - pointer to the corresponding non-scaled 
/// This is non-scaled (gamma data is full 16-bit)
              "newer_non_scaled" =>      $g_raw_ul[ 5], /// table type (non-scaled prototype) used later than this one
              "older_non_scaled" =>      $g_raw_ul[ 6], /// table type (non-scaled prototype) used before this one

              "newer_all" =>             $g_raw_ul[ 7], /// newer in a single  chain of all scaled tables, regardless of the prototype
              "older_all" =>             $g_raw_ul[ 8], /// older in a single  chain of all scaled tables, regardless of the prototype
///Next two pairs are the same (union)
              "oldest_scaled" =>         $g_raw_ul[ 9], /// oldest derivative of this prototype (scaled)
              "newest_scaled" =>         $g_raw_ul[10], /// newest derivative of this prototype (scaled)
              "newer_scaled" =>          $g_raw_ul[ 9], /// table type (non-scaled prototype) used later than this one
              "older_scaled" =>          $g_raw_ul[10] /// table type (non-scaled prototype) used before this one
              );
            }
          }
          return $gammaStructure;
        }

function printRawGamma($page=0) {
        $g_raw=elphel_gamma_get_raw($page);
//var_dump()
        $g_raw_ul=unpack('V*',$g_raw);
        echo "<pre>\n";
        printf ("Gamma cache page %d, length=%d\n",$page, strlen($g_raw));
        $a=1; /// unpack started with index 1
        $hash32= $g_raw_ul[$a++];
        $scale= ($hash32 & 0xffff)/1024.0;
        $gamma= (($hash32 >> 16) & 0xff)/100.0;
        $black= (($hash32 >> 24) & 0xff);
        printf ("hash32= %08x (scale=%f gamma=%f black=%d)\n",$hash32,$scale,$gamma,$black);
        $valid= $g_raw_ul[$a++];
        printf ("valid=%d, locked=%d\n",$valid & 1, $valid & 2);

        $locked= $g_raw_ul[$a++];
        printf ("locked= 0x%x (for frame=%d/color=%d)\n",$locked, $locked & 7, ($locked>>3) & 3);

        $this_non_scaled=$g_raw_ul[$a++]; /// 0 for non-scaled
        printf ("this_non_scaled=%d\n",$this_non_scaled);
        if ($page==0) {
          printf ("oldest_non_scaled=%d\n",$g_raw_ul[$a++]);
          printf ("newest_non_scaled=%d\n",$g_raw_ul[$a++]);
        } else {
          printf ("newer_non_scaled=%d\n",$g_raw_ul[$a++]);
          printf ("older_non_scaled=%d\n",$g_raw_ul[$a++]);
        }

        if ($page==0) {
          printf ("oldest_all=%d\n",$g_raw_ul[$a++]);
          printf ("newest_all=%d\n",$g_raw_ul[$a++]);
        } else {
          printf ("newer_all=%d\n",$g_raw_ul[$a++]);
          printf ("older_all=%d\n",$g_raw_ul[$a++]);
        }

        if ($page==0) {
          printf ("non_scaled_length=%d\n",$g_raw_ul[$a++]); /// current number of different hash values
          printf ("num_locked=%d\n",$g_raw_ul[$a++]);        /// number of nodes locked (until table sent to FPGA)
        } else if ($this_non_scaled==0){
          printf ("oldest_scaled=%d\n",$g_raw_ul[$a++]);
          printf ("newest_scaled=%d\n",$g_raw_ul[$a++]);
        } else {
          printf ("newer_scaled=%d\n",$g_raw_ul[$a++]);
          printf ("older_scaled=%d\n",$g_raw_ul[$a++]);
        }
///data tables
        if ($page==0) {
          printf ("\nTable of locked indexes\n");
          for ($color=0;$color<4; $color++) {
//            for ($frame=0;$frame<8; $frame++) {
              printf (" %4d",$g_raw_ul[$a++]);
//            }
//            printf ("\n");
          }
          printf ("\n");
/// no need to dump the rest - it is unused in the page 0
          printf ("\n\nUnused area on page 0:");
//          for ($i=0; $i<417; $i++) {
          for ($i=0; $i<445; $i++) {
            if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
            $d=$g_raw_ul[$a++];
            printf (" %08x",$d);
          }

        } else {
          printf ("\nGamma table (direct):");
          for ($i=0; $i<129; $i++) {
            if (($i & 0x07)==0) printf ("\n0x%03x:",$i*2);
            $d=$g_raw_ul[$a++];
            printf (" %04x %04x",$d & 0xffff, ($d>>16) & 0xffff );
          }
          printf ("\n\nGamma table (reverse):");
          for ($i=0; $i<64; $i++) {
            if (($i & 0x03)==0) printf ("\n0x%03x:",$i*4);
            $d=$g_raw_ul[$a++];
            printf (" %02x %02x %02x %02x",$d & 0xff, ($d>>8) & 0xff, ($d>>16) & 0xff, ($d>>24) & 0xff);
          }

          printf ("\n\nFPGA gamma data:");
          for ($i=0; $i<256; $i++) {
            if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
            $d=$g_raw_ul[$a++];
            printf (" %05x",$d);
          }
        }
        echo "</pre>\n";
}

function printHistogram($frame) {
     if (!$frame) $frame=elphel_get_frame()-1;
     $colors=array(0=>"R",1=>"G",2=>"GB",3=>"B");
     $h_arr=elphel_histogram_get(0xfff,$frame);
     $a=0;
     $offset2sum=1024+255; /// last in cumulative histogram for the same color
     echo "<pre>\n";
     for ($color=0;$color<4;$color++) {
       printf("\nhistogram for color #%d %s, Total number of pixels=%d (0x%x):",$color,$colors[$color],$h_arr[$a+$offset2sum],$h_arr[$a+$offset2sum]);
       for ($i=0; $i<256; $i++) {
         if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
         printf (" %05x",$h_arr[$a++]);
       }
       printf ("\n");
     }
     for ($color=0;$color<4;$color++) {
       printf("\ncumulative histogram for color #%d %s:",$color,$colors[$color]);
       for ($i=0; $i<256; $i++) {
         if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
         printf (" %08x",$h_arr[$a++]);
       }
       printf ("\n");
     }
     for ($color=0;$color<4;$color++) {
       printf("\npercentile for color #%d %s:",$color,$colors[$color]);
       for ($i=0; $i<256; $i++) {
         if (($i & 0x01f)==0) printf ("\n0x%03x:",$i);
         printf (" %02x",$h_arr[$a++]);
       }
       printf ("\n");
     }
     echo "</pre>\n";
}

function printRawHistogram($needed,$frame) {
        if (!$frame) $frame=elphel_get_frame()-1;
///FIXME:
/// Use ELPHEL_CONST_HISTOGRAM_TABLE_OFFSET - byte offset of the first histogram table
//        $percentile_start=8232;
//        $percentile_start=8216;
        $percentile_start=8192+ ELPHEL_CONST_HISTOGRAM_TABLE_OFFSET;
        $colors=array(0=>"R",1=>"G",2=>"GB",3=>"B");
        $h_raw=elphel_histogram_get_raw($needed,$frame);
//var_dump()
        $h_raw_ul=unpack('V*',substr($h_raw,0,$percentile_start));
        echo "<pre>\n";
        $a=1; /// unpack started with index 1
        $hframe=   $h_raw_ul[$a++];
        $gainr=    $h_raw_ul[$a++];
        $gaing=    $h_raw_ul[$a++];
        $gaingb=   $h_raw_ul[$a++];
        $gainb=    $h_raw_ul[$a++];
        $expos=    $h_raw_ul[$a++];
        $vexpos=   $h_raw_ul[$a++];
        $focus=    $h_raw_ul[$a++];
        $valid=    $h_raw_ul[$a++];
        $hash32_r= $h_raw_ul[$a++];
        $hash32_g= $h_raw_ul[$a++];
        $hash32_gb=$h_raw_ul[$a++];
        $hash32_b= $h_raw_ul[$a++];


/// When not parsing all the data above - just skip ELPHEL_CONST_HISTOGRAM_TABLE_OFFSET bytes
        printf ("Histogram for frame= %d (0x%x), valid mask=0x%x, requested=0x%x, data length=%d (0x%x)\n",$hframe,$hframe, $valid,$needed,strlen($h_raw),strlen($h_raw));
        printf ("Exposure = %d (0x%x)usec, in scan lines (vexpos) =%d (0x%x)\n",$expos,$expos,$vexpos,$vexpos);
        printf ("Gains: R:0x%x G:0x%x GB:0x%x B:0x%x)\n",$gainr,$gaing,$gaingb,$gainb);
        printf ("Focus quality=%d (0x%x)\n",$focus,$focus);
        printf ("hash32: R:0x%x G:0x%x GB:0x%x B:0x%x)\n",$hash32_r,$hash32_g,$hash32_gb,$hash32_b);
        for ($color=0;$color<4;$color++) {
          $sum=0;
          for ($i=0; $i<256; $i++)   $sum+=$h_raw_ul[$a+$i];
          printf("\nhistogram for color #%d %s sum=%d (0x%x):",$color,$colors[$color],$sum,$sum);
          for ($i=0; $i<256; $i++) {
            if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
            $d=$h_raw_ul[$a++];
            printf (" %05x",$d);
          }
          printf ("\n");
        }
        for ($color=0;$color<4;$color++) {
          printf("\ncumulative histogram for color #%d %s:",$color,$colors[$color]);
          for ($i=0; $i<256; $i++) {
            if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
            $d=$h_raw_ul[$a++];
            printf (" %08x",$d);
          }
          printf ("\n");
        }
        for ($color=0;$color<4;$color++) {
          printf("\npercentile for color #%d %s:",$color,$colors[$color]);
          for ($i=0; $i<256; $i++) {
            if (($i & 0x01f)==0) printf ("\n0x%03x:",$i);
            printf (" %02x",ord($h_raw[$percentile_start+(256*$color)+$i]));
          }
          printf ("\n");
        }
        echo "</pre>\n";
}


function printRawFrame($frame) {
        $fp_raw=elphel_framepars_get_raw($frame);
        $fp_raw_ul=unpack('V*',$fp_raw);
        echo "<pre>\n";
        printf ("\nFrame= %d(%08x)\n",$frame,$frame);
        $a=1; /// unpack started with index 1
        echo ".pars:";
        for ($i=0; $i<927; $i++) {
          if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
          printf (" %08x:",$fp_raw_ul[$a++]);
        }
        printf ("\n.functions= %08x:",$fp_raw_ul[$a++]);
        echo "\n.modsince:";
        for ($i=0; $i<31; $i++) {
          if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
          printf (" %08x:",$fp_raw_ul[$a++]);
        }
        printf ("\n.modsince32= %08x:",$fp_raw_ul[$a++]);
        echo "\n.mod:";
        for ($i=0; $i<31; $i++) {
          if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
          printf (" %08x:",$fp_raw_ul[$a++]);
        }
        printf ("\n.mod32= %08x:",$fp_raw_ul[$a++]);
        echo "\n.needproc:";
        for ($i=0; $i<31; $i++) {
          if (($i & 0x0f)==0) printf ("\n0x%03x:",$i);
          printf (" %08x:",$fp_raw_ul[$a++]);
        }
        printf ("\n.needproc32= %08x:",$fp_raw_ul[$a++]);
//        var_dump($fp_raw_ul);
        echo "</pre>\n";
}


function myval ($s) {
  $current_frame=elphel_get_frame();
  $s=trim($s,"\" ");
  if (strtoupper(substr($s,0,2))=="0X")   return intval(hexdec($s));
  else  switch ($s) {
           case "this":  return elphel_get_frame();
              break;
           case "next":
           case "next1":  return elphel_get_frame()+1;
              break;
           case "next2": return elphel_get_frame()+2;
              break;
           case "next3": return elphel_get_frame()+3;
              break;
           case "next4": return elphel_get_frame()+4;
              break;
           case "next5": return elphel_get_frame()+5;
              break;
           case "next6": return elphel_get_frame()+6;
              break;
              break;
           case "prev":
           case "prev1":  return elphel_get_frame()-1;
              break;
           case "prev2": return elphel_get_frame()-2;
              break;
           case "prev3": return elphel_get_frame()-3;
              break;
           case "prev4": return elphel_get_frame()-4;
              break;
           case "prev5": return elphel_get_frame()-5;
              break;
           case "prev6": return elphel_get_frame()-6;
              break;
           default:
             return intval($s);
  }
}
?>