/**************************************************************************/
/* svgafft --  Spectrum Analyzer                                          */
/*                                                                        */ 
/* analyzer implementation                                                */
/**************************************************************************/

/**************************************************************************

    Copyright (C) 1995 Andrew Veliath

    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 2 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, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


***************************************************************************/

// $Id: analyzer.cc,v 0.21 1995/05/02 16:02:13 drewvel Exp $             

#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <vga.h>
#include "analyzer.h"
#include "gconfig.h"
//#include "cfft.cc"
#include "ffts.h"
#include "keymap.h"
#include "s_line_display.h"
#include "s_bar_display.h"
#include "s_3d_display.h"

extern globalConfig gConfig, gStore;
extern char *gProgInfo,*gAnalyzerHelpText;
extern aScreen Screens[];

globalConfig gPassConfig;

#include "misc.h"

/*******************************************************************/
// Fortran FFT macros
#define INITFFT(n,w) rffti_((n),(w))
#define FORWARDFFT(n,a,w) rfftf_((n),(a),(w))
#define WORKDIMSIZE(a) (3*(a)+15)
/*******************************************************************/

void PrintNonDefaultConfig(globalConfig& config, ostream& out);


/*******************************************************************/
SpectrumAnalyzer::SpectrumAnalyzer(int DBScale, 
				   Signal *theSignal,
				   int displaynum,
				   int sampbufsize)
{ 
  buffer_allocated=false;
  buffer8=NULL;
  buffer16=NULL;
  sigbuf=NULL;
  workbuf=NULL;
  dbScale=DBScale;
  bufsize=sampbufsize;
  sizescale=gConfig.SizeScale.val;

  if (theSignal) SetSignal(*theSignal);
  else signal=NULL;
  display=NULL;
  DisplayNum=displaynum;
}


/*******************************************************************/
SpectrumAnalyzer::~SpectrumAnalyzer() 
{ 
  FreeDisplay();
  DestroyBuffer();
}


/*******************************************************************/
void SpectrumAnalyzer::Show() 
{ 
  if (display) display->Show(); 
}


/*******************************************************************/
void SpectrumAnalyzer::UnShow() 
{ 
  if (display) display->UnShow(); 
}


/*******************************************************************/
void SpectrumAnalyzer::AllocDisplay(int type, int scr)
{
  if (type>=1) {

    FreeDisplay();
    Display *d;

    DOUT("AllocDisplay: nbands: " << 
	 gConfig.NumBands.val << " sig: " << signal << " scrnum: " << scr 
	 << " bt: " << gConfig.BarType.val);

    switch (type) {

    case 1:      
      d=new SVGA_Line_Display(gConfig.NumBands.val, *signal,
			      Screens[scr]);    
      break;    


    case 3:
      d=new SVGA_Waveform_Display_3D(gConfig.NumBands.val, *signal,
				      Screens[scr]);    
      break;

#if 0
    case 4:
      d=new SVGA_Continuous_Waveform_Display_3D(gConfig.NumBands.val, *signal,
						Screens[scr]);    
      break;
#endif
      
    case 2: 
      // fall through
    default:
      d=new SVGA_Bar_Display(gConfig.NumBands.val, *signal,
			     Screens[scr], 
			     gConfig.BarType.val);    
      break;
    }
    if (d) {
      SetDisplay(*d);
      DOUT("display (plot) type " << type << " allocated");
      DisplayNum=type;
    }
    else cerr << "Error:  couldn't allocate display!" << endl;
  }
}


/*******************************************************************/
void SpectrumAnalyzer::FreeDisplay()
{
  DOUT("free display()");
  if (display) {
    DOUT("deleting display");
    UnShow();
    delete display;
    display=NULL;
    DOUT("display destroyed");
  }
}


/*******************************************************************/
void SpectrumAnalyzer::ChangeDisplay(int type, int scr)
{
  AllocDisplay(type, scr);
  if (display) {
    display->SetSignal(*signal);
    display->SetAtten(gConfig.Attenuation.val);
    display->SetLogAtten(LOGATTEN);
    display->InitRange(sizescale);
    display->SetBuffer(sigbuf);
    display->Show();
    UpdateFrequencies();
    display->WriteInfo();
    display->controls.Attenuation.step=gConfig.Bits8.val ? 1 : 64;
  }
  else {
    cerr << "Couldn't allocate display, aborting..." << endl;
    exit(-1);
  }
}


/*******************************************************************/
void SpectrumAnalyzer::SetSignal(Signal& s)
{
  signal=&s;
  if (bufsize!=0) signal->SetBufferSize(bufsize);
  AllocBuffer();
}


