code:
#include "echograph.h"
#include "commons.h"
#include <QSettings>
#include <QApplication>
#include "echoplot.h"
#include "ui_echograph.h"
#include "moc_echograph.cpp"

#define NSMAX2 1366

EchoGraph::EchoGraph(QSettings * settings, QWidget *parent) :
  QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint},
  m_settings (settings),
  ui(new Ui::EchoGraph)
{
  ui->setupUi(this);
  setWindowTitle (QApplication::applicationName () + " - " + tr ("Echo Graph"));
  installEventFilter(parent);                   //Installing the filter
  ui->echoPlot->setCursor(Qt::CrossCursor);
  setMaximumWidth(2048);
  setMaximumHeight(880);
  ui->echoPlot->setMaximumHeight(800);

//Restore user's settings
  m_settings->beginGroup("EchoGraph");
  restoreGeometry (m_settings->value ("geometry", saveGeometry ()).toByteArray ());
  ui->echoPlot->setPlotZero(m_settings->value("PlotZero", 0).toInt());
  ui->echoPlot->setPlotGain(m_settings->value("PlotGain", 0).toInt());
  ui->zeroSlider->setValue(ui->echoPlot->getPlotZero());
  ui->gainSlider->setValue(ui->echoPlot->getPlotGain());
  int n=m_settings->value("Smooth",0).toInt();
  ui->echoPlot->m_smooth=n;
  ui->smoothSpinBox->setValue(n);
  n=m_settings->value("EchoBPP",1).toInt();
  ui->echoPlot->m_binsPerPixel=n;
  ui->binsPerPixelSpinBox->setValue(n);
  ui->echoPlot->m_blue=m_settings->value("BlueCurve",false).toBool();
  m_nColor=m_settings->value("EchoColors",0).toInt();
  m_settings->endGroup();
  ui->echoPlot->setColors(m_nColor);
}

EchoGraph::~EchoGraph()
{
  saveSettings();
}

void EchoGraph::closeEvent (QCloseEvent * e)
{
  saveSettings ();
  QDialog::closeEvent (e);
}

void EchoGraph::saveSettings()
{
//Save user's settings
  m_settings->beginGroup("EchoGraph");
  m_settings->setValue ("geometry", saveGeometry ());
  m_settings->setValue("PlotZero",ui->echoPlot->m_plotZero);
  m_settings->setValue("PlotGain",ui->echoPlot->m_plotGain);
  m_settings->setValue("Smooth",ui->echoPlot->m_smooth);
  m_settings->setValue("EchoBPP",ui->echoPlot->m_binsPerPixel);
  m_settings->setValue("BlueCurve",ui->echoPlot->m_blue);
  m_settings->setValue("EchoColors",m_nColor);
  m_settings->endGroup();
}

void EchoGraph::plotSpec()
{
  ui->echoPlot->draw();
  ui->nsum_label->setText("N: " + QString::number(echocom_.nsum));
}

void EchoGraph::on_smoothSpinBox_valueChanged(int n)
{
  ui->echoPlot->setSmooth(n);
  ui->echoPlot->draw();
}

void EchoGraph::on_gainSlider_valueChanged(int value)
{
  ui->echoPlot->setPlotGain(value);
  ui->echoPlot->draw();
}

void EchoGraph::on_zeroSlider_valueChanged(int value)
{
  ui->echoPlot->setPlotZero(value);
  ui->echoPlot->draw();
}

void EchoGraph::on_binsPerPixelSpinBox_valueChanged(int n)
{
  ui->echoPlot->m_binsPerPixel=n;
  ui->echoPlot->DrawOverlay();
  ui->echoPlot->draw();
}

void EchoGraph::on_pbColors_clicked()
{
  m_nColor = (m_nColor+1) % 6;
  ui->echoPlot->setColors(m_nColor);
}


code:
#ifndef ECHOGRAPH_H
#define ECHOGRAPH_H

#include <QDialog>
#include <QScopedPointer>

namespace Ui {
  class EchoGraph;
}

class QSettings;

class EchoGraph : public QDialog
{
  Q_OBJECT

protected:
  void closeEvent (QCloseEvent *) override;

public:
  explicit EchoGraph(QSettings *, QWidget *parent = 0);
  ~EchoGraph();

  void   plotSpec();
  void   saveSettings();

private slots:
  void on_smoothSpinBox_valueChanged(int n);
  void on_gainSlider_valueChanged(int value);
  void on_zeroSlider_valueChanged(int value);
  void on_binsPerPixelSpinBox_valueChanged(int n);
  void on_pbColors_clicked();

private:
  QSettings * m_settings;
  qint32 m_nColor;

  QScopedPointer<Ui::EchoGraph> ui;
};

#endif // ECHOGRAPH_H

license: mit


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>EchoGraph</class>
 <widget class="QDialog" name="EchoGraph">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>625</width>
    <height>336</height>
   </rect>
  </property>
  <property name="sizePolicy">
   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
    <horstretch>0</horstretch>
    <verstretch>0</verstretch>
   </sizepolicy>
  </property>
  <property name="minimumSize">
   <size>
    <width>570</width>
    <height>0</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Echo Graph</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <property name="spacing">
    <number>2</number>
   </property>
   <property name="topMargin">
    <number>2</number>
   </property>
   <property name="bottomMargin">
    <number>2</number>
   </property>
   <item>
    <layout class="QVBoxLayout" name="verticalLayout">
     <property name="spacing">
      <number>2</number>
     </property>
     <item>
      <widget class="EPlotter" name="echoPlot">
       <property name="enabled">
        <bool>true</bool>
       </property>
       <property name="sizePolicy">
        <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="minimumSize">
        <size>
         <width>273</width>
         <height>200</height>
        </size>
       </property>
       <property name="frameShape">
        <enum>QFrame::StyledPanel</enum>
       </property>
       <property name="frameShadow">
        <enum>QFrame::Sunken</enum>
       </property>
       <property name="lineWidth">
        <number>1</number>
       </property>
      </widget>
     </item>
     <item>
      <layout class="QHBoxLayout" name="horizontalLayout_3">
       <property name="spacing">
        <number>6</number>
       </property>
       <item>
        <spacer name="horizontalSpacer_2">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Expanding</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QSpinBox" name="binsPerPixelSpinBox">
         <property name="minimumSize">
          <size>
           <width>60</width>
           <height>0</height>
          </size>
         </property>
         <property name="toolTip">
          <string>
Compression factor for frequency scale
</string>
         </property>
         <property name="prefix">
          <string>Bins/Pixel  </string>
         </property>
         <property name="minimum">
          <number>1</number>
         </property>
         <property name="maximum">
          <number>10</number>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QLabel" name="label_4">
         <property name="text">
          <string>Gain</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QSlider" name="gainSlider">
         <property name="toolTip">
          <string>
Echo spectrum gain
</string>
         </property>
         <property name="minimum">
          <number>-30</number>
         </property>
         <property name="maximum">
          <number>30</number>
         </property>
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="horizontalSpacer_7">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Preferred</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="label">
         <property name="text">
          <string>Zero</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QSlider" name="zeroSlider">
         <property name="toolTip">
          <string>
Echo spectrum zero
</string>
         </property>
         <property name="minimum">
          <number>-150</number>
         </property>
         <property name="maximum">
          <number>150</number>
         </property>
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="horizontalSpacer">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Preferred</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QSpinBox" name="smoothSpinBox">
         <property name="minimumSize">
          <size>
           <width>60</width>
           <height>0</height>
          </size>
         </property>
         <property name="toolTip">
          <string>
Smoothing of echo spectrum
</string>
         </property>
         <property name="prefix">
          <string>Smooth  </string>
         </property>
         <property name="maximum">
          <number>20</number>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="horizontalSpacer_3">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Preferred</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="nsum_label">
         <property name="minimumSize">
          <size>
           <width>30</width>
           <height>0</height>
          </size>
         </property>
         <property name="toolTip">
          <string>
Number of echo transmissions averaged
</string>
         </property>
         <property name="text">
          <string>N: 0</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignCenter</set>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QPushButton" name="pbColors">
         <property name="maximumSize">
          <size>
           <width>50</width>
           <height>16777215</height>
          </size>
         </property>
         <property name="toolTip">
          <string>
Click to cycle through a sequence of colors and line widths.
</string>
         </property>
         <property name="text">
          <string>Colors</string>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="horizontalSpacer_5">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Expanding</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
      </layout>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>EPlotter</class>
   <extends>QFrame</extends>
   plotter.h
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>


code:
#include "echoplot.h"
#include "commons.h"
#include <math.h>
#include <QDebug>
#include "moc_echoplot.cpp"

#define MAX_SCREENSIZE 2048

EPlotter::EPlotter(QWidget *parent) :                  //EPlotter Constructor
  QFrame(parent)
{
  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  setFocusPolicy(Qt::StrongFocus);
  setAttribute(Qt::WA_PaintOnScreen,false);
  setAutoFillBackground(false);
  setAttribute(Qt::WA_OpaquePaintEvent, false);
  setAttribute(Qt::WA_NoSystemBackground, true);

  m_StartFreq = -200;
  m_fftBinWidth=12000.0/32768.0;
  m_binsPerPixel=1;
  m_fSpan=1000.0;
  m_hdivs = HORZ_DIVS;
  m_Running = false;
  m_paintEventBusy=false;
  m_2DPixmap = QPixmap(0,0);
  m_ScalePixmap = QPixmap(0,0);
  m_OverlayPixmap = QPixmap(0,0);
  m_Size = QSize(0,0);
  m_TxFreq = 1500;
  m_line = 0;
  m_dBStepSize=10;
}

EPlotter::~EPlotter() { }                                      // Destructor

QSize EPlotter::minimumSizeHint() const
{
  return QSize(50, 50);
}

QSize EPlotter::sizeHint() const
{
  return QSize(180, 180);
}

void EPlotter::resizeEvent(QResizeEvent* )                    //resizeEvent()
{
  if(!size().isValid()) return;
  if( m_Size != size() ) {  //if changed, resize pixmaps to new screensize
    m_Size = size();
    m_w = m_Size.width();
    m_h = m_Size.height();
    m_h1=30;
    m_h2=m_h-m_h1;
    m_2DPixmap = QPixmap(m_Size.width(), m_h2);
    m_2DPixmap.fill(Qt::black);
    m_OverlayPixmap = QPixmap(m_Size.width(), m_h2);
    m_ScalePixmap = QPixmap(m_w,30);
    m_ScalePixmap.fill(Qt::white);
    m_fSpan=m_w*m_fftBinWidth*m_binsPerPixel;
    m_StartFreq=50 * int((-0.5*m_fSpan)/50.0 - 0.5);
  }
  DrawOverlay();
  draw();
}

void EPlotter::paintEvent(QPaintEvent *)                    // paintEvent()
{
  if(m_paintEventBusy) return;
  m_paintEventBusy=true;
  QPainter painter(this);
  painter.drawPixmap(0,0,m_ScalePixmap);
  painter.drawPixmap(0,m_h1,m_2DPixmap);
  m_paintEventBusy=false;
}