/*******************************************************************/
void SpectrumAnalyzer::SetDisplay(Display& d)
{
  display=&d;
  display->SetParent(*this);
}


/*******************************************************************/
void SpectrumAnalyzer::AllocBuffer()
{
  if (!buffer_allocated) {
    integer size=signal->GetBufferSize();

    // allocate an 8-bit or 16-bit buffer
    if (gConfig.Bits8.val) 
      buffer8=new sample8[size];
    else
      buffer16=new sample16[size];
    
    sigbuf=new float[size];
    workbuf=new real[WORKDIMSIZE(size)];

    if ((buffer8 || buffer16) && sigbuf && workbuf) 
      buffer_allocated=true;
  }
}


/*******************************************************************/
void SpectrumAnalyzer::DestroyBuffer()
{
  if (buffer_allocated) {
    if (buffer8) delete [] buffer8;
    if (buffer16) delete [] buffer16;
    if (sigbuf) delete [] sigbuf;
    if (workbuf) delete [] workbuf; 

    buffer8=NULL;
    buffer16=NULL;
    sigbuf=NULL;

    buffer_allocated=false;
  }
}


/*******************************************************************/
void SpectrumAnalyzer::AutoCalibrate()
{
  int i, k;
  char ans=' ';
  while (ans!='y') {
    for (i=0; i<24; i++) cerr << endl;
    cerr << "Please zero the input level, then press" << endl;
    cerr << "enter to determine an average offset value." << endl;
    cerr << "This value can then be specified on the command-line" << endl;
    cerr << "with the options -ofs8 and -ofs16 (8 and 16 bits resp.)." 
      << endl;
    cerr << "Hit <enter> to continue..." << endl;
    getchar();
    cerr << endl << "Calibrating with respect to " << 
      NUMCALSAMPLES << " calibration samples..." << endl;
    
    int ofstmp=0, ofssum=0;
    
    if (gConfig.Bits8.val) {
      cerr << "(8-bit mode) Old value: " 
	<< gConfig.Offset8.val << endl;
      cerr << "New value: (";
      
      for (k=0; k<NUMCALSAMPLES; k++) {
	ofstmp=Calibrate8(signal->sig);
	cerr << ofstmp << (k==(NUMCALSAMPLES-1)?")":"+");
	ofssum+=ofstmp;
      }
      ofstmp=ofssum/NUMCALSAMPLES;	  
      
      cerr << "\n\n\t\t avg: " << ofstmp << 
	"\n\n\t--\taccept? (y=yes/r=redo/c=cancel) ";
      ans=getchar();
      
      if (ans=='y') {
	cerr << "Value changed." << endl;
	gConfig.Offset8.val=ofstmp;
      } else cerr << "Aborted." << endl;
    } 
    else {
      cerr << "(16-bit mode) Old value: " 
	<< gConfig.Offset16.val << endl;
      cerr << "New value: (";
      
      for (k=0; k<NUMCALSAMPLES; k++) {
	ofstmp=Calibrate16(signal->sig);
	cerr << ofstmp << (k==(NUMCALSAMPLES-1)?")":"+");
	ofssum+=ofstmp;
      }
      ofstmp=ofssum/NUMCALSAMPLES;
      
      cerr << "\n\n\t\t avg: " << ofstmp << 
	"\n\n\t--\taccept? (y=yes/r=redo/c=cancel) ";
      ans=getchar();
      
      if (ans=='y') {
	cerr << "Value changed." << endl;
	gConfig.Offset16.val=ofstmp;
      } else cerr << "Aborted." << endl;	  
    }
    if (ans=='c') break;
  }
}