void EPlotter::draw()                           //draw()
{
  int i,j,y;
  float blue[4096],red[4096];
  float gain = pow(10.0,(m_plotGain/20.0));
  QPen penBlue(QColor(0,255,255),1);
  QPen penRed(Qt::red,1);
  QPen penRed2(Qt::red,2);
  QPen penBlack(Qt::black,1);
  QPen penBlack2(Qt::black,2);

  if(m_2DPixmap.size().width()==0) return;
  QPainter painter2D(&m_2DPixmap);
  QRect tmp(0,0,m_w,m_h2);
  if(m_nColor < 2) {
    painter2D.fillRect(tmp,Qt::black);
  } else {
    painter2D.fillRect(tmp,Qt::white);
    painter2D.setPen(penBlack);
    painter2D.drawLine(0,0,m_w,0);
  }

  QPoint LineBuf[MAX_SCREENSIZE];

  if(m_binsPerPixel==0) m_binsPerPixel=1;
  j=0;
  for(i=0; i<4096/m_binsPerPixel; i++) {
    blue[i]=0.0;
    red[i]=0.0;
    for(int k=0; k<m_binsPerPixel; k++) {
      blue[i]+=echocom_.blue[j];
      red[i]+=echocom_.red[j];
      j++;
    }
  }
  if(m_smooth>0) {
    for(i=0; i<m_smooth; i++) {
      int n4096=4096;
      smo121_(blue,&n4096);
      smo121_(red,&n4096);
    }
  }

// check i0 value! ...
  int i0=2048/m_binsPerPixel + int(m_StartFreq/(m_fftBinWidth*m_binsPerPixel));
  if(m_blue) {
    painter2D.setPen(penBlue);
    j=0;
    for(i=0; i<m_w; i++) {
      y = 0.9*m_h2 - gain*(m_h/10.0)*(blue[i0+i]-1.0) - 0.01*m_h2*m_plotZero;
      LineBuf[j].setX(i);
      LineBuf[j].setY(y);
      j++;
    }
    painter2D.drawPolyline(LineBuf,j);
  }
  switch (m_nColor) {
    case 0: painter2D.setPen(penRed); break;
    case 1: painter2D.setPen(penRed2); break;
    case 2: painter2D.setPen(penRed); break;
    case 3: painter2D.setPen(penRed2); break;
    case 4: painter2D.setPen(penBlack); break;
    case 5: painter2D.setPen(penBlack2); break;
  }

  j=0;
  for(int i=0; i<m_w; i++) {
    y = 0.9*m_h2 - gain*(m_h/10.0)*(red[i0+i]-1.0) - 0.01*m_h2*m_plotZero;
    LineBuf[j].setX(i);
    LineBuf[j].setY(y);
    j++;
  }
  painter2D.drawPolyline(LineBuf,j);
  update();                              //trigger a new paintEvent
}

void EPlotter::DrawOverlay()                                 //DrawOverlay()
{
  if(m_OverlayPixmap.isNull() or m_2DPixmap.isNull()) return;
//  int w = m_WaterfallPixmap.width();
  int x,y;

  QRect rect;
  QPainter painter(&m_OverlayPixmap);
  painter.initFrom(this);
  QLinearGradient gradient(0, 0, 0 ,m_h2);  //fill background with gradient
  gradient.setColorAt(1, Qt::black);
  gradient.setColorAt(0, Qt::darkBlue);
  painter.setBrush(gradient);
  painter.drawRect(0, 0, m_w, m_h2);
  painter.setBrush(Qt::SolidPattern);

  m_fSpan = m_w*m_fftBinWidth*m_binsPerPixel;
  m_freqPerDiv=20;
  if(m_fSpan>250) m_freqPerDiv=50;
  if(m_fSpan>500) m_freqPerDiv=100;
  if(m_fSpan>1000) m_freqPerDiv=200;
  if(m_fSpan>2000) m_freqPerDiv=500;

//  m_StartFreq=50 * int((-0.5*m_fSpan)/50.0 - 0.5);
  m_StartFreq=m_freqPerDiv * int((-0.5*m_fSpan)/m_freqPerDiv - 0.5);

  float pixPerHdiv = m_freqPerDiv/(m_fftBinWidth*m_binsPerPixel);
  float pixPerVdiv = float(m_h2)/float(VERT_DIVS);

  m_hdivs = m_w*m_fftBinWidth*m_binsPerPixel/m_freqPerDiv + 0.9999;

  painter.setPen(QPen(Qt::white, 1,Qt::DotLine));
  for( int i=1; i<m_hdivs; i++)                   //draw vertical grids
  {
    x=int(i*pixPerHdiv);
    painter.drawLine(x,0,x,m_h2);
  }

  for( int i=1; i<VERT_DIVS; i++)                 //draw horizontal grids
  {
    y = (int)( (float)i*pixPerVdiv );
    painter.drawLine(0,y,m_w,y);
  }

  QRect rect0;
  QPainter painter0(&m_ScalePixmap);
  painter0.initFrom(this);

  //create Font to use for scales
  QFont Font("Arial");
  Font.setPointSize(12);
  QFontMetrics metrics(Font);
  Font.setWeight(QFont::Normal);
  painter0.setFont(Font);
  painter0.setPen(Qt::black);

  m_ScalePixmap.fill(Qt::white);
  painter0.drawRect(0, 0, m_w, 30);

//draw tick marks on upper scale
  for( int i=1; i<m_hdivs; i++) {         //major ticks
    x = (int)( (float)i*pixPerHdiv );
    painter0.drawLine(x,18,x,30);
  }
  int minor=5;
  if(m_freqPerDiv==200) minor=4;
  for( int i=1; i<minor*m_hdivs; i++) {   //minor ticks
    x = i*pixPerHdiv/minor;
    painter0.drawLine(x,24,x,30);
  }

//draw frequency values
  MakeFrequencyStrs();
  for( int i=0; i<=m_hdivs; i++) {
    if(0==i) {
      //left justify the leftmost text
      x = (int)( (float)i*pixPerHdiv);
      rect0.setRect(x,0, (int)pixPerHdiv, 20);
      painter0.drawText(rect0, Qt::AlignLeft|Qt::AlignVCenter,
                       m_HDivText[i]);
    }
    else if(m_hdivs == i) {
      //right justify the rightmost text
      x = (int)( (float)i*pixPerHdiv - pixPerHdiv);
      rect0.setRect(x,0, (int)pixPerHdiv, 20);
      painter0.drawText(rect0, Qt::AlignRight|Qt::AlignVCenter,
                       m_HDivText[i]);
    } else {
      //center justify the rest of the text
      x = (int)( (float)i*pixPerHdiv - pixPerHdiv/2);
      rect0.setRect(x,0, (int)pixPerHdiv, 20);
      painter0.drawText(rect0, Qt::AlignHCenter|Qt::AlignVCenter,
                       m_HDivText[i]);
    }
  }

/*
  QPen pen1(Qt::red, 3);                         //Mark Tx Freq with red tick
  painter0.setPen(pen1);
  x = XfromFreq(m_TxFreq);
  painter0.drawLine(x,17,x,30);
*/
}

void EPlotter::MakeFrequencyStrs()                       //MakeFrequencyStrs
{
  float freq;
  for(int i=0; i<=m_hdivs; i++) {
    freq=m_StartFreq + i*m_freqPerDiv;
    m_HDivText[i].setNum((int)freq);
  }
}

int EPlotter::XfromFreq(float f)                               //XfromFreq()
{
  int x = (int) m_w * (f - m_StartFreq)/m_fSpan;
  if(x<0 ) return 0;
  if(x>m_w) return m_w;
  return x;
}

float EPlotter::FreqfromX(int x)                               //FreqfromX()
{
  return float(m_StartFreq + x*m_fftBinWidth*m_binsPerPixel);
}

void EPlotter::SetRunningState(bool running)              //SetRunningState()
{
  m_Running = running;
}

void EPlotter::setPlotZero(int plotZero)                  //setPlotZero()
{
  m_plotZero=plotZero;
}

int EPlotter::getPlotZero()                               //getPlotZero()
{
  return m_plotZero;
}

void EPlotter::setPlotGain(int plotGain)                  //setPlotGain()
{
  m_plotGain=plotGain;
}

int EPlotter::getPlotGain()                               //getPlotGain()
{
  return m_plotGain;
}

void EPlotter::setSmooth(int n)                               //setSmooth()
{
  m_smooth=n;
}

int EPlotter::getSmooth()                                    //getSmooth()
{
  return m_smooth;
}

void EPlotter::setColors(qint32 n)                               //setSmooth()
{
  m_nColor=n;
  draw();
}

int EPlotter::plotWidth(){return m_2DPixmap.width();}

void EPlotter::UpdateOverlay() {DrawOverlay();}


code:
///////////////////////////////////////////////////////////////////////////
// Some code in this file and accompanying files is based on work by
// Moe Wheatley, AE4Y, released under the "Simplified BSD License".
// For more details see the accompanying file LICENSE_WHEATLEY.TXT
///////////////////////////////////////////////////////////////////////////

#ifndef EPLOTTER_H
#define EPLOTTER_H

#include <QtWidgets>
#include <QFrame>
#include <QImage>
#include <cstring>

#define VERT_DIVS 7	//specify grid screen divisions
#define HORZ_DIVS 20

class EPlotter : public QFrame
{
  Q_OBJECT
public:
  explicit EPlotter(QWidget *parent = 0);
  ~EPlotter();

  QSize minimumSizeHint() const;
  QSize sizeHint() const;
  float   m_fSpan;
  qint32  m_TxFreq;
  qint32  m_w;
  qint32  m_plotZero;
  qint32  m_plotGain;
  qint32  m_smooth;
  qint32  m_binsPerPixel;
  qint32  m_nColor;
  bool    m_blue;

  void draw();		                                    //Update the Echo plot
  void SetRunningState(bool running);
  void setPlotZero(int plotZero);
  int  getPlotZero();
  void setPlotGain(int plotGain);
  int  getPlotGain();
  int  plotWidth();
  void UpdateOverlay();
  void DrawOverlay();
  void setSmooth(int n);
  int  getSmooth();
  void setColors(qint32 n);

//  void SetPercent2DScreen(int percent){m_Percent2DScreen=percent;}

protected:
  //re-implemented widget event handlers
  void paintEvent(QPaintEvent *event);
  void resizeEvent(QResizeEvent* event);

private:

  void MakeFrequencyStrs();
  int XfromFreq(float f);
  float FreqfromX(int x);
  qint64 RoundFreq(qint64 freq, int resolution);

  QPixmap m_2DPixmap;
  QPixmap m_ScalePixmap;
  QPixmap m_OverlayPixmap;
  QSize   m_Size;
  QString m_Str;
  QString m_HDivText[483];

  double  m_fftBinWidth;

  qint64  m_StartFreq;

  qint32  m_dBStepSize;
  qint32  m_hdivs;
  qint32  m_line;
  qint32  m_freqPerDiv;
  qint32  m_h;
  qint32  m_h1;
  qint32  m_h2;

  bool    m_Running;
  bool    m_paintEventBusy;

};

extern "C" {
//--------------------------------------------------- C and Fortran routines

void smo121_(float x[], int* npts);
}
#endif // EPLOTTER_H


code:
#include "fastgraph.h"

#include "commons.h"
#include <QSettings>
#include <QApplication>
#include "fastplot.h"
#include "SettingsGroup.hpp"

#include "ui_fastgraph.h"
#include "moc_fastgraph.cpp"

#define NSMAX2 1366

FastGraph::FastGraph(QSettings * settings, QWidget *parent) :
  QDialog {parent, Qt::Window | Qt::WindowTitleHint |
           Qt::WindowCloseButtonHint |
           Qt::WindowMinimizeButtonHint},
  m_settings {settings},
  m_ave {40},
  ui {new Ui::FastGraph}
{
  ui->setupUi(this);
  setWindowTitle (QApplication::applicationName () + " - " + tr ("Fast Graph"));
  installEventFilter(parent);                   //Installing the filter
  ui->fastPlot->setCursor(Qt::CrossCursor);

  //Restore user's settings
  SettingsGroup g {m_settings, "FastGraph"};
  restoreGeometry (m_settings->value ("geometry", saveGeometry ()).toByteArray ());
  ui->fastPlot->setPlotZero(m_settings->value("PlotZero", 0).toInt());
  ui->fastPlot->setPlotGain(m_settings->value("PlotGain", 0).toInt());
  ui->zeroSlider->setValue(ui->fastPlot->m_plotZero);
  ui->gainSlider->setValue(ui->fastPlot->m_plotGain);
  ui->fastPlot->setGreenZero(m_settings->value("GreenZero", 0).toInt());
  ui->greenZeroSlider->setValue(ui->fastPlot->m_greenZero);
//  ui->controls_widget->setVisible (!m_settings->value("HideControls", false).toBool ());
  ui->controls_widget->setVisible(true);
  connect (ui->fastPlot, &FPlotter::fastPick, this, &FastGraph::fastPick);
}

void FastGraph::closeEvent (QCloseEvent * e)
{
  saveSettings ();
  QDialog::closeEvent (e);
}

FastGraph::~FastGraph ()
{
}

void FastGraph::saveSettings()
{
//Save user's settings
  SettingsGroup g {m_settings, "FastGraph"};
  m_settings->setValue ("geometry", saveGeometry ());
  m_settings->setValue("PlotZero",ui->fastPlot->m_plotZero);
  m_settings->setValue("PlotGain",ui->fastPlot->m_plotGain);
  m_settings->setValue("GreenZero",ui->fastPlot->m_greenZero);
  m_settings->setValue("GreenGain",ui->fastPlot->m_greenGain);
//  m_settings->setValue ("HideControls", ui->controls_widget->isHidden ());
}