/*******************************************************************/
inline boolean SpectrumAnalyzer::ProcessKeys()
{
  static int key;
  static int old_16atten=-1;
  static int old_8atten=-1;
  static int dType=DisplayNum;

  if ((key=vga_getkey())>0) {
    
    // inherit display controls
    display->ProcessControl(key);
    
    switch (key) {

    case K_RESET:
      display->Reset();
      break;

    case K_HELP:
      for (int i=0; i<24; i++) 
	cerr << endl;
      cerr << gProgInfo << endl;
      cerr << gAnalyzerHelpText << endl;
      cerr << "Press enter to return to display..." << endl;
      getchar();
      signal->Flush();
      display->Show();
      break;

    case K_CALIBRATE: // calibrate
      display->UnShow();      
      AutoCalibrate();
      cerr << "Hit <enter> to continue...";
      getchar();      
      signal->Flush();
      display->Show();      
      break;

    case K_SAVEOPTIONS:
      char fname[128];
      display->UnShow();
      cerr << endl << 
	"Write command-line options file; use something like" << endl 
	<< "svgafft `cat <filename>` to use." << endl << endl 
	<< "Filename: ";
      cin >> fname;
      if (*fname) {
	cerr << "Writing global configuraion file...";
	WriteOptions(gPassConfig, fname); // gPassConfig updated through
	cerr << " done." << endl;         // display control procedures
      }
      signal->Flush();
      display->Show();      
      break;

    case K_REVERTDEFAULTS:
    case K_TOGGLEBITRES:
      {
      Parms t=signal->p;
      gConfig.Bits8.val=!gConfig.Bits8.val;
      t.Bit_Resolution=NUMBITS;

      if (gConfig.Bits8.val) {
	old_16atten=display->controls.Attenuation.val;
	if (old_8atten==-1)
	  display->controls.Attenuation=DEFATTENUATION_8;
	else 
	  display->controls.Attenuation=old_8atten;	
      }
      else {
	old_8atten=display->controls.Attenuation.val;
	if (old_16atten==-1)
	  display->controls.Attenuation=DEFATTENUATION_16;
	else 
	  display->controls.Attenuation=old_16atten;
      }

      if (key==K_REVERTDEFAULTS) {
	gConfig=gStore;
	ChangeDisplay(gStore.PlotType.val, gStore.Screen.val);
	display->SetExtents(gStore.x1.val, gStore.y1.val, 
			    gStore.x2.val, gStore.y2.val);

	t.Bit_Resolution=NUMBITS;
	t.Sample_Rate=gConfig.SampleRate.val;
	display->SetAtten(gConfig.Attenuation.val);
	UpdateFrequencies();
      }

      display->SetLogAtten(LOGATTEN);
      display->controls.Attenuation.step=gConfig.Bits8.val ? 1 : 64;

      DestroyBuffer();
      AllocBuffer();
      
      signal->UpdateParms(t);
      if (key==K_TOGGLEBITRES) display->WriteInfo();
      }
      break;
            
    case K_EXIT:
      done=true;
      signal->Flush();
      break;	             

    case K_GO_SVGA_LINE_DISPLAY:
      ChangeDisplay(1, gConfig.Screen.val);
      signal->Flush();
      break;

    case K_GO_SVGA_BAR_DISPLAY:
      ChangeDisplay(2, gConfig.Screen.val);
      signal->Flush();
      break;

    case K_GO_SVGA_WAVEFORM_DISPLAY_3D:
      ChangeDisplay(3, gConfig.Screen.val);
      signal->Flush();
      break;
    }
  }
}


/*******************************************************************/
void SpectrumAnalyzer::WriteOptions(globalConfig& config, char *fname)
{
  globalConfig aconfig=config;

  display->controls.Get(aconfig);

  aconfig.BarType.val=display->GetDisplayMethod();

  ofstream out(fname);
  
  PrintNonDefaultConfig(aconfig, out);
}


/*******************************************************************/
void SpectrumAnalyzer::UpdateFrequencies()
{
  display->InitRange();
  display->RefreshFreqs();
}


/*******************************************************************/
void SpectrumAnalyzer::Analyze()
{
  cerr << endl;
  cerr << "startup settings for " << gConfig.Device.val << ":" << endl;
  ShowDSPSettings(*signal);
  cerr << endl;
  
  ChangeDisplay(DisplayNum, gConfig.Screen.val);
  
  if (buffer_allocated && display && 
      signal && signal->SignalOK()) {
    
    int size=signal->GetBufferSize();
    sigfd=signal->sig;

    done=false;
    
    // for fft
    fbufsize=size;

#ifdef USEFORTRANFFT
    INITFFT((long int *) &size, workbuf);
#endif 

    display->controls.Attenuation.step=gConfig.Bits8.val ? 1 : 64;

    display->SetAtten(dbScale);

    while (! done) {
      // fill buffer with data
      if (gConfig.Bits8.val) {
	signal->Receive8(buffer8);
	ConvertBuffer8SR(buffer8, sigbuf, size);
      } else {
	signal->Receive16(buffer16);
	ConvertBuffer16SR(buffer16, sigbuf, size);
      } 
      

#ifdef USEFORTRANFFT
      FORWARDFFT((long int *) &size, sigbuf, workbuf);
#else
      fft(sigbuf, fbufsize, 1);
#endif 

      display->UpdateCoefficients();
      
      // check for user interaction
      ProcessKeys();
    } // end while
  } // end if
  else 
    cerr << "Error:  could not initialize signal!"; 
  cerr << ENDMESSAGE << endl << endl;
}