void FastGraph::plotSpec(bool diskData, int UTCdisk)
{
  ui->fastPlot->m_diskData=diskData;
  ui->fastPlot->m_UTCdisk=UTCdisk;
  ui->fastPlot->draw();
}

void FastGraph::on_gainSlider_valueChanged(int value)
{
  ui->fastPlot->setPlotGain(value);
  ui->fastPlot->draw();
}

void FastGraph::on_zeroSlider_valueChanged(int value)
{
  ui->fastPlot->setPlotZero(value);
  ui->fastPlot->draw();
}

void FastGraph::on_greenZeroSlider_valueChanged(int value)
{
  ui->fastPlot->setGreenZero(value);
  ui->fastPlot->draw();
}

void FastGraph::setTRPeriod(int n)
{
  m_TRperiod=n;
  ui->fastPlot->setTRperiod(m_TRperiod);
}

void FastGraph::on_pbAutoLevel_clicked()
{
  float sum=0.0;
  for(int i=0; i<=fast_jh; i++) {
    sum += fast_green[i];
  }
  m_ave=sum/fast_jh;
  ui->gainSlider->setValue(127-int(2.2*m_ave));
  ui->zeroSlider->setValue(int(m_ave)+20);
  ui->greenZeroSlider->setValue(160-int(3.3*m_ave));
}

void FastGraph::setMode(QString mode)                              //setMode
{
  ui->fastPlot->setMode(mode);
}

void FastGraph::keyPressEvent(QKeyEvent *e)
{
  switch(e->key())
  {
/*
  case Qt::Key_M:
    if(e->modifiers() & Qt::ControlModifier) {
      ui->controls_widget->setVisible (!ui->controls_widget->isVisible ());
    }
    break;
*/
  default:
    QDialog::keyPressEvent (e);
  }
}


code:
#ifndef FASTGRAPH_H
#define FASTGRAPH_H

#include <QDialog>
#include <QScopedPointer>

namespace Ui {
  class FastGraph;
}

class QSettings;

class FastGraph : public QDialog
{
  Q_OBJECT

public:
  explicit FastGraph(QSettings *, QWidget *parent = 0);
  ~FastGraph ();

  void   plotSpec(bool diskData, int UTCdisk);
  void   saveSettings();
  void   setTRPeriod(int n);
  void   setMode(QString mode);

signals:
  void fastPick(int x0, int x1, int y);

private slots:
  void on_gainSlider_valueChanged(int value);
  void on_zeroSlider_valueChanged(int value);
  void on_greenZeroSlider_valueChanged(int value);
  void on_pbAutoLevel_clicked();

protected:
  void closeEvent (QCloseEvent *) override;
  void keyPressEvent( QKeyEvent *e ) override;

private:
  QSettings * m_settings;
  float m_ave;
  qint32  m_TRperiod;

  QScopedPointer<Ui::FastGraph> ui;
};

extern float fast_green[703];
extern int   fast_jh;

#endif // FASTGRAPH_H


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>FastGraph</class>
 <widget class="QDialog" name="FastGraph">
  <property name="windowTitle">
   <string>Fast Graph</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <property name="spacing">
    <number>2</number>
   </property>
   <property name="sizeConstraint">
    <enum>QLayout::SetFixedSize</enum>
   </property>
   <property name="leftMargin">
    <number>2</number>
   </property>
   <property name="topMargin">
    <number>2</number>
   </property>
   <property name="rightMargin">
    <number>2</number>
   </property>
   <property name="bottomMargin">
    <number>2</number>
   </property>
   <item>
    <widget class="FPlotter" name="fastPlot">
     <property name="enabled">
      <bool>true</bool>
     </property>
     <property name="sizePolicy">
      <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
       <horstretch>0</horstretch>
       <verstretch>0</verstretch>
      </sizepolicy>
     </property>
     <property name="minimumSize">
      <size>
       <width>703</width>
       <height>220</height>
      </size>
     </property>
     <property name="maximumSize">
      <size>
       <width>703</width>
       <height>220</height>
      </size>
     </property>
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Sunken</enum>
     </property>
     <property name="lineWidth">
      <number>1</number>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QWidget" name="controls_widget" native="true">
     <layout class="QHBoxLayout" name="horizontalLayout">
      <property name="leftMargin">
       <number>2</number>
      </property>
      <property name="topMargin">
       <number>2</number>
      </property>
      <property name="rightMargin">
       <number>2</number>
      </property>
      <property name="bottomMargin">
       <number>2</number>
      </property>
      <item>
       <spacer name="horizontalSpacer_2">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Expanding</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>99</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QSlider" name="gainSlider">
        <property name="toolTip">
         <string>Waterfall gain</string>
        </property>
        <property name="minimum">
         <number>-60</number>
        </property>
        <property name="maximum">
         <number>140</number>
        </property>
        <property name="pageStep">
         <number>40</number>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer_7">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Preferred</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QSlider" name="zeroSlider">
        <property name="toolTip">
         <string>Waterfall zero</string>
        </property>
        <property name="minimum">
         <number>-60</number>
        </property>
        <property name="maximum">
         <number>120</number>
        </property>
        <property name="value">
         <number>60</number>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Preferred</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QSlider" name="greenZeroSlider">
        <property name="toolTip">
         <string>Spectrum zero</string>
        </property>
        <property name="minimum">
         <number>-100</number>
        </property>
        <property name="maximum">
         <number>160</number>
        </property>
        <property name="value">
         <number>30</number>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer_3">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Preferred</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pbAutoLevel">
        <property name="toolTip">
         <string>
Set reasonable levels for gain and zero sliders.
</string>
        </property>
        <property name="text">
         <string>Auto Level</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer_5">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeType">
         <enum>QSizePolicy::Expanding</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>99</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>FPlotter</class>
   <extends>QFrame</extends>
   fastplot.h
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>


code:
#include "fastplot.h"
#include "commons.h"
#include <math.h>
#include <QDebug>

#include "DriftingDateTime.h"

#include "moc_fastplot.cpp"

#define MAX_SCREENSIZE 2048

FPlotter::FPlotter(QWidget *parent) :                  //FPlotter Constructor
  QFrame {parent},
  m_w {703},
  m_plotGain {0},
  m_greenZero {0},
  m_x0 {0},
  m_x1 {0},
  m_ScalePixmap {QPixmap {703, 20}},
  m_pixPerSecond {12000.0/512.0},
  m_hdivs {30},
  m_h {220},
  m_h1 {20},
  m_h2 {m_h-m_h1},
  m_HorizPixmap {QPixmap {m_w, m_h2}},
  m_jh0 {9999},
  m_bPaint2 {true}
{
  m_diskData=false;
  setFocusPolicy(Qt::StrongFocus);
  setAttribute(Qt::WA_PaintOnScreen,false);
  setAutoFillBackground(false);
  setAttribute(Qt::WA_OpaquePaintEvent, false);
  setAttribute(Qt::WA_NoSystemBackground, true);

  m_HorizPixmap.fill(Qt::black);
  m_HorizPixmap.fill(Qt::black);
  m_ScalePixmap.fill(Qt::white);
  drawScale();
  draw();
  setMouseTracking(true);
}

FPlotter::~FPlotter() { }                                      // Destructor

void FPlotter::paintEvent(QPaintEvent *)                       // paintEvent()
{
  QPainter painter(this);
  painter.drawPixmap(0,0,m_ScalePixmap);
  painter.drawPixmap(0,m_h1,m_HorizPixmap);
}

void FPlotter::drawScale()                                 //drawScale()
{
  if(m_ScalePixmap.isNull()) return;
  int x;

  QRect rect0;
  QPainter painter0(&m_ScalePixmap);
  painter0.initFrom(this);

  //create Font to use for scales
  QFont Font("Arial");
  Font.setPointSize(8);
  QFontMetrics metrics(Font);
  Font.setWeight(QFont::Normal);
  painter0.setFont(Font);
  painter0.setPen(Qt::white);
  m_ScalePixmap.fill(Qt::black);
  painter0.drawRect(0, 0,m_w,19);
  painter0.drawLine(0,19,m_w,19);

//Draw ticks at 1-second intervals
  for( int i=0; i<=m_hdivs; i++) {
    x = (int)( (float)i*m_pixPerSecond );
    painter0.drawLine(x,15,x,19);
  }

//Write numbers on the time scale
  MakeTimeStrs();
  for( int i=0; i<=m_hdivs; i++) {
    if(0==i) {
      //left justify the leftmost text
      x = (int)( (float)i*m_pixPerSecond);
      rect0.setRect(x,0, (int)m_pixPerSecond, 20);
      painter0.drawText(rect0, Qt::AlignLeft|Qt::AlignVCenter,m_HDivText[i]);
    }
    else if(m_hdivs == i) {
      //right justify the rightmost text
      x = (int)( (float)i*m_pixPerSecond - m_pixPerSecond);
      rect0.setRect(x,0, (int)m_pixPerSecond, 20);
      painter0.drawText(rect0, Qt::AlignRight|Qt::AlignVCenter,m_HDivText[i]);
    } else {
      //center justify the rest of the text
      x = (int)( (float)i*m_pixPerSecond - m_pixPerSecond/2);
      rect0.setRect(x,0, (int)m_pixPerSecond, 20);
      painter0.drawText(rect0, Qt::AlignHCenter|Qt::AlignVCenter,m_HDivText[i]);
    }
  }
}

void FPlotter::MakeTimeStrs()                              //MakeTimeStrs
{
  for(int i=0; i<=m_hdivs; i++) {
    m_HDivText[i].setNum(i);
  }
}

int FPlotter::XfromTime(float t)                               //XfromFreq()
{
  return int(t*m_pixPerSecond);
}

float FPlotter::TimefromX(int x)                               //FreqfromX()
{
  return float(x/m_pixPerSecond);
}

void FPlotter::setPlotZero(int plotZero)                  //setPlotZero()
{
  m_plotZero=plotZero;
  m_bPaint2=true;
}

void FPlotter::setPlotGain(int plotGain)                  //setPlotGain()
{
  m_plotGain=plotGain;
  m_bPaint2=true;
}

void FPlotter::setGreenZero(int n)
{
  m_greenZero=n;
  m_bPaint2=true;
}

void FPlotter::setTRperiod(int n)
{
  m_TRperiod=n;
  m_pixPerSecond=12000.0/512.0;
  if(m_TRperiod<30) m_pixPerSecond=12000.0/256.0;
  drawScale();
  update();
}

void FPlotter::draw()                                         //draw()
{
  QPainter painter1(&m_HorizPixmap);
  QPoint LineBuf[703];
  QPen penGreen(Qt::green,1);

  if(m_diskData) {
    int ih=m_UTCdisk/10000;
    int im=m_UTCdisk/100 % 100;
    int is=m_UTCdisk % 100;
    m_t.sprintf("%2.2d:%2.2d:%2.2d",ih,im,is);
  }

  int k0=m_jh0;
  if(fast_jh < m_jh0 or m_bPaint2) {
    k0=0;
    QRect tmp(0,0,m_w,119);
    painter1.fillRect(tmp,Qt::black);
    painter1.setPen(Qt::white);
    if(m_diskData) {
      int ih=m_UTCdisk/10000;
      int im=m_UTCdisk/100 % 100;
      int is=m_UTCdisk % 100;
      m_t.sprintf("%2.2d:%2.2d:%2.2d",ih,im,is);
    } else {
      m_t=DriftingDateTime::currentDateTimeUtc().toString("hh:mm:ss");
    }
    if(fast_jh>0) painter1.drawText(10,95,m_t);
  }

  float gain = pow(10.0,(m_plotGain/20.0));
  for(int k=64*k0; k<64*fast_jh; k++) {                     //Upper spectrogram
    int i = k%64;
    int j = k/64;
    int y=0.005*gain*fast_s[k] + m_plotZero;
    if(y<0) y=0;
    if(y>254) y=254;
    painter1.setPen(g_ColorTbl[y]);
    painter1.drawPoint(j,64-i);
  }

  painter1.setPen(penGreen);                                // Upper green curve
  int j=0;
  m_greenGain=10;
  float greenGain = pow(10.0,(m_greenGain/20.0));
  for(int x=k0; x<=fast_jh; x++) {
    int y = 0.9*m_h - greenGain*fast_green[x] - m_greenZero + 40;
    if(y>119) y=119;
    LineBuf[j].setX(x);
//    LineBuf[j].setX(2*x);
    LineBuf[j].setY(y);
    j++;
  }
  painter1.drawPolyline(LineBuf,j);

  if((fast_jh < m_jh0) or m_bPaint2) {
    QRect tmp(0,120,m_w,219);
    painter1.fillRect(tmp,Qt::black);
    painter1.setPen(Qt::white);
    if(fast_jh>0 and m_jh0 < 9999) painter1.drawText(10,195,m_t0);
    m_t0=m_t;

    for(int k=0; k<64*fast_jh2; k++) {                      //Lower spectrogram
      int i = k%64;
      int j = k/64;
      int y=0.005*gain*fast_s2[k] + m_plotZero;
      if(y<0) y=0;
      if(y>254) y=254;
      painter1.setPen(g_ColorTbl[y]);
      painter1.drawPoint(j,164-i);
    }

    painter1.setPen(penGreen);                              //Lower green curve
    j=0;
    for(int x=0; x<=fast_jh2; x++) {
      int y = 0.9*m_h - greenGain*fast_green2[x] - m_greenZero + 140;
      if(y>219) y=219;
      LineBuf[j].setX(x);
      LineBuf[j].setY(y);
      j++;
    }
    painter1.drawPolyline(LineBuf,j);
    m_bPaint2=false;
  }

  painter1.setPen(Qt::white);
  painter1.drawLine(0,100, m_w,100);
  if(fast_jh>0) m_jh0=fast_jh;
  update();                                             //trigger a new paintEvent
}

void FPlotter::mouseMoveEvent(QMouseEvent *event)
{
  QPainter painter(&m_HorizPixmap);
  int x=event->x();
//  int y=event->y();
  float t=x/m_pixPerSecond;
  QString t1;
  t1.sprintf("%5.2f",t);
  QRectF rect0(78,85,40,13);              //### Should use font metrics ###
  painter.fillRect(rect0,Qt::black);
  painter.setPen(Qt::yellow);
  painter.drawText(80,95,t1);
  update();
}

void FPlotter::mousePressEvent(QMouseEvent *event)      //mousePressEvent
{
  if(m_mode=="MSK144") return;
  int x=event->x();
  int y=event->y();
  int n=event->button();
//  bool ctrl = (event->modifiers() & Qt::ControlModifier);
  QPainter painter(&m_HorizPixmap);
  int x0=x-n*m_pixPerSecond;
  int x1=x+n*m_pixPerSecond;
  int xmax=m_TRperiod*m_pixPerSecond;
  if(x0 < 0) x0=0;
  if(x1 > xmax) x1=xmax;
  if(x1 > 702) x1=702;
  Q_EMIT fastPick (x0,x1,y);
  int y0=64;
  if(y >= 120) y0+=100;
  if(m_x0+m_x1 != 0) {
    painter.setPen(Qt::black);
    painter.drawLine(m_x0,m_y0,m_x1,m_y0);              //Erase previous yellow line
    painter.drawLine(m_x0,m_y0-3,m_x0,m_y0+3);
    painter.drawLine(m_x1,m_y0-3,m_x1,m_y0+3);
  }
  painter.setPen(Qt::yellow);
  painter.drawLine(x0,y0,x1,y0);                        //Draw yellow line
  painter.drawLine(x0,y0-3,x0,y0+3);
  painter.drawLine(x1,y0-3,x1,y0+3);
  update();                                             //trigger a new paintEvent
  m_x0=x0;
  m_x1=x1;
  m_y0=y0;
}

void FPlotter::setMode(QString mode)                            //setMode
{
  m_mode=mode;
}


code:
///////////////////////////////////////////////////////////////////////////
// Some code in this file and accompanying files is based on work by
// Moe Wheatley, AE4Y, released under the "Simplified BSD License".
// For more details see the accompanying file LICENSE_WHEATLEY.TXT
///////////////////////////////////////////////////////////////////////////

#ifndef FPLOTTER_H
#define FPLOTTER_H

#include <QtWidgets>
#include <QFrame>
#include <QImage>
#include <cstring>

class FPlotter : public QFrame
{
  Q_OBJECT
public:
  explicit FPlotter(QWidget *parent = 0);
  ~FPlotter();

  qint32  m_w;
  qint32  m_plotZero;
  qint32  m_plotGain;
  qint32  m_greenGain;
  qint32  m_greenZero;
  qint32  m_x0;
  qint32  m_x1;
  qint32  m_y0;
  qint32  m_UTCdisk;
  bool    m_diskData;

  void draw();		                                    //Update the Fast plot
  void setPlotZero(int plotZero);
  void setPlotGain(int plotGain);
  void setGreenZero(int n);
  void setTRperiod(int n);
  void drawScale();
  void setMode(QString mode);

signals:
  void fastPick (int x0, int x1, int y);

protected:
  //re-implemented widget event handlers
  void paintEvent(QPaintEvent *event);
//  void resizeEvent(QResizeEvent* event);

private slots:
  void mousePressEvent(QMouseEvent *event);
  void mouseMoveEvent(QMouseEvent *event);

private:

  void MakeTimeStrs();
  int XfromTime(float t);
  float TimefromX(int x);
  qint64 RoundFreq(qint64 freq, int resolution);

  QPixmap m_ScalePixmap;
  QString m_HDivText[483];
  QString m_t;
  QString m_t0;
  QString m_t1;
  QString m_mode;

  double  m_pixPerSecond;

  qint32  m_hdivs;
  qint32  m_h;
  qint32  m_h1;
  qint32  m_h2;
  QPixmap m_HorizPixmap;
  qint32  m_jh0;
  qint32  m_TRperiod;

  bool    m_bPaint2;
};

extern float fast_green[703];
extern float fast_green2[703];
extern float fast_s[44992];                                    //44992=64*703
extern float fast_s2[44992];
extern int   fast_jh;
extern int   fast_jh2;
extern QVector<QColor> g_ColorTbl;

#endif // FPLOTTER_H


code:
#include "getfile.h"
#include <QDir>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

#ifdef WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <err.h>
#endif

#include "commons.h"

void getfile(QString fname, int ntrperiod)
{
  struct WAVHDR {
    char ariff[4];
    int lenfile;
    char awave[4];
    char afmt[4];
    int lenfmt;
    short nfmt2;
    short nchan2;
    int nsamrate;
    int nbytesec;
    short nbytesam2;
    short nbitsam2;
    char adata[4];
    int ndata;
  } hdr;

  char name[512];
  strncpy(name,fname.toLatin1(), sizeof (name) - 1);
  name[sizeof (name) - 1] = '\0';

  FILE* fp=fopen(name,"rb");

  int i1=fname.lastIndexOf("/");
  QString baseName=fname.mid(i1+1);

  i1=fname.indexOf(".wav",0,Qt::CaseInsensitive);
  dec_data.params.nutc=0;
  if(i1>0) {
    int i0=fname.indexOf("_",-11);
    if(i1==i0+7) {
      dec_data.params.nutc=fname.mid(i1-6,6).toInt();
    } else {
      dec_data.params.nutc=100*fname.mid(i1-4,4).toInt();
    }
  }
  if(ntrperiod > 120 or ntrperiod <0) ntrperiod=120;
  int npts=ntrperiod*12000;
  memset(dec_data.d2,0,2*npts);

  if(fp != NULL) {
    struct
    {
      char id[4];
      uint32_t size;
    } desc;
    char type[4];
    struct
    {
      uint16_t nfmt2;
      uint16_t nchan2;
      uint32_t nsamrate;
      uint32_t nbytesec;
      uint16_t nbytesam2;
      uint16_t nbitsam2;
    } fmt;

    // read header
    if (fread(&desc, sizeof desc, 1, fp) < 1) return; // RIFF
    if (fread(type, sizeof type, 1, fp) < 1) return;  // WAVE
    do
      {
        if (fread(&desc, sizeof desc, 1, fp) < 1) return; // WAVE component
        if (!memcmp(desc.id,"fmt ",4)) {
          fpos_t pos;
          fgetpos(fp,&pos);
          if (fread(&fmt,sizeof fmt,1,fp) < 1) return;
          fsetpos(fp,&pos);
        }
        if (!memcmp(desc.id,"data",sizeof desc.id)) break;
      } while (!fseek(fp,(desc.size + 1) / 2 * 2,SEEK_CUR));

// Read (and ignore) a 44-byte WAV header; then read data
//    int n=fread(&hdr,1,44,fp);
    int n=fread(dec_data.d2,2,npts,fp);
    if(hdr.nsamrate==11025) wav12_(dec_data.d2,dec_data.d2,&n,(short*)&fmt.nbitsam2);
    fclose(fp);
    dec_data.params.newdat=1;
    dec_data.params.kin=n;
  }
}

void savewav(QString fname, int ntrperiod)
{
  struct {
    char ariff[4];         //ChunkID:    "RIFF"
    int nchunk;            //ChunkSize: 36+SubChunk2Size
    char awave[4];         //Format: "WAVE"
    char afmt[4];          //Subchunk1ID: "fmt "
    int lenfmt;            //Subchunk1Size: 16
    short int nfmt2;       //AudioFormat: 1
    short int nchan2;      //NumChannels: 1
    int nsamrate;          //SampleRate: 12000
    int nbytesec;          //ByteRate: SampleRate*NumChannels*BitsPerSample/8
    short int nbytesam2;   //BlockAlign: NumChannels*BitsPerSample/8
    short int nbitsam2;    //BitsPerSample: 16
    char adata[4];         //Subchunk2ID: "data"
    int ndata;             //Subchunk2Size: numSamples*NumChannels*BitsPerSample/8
  } hdr;

  int npts=ntrperiod*12000;
//  qint16* buf=(qint16*)malloc(2*npts);
  char name[512];
  strncpy(name,fname.toLatin1(),sizeof (name) - 1);
  name[sizeof (name) - 1] = '\0';
  FILE* fp=fopen(name,"wb");

  if(fp != NULL) {
// Write a WAV header
    hdr.ariff[0]='R';
    hdr.ariff[1]='I';
    hdr.ariff[2]='F';
    hdr.ariff[3]='F';
    hdr.nchunk=36 + 2*npts;
    hdr.awave[0]='W';
    hdr.awave[1]='A';
    hdr.awave[2]='V';
    hdr.awave[3]='E';
    hdr.afmt[0]='f';
    hdr.afmt[1]='m';
    hdr.afmt[2]='t';
    hdr.afmt[3]=' ';
    hdr.lenfmt=16;
    hdr.nfmt2=1;
    hdr.nchan2=1;
    hdr.nsamrate=12000;
    hdr.nbytesec=2*12000;
    hdr.nbytesam2=2;
    hdr.nbitsam2=16;
    hdr.adata[0]='d';
    hdr.adata[1]='a';
    hdr.adata[2]='t';
    hdr.adata[3]='a';
    hdr.ndata=2*npts;

    fwrite(&hdr,sizeof(hdr),1,fp);
//    memcpy(dec_data.d2,buf,2*npts);
//    fwrite(buf,2,npts,fp);
    fwrite(dec_data.d2,2,npts,fp);
    fclose(fp);
  }
//  free(buf);
}

//#define	MAX_RANDOM	0x7fffffff
/* Generate gaussian random float with mean=0 and std_dev=1 */
float gran()
{
  float fac,rsq,v1,v2;
  static float gset;
  static int iset;

  if(iset){
    /* Already got one */
    iset = 0;
    return gset;
  }
  /* Generate two evenly distributed numbers between -1 and +1
   * that are inside the unit circle
   */
  do {
    v1 = 2.0 * (float)qrand() / RAND_MAX - 1;
    v2 = 2.0 * (float)qrand() / RAND_MAX - 1;
    rsq = v1*v1 + v2*v2;
  } while(rsq >= 1.0 || rsq == 0.0);
  fac = sqrt(-2.0*log(rsq)/rsq);
  gset = v1*fac;
  iset++;
  return v2*fac;
}

int ptt(int nport, int ntx, int* iptt, int* nopen)
{
#ifdef WIN32
  static HANDLE hFile;
  char s[10];
  int i3=1,i4=1,i5=1,i6=1,i9=1,i00=1;  //Defs to silence compiler warning

  if(nport==0) {
    *iptt=ntx;
    return 0;
  }

  if(ntx && (!(*nopen))) {
    sprintf(s,"\\\\.\\COM%d",nport);
    hFile=CreateFile(TEXT(s),GENERIC_WRITE,0,NULL,OPEN_EXISTING,
                     FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE) {
      QString t;
      t.sprintf("Cannot open COM port %d for PTT\n",nport);
      return 1;
    }
    *nopen=1;
  }

  if(ntx && *nopen) {
    i3=EscapeCommFunction(hFile,SETRTS);
    i5=EscapeCommFunction(hFile,SETDTR);
    *iptt=1;
  }

  else {
    i4=EscapeCommFunction(hFile,CLRRTS);
    i6=EscapeCommFunction(hFile,CLRDTR);
    i9=EscapeCommFunction(hFile,CLRBREAK);
    i00=CloseHandle(hFile);
    *iptt=0;
    *nopen=0;
  }
  if((i3+i4+i5+i6+i9+i00)==-999) return 1;    //Silence compiler warning
  return 0;
#else
  int control=TIOCM_RTS | TIOCM_DTR;
//  int control = TIOCM_RTS;
  static int fd;

  if(*nopen==0) {
    fd=open("/dev/ttyUSB0",O_RDWR | O_NONBLOCK);
    if(fd<0) {
      return -1;
    }
    *nopen=1;
  }

  if(ntx) {
    ioctl(fd, TIOCMBIS, &control);
    *iptt=1;
    *nopen=1;
  } else {
    ioctl(fd, TIOCMBIC, &control);
    close(fd);
    *iptt=0;
    *nopen=0;
  }
  return 0;
#endif
  if((nport+ntx+(*iptt)==-99999)) *nopen=0;   //Silence compiler warning
  return 0;
}


code:
// -*- Mode: C++ -*-
#ifndef GETFILE_H
#define GETFILE_H
#include <QString>
#include <QFile>
#include <QDebug>

void getfile(QString fname, int ntrperiod);
float gran();
//int ptt(int* nport, int* ntx, int* iptt);
int ptt(int nport, int ntx, int* iptt, int* nopen);

extern "C" {
int ptt_(int nport, int ntx, int* iptt, int* nopen);
void wav12_(short d2[], short d1[], int* nbytes, short* nbitsam2);
}

#endif // GETFILE_H


code:
Files under this directory are generated. Even though they are checked
into  source  control  they  should  not be  modified  directly.   See
../artwork/README for details of generating these files.

path: icons/README


code:
[Desktop Entry]
Version=1.0
Name=js8call
Comment=Amateur Radio Weak Signal Operating
Exec=js8call
Icon=js8call_icon
Terminal=false
X-MultipleArgs=false
Type=Application
Categories=AudioVideo;Audio;HamRadio;
StartupNotify=true


code:
/**
 * This file is part of JS8Call.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * (C) 2018 Jordan Sherer <kn4crd@gmail.com> - All Rights Reserved
 *
 **/

#include "jsc.h"
#include "varicode.h"

#include <cmath>

Codeword JSC::codeword(quint32 index, bool separate, quint32 bytesize, quint32 s, quint32 c){
    QList<Codeword> out;

    quint32 v = ((index % s) << 1) + (quint32)separate;
    out.prepend(Varicode::intToBits(v, bytesize + 1));

    quint32 x = index / s;
    while(x > 0){
        x -= 1;
        out.prepend(Varicode::intToBits((x % c) + s, bytesize));
        x /= c;
    }

    Codeword word;
    foreach(auto w, out){
        word.append(w);
    }

    return word;
}

QList<CodewordPair> JSC::compress(QString text){
    QList<CodewordPair> out;

    const quint32 b = 4;
    const quint32 s = 7;
    const quint32 c = pow(2, 4) - s;

    foreach(QString w, text.split(" ", QString::SkipEmptyParts)){
        bool ok = false;

        while(!w.isEmpty()){
            // this does both prefix and full match lookup
            auto index = lookup(w, &ok);
            if(!ok){
                break;
            }

            auto t = JSC::map[index];
            w = QString(w.mid(t.size));

            bool isLast = w.isEmpty();
            out.append({ codeword(index, isLast, b, s, c), (quint32)t.size + (isLast ? 1 : 0) /* for the space that follows */});
        }
    }

    return out;
}

QString JSC::decompress(Codeword const& bitvec){
    const quint32 b = 4;
    const quint32 s = 7;
    const quint32 c = pow(2, b) - s;

    QStringList out;

    quint32 base[8];
    base[0] = 0;
    base[1] = s;
    base[2] = base[1] + s*c;
    base[3] = base[2] + s*c*c;
    base[4] = base[3] + s*c*c*c;
    base[5] = base[4] + s*c*c*c*c;
    base[6] = base[5] + s*c*c*c*c*c;
    base[7] = base[6] + s*c*c*c*c*c*c;

    QList<quint64> bytes;
    QList<int> separators;
    auto iter = bitvec.begin();
    while(iter != bitvec.end()){
        quint64 byte = Varicode::bitsToInt(iter, 4);
        iter += 4;
        bytes.append(byte);

        if(byte < s){
            if(*iter){
                separators.append(bytes.length()-1);
            }
            iter += 1;
        }
    }

    int start = 0;
    while(start < bytes.length()){
        int k = 0;
        int j = 0;

        while(start + k < bytes.length() && bytes[start + k] >= s){
            j = j*c + (bytes[start + k] - s);
            k++;
        }

        j = j*s + bytes[start + k] + base[k];

        out.append(QString(JSC::map[j].str));
        if(!separators.isEmpty() && separators.first() == start + k){
            out.append(" ");
            separators.removeFirst();
        }

        start = start + (k + 1);
    }

    return out.join("");
}

quint32 JSC::lookup(QString w, bool * ok){
    return lookup(w.toLatin1().data(), ok);
}

quint32 JSC::lookup(char const* b, bool *ok){
    quint32 index = 0;
    quint32 count = 0;
    bool found = false;

    // first find prefix match to jump into the list faster
    for(quint32 i = 0; i < JSC::prefixSize; i++){
        // skip obvious non-prefixes...
        if(b[0] != JSC::prefix[i].str[0]){
            continue;
        }

        // ok, we found one... let's end early for single char strings.
        if(JSC::prefix[i].size == 1){
            if(ok) *ok = true;
            return JSC::list[JSC::prefix[i].index].index;
        }

        // otherwise, keep track of the first index in the list and the number of elements
        index = JSC::prefix[i].index;
        count = JSC::prefix[i].size;
        found = true;
        break;
    }

    // no prefix found... no lookup
    if(!found){
        if(ok) *ok = false;
        return 0;
    }

    // now that we have the first index in the list, let's just iterate through the list, comparing words along the way
    for(quint32 i = index; i < index + count; i++){
        // if we're no longer a prefix match, end.
        if(b[0] != JSC::list[i].str[0]){
            break;
        }

        quint32 len = JSC::list[i].size;
        if(strncmp(b, JSC::list[i].str, len) == 0){
            if(ok) *ok = true;
            return JSC::list[i].index;
        }
    }

    if(ok) *ok = false;
    return 0;
}


code:
#ifndef JSC_H
#define JSC_H

/**
 * (C) 2018 Jordan Sherer <kn4crd@gmail.com> - All Rights Reserved
 **/

#include <QTextStream>
#include <QList>
#include <QStringList>
#include <QMap>
#include <QPair>
#include <QVector>

typedef QPair<QVector<bool>, quint32> CodewordPair;        // Tuple(Codeword, N) where N = number of characters
typedef QVector<bool> Codeword;                        // Codeword bit vector

typedef struct Tuple{
    char const * str;
    int size;
    int index;
} Tuple;

class JSC
{
public:
#if 0
    static CompressionTable loadCompressionTable();
    static CompressionTable loadCompressionTable(QTextStream &stream);
#endif
    static Codeword codeword(quint32 index, bool separate, quint32 bytesize, quint32 s, quint32 c);
    static QList<CodewordPair> compress(QString text);
    static QString decompress(Codeword const& bits);

    static quint32 lookup(QString w, bool *ok);
    static quint32 lookup(char const* b, bool *ok);

    static const quint32 size = 262144;
    static const Tuple map[262144];
    static const Tuple list[262144];

    static const quint32 prefixSize = 68;
    static const Tuple prefix[68];
};

#endif // JSC_H


code:
#include "keyeater.h"

bool EscapeKeyPressEater::eventFilter(QObject *obj, QEvent *event){
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if(keyEvent->key() == Qt::Key_Escape){
            return true;
        }
    }

    // standard event processing
    return QObject::eventFilter(obj, event);
}

bool EnterKeyPressEater::eventFilter(QObject *obj, QEvent *event){
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return){
            bool processed = false;
            emit this->enterKeyPressed(obj, keyEvent, &processed);
            if(processed){
                return true;
            }
        }
    }

    // standard event processing
    return QObject::eventFilter(obj, event);
}


code:
#ifndef KEYEATER_H
#define KEYEATER_H

#include <QObject>
#include <QKeyEvent>

class EscapeKeyPressEater : public QObject
{
    Q_OBJECT
public:
    EscapeKeyPressEater(){}
    virtual ~EscapeKeyPressEater(){}

protected:
    bool eventFilter(QObject *obj, QEvent *event);
};

class EnterKeyPressEater : public QObject
{
    Q_OBJECT
public:
    EnterKeyPressEater(){}
    virtual ~EnterKeyPressEater(){}

protected:
    bool eventFilter(QObject *obj, QEvent *event);

public:
    Q_SIGNAL void enterKeyPressed(QObject *obj, QKeyEvent *evt, bool *pProcessed);
};

#endif // KEYEATER_H


code:
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

int killbyname(const char *szToTerminate)
// Created: 6/23/2000  (Ravi Kochhar)
// Last modified: 3/10/2002  (RK)
// Please report any problems or bugs to kochhar@physiology.wisc.edu
// The latest version of this routine can be found at:
//     http://www.neurophys.wisc.edu/ravi/software/killproc/
// Terminate the process "szToTerminate" if it is currently running
// This works for Win/95/98/ME and also Win/NT/2000/XP
// The process name is case-insensitive, i.e. "notepad.exe" and "NOTEPAD.EXE"
// will both work (for szToTerminate)
// Return codes are as follows:
//   0   = Process was successfully terminated
//   602 = Unable to terminate process for some other reason
//   603 = Process was not currently running
//   604 = No permission to terminate process
//   605 = Unable to load PSAPI.DLL
//   606 = Unable to identify system type
//   607 = Unsupported OS
//   632 = Invalid process name
//   700 = Unable to get procedure address from PSAPI.DLL
//   701 = Unable to get process list, EnumProcesses failed
//   702 = Unable to load KERNEL32.DLL
//   703 = Unable to get procedure address from KERNEL32.DLL
//   704 = CreateToolhelp32Snapshot failed

{
  BOOL bResult,bResultm;
  DWORD aiPID[1000],iCb=1000,iNumProc;  //,iV2000=0;
  DWORD iCbneeded,i,iFound=0;
  char szName[MAX_PATH],szToTermUpper[MAX_PATH];
  HANDLE hProc,hSnapShot,hSnapShotm;
  OSVERSIONINFO osvi;
  HINSTANCE hInstLib;
//  int iLen,iLenP,indx;
  int iLenP,indx;
  HMODULE hMod;
  PROCESSENTRY32 procentry;
  MODULEENTRY32 modentry;

  // Transfer Process name into "szToTermUpper" and convert to upper case
  iLenP=strlen(szToTerminate);
  if(iLenP<1 || iLenP>MAX_PATH) return 632;
  for(indx=0;indx<iLenP;indx++)
    szToTermUpper[indx]=toupper(szToTerminate[indx]);
  szToTermUpper[iLenP]=0;

  // PSAPI Function Pointers.
  BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * );
  BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *,
                                        DWORD, LPDWORD );
  DWORD (WINAPI *lpfGetModuleBaseName)( HANDLE, HMODULE,
                                        LPTSTR, DWORD );

  // ToolHelp Function Pointers.
  HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
  BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
  BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;
  BOOL (WINAPI *lpfModule32First)(HANDLE,LPMODULEENTRY32) ;
  BOOL (WINAPI *lpfModule32Next)(HANDLE,LPMODULEENTRY32) ;

  // First check what version of Windows we're in
  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  bResult=GetVersionEx(&osvi);
  if(!bResult) return 606;    // Unable to identify system version

  // At Present we only support Win/NT/2000/XP or Win/9x/ME
  // Seems to work OK in Win7
  if((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) &&
     (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)) return 607;

  if(osvi.dwPlatformId==VER_PLATFORM_WIN32_NT)
  {
    // Win/NT or 2000 or XP

    // Load library and get the procedures explicitly. We do
    // this so that we don't have to worry about modules using
    // this code failing to load under Windows 9x, because
    // it can't resolve references to the PSAPI.DLL.
    hInstLib = LoadLibraryA("PSAPI.DLL");
    if(hInstLib == NULL) return 605;

    // Get procedure addresses.
    lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))
        GetProcAddress( hInstLib, "EnumProcesses" ) ;
    lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *,
        DWORD, LPDWORD)) GetProcAddress( hInstLib,                                                                     "EnumProcessModules" ) ;
        lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE, LPTSTR,
        DWORD )) GetProcAddress( hInstLib, "GetModuleBaseNameA" ) ;

    if(lpfEnumProcesses == NULL || lpfEnumProcessModules == NULL ||
         lpfGetModuleBaseName == NULL) {
      FreeLibrary(hInstLib);
      return 700;
    }

    bResult=lpfEnumProcesses(aiPID,iCb,&iCbneeded);
    if(!bResult) {
      // Unable to get process list, EnumProcesses failed
      FreeLibrary(hInstLib);
      return 701;
    }

    // How many processes are there?
    iNumProc=iCbneeded/sizeof(DWORD);

    // Get and match the name of each process
    for(i=0;i<iNumProc;i++) {
      // Get the (module) name for this process
      strcpy(szName,"Unknown");
      // First, get a handle to the process
      hProc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,
                        aiPID[i]);
      // Now, get the process name
      if(hProc) {
        if(lpfEnumProcessModules(hProc,&hMod,sizeof(hMod),&iCbneeded) ) {
//          iLen=lpfGetModuleBaseName(hProc,hMod,szName,MAX_PATH);
          lpfGetModuleBaseName(hProc,hMod,szName,MAX_PATH);
        }
      }
      CloseHandle(hProc);
      // We will match regardless of lower or upper case
      if(strcmp(_strupr(szName),szToTermUpper)==0) {
        // Process found, now terminate it
        iFound=1;
        // First open for termination
        hProc=OpenProcess(PROCESS_TERMINATE,FALSE,aiPID[i]);
        if(hProc) {
          if(TerminateProcess(hProc,0)) {
            // process terminated
            CloseHandle(hProc);
            FreeLibrary(hInstLib);
            return 0;
          } else {
            // Unable to terminate process
            CloseHandle(hProc);
            FreeLibrary(hInstLib);
            return 602;
          }
        } else {
          // Unable to open process for termination
          FreeLibrary(hInstLib);
          return 604;
        }
      }
    }
  }

  if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
  {
    // Win/95 or 98 or ME

    hInstLib = LoadLibraryA("Kernel32.DLL");
    if( hInstLib == NULL )
      return 702;

    // Get procedure addresses.
    // We are linking to these functions of Kernel32
    // explicitly, because otherwise a module using
    // this code would fail to load under Windows NT,
    // which does not have the Toolhelp32
    // functions in the Kernel 32.
    lpfCreateToolhelp32Snapshot=
        (HANDLE(WINAPI *)(DWORD,DWORD))
        GetProcAddress( hInstLib,
                        "CreateToolhelp32Snapshot" ) ;
    lpfProcess32First=
        (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
        GetProcAddress( hInstLib, "Process32First" ) ;
    lpfProcess32Next=
        (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
        GetProcAddress( hInstLib, "Process32Next" ) ;
    lpfModule32First=
        (BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
        GetProcAddress( hInstLib, "Module32First" ) ;
    lpfModule32Next=
        (BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
        GetProcAddress( hInstLib, "Module32Next" ) ;
    if( lpfProcess32Next == NULL ||
        lpfProcess32First == NULL ||
        lpfModule32Next == NULL ||
        lpfModule32First == NULL ||
        lpfCreateToolhelp32Snapshot == NULL )
    {
      FreeLibrary(hInstLib);
      return 703;
    }

    // The Process32.. and Module32.. routines return names in all uppercase

    // Get a handle to a Toolhelp snapshot of all the systems processes.

    hSnapShot = lpfCreateToolhelp32Snapshot(
          TH32CS_SNAPPROCESS, 0 ) ;
    if( hSnapShot == INVALID_HANDLE_VALUE )
    {
      FreeLibrary(hInstLib);
      return 704;
    }

    // Get the first process' information.
    procentry.dwSize = sizeof(PROCESSENTRY32);
    bResult=lpfProcess32First(hSnapShot,&procentry);

    // While there are processes, keep looping and checking.
    while(bResult)
    {
      // Get a handle to a Toolhelp snapshot of this process.
      hSnapShotm = lpfCreateToolhelp32Snapshot(
            TH32CS_SNAPMODULE, procentry.th32ProcessID) ;
      if( hSnapShotm == INVALID_HANDLE_VALUE )
      {
        CloseHandle(hSnapShot);
        FreeLibrary(hInstLib);
        return 704;
      }
      // Get the module list for this process
      modentry.dwSize=sizeof(MODULEENTRY32);
      bResultm=lpfModule32First(hSnapShotm,&modentry);

      // While there are modules, keep looping and checking
      while(bResultm)
      {
        if(strcmp(modentry.szModule,szToTermUpper)==0)
        {
          // Process found, now terminate it
          iFound=1;
          // First open for termination
          hProc=OpenProcess(PROCESS_TERMINATE,FALSE,procentry.th32ProcessID);
          if(hProc)
          {
            if(TerminateProcess(hProc,0))
            {
              // process terminated
              CloseHandle(hSnapShotm);
              CloseHandle(hSnapShot);
              CloseHandle(hProc);
              FreeLibrary(hInstLib);
              return 0;
            }
            else
            {
              // Unable to terminate process
              CloseHandle(hSnapShotm);
              CloseHandle(hSnapShot);
              CloseHandle(hProc);
              FreeLibrary(hInstLib);
              return 602;
            }
          }
          else
          {
            // Unable to open process for termination
            CloseHandle(hSnapShotm);
            CloseHandle(hSnapShot);
            FreeLibrary(hInstLib);
            return 604;
          }
        }
        else
        {  // Look for next modules for this process
          modentry.dwSize=sizeof(MODULEENTRY32);
          bResultm=lpfModule32Next(hSnapShotm,&modentry);
        }
      }

      //Keep looking
      CloseHandle(hSnapShotm);
      procentry.dwSize = sizeof(PROCESSENTRY32);
      bResult = lpfProcess32Next(hSnapShot,&procentry);
    }
    CloseHandle(hSnapShot);
  }
  if(iFound==0)
  {
    FreeLibrary(hInstLib);
    return 603;
  }
  FreeLibrary(hInstLib);
  return 0;
}


code:
Before using the new "CQ nnn ..." feature in JTMSK mode, I suggest
performing the following tests of the necessary CAT control for your
radio.  (I'm assuming that you already have some experience with
JTMSK.)

TEST 1:
--------------------------------------------------------------------------
1. Start WSJT-X
2. Settings: "Enable VHF/UHF/Microwave features, "Rx frequency offset
   with 'CQ nnn ...'", Split=Rig, or Split="Fake it"
3. Main screen settings: Band=50.280, mode=JTMSK, T/R=15 s
4. Activate CQRx, set CQ Rx=265
   ==> Rx dial should now read 50.265 and Tx6 should be queued
5. Click "Enable Tx"
   ==> Tx sequences should occur at 50.280, Rx at 50.265

With most rigs, this test should work with Split configured as either
"Rig" or "Fake it".

TEST 2:
--------------------------------------------------------------------------
1. Start WSJT-X
2. Settings: MyCall=K1JT, "Enable VHF/UHF/Microwave features,
   "Rx frequency offset with 'CQ nnn ...'", Split=Rig or "Fake it"
4. Main screen settings: Band=50.280, mode=JTMSK, T/R=15 s
5. Open file 150826_120515.wav
   ==> see decoded message "K1JT VE1SKY FN74"
6. Click "Monitor" to restart monitoring
7. Activate CQRx, set CQ Rx=265
   ==> Rx dial should now read 50.265; Tx6 should be queued
8. Click "Enable Tx"
   ==> Tx sequences should occur at 50.280, Rx at 50.265
9. After the start of a transmission, double-click on the decoded message
   "K1JT VE1SKY FN74"
   ==> Tx2 should be generated and queued; transmission will pause
   briefly, Tx freq changed to 50.265, then Tx resumed.

The test file is posted at
http://physics.princeton.edu/pulsar/K1JT/150826_120515.wav


code:
                    White Paper: FT8 for DXpeditions
		  -----------------------------------
		  Joe Taylor, K1JT - October 27, 2017

Assumptions:

1. WSJT-X will have two distinct options that enable the maximum-rate
QSO exchanges described below.  Fox must select "Fox"; all Hounds must
select "Hound".

2. There will be an announced basic dial frequency for each band, say
f0=14082 kHz for 20m.  This is the basic Channel.

3. Fox always transmits in the 1st sequence, 200-800 Hz above f0.

4. Hounds call in 2nd sequence, 1000-5000 Hz above f0. Hounds
transmitting below f0+1000 Hz will not be answered.

5. If found necessary, additional Channels may be defined in which
Hounds can transmit.  (However, I suggest that CQ-by-call-area may
be easier to implement and use; and the software could be made to
prevent Hounds in the wrong area from transmitting.)

6. Ideally, Fox and Hounds should all use CAT control configured with
*Split Operation* set to *Rig* or *Fake It*, and transceiver dial
frequencies should best be calibrated to within a few Hz.  (WSJT-X
provides tools that make this fairly easy to do.)

When Fox is running a pileup, QSOs will look something like the
following exchanges.  Here I've assumed the Fox callsign is KH1DX,
his locator AJ10:

------------------------------------------------------------------------
    Fox                              Hounds
------------------------------------------------------------------------
1.  CQ KH1DX AJ10
2.                                   KH1DX K1ABC FN42, KH1DX W9XYZ EN37, ...
3.  K1ABC KH1DX -13
4.                                   KH1DX K1ABC R-11
5.  K1ABC RR73; W9XYZ <KH1DX> -17
6.                                   ... no copy from W9XYZ ...
7.  W9XYZ KH1DX -17
8.                                   ... no copy from W9XYZ ...
9.  G4AAA KH1DX -11
10.                                  KH1DX G4AAA R-03
11. G4AAA RR73; DL3BBB <KH1DX> -12
12.                                  KH1DX DL3BBB R-09
13. DL3BBB RR73; DE <KH1DX>
14. ...
------------------------------------------------------------------------

All messages except those containing "<...>" are standard FT8 messages
(i3bit=0, iFreeText=0).  Hounds transmit only standard messages.

Fox transmits standard messages and also special messages with
i3bit=1.  The special messages contain a callsign whose completed QSO
is being acknowledged; a callsign for the next station to be worked; a
hash code corresponding to the Fox callsign; and a signal report.
Users will see the Fox callsign enclosed in angle brackets, <KH1DX>.
The 72-bit message payload contains two 28-bit callsigns, a 10-bit
hash code, and a 6-bit signal report.  If no call has been queued up
by Fox for the next QSO, the acknowledgment message takes the
abbreviated form shown in line 13 above.

When a Hound receives a message with i3bit=1, the decoder interprets
the remaining 72 bits as described above.  If the 10-bit hash code
matches that for Fox's callsign, the message is displayed as in the
QSO exchanges shown above.  Otherwise the message is considered a
false decode and is not displayed.

Station Setup and Operation for FOX
-----------------------------------

A wide Rx bandwidth (up to 5 kHz) is selected.  The basic dial
frequency is set 1 kHz above f0 (thus 14083 kHz in my example) and the
audio TxFreq somewhere between -200 and -800 Hz.  (Yes, negative
numbers are OK.  *Split Operation* will reset the Tx dial frequency as
needed and will keep the generated Tx audio frequency between 1500 and
2000 Hz.)  Hounds with audio TxFreq set to N Hz will be received by Fox
at N-1000 Hz.

WSJT-X at Fox will maintain and display a list of all decoded Hounds
calling Fox in the past 2 to 4 Rx cycles.  The list might look
something like this (but typically will be much longer):

----------------------------
Call        Grid  Rpt  Freq
----------------------------
AA2UK       FM29  -11   240
AD9H        EN61  +02  1260
K0TPP       EM48  -15  1980
N2BJ        EN61  +11   540
N4NDR       EL98  -17  4620
NX4E        EM70  +00  3780
ON3LA       JN29  -10  3300
PD9BG       JO21  -21  2100
PJ4/KA1XYZ  FK60  -07  1020
VE1SKY      FN74  +03  1620
WB2REM      EL97  -13  3060
...
----------------------------

Fox can choose to have the list sorted on any column.

Fox selects a Hound to call next by clicking on a line.  Or he can hit
"F1" to have the program select a caller according to one of these
criteria (maybe others as well?):

 - Weakest caller
 - Strongest caller
 - Strongest one below -N dB (with N selectable)
 - Choose a call at random
 - Random choice with S/N between snrMin and snrMax dB.

After a particular Hound has been called, Fox's Auto-Sequencer looks
for a response containing "R+rpt" originating from that same callsign.
If such a message is received, Fox's next transmission will be the
special "acknowledge-and-call-next" type, with i3bit=1.  If the
expected message is not received, as in example line 6 above, the
report is sent to the same station again.  If the second attempt fails
and another Hound callsign has been queued up, the QSO is aborted and
the next Hound is called.

Station Setup and Operation for Hounds
--------------------------------------

Dial frequency is set to f0, 14082 kHz in my example. Rx bandwidth and
displayed range on the Wide Graph can be anything convenient, say 200
to 2600 Hz.  (Signal from Fox will be expected between 200 and 800
Hz.)  Enter callsign and locator of Fox on WSJT-X main window as *DX
Call* and *DX Grid*.  Choose a TxFreq offset of 1000 + 60*N for some N
in the range 1 to 80 (maybe even higher?).  Move TxFreq as desired,
hoping to find a clear slot, by using Shift+F11 and Shift+F12.

 - Hit F1 to call Fox in your next Tx sequence.  Yes, you must hit F1
   repeatedly, in order to keep calling.

 - The Auto-sequencer will watch for a decoded message that contains
   "MyCall DXcall rpt" or "MyCall <DXcall> rpt".  When one of these is
   received, your next transmission will be "DXcall MyCall R+rpt",
   sent automatically.

 - After you send the "R+rpt" message, AutoSeq will watch for a
   message that starts with "MyCall RR73; ...".  When that is
   received, you're in his log, and you'll be prompted to log the QSO.

Random thoughts
---------------

Fox's decoder has access to signals in a 4 kHz (maybe even 5 kHz?)
window.  At 60 Hz intervals, that's enough for around 65 (or 80?)
non-overlapping Hound signals.  If the pileup becomes too deep, more
spectrum might be used; but note that WSJT-X can't access more than 5
kHz at one time.  A better solution might be for Fox to call "CQ n
KH1DX AJ10", where n is a single digit indicating call area.  The
decoder could then limit the list of eligible calls to those in the
specified call area.  After decoding such a CQ, the software at Hound
could refuse to transmit unless MyCall falls in the specified call
area.  (Other special CQ formats can be imagined that would limit the
eligible Hound callsigns even further.)

We haven't thought much, yet, about logging issues for Fox.  I imagine
we could do what's necessary to join a N1MM+ logging network, if that's
deemed desirable.

A few questions:

Q1: Should the Auto-Sequencer allow for other cases in which a QSO has
been initiated by Fox, but one of next two messages is not copied by
either Fox or Hound?  For example, what if K1ABC does not copy message
#5?  Should he keep sending his message "KH1DX K1ABC R-11" ?  If Fox
receives this message again, should he acknowledge again?  And poor
W9XYZ, who never received an acknowledgment, will probably keep
sending "KH1DX W9XYZ R-19", or whatever.  If Fox eventually copies the
message, should the program remember that W9XYZ had been called, and
thus send him an acknowledgment?

Q2: Should we provide a stack for several to-be-called callsigns,
rather than just one?  Should re-ordering of calls in the stack be
permitted?

Q3: Can we handle WSJT-X "Type 1" and "Type 2" compound callsigns, for
Hounds?


code:
Auto-Sequencing algorithm for DXpedition station:

Start:
    CQMsg = "CQ KH1DX"   (or "CQ UP KH1DX", "CQ 116 KH1DX", etc.)
    TxMsg = CQMsg
    Ntry = 0
    QCALL = ""                                        # Callsign of current QSO partner
    go to Transmit

Transmit:
    TX                                                # (... takes 13.6 s)
    go to Receive

Receive:
    RX                                                # (... takes ~14 s)
    N = number of decodes                             # RxMsg[i], i=1,N
    if(N == 0)
        go to Transmit
    J = index of a reply from current QCALL           # RxMsg[J] = "KH1DX QCALL R<rpt>"

    if(QCALL == "")                                   # No QSO in progress
        Select new QCALL                              # Op chooses a caller
	if(QCALL == "")
	    TxMsg = CQMsg                             # No callers, we'll CQ again
    else                                              # QSO in progress
        if(J >= 1)                                    # Expected message was received
            log the QSO with QCALL
	    QCALL = ""
	    Select new QCALL                          # Op chooses a new caller
	    if(QCALL != "")
	        TxMsg = "73 NOW QCALL <rpt>"          # Start a new QSO
	    else
                TxMsg = "73 " + CQMsg                 # No callers, we'll CQ again
        else
            Ntry = Ntry + 1                           # Expected msg not received
	    if(Ntry <= NtryMax)
	        go to Transmit                        # Ask for a repeat
	    else
		QCALL = ""                            # Max tries exceeded, abort this QSO
	        Select new QCALL                      # Choose a new caller
	        if(QCALL != "")
	            TxMsg = "NIL NOW QCALL <rpt>"     # Start a new QSO
	        else
                    TxMsg = "NIL " + CQMSG            # No callers, we'll CQ again
    go to Transmit

Auto-Sequencing algorithm for those calling the DXpedition:

Start:
    TxMsg = "KH1DX MyCall"
    InQSO = false

Transmit:
    TX                                                # (... takes 13.6 s)
    go to Receive

Receive:
    RX                                                # (... takes ~14 s)
    if(RxMsg[i] contains "MyCall <rpt>")
        InQSO = true
        TxMsg = "KH1DX MyCall R<rpt>"
	go to Transmit

    if(RxMsg[i] contains "<rpt>")
        TxEnable = false
	go to Receive

    if(RxMsg[i] contains "CQ KH1DX")
        TxEnable = true
	go to Transmit


code:
			Fast Modes in WSJT-X
			--------------------

#######################################################################

IMPORTANT - IMPORTANT - IMPORTANT - IMPORTANT - IMPORTANT - IMPORTANT

Third-party individuals (i.e., others not part of the WSJT development
team) have been compiling WSJT-X from the open source code and making
unauthorized "releases" of their builds.  I do NOT reccommend use of
unauthorized builds on the air.  If a program revision has been released
in an official way, you will see it listed here.

If you operate with an unauthorized "rXXXX" code revisions in our
experimental code branch you have no idea what you've got.  Quite
possibly, the program was built from an intermediate temporary "save"
of various files, and was not even supposed to produce a usable
program.  Your observetions about what works or does not work are then
worse than useless -- they waste your time and ours.

So please, PLEASE, *PLEASE*: use *authorized*, "released* revisions
of this still-in-development software, like the revisions described here.

*ALSO:* If you choose to try an experimental release of JTMSK, please
accept the responsibility of reporting on your results.  You can send
reports to the "wsjtgroup" reflector, wsjtgroup@yahoogroups.com, or
email them directly to me.  All suggestions for improvements are
welcome!

Bug reports should include details on how to reproduce the undesirable
program behavior.  Reports on decoding performance are especially
useful when accompanied by example *.wav files with signals that
you think should have decoded, but did not.

#######################################################################

September 18, 2015
-----------------

New alpha release of experimental WSJT-X v1.6.1, r5910
------------------------------------------------------
Changes since revision 5889 include the following:

1. Improved behavior for auto-QSY with "CQ nnn ..." feature.  (May not
be exactly correct, yet, for all radios.  Please report if you find
problems with your rig.)

2. Allow optional use of Wide Graph in fast modes.

3. Add UTC labels to Fast Graph spectrograms.

4. Display correct DXCC entiry for "CQ nnn ..." messages.

5. Implement "Save Decoded" for fast modes.

6. Select Tx6 when "CQ Rx nnn" is enabled.

7. Fix bug in setting of TRperiod after switch to ISCAT mode.

8. Display proper symbol '&' in Tx messages in JTMSK mode.

To download this alpha release for Windows, paste the following link
into your browser:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5910-win32.exe

    -- 73, Joe, K1JT

#######################################################################

September 12, 2015
-----------------

New alpha release of experimental WSJT-X v1.6.1, r5889
------------------------------------------------------

A principal new feature in this release is designed to promote the use
of an agreed "calling frequency" for transmissions of the form

  CQ 265 K1ABC FN42

signifying that K1ABC will listen for replies on 50.265 (or 144.265,
or whatever) and will complete the QSO there.  The feature uses the
rig-control features of WSJT-X to handle the necessary frequency
switching.

##########################################################################
Changes since revision 5865 include the following:

1. New features that allow automatic rig control when you transmit or
respond to messages of the form "CQ 265 K1ABC FN42" on an agreed
calling frequency.  This feature should be especially useful for
meteor scatter.

2. Yellow-highlighted "Tx" messages in the right text window are now
properly labeled with 6-digit UTC (hhmmss) in all fast modes.

3. Fixed a bug (introduced in r5865) that inhibited transmitting in
JT4 mode.

4. Fixed a bug that caused Wide Graph to continue issuing green
separator lines at short (e.g. 15 s) intervals after you have switched
to a slow mode.

5. Fixed several more GUI appearance bugs associated with changing
modes or submodes.

6. Fixed a bug in which double-clicking on the Fast Graph could cause
program crashes.

7. Fixed a bug that sometimes caused "high tones" to be emitted in
JTMSK mode.

#######################################################################
Here's a brief description of how to use the "CQ nnn ..." features.

1. On program startup, go to the Settings | General tab and tick the
box labeled 'Rx frequency offset with "CQ nnn ..."'

2. Select JTMSK mode and 50.280 (or your some other agreed calling
frequency) from the drop-down band menu.  Remember that this menu is
not pre-populated with preferred frequencies for all modes on all
bands. Use Settings | Frequencies to add your desired modes and
frequencies to the list.

3. Tick the unlabeled checkbox just under the "Report: spinner to
activate the "CQ Rx nnn" spinner.  Set this control to your desired
QSO frequency in kHz above the nominal band edge.  On 6 meters, for
example, "265" means "50.265".

4. Your transceiver dial frequency should now show 50.265.  Changes to
the "CQ Rx nnn" spinner value should be reflected immediately in the
transceiver dial frequency, the displayed value on the WSJT-X main
screen, and in Tx message #6, the "CQ nnn... " message.

5. When you transmit the Tx6 message, the Tx frequency will be set at
the calling frequency.  Otherwise (when receiving, or when
transmitting any of the messages Tx1 through Tx5) the offset frequency
(50.265 in my example) will be used.

6. If you double-click on a received "CQ nnn ..." message on the
calling frequency, your rig will QSY to specified response frequency,
e.g. 50.265, for both Rx and Tx.

7. To go back to listening on the calling frequency, uncheck the box
that activated the "CQ Rx nnn" spinner.

#######################################################################

Fair warning: I have not yet tested all possible combinations of
"Split Operation" configuration (i.e., "None", "Rig", and "Fake it").
If you normally use Split operation, that should be OK for the "CQ
nnn ..." feature.

As always, please report any bugs that you find in r5889, including
pertinent details on your settings and the exact series of steps
required to reproduce the bug.

To download this alpha release for Windows, paste the following link
into your browser:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5889-win32.exe

    -- 73, Joe, K1JT

#######################################################################

New alpha release of experimental WSJT-X v1.6.1, r5865
------------------------------------------------------

This alpha release of WSJT-X includes major improvements to the JTMSK
decoder.  Changes since revision 5823 include the following:

1. On-screen controls labeled "Rx nnnn Hz" and "F Tol" (Rx frequency
and tolerance) now function as expected in JTMSK mode.  The frequency
search range can be up to 500 Hz, but note that sensitivity is
necessarily reduced for signals off frequency by more than about 250
Hz.  Normally you should leave Rx Freq set at 1500 Hz; suitable values
for F Tol are 100 to 500 Hz.

2. The JTMSK decoder now makes good use of strong, short pings (as
short as 0.1 s) as well as weak pings several times longer.

3. Improved calculation of S/N and frequency of decoded signals.

4. Unified appearance of window titles on all non-modal windows.

5. CW ID is disabled (for now, at least) when operating in any of the
WSJT fast modes.

6. In WSPR mode, display of "Receiving ... <band>" messages is
disabled when band-hopping is not in use.

7. Fixed several bugs affecting status and visibility of certain
on-screen controls after changes in operating mode.

8. Fixed a bug allowing display of duplicate decodes for the same
signal.

9. Fixed a bug preventing compilation on 64-bit systems, and cleaned
up some harmless compiler warnings.

#######################################################################
		Summary Description of JTMSK Protocol

JTMSK uses the same standard message structure as slow modes JT4, JT9,
and JT65.  User information is "source encoded" to 72 bits. A 15-bit
CRC is appended and a convolutional code with constraint length K=13
and rate r=1/2 is applied, making a total of (72+15+12)*2 = 198
information bits.  Three copies of the "Barker-11" code and three
even-parity bits are added for synchronization, making a total of
198+33+3 = 234 channel symbols.  Modulation uses a constant-envelope,
continuous-phase "minimum-shift keying" (MSK) waveform, with tone
frequencies of 1000 and 2000 Hz.

#######################################################################

To download this alpha release for Windows, paste the following link
into your browser:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5865-win32.exe

	-- 73, Joe, K1JT

August 28, 2015
---------------

New release of experimental WSJT-X v1.6.1, r5823
------------------------------------------------

To download for Windows, paste the following link into your browser:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5823-win32.exe

This experimental version of WSJT-X introduces a new fast mode called
JTMSK.  The letters MS, of course, imply meteor scatter; the three
letters MSK mean "Minimum Shift Keying", the modulation scheme used in
this mode.

Revision 5823 also includes a number of (mostly minor) bug fixes
relative to r5789.

IMPORTANT: If you choose to try JTMSK, please accept the
responsibility of reporting on your results.  You can send reports to
the "wsjtgroup" reflector, wsjtgroup@yahoogroups.com or email them
directly to me.  All suggestions for improvements are welcome!  Bug
reports should include details on how to reproduce the undesirable
program behavior.

The present JTMSK decoder has been optimized for short pings.  It does
not yet do a wide search for proper frequency alignment; you and your
QSO partner need to be "on frequency" to within +/- 100 Hz or better.
The decoder does not (yet) attempt to make optimal use of weak, slowly
varying signals.  These and other characteristics may be improved in
coming revisions.

KNOWN BUG: At present you should use T/R sequence lengths 15 s in
JTMSK mode.  If you have a program crash, open Windows Task Manager,
select the "Processes" tab, right-click on wsjtx.exe, and select "End
Process Tree".  Then restart the program.

I view JTMSK as a candidate for replacing both FSK441 and JTMS for all
meteor scatter work.  JTMSK has the major advantage of including
strong forward error correction (FEC), similar in usage to the schemes
used for many years in JT4, JT9, and JT65.  The structure of user
messages and the format of minimal QSOs is also identical to those
other modes.  But JTMSK is very fast, transmitting its full encoded
message content in 0.117 s, in a 2 kHz bandwidth.  JTMSK therefore
makes much better use of short pings than (for example) JT9H can do.

The Tx waveform of JTMSK has been carefully designed to have a number
of desirable features.  All messages are exactly the same length: 72
bits of user information are followed by a 15-bit CRC and encoded into
198 channel bits with a convolutional code (constraint length K=13,
rate r=1/2).  Three sequences of the "Barker-11" code are added, along
with three parity bits, making a total of 234 channel bits in each
message.  The MSK symbols for these bits are transmitted at 2000 baud,
and the full encoded message is repeated every 117 ms.

A summary description of modulation parameters for all WSJT(-X) modes
is shown in a table posted here:
http://physics.princeton.edu/pulsar/K1JT/wsjt_modes.txt and
illustrated graphically here:
http://physics.princeton.edu/pulsar/K1JT/wsjt_modes.pdf

	-- 73, Joe, K1jT

August 15, 2015
---------------

New release of experimental WSJT-X v1.6.1, r5789

Changes since WSJT-X v1.6.1, revision 5779:

1.  Major speedup (5x) of fast-JT9 decoder.
2.  Corrected logic for Auto-Sequencing operation.
3.  Stop after sending 73 five times in auto-sequence mode.
4.  Add an "Auto-Level" control to Fast Graph window.
5.  Send fast-mode decodes to PSKreporter web site.
6.  Support automatic logging via JTAlert-X.
7.  Send fast-mode output to file ALL.TXT.
8.  Better definition of dB levels for fast-JT9 signals.
9.  Rationalize the GUI behavior when changing mode, submode, fast/slow
    status, and T/R period.
10. Correct a flaw in the display of multiple decodes in a single
    fast-JT9 sequence.
11. Fix minor bugs reported by G3WDG, ND0B, OZ1PIF, and others.

To download for Windows, paste the following link into your browser:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5789-win32.exe

Please keep in mind that this is an experimental version of WSJT-X.
Bug reports and other feedback will be much appreciated, and will help
to make the program better!

    -- 73, Joe, K1JT

August 11, 2015
---------------

Since its origin in the dark ages (ca. 2001) WSJT has supported "fast"
modes (designed for meteor scatter, etc.) and "slow" modes (optimized
for EME and other weak-signal propagation types).  The most recent new
mode, JT9, now has *both* fast and slow submodes.

JT9A (the "original" JT9) is like JT65 and JT4: its T/R sequences are
one minute long, and its primary goal is best possible sensitivity for
very weak, approximately steady signals.  The new experimental JT9
submodes use the same message structure, encoding, and modulation type
(9-tone FSK) as JT9A, but wider tone spacing and (optionally) faster
keying rates.

You can download an experimental version of WSJT-X (v1.6.1, r5779) here:
http://physics.princeton.edu/pulsar/K1JT/wsjtx-1.6.1-r5779-win32.exe

The fast submodes currently being tested, JT9E - JT9H, have been found
highly effective for meteors and ionoscatter propagation on 6 and 10
meters.  Sensitivity is similar to ISCAT, or slightly better.
Decoding is much more reliable, because the JT9 protocol includes
strong forward error correction.  Decoding results are like those for
all the WSJT "slow" modes: you should see messages exactly as they
were transmitted, or nothing at all.  A potential side benefit is
automatic reporting of decodes to PSKreporter.

For details on the modulation parameters of the JT9 submodes, see the
table posted at
http://physics.princeton.edu/pulsar/K1JT/wsjt_modes.txt.

WSJT-X v1.6.1 r5779 has the following changes from r5769:

1. Numerous bug fixes
2. Double-click on decoded message now behaves properly
3. Faster decoding (further optimization still to come)
4. Decoded text is highlighted as in WSJT-X slow modes
5. Optional auto-sequencing

Fair warning: auto-sequencing is basically functional, but scarcely
tested.  Please watch what it is doing, and tell us how you think it
should be improved!

If you use WSJT-X also for other modes and other purposes, you may
want to save several different sets of configuration settings.  In
that case it's convenient to use the "-r xxx" option and start the
program from a command-prompt window.  For example:

C:\Users\joe> cd \wsjt\wsjtx\bin
C:\WSJT\wsjtx\bin> wsjtx -r xxx

... where "xxx" can be anything you like, for example "ISCAT",
"FAST9", etc.

Proper configuration for the JT9 fast modes includes the following
settings:

On the Settings | General tab:

 - check "Enable VHF/UHF/Microwave features"

Main window settings:

 - Mode JT9
 - Tx 700 Hz
 - Rx 700 Hz
 - Sync 0
 - Submode G  ... or E, F, and H (H not legal in US on 10m)
 - Tick "Fast"
 - T/R 30 s     (also 5, 10, 15 s)
 - FTol 500

Please keep in mind that this is an experimental version of WSJT-X.
It still has some rough edges, and no doubt some bugs.  Your feedback
will be much appreciated, and will help to make the program better!

    -- 73, Joe, K1JT


code:
CC = gcc
CXX = g++
FC = gfortran

FFLAGS = -O2 -fbounds-check -Wall -Wno-conversion
CFLAGS = -O2 -I.

# Default rules
%.o: %.c
	${CC} ${CFLAGS} -c $<
%.o: %.f
	${FC} ${FFLAGS} -c $<
%.o: %.F
	${FC} ${FFLAGS} -c $<
%.o: %.f90
	${FC} ${FFLAGS} -c $<
%.o: %.F90
	${FC} ${FFLAGS} -c $<

#all:	 qratest.exe tplt.exe
all:    qra64d.exe

OBJS1 = qratest.o qra64a.o ana64.o sync64a.o four2a.o smo.o smo121.o averms.o \
	timer_module.o packjt.o twkfreq.o spec64.o fmtmsg.o pctile.o \
	grid2deg.o deg2grid.o shell.o badmsg.o qra64_subs.o \
	qracodes.o npfwht.o pdmath.o qra12_63_64_irr_b.o \
	qra13_64_64_irr_e.o qra64.o image.o \
	zplt64a.o zplt64b.o lorentzian.o fchisq0.o peakup.o sync64.o

qra64_subs.o: qra/qra64/qra64_subs.c
	gcc -c -O2 -o qra64_subs.o qra/qra64/qra64_subs.c

qracodes.o: qra/qracodes/qracodes.c
	gcc -c -O2 -o qracodes.o qra/qracodes/qracodes.c

qra64.o: qra/qra64/qra64.c
	gcc -c -O2 -o qra64.o qra/qra64/qra64.c

npfwht.o: qra/qracodes/npfwht.c
	gcc -c -O2 -o npfwht.o qra/qracodes/npfwht.c

pdmath.o: qra/qracodes/pdmath.c
	gcc -c -O2 -o pdmath.o qra/qracodes/pdmath.c

qra12_63_64_irr_b.o: qra/qracodes/qra12_63_64_irr_b.c
	gcc -c -O2 -o qra12_63_64_irr_b.o  qra/qracodes/qra12_63_64_irr_b.c

qra13_64_64_irr_e.o: qra/qracodes/qra13_64_64_irr_e.c
	gcc -c -O2 -o qra13_64_64_irr_e.o  qra/qracodes/qra13_64_64_irr_e.c

qratest.exe: $(OBJS1)
	$(FC) -o qratest.exe $(OBJS1) C:\JTSDK\fftw3f\libfftw3f-3.dll

OBJS2 = tplt.o zplt64.o image.o
tplt.exe: $(OBJS2)
	$(FC) -o tplt.exe $(OBJS2)

OBJS3 = qra64d.o sync64a.o four2a.o smo.o smo121.o averms.o \
	timer_module.o packjt.o twkfreq.o spec64.o fmtmsg.o pctile.o \
	grid2deg.o deg2grid.o shell.o badmsg.o qra64_subs.o \
	qracodes.o npfwht.o pdmath.o qra12_63_64_irr_b.o \
	qra13_64_64_irr_e.o qra64.o image.o \
	zplt64a.o zplt64b.o lorentzian.o fchisq0.o peakup.o sync64.o

qra64d.exe: $(OBJS3)
	$(FC) -o qra64d.exe $(OBJS3) C:\JTSDK\fftw3f\libfftw3f-3.dll

.PHONY : clean

clean:
	$(RM) *.o qratest.exe tplt.exe

path: lib/Makefile