/*
 * g_reconvert.cpp
 *
 */


#include "gtl_cmdio.h"
#include "gtl_cmdopt.h"
#include "gtl_fileutils.h"
#include "gtl_osbox.h"
#include "gtl_md5.h"

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include <new>

#include "bsk_tiny_json.h"


#include "bsk_tiny_json.h"

#include "win_version.h"

#include "hdr/hdr_extract.h"

#include "postprocess.h"

static bool fix_date_only;

static bool fix_date(const char* current_name, unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minutes, unsigned int seconds, unsigned int fix_mask=0x0FF);

//#define WIN32_FAST_READ

#ifdef WIN32_FAST_READ
#include <windows.h>
#include <errno.h>
typedef HANDLE FD_T;

static unsigned long fr_fpos_;

static FD_T fr_open(const char* fname)
{
	fr_fpos_=0;
	HANDLE h=CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
	if (h==INVALID_HANDLE_VALUE)
	{
		errno=EACCES;
		return 0;
	}
	return h;
}

static unsigned int fr_size(FD_T h)
{
	unsigned int s=GetFileSize(h, 0);
	return s;
}

static int fr_seek(FD_T fd, long bpos)
{
	fr_fpos_=SetFilePointer(fd, bpos, 0, FILE_BEGIN);
	return 0;
}

static unsigned int fr_ftell(FD_T h)
{
	return fr_fpos_; //SetFilePointer(h, 0, 0, FILE_CURRENT);
}

static inline size_t fr_read(FD_T h, void* buffer, unsigned int size)
{
	DWORD readIn=0;
	BOOL ok=ReadFile(h, buffer, size, &readIn, 0);
	if (!ok)
		return 0;
	fr_fpos_+=readIn;
	return readIn;
}

static void fr_close(FD_T fd)
{
	CloseHandle(fd);
}

#else
typedef FILE* FD_T;

static unsigned long fr_fpos_;

static void fr_close(FD_T fd)
{
	fclose(fd);
}

struct rf_iobuf_t
{
	double align64;
	char buff[130*1024*1024];
};

static rf_iobuf_t rf_iobuf;

static FD_T fr_open(const char* fname)
{
	fr_fpos_=0;
	FD_T h=fopen(fname, "rb");
	if (!h)
	{
		return 0;
	}
	if (!fix_date_only)
	{
		int r=setvbuf(h, rf_iobuf.buff, _IOFBF, sizeof rf_iobuf.buff); //128*1024*1024);
		if (r)
			cmdio_oserror("sevbuf()");
	}
	return h;
}

static unsigned int fr_size(FD_T h)
{
    fseek(h, 0, SEEK_END);
    auto size=ftell(h);
    fseek(h, 0, SEEK_SET);
    return size;
}

static int fr_seek(FD_T fd, long bpos)
{
	auto r=fseek(fd, bpos, SEEK_SET);
	fr_fpos_=bpos;
	return r;
}

static unsigned int fr_ftell(FD_T h)
{
    return fr_fpos_; //ftell(h);
}

static inline size_t fr_read(FD_T h, void* buffer, unsigned int size)
{
	size_t s=fread(buffer, 1, size, h);
	fr_fpos_+=s;
	return s;
}

#endif

static int pp_level=-2;

/*Expected JSON:
 *
 * {
 *
 */

class cmd_options: public gtl::cmdopt_autoset
{
public:
  //vidgen_cmdopt() {}


  virtual void onVersion()
  {
    printf("g_reconvert v.%d.%d: Convert GRIFO-E recorded data\n", VMAJOR, VMINOR);
  }
  virtual void onHelp()
  {
    onVersion();
    cmdopt::onHelp();

    printf(
    		"Change list:\n"
    		"  - Post-process data for session history (pp-*.txt file)\n"
    		"  - Support DFE CAL data\n"
    		"  - generate flight track\n"
    		"  - add option /pbug, as workaround bug in DSP that set latitude = 0 in recored header\n"
    		"  - Generate decoded AESA beam position file 'ant_pos-*.txt'\n"
    		);

  }
};



GTL_CMDOPT_PARAMETERS;
  static gtl::cmdopt_pInFile pInFile("_b", "binfile", "source binary file", true, gtl::cmdopt::f_required);

  //static gtl::cmdopt_pInFile pInData("b", "data file", "source data file", true, gtl::cmdopt::f_required);

GTL_CMDOPT_QUALIFIERS;
static gtl::cmdopt_qualifier qNoSignal("nosign", "nosignal", "don't save signal");

  static gtl::cmdopt_pInt qConcatenate("n", "concatenate", "concatenate REC files", false, gtl::cmdopt::f_optionalvalue);

  static gtl::cmdopt_qualifier qFWH("hfw", "firmwareheader", "use FW header instead of Software");

  static gtl::cmdopt_qualifier qPosBug("$pbug", "posbug", "fix DSP header geopos bug");

  static gtl::cmdopt_pInt qPP("p", "postprocess", "post process session history");

  static gtl::cmdopt_qualifier qHSynch("h", "hsynch", "SYnch signal flows with header");

  static gtl::cmdopt_qualifier qSw("sw", "onlysw", "extract only SW data", false, gtl::cmdopt::f_negatable|gtl::cmdopt::f_repeatable);
  static gtl::cmdopt_pUInt qDsp("dsp", "onlydsp", "extract only DSP data", false, gtl::cmdopt::f_optionalvalue|gtl::cmdopt::f_negatable|gtl::cmdopt::f_repeatable);

  static gtl::cmdopt_qualifier qStop("ss", "stop", "stop on discontinuity");

  static gtl::cmdopt_pOutFile qOutFile("o", "outfile", "output file", true, gtl::cmdopt::f_required);
  static gtl::cmdopt_qualifier qDryrun("dryrun", "dryrun", "dryrun");

  static gtl::cmdopt_pInFile qCfgFile("j", "json", "json configuration file", false, gtl::cmdopt::f_optionalvalue);

  static gtl::cmdopt_pOutFile qLog("l", "log", "log file", false, gtl::cmdopt::f_optionalvalue);

  static gtl::cmdopt_pEnum qOutFmt("f", "format", "bhc", "output format");

  static gtl::cmdopt_pInt qMaxBatch("m", "max", "Maximum number of batches");

  static gtl::cmdopt_pInt qRows("r", "rows", "PRI number");
  static gtl::cmdopt_pInt qCols("c", "cols", "RBIN number");

  static gtl::cmdopt_pInt qZeroCheck("zero", "zero", "check signal zeros");

  static gtl::cmdopt_qualifier qFromStby("z", "fromstby", "start save from stby");

  static gtl::cmdopt_qualifier qAnalyze("a", "analyze", "Analyze only");

  static gtl::cmdopt_qualifier qFixDate("d", "fixdate", "Fix SATA file date");

  static gtl::cmdopt_pUInt qExtact("x", "examine", "examine some data");

  static gtl::cmdopt_pUInt qVideoOut("vout", "videoout", "out video to rec results", false, gtl::cmdopt::f_optionalvalue|gtl::cmdopt::f_negatable|gtl::cmdopt::f_repeatable);
  static gtl::cmdopt_pUInt qVideoShow("vshow", "videoshow", "show video", false, gtl::cmdopt::f_optionalvalue|gtl::cmdopt::f_negatable|gtl::cmdopt::f_repeatable);
  static gtl::cmdopt_qualifier qVideoSave("vsave", "videosave", "save video");
  static gtl::cmdopt_pInt qVideoFrame("vframe", "videoframe", "fix frame rate", false);

  static gtl::cmdopt_pUInt qGps("gps", "gps", "save gps track", false, gtl::cmdopt::f_optionalvalue);

  static gtl::cmdopt_qualifier qFixHR("fixhr", "fixhr", "fix HR debug data");

  static gtl::cmdopt_qualifier qSar("sar", "sar", "save only SAR");

  static gtl::cmdopt_qualifier qVideoST("vst", "videosubtitle", "Add AVi subtitles");

  static gtl::cmdopt_pUInt qBatch("b", "bstart", "start batch to save");

  //static gtl::cmdopt_pString qSF("elf", "patchs ELF section", "fix the specified ELF section to point to the embedded data");
//--end params

static cmd_options vidopt;

static bool show_video=false;
static bool save_video=false;
static unsigned int video_sleep=0;

struct srio_header_t
{
    uint32_t address;
    uint16_t size;
    uint16_t type;
    uint8_t dst;
    uint8_t src;
    char marker[6];
};

#include <map>
#include <vector>

#include <string>


struct map_selector_t
{
    std::string name;
    unsigned int base;
    unsigned int size;

    map_selector_t():
        base(0xFFFFFFFF),
        size(0)
    {
    }

    map_selector_t(const char* n, unsigned int b, unsigned int s):
        name(n),
        base(b),
        size(s)
    {
    }

    map_selector_t(const map_selector_t& s):
        name(s.name),
        base(s.base),
        size(s.size)
    {
    }

    void operator=(const map_selector_t& s)
    {
        name=s.name;
        base=s.base;
        size=s.size;
    }

};

static unsigned int g_last_batch;


struct rep_time_of_day
{
    unsigned int h;
    unsigned int m;
    unsigned int s;

    rep_time_of_day(unsigned int seconds=0)
    {
        set(seconds);
    }

    void set(unsigned int seconds)
    {
        unsigned int t1=seconds;
        h=t1/3600;
        m=(t1-h*3600)/60;
        s=(t1-(h*3600+m*60));
    }
};


class Flow;

static bool sar_mode;

class DataWriter
{
public:
    FILE* fd;

    bool write(Flow* f);

    unsigned long long close();
};

extern DataWriter dw;

class IFlowObserver
{
public:
    enum err_t { err_none, err_missed_blocks, err_duplicated_block};
    virtual ~IFlowObserver() {}

    virtual bool flowBegin(Flow*) { return true;}

    virtual void flowEnd(Flow*) {}
    virtual void flowFinalize(Flow* f) { flowEnd(f);}

    virtual bool flowError(Flow*, err_t err) { return true;}

    virtual bool flowBlock(Flow*, unsigned int /*index*/, const void* /*data*/) { return true;}

};

static bool flow_no_check=false;

class Flow
{
public:
    Flow(unsigned int wm, const char* name_, unsigned int srcId, unsigned int base_, unsigned int pp_, unsigned int size_, IFlowObserver* observer=0, unsigned int marker_=0):
        wmarker(wm),
        name(name_),
        abase(base_),
        pp(pp_),
        src(srcId),
        max_size(size_),
        rec_max_size(0),
        marker(marker_),
        recorded(0),
        pending(0),
        bsize(0),
        g_nrbin(0),
        g_npri(0)
    {
        cpp=0;
        bpp[0]=abase;
        pend[0]=abase+max_size;
        bpp[1]=bpp[0]+pp;
        pend[1]=pend[0]+pp;

        buffer_prologue=new unsigned char[max_size+2048];
        buffer=&buffer_prologue[1024]; //new unsigned char[max_size+1024];
        buffer_size=max_size;
        bsize=256;
        max_blocks=max_size/bsize;
        map=new int [max_size+1024];
        //isSignal=false;
        setObserver(observer); //obs=&dummyObs;
        clear();
    }
    Flow(const Flow& f): Flow(f.wmarker, f.name, f.src, f.abase, f.pp, f.max_size, f.obs, f.marker)
    {
    }

    void setObserver(IFlowObserver* observer)
    {
        if (observer)
            obs=observer;
        else
            obs=&dummyObs;

    }
    void clear()
    {
        memset(map, 0, max_size+1024); //sizeof map);
        memset(buffer, 0xCA, buffer_size);
        blocks=0;
        size=0;
        arrived_blocks=0;
        pending=0;
        written=1;
        begin_offset=0;
        last_offset=0;
    }

    inline int hit(unsigned int address)
    {
        if ((address>=bpp[0]) && (address<pend[0]))
            return 0;
        if ((address>=bpp[1]) && (address<pend[1]))
            return 1;
        return -1;
    }

    bool addBlock(unsigned int address, const void* b, unsigned f_offset)
    {
        int pp_index=hit(address);
        if (pp_index<0)
            return false;

        unsigned int base=bpp[pp_index];
        //unsigned int end=pend[pp_index];

        unsigned int index=(address-base)/bsize;
        if (index>max_blocks)
            return false;
        bool bok=true;
        if ((address==base) || (pp_index!=cpp))
        {
            if (blocks>0)
            {
                unsigned int ok_blocks=arrived_blocks;
                unsigned int n_blocks=size/bsize;

                if (!flow_no_check)
                {
                	ok_blocks=0;
                	for(unsigned int i=0; i<n_blocks; ++i)
                	{
                		ok_blocks+=map[i] ? 1 : 0;
                	}
                }
                arrived_blocks=ok_blocks;
                if (ok_blocks!=(n_blocks))
                {
                    cmdio_perror("%s: partial flow disregarded, arrived=%u, expected=%u", name, ok_blocks, n_blocks);
                }
                else
                {
                    obs->flowEnd(this);
                    //++recorded;
                }
                clear();
            }
            //obs->flowBegin(this);
        }
        cpp=pp_index;
        if (map[index])
        {
            cmdio_pwarning("%s: duplicated: %u", name, index);
            bok=obs->flowError(this, IFlowObserver::err_duplicated_block);
        }
        ++arrived_blocks;
        written=0;
        //cmdio_pdebug("%s[%d]: + %u", name, cpp, index);
        memcpy(&buffer[index*bsize], b, bsize);
        ++map[index];
        ++blocks;
        unsigned int current_size=(index+1)*bsize;
        if (current_size>size)
            size=current_size;
        if (size>rec_max_size)
        	rec_max_size=size;

        if (address==base)
        {
        	begin_offset=f_offset;
            bok=obs->flowBegin(this);
        }
        else
        {
        	last_offset=f_offset;
            //obs->flowBlock(this, index, b);

        }

        if (!bok)
            clear();

        return true;
    }

    unsigned int wmarker;
    const char* name;
    unsigned int abase;
    unsigned int pp;
    unsigned int bpp[2];
    unsigned int src;
    unsigned int dst;
    unsigned int max_size;
    unsigned int marker;
    unsigned int recorded;
    unsigned int bsize;

    unsigned int g_nrbin;
    unsigned int g_npri;

    int cpp;
    unsigned int pend[2];
    unsigned int max_blocks;
    int expected_blocks;
    int arrived_blocks;

    unsigned int blocks;
    int* map; //[2048];
    unsigned char* buffer_prologue;
    unsigned char* buffer;
    unsigned int buffer_size;
    unsigned int size;
    unsigned int rec_max_size;
    int written;
    int pending;

    unsigned int begin_offset;
    unsigned int last_offset;

    //bool isSignal;

    IFlowObserver* obs;

    IFlowObserver dummyObs;
};

class FlowSet
{
public:
    std::vector<Flow*> flows;

    void addFlow(Flow* f)
    {
        flows.push_back(f);
    }

    Flow* lookup(unsigned int address, unsigned int srcId, const void* block)
    {
        Flow* hit=0;
        for(Flow* f: flows)
        {
            if (f->hit(address)>=0) //(address>=f->base) && (address<f->end))
            {
                if (f->src==0)
                    f->src=srcId;
                if (f->src==srcId)
                    return f;
                else
                    hit=f;
            }
        }
        if (hit)
        {
            Flow* newFlow=new Flow(*hit);
            newFlow->src=srcId;
            addFlow(newFlow);
            cmdio_pinfo("Flow: %s@%u b=0x%X a=0x%X", newFlow->name, srcId, newFlow->abase, address);
            return newFlow;
        }
        return 0;
    }
};

//FILE* outfd;
unsigned int n_rbin;
unsigned int n_pri;
unsigned int n_batch;
unsigned int r_mode;

static unsigned int examine;

static unsigned int last_synth_frq;

#if 0
struct gc_vcr_frame_t
{
	unsigned int fnum;
	unsigned int dx;
	unsigned int dy;
	unsigned int stride;

	unsigned int validity;
	unsigned int time_tag;
	unsigned int batch_tag;
	unsigned int mission_date;
	unsigned int mission_time;

	unsigned int video_marker;

	unsigned int spare[8];

	int palette_updated;
	uint32_t palette[256];
};
#endif

#include "videowin.h"


static bool fw_header;

extern char time_of_mission[128];
extern const char* lbl_mode;

static void timer_examine(Flow* f, void* af, void* aesax, void* ext, void* hdr_fix=0);

static int gps_save_mode=-1;

void d1553_extract(const void* data, int gps_save_mode);

static void softHeaderProcess(Flow*);

unsigned int* fpga_sum;
unsigned int fpga_sum_size;

class FlowObserver: public IFlowObserver
{
public:

    virtual bool flowBegin(Flow* f)
    {
    	//if (f->pending)
    	//	flowEnd(f);

        f->g_nrbin=n_rbin;
        f->g_npri=n_pri;
        cmdio_pinfo1("++%s@%X[%d]: 0x%X", f->name, f->src, f->cpp, f->bpp[f->cpp]);

        f->pending=1;
        return true;
    }

    virtual void flowEnd(Flow* f)
    {

        //unsigned int n=f->size/sizeof(int16_t);
        f->expected_blocks=f->size/256;
        int b=f->expected_blocks-f->arrived_blocks;
        if (b)
        {
        	cmdio_pwarning("--%s@%X[%d]: %u b=%u (%d) [%u x %u]", f->name, f->src, f->cpp, f->size, f->arrived_blocks, b, f->g_nrbin, f->g_npri);
        }
        else
        	cmdio_pinfo1("--%s@%X[%d]: %u b=%u (%d) [%u x %u]", f->name, f->src, f->cpp, f->size, f->arrived_blocks, b, f->g_nrbin, f->g_npri);

        ++f->recorded;
        //dw.write(f);

        f->pending=0;

#if 1
        if (strcmp(f->name, "F-SUM")==0)
        {
        	if (qZeroCheck.present())
        	{
        		int n_zero=qZeroCheck.value();
        		if (n_zero<0)
        		{
					unsigned int tail=f->size-1;

					const unsigned char* p=&f->buffer[0];
					int zeros=0;
					for(; (tail>0) && (zeros<512); --tail, ++zeros)
					{
						if (p[tail])
							break;
					}
					if (zeros>(-n_zero))
					{
						cmdio_perror("Signal Trail Zeros: %u = %f words, %f blocks", zeros, zeros/4.0, zeros/(256.0));
					}
        		}
        		else if (n_zero>0)
        		{
					unsigned int s=(f->size/2)-128;

					const uint16_t* p=(uint16_t*)&f->buffer[0];
					int zeros=0;
					for(unsigned int i=0; (i<s) && (zeros<n_zero); ++i)
					{
						if (p[i]==0)
						{
							++zeros;
							if (zeros>=n_zero)
							{
								cmdio_perror("Signal zeros: %d - batch %u %u/%u", zeros, g_last_batch, i, s);
								break;
							}
						}
						else
							zeros=0;
					}
        		}
        		else
        		{
        			if (fpga_sum==0)
        				fpga_sum=(unsigned int*)malloc(4*1024*1024);
        			memcpy(fpga_sum, f->buffer, f->size);
        			fpga_sum_size=f->size;
        		}
        	}
        }
#endif
        if ((!qDsp.isActive()) &&  (strcmp(f->name, "SOFT")==0) && (f->src==0x53)) //EIF?
        {
			uint32_t* p=(uint32_t*)f->buffer;
			//cmdio_pwarning("SOFTDFE: 0x%X, 0x%X, 0x%X", p[0], p[17], p[18]);
			if ((p[17]==0x35353144) && (p[18]==0x00000033)) //1553?
			{
				d1553_extract(p, gps_save_mode);
			}
        }

        if ((save_video || show_video) && (strcmp(f->name, "VIDEO")==0))
        {
			static int video_initialized;
			const gc_vcr_frame_t* frame=reinterpret_cast<gc_vcr_frame_t*>(f->buffer);
        	if (show_video)
        	{
				if (!video_initialized)
				{
					video_initialized=1;

					RisWinStart(0, 0, 0, frame->dx, frame->dy, 0, 0, 0);
					cmdio_pinfo("video window: %ux%u (%u)", frame->dx, frame->dy, frame->stride);
				}
				//gc_remote_intruder((uint8_t*)&frame[1], frame->dx, frame->dy, frame->stride, (unsigned int*)frame->palette);
				gc_remote_intruder(frame);
				if (video_sleep>0)
					win_sleep(video_sleep);
        	}
        	if (save_video)
        	{
        		//gc_save((uint8_t*)&frame[1], frame->dx, frame->dy, frame->stride, (unsigned int*)frame->palette);
        		//gc_save_video()
        	}
        }

        if (strcmp(f->name, "VIDEO")==0)
        {
        	if (qVideoOut.isActive())
        	{
        		unsigned int dec_value=1;
        		if (qVideoOut.hasvalue())
        			dec_value=qVideoOut.value();
        		static unsigned int decimator;
        		++decimator;
        		if (decimator>=dec_value)
        		{
        			bool ok=dw.write(f);
        	        ++f->recorded;
        			decimator=0;
        			if (!ok)
        			{
        				//cmdio_pwarning("no video saved");
        			}
        		}
        	}
        	return;
        }

        //dw.write(f);

        if (strcmp(f->name, "SOFT")==0)
        {
			uint32_t* p=(uint32_t*)f->buffer;
			//cmdio_pwarning("SOFTDFE: 0x%X, 0x%X, 0x%X", p[0], p[17], p[18]);
			if ((p[17]==0x48505344) && (p[18]==0x4E495244))
			{
				softHeaderProcess(f);
			}
        }

        //++f->recorded;
        dw.write(f);

        if ((n_rbin==0) || (n_pri==0))
            return;

        if (!qDsp.isActive())
        {
			if ((examine & 4) && (strcmp(f->name, "EXTLINK")==0))
			{
				unsigned int xsize=f->size/4;
				uint32_t* p=reinterpret_cast<uint32_t*>(f->buffer);
				for(unsigned int i=0; i<xsize; ++i)
				{
					if (p[i]==0xa5a5a5a5)
						break;
					if (p[i]==0x5a5a5a5a)
					{
						++i;
						if ((p[i]==0x0000a0a2) || (p[i]==0x0000a0a1))
						{
							i+=2;
							unsigned int n=p[i];
							unsigned int fx=p[i+1];
							if (last_synth_frq!=fx)
							{
								cmdio_pwarning("***F: %u -> 0x%X = ", n, fx);
								last_synth_frq=fx;
							}
							if (n>1)
							{
							}
							break;
						}
					}
				}
			}
			if (examine & 1)
			{
				static void* softdfe_last=0;
				static void* aesax_last=0;
				static void* ext_last=0;
				if (strcmp(f->name, "TIMER")==0)
					timer_examine(f, softdfe_last, aesax_last, ext_last);
				else if (strcmp(f->name, "SOFTDFE")==0)
				{
					uint32_t* p=(uint32_t*)f->buffer;
					//cmdio_pwarning("SOFTDFE: 0x%X, 0x%X, 0x%X", p[0], p[17], p[18]);
					if ((p[17]==0x41534541) && (p[18]==0x005854))
					{
						static uint32_t softdfe_buffer[8*1024];
						memcpy(softdfe_buffer, p, sizeof softdfe_buffer);
						softdfe_last=softdfe_buffer;
					}
					if ((p[17]==0x41534541) && (p[18]==0x005852))
					{
						static uint32_t aesax_buffer[8*1024];
						memcpy(aesax_buffer, p, sizeof aesax_buffer);
						aesax_last=aesax_buffer;
					}
				}
				else if (strcmp(f->name, "EXTLINK")==0)
				{
					static uint32_t ext_buffer[8*1024];
					uint32_t* p=(uint32_t*)f->buffer;
					memcpy(ext_buffer, p, sizeof ext_buffer);
					ext_last=ext_buffer;
				}
			}
        }
    }

    virtual bool flowError(Flow*, err_t err) { return true;}

};

FlowSet fs;

static float rad2deg(float x)
{
	return 57.29577951308*x;
}

struct ant_pos_t
{
	unsigned int antttag;
	float navaz_rad;
	float navel_rad;
	float bodyaz_rad;
	float bodyel_rad;
	float antennaaz_rad;
	float antennael_rad;
	float utx_rad;
	float vtx_rad;
	float urx_rad;
	float vrx_rad;
	float uguardrx_rad;
	float vguardrx_rad;
	unsigned int spare[4];
};

static float saved_lat;
static float saved_lon;
static ant_pos_t saved_ant;
static hdr_extract_info_t hdr_saved_info;

class HeaderObserver: public FlowObserver //IFlowObserver
{
public:
    unsigned int batches;

    unsigned int rpos;
    unsigned int cpos;
    unsigned int bpos;
    unsigned int tpos;

    unsigned int timepos;

    unsigned int start_timestamp;
    unsigned int last_timestamp;
    unsigned int start_tt;
    unsigned int last_tt;
    unsigned int start_batch;
    unsigned int last_batch;
    unsigned int previous_batch;

    bool first_header;
    bool new_file;

    unsigned int ant_tt;
    int ant_dly;

    //unsigned int n_pri;
    //unsigned int cols;
    //unsigned int batch;
    HeaderObserver():
        batches(0),
        rpos(26),
        cpos(27),
        bpos(24),
        tpos(25),
        timepos(0),
        start_timestamp(0),
        last_timestamp(0),
        start_tt(0),
        last_tt(0),
        start_batch(0),
        last_batch(0),
        previous_batch(0),
        first_header(true),
        new_file(true),
        ant_tt(0),
        ant_dly(-1)
    {
    }

    void resetTimestamp()
    {
    	start_timestamp=0;
    	last_timestamp=0;
    	first_header=true;
    }

    virtual bool flowBegin(Flow* f)
    {
    	if (!fw_header)
    		return true;

    	synchFlows(f);
    	return flowBeginProcess(f, 0);
    }

    virtual void synchFlows(Flow* f)
    {
        if (qHSynch.present())
        {
            for(auto fx: fs.flows)
            {
                if (fx->size && fx->wmarker && (fx->written==0) && (fx!=f))
                    fx->obs->flowEnd(f);
            }
        }
    }

    virtual bool flowBeginProcess(Flow* f, unsigned int h_offset=0)
    {
#if 0
        if (qHSynch.present())
        {
            for(auto fx: fs.flows)
            {
                if (fx->size && fx->wmarker && (fx->written==0) && (fx!=f))
                    fx->obs->flowEnd(f);
            }
        }
#endif
        uint32_t* p=(uint32_t*)f->buffer;
        p=&p[h_offset];

        //cmdio_pwarning("SWHDR=0x%X %X %X %X", p[0], p[1], p[2], p[4]);
        for(int i=0; i<100; ++i)
        {
        	//if (p[i]==0x12345678)
        	//	cmdio_pwarning(".... %u", i);
        }
        if ((h_offset==0) && p[4]!=0x30524448)
        {
            cmdio_pinfo2("****%s@%X[%d]: 0x%X", f->name, f->src, f->cpp, f->bpp[f->cpp]);
            return false;
        }

        ++batches;
        n_pri=p[rpos]; //4*6+2];
        n_rbin=p[cpos]; //4*6+3];
        n_batch=p[bpos];
        unsigned int n_tag=p[tpos];
        //r_mode=p[bpos+10];

        last_tt=n_tag;
        last_batch=n_batch;
        g_last_batch=last_batch;

        if (first_header)
        {
            start_tt=n_tag;
            start_batch=n_batch;
            cmdio_pinfo("!!%s@%X[%d]: 0x%X  %u (%u): %ux%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri);
        }
        else
        {
        	static bool b_hole;
            if ((previous_batch+1)!=last_batch)
            {
            	if (!b_hole)
            		cmdio_pwarning("batch hole: %u -> %u (%u) - standby?", previous_batch, last_batch, last_batch-previous_batch);
            	b_hole=true;
            }
            else
            {
            	if (b_hole)
            	{
            		cmdio_pinfo("batch restart: %u -> %u (%u) - exit from standby?", previous_batch, last_batch, last_batch-previous_batch);
            		qFromStby.reset();
            	}
            	b_hole=false;
            }
            cmdio_pinfo2("!!%s@%X[%d]: 0x%X  %u (%u): %ux%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri);
        }

        previous_batch=last_batch;

#if 0
        //unsigned int t0=p[timepos];
        unsigned int t1=p[timepos+1];
        unsigned int h=t1/3600;
        unsigned int m=(t1-h*3600)/60;
        unsigned int s=(t1-(h*3600+m*60));

        last_timestamp=t1;

        if (first_header)
        {
            start_timestamp=t1;
            first_header=false;
            cmdio_pinfo("++%s@%X[%d]: 0x%X  %u (%u): %ux%u %u:%u:%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri, h, m, s);
        }
        else
            cmdio_pinfo1("++%s@%X[%d]: 0x%X  %u (%u): %ux%u %u:%u:%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri, h, m, s);
#endif
        return true;
    }

    bool flowBlock(Flow*, unsigned int /*index*/, const void* /*data*/) override
    {
        return true;
    }

    virtual void flowEnd(Flow* f) override
    {
    	if (fw_header)
    	{
    		flowEndProcess(f);
    		FlowObserver::flowEnd(f);
    	}
    	//else
        {//patch a DSP bug
        	unsigned int mode=((unsigned int*)f->buffer)[tpos+8];
        	if (mode==21)
        	{
        		sar_mode=true;
        	}
            float* p=(float*)f->buffer;
            //if (p[tpos+77]==0)
            {
            	saved_lat=p[tpos+77];
            	saved_lon=p[tpos+78];

            	saved_ant=*(ant_pos_t*)&p[tpos+77+10];
            }
            hdr_saved_info=*hdr_extract_antenna(&p[0]);
        }
    }

    virtual void flowEndProcess(Flow* f, unsigned int h_offset=0)
    {

        uint32_t* p=(uint32_t*)f->buffer;
        p=&p[h_offset];
        if ((h_offset==0) && p[4]!=0x30524448)
            return;

        unsigned int n_tag=p[tpos];
        //unsigned int t0=p[timepos];
        unsigned int t1=p[timepos+1];
        unsigned int h=t1/3600;
        unsigned int m=(t1-h*3600)/60;
        unsigned int s=(t1-(h*3600+m*60));

        unsigned int ant_now=p[tpos+87];

        r_mode=p[bpos+10];

        //int ant_now_delay=0;

        last_timestamp=t1;

        if (first_header)
        {
            start_timestamp=t1;
            first_header=false;
            cmdio_pinfo("++%s@%X[%d]: 0x%X  %u (%u): %ux%u %u:%u:%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri, h, m, s);
        }
        else
        {
            cmdio_pinfo1("++%s@%X[%d]: 0x%X  %u (%u): %ux%u %u:%u:%u", f->name, f->src, f->cpp, f->bpp[f->cpp], n_batch, n_tag, n_rbin, n_pri, h, m, s);
            if (true) //examine)
            {
				if (ant_now!=n_tag)
				{
					if (ant_now==ant_tt)
					{
						if (ant_dly<=0)
							cmdio_pwarning("Ant delay=1: %d 0x%X 0x%X (0x%X)", n_tag-ant_now, n_tag, ant_now, ant_tt);
						ant_dly=1;
					}
					else
					{
						cmdio_pwarning("Ant delay>1: %d 0x%X 0x%X (0x%X)", n_tag-ant_now, n_tag, ant_now, ant_tt);
						ant_dly=0;
					}
				}
				else if (ant_dly!=0)
				{
					cmdio_psuccess("Ant delay=0: %d 0x%X 0x%X (0x%X)", n_tag-ant_now, n_tag, ant_now, ant_tt);
					ant_dly=0;
				}
            }
        }
        ant_tt=n_tag;

    	float* fp=(float*)&p[tpos];

        if (h_offset)
        {
        	//cmdio_pwarning("Patched lat!!! %f", saved_lat);
        	//fp[77]=saved_lat;
        }
        if (new_file)
        {
        	float lat=fp[77];
        	float lon=fp[78];
            cmdio_pinfo("A/C position: %f latitude, %f longitude", rad2deg(lat), rad2deg(lon));
        	new_file=false;
        }
    }

    virtual void flowFinalize(Flow* f) override { FlowObserver::flowEnd(f);}

    virtual bool flowError(Flow*, err_t err) { return true;}

};

class SoftHeaderObserver: public HeaderObserver
{
public:
    SoftHeaderObserver() {}

    virtual bool flowBegin(Flow* f)
    {
    	//synchFlows(f);
    	return true;
    }

    bool flowBlock(Flow*, unsigned int /*index*/, const void* /*data*/) override
    {
        return true;
    }

    virtual void flowEnd(Flow* f) override
    {
        //FlowObserver::flowEnd(f);
        //float* p=(float*)f->buffer;

    	//hdr_extract_antenna(&p[36-11]);
        flowBeginProcess(f, (36-11));
        flowEndProcess(f, (36-11));
    }
};

FlowObserver obs;
SoftHeaderObserver/*HeaderObserver*/ swhobs;
HeaderObserver fwhobs;

HeaderObserver* activeHOBS;

#define hobs (*activeHOBS)

static void softHeaderProcess(Flow* f)
{
	if (!fw_header)
	{
		if (swhobs.flowBegin(f))
			swhobs.flowEnd(f);
	}
}


static const char* producerName(unsigned int id)
{
	if (id==0x53)
		return "EIF";
	if (id==0x33)
		return "DSP";
	if (id==0x56)
		return "DFE";
	return 0;
}

//FlowSet fs;

unsigned long long DataWriter::close()
{
    if (fd)
    {
        fflush(fd);
        unsigned long long total_size=ftello64(fd);
        return total_size;
    }
    return 0;
}

bool DataWriter::write(Flow* f)
{
	static bool write_active;

	if (!write_active)
	{
		write_active=qBatch.present() ? false : true;
		if (!write_active)
		{
			if (hobs.last_batch>=qBatch.value())
			{
				write_active=true;
				cmdio_pinfo("batch %u - start saving", hobs.last_batch);
			}
			else
			{
				return true;
			}
		}
	}

    //if (fd==0)
    //    return;

    unsigned int bn=(f->size & 0x0FF);
    if (bn)
    {
        cmdio_pwarning("Flow size not multiple of block size: %u", f->size);
    }
    bool write_marker=f->wmarker;
    if (qSw.isActive() && (strcmp(f->name, "VIDEO")))
    {
        if (f->wmarker!=0)
        {
            f->written=1;
            return false;
        }
    }
    if (qPosBug.present())
    {
		uint32_t* p=(uint32_t*)f->buffer;
		uint32_t smarker=p[17];
		if (smarker==0x48505344) //DSP SW DSPHDR?
		{
			int xpos=25;
			float* fp=(float*)&p[(36-11)+(xpos+77)];
			fp[0]=saved_lat;
			//cmdio_pwarning("Patched lat %f %f", fp[0], fp[1]);
		}

    }

    if ((f->src==0x33) && qNoSignal.present())
    {
        f->written=1;
        return false;
    }
    if (qDsp.isActive())
    {
    	bool disregard=true;

    	if (f->src==0x33)
    	{
    		disregard=false;
#if 0
    		uint32_t* p=(uint32_t*)f->buffer;
    		uint32_t smarker=p[17];

    		if (qDsp.hasvalue())
    		{
    			unsigned int level=qDsp.value();
    			if ((smarker==0x52415547) && (level<2))
    				disregard=true;
    			if ((smarker==0x52415547) && (level<3))
    				disregard=true;

    		}
#endif
    	}
		if (disregard)
        {
        	//cmdio_pwarning("Drop %s 0x%X", f->name, f->src);
            f->written=1;
            return false;
        }
    }
    else
        write_marker=true;

    if (qFromStby.present())
    {
    	f->written=1;
    	return false;
    }

    /*
    if (fd==0)
    {
        f->written=1;
    	return;
    }
	*/
    int prologue_offset=0;
    if (write_marker)
    {
        prologue_offset=(8*4);

        unsigned long long pos=fd ? ftello64(fd) : 0;
        //static uint32_t marker[8];
        uint32_t* marker=(uint32_t*)&f->buffer[-prologue_offset];
        marker[0]=0x7A7A7A7A; //==zzzz
        marker[1]=0x7A7A7A7A; //==zzzz
        marker[2]=f->g_nrbin;
        marker[3]=f->g_npri;
        strncpy((char*)&marker[2], f->name, 2*sizeof marker[1]);
        marker[4]=pos+(f->size)+sizeof marker;
        marker[5]=f->size;
        marker[6]=(f->dst<<8)|f->src;
        //fwrite(marker, 1, sizeof marker, fd);
    }

    bool saved=false;

    if (pp_level>-2)
    {
		int saved_pline=cmdio_get_proc_line();
		cmdio_pinfo2("PP %u...%u", f->begin_offset, f->last_offset);
		cmdio_proc_line(f->begin_offset);
		postprocess_block(f->src, producerName(f->src), &f->buffer[-prologue_offset], f->size+prologue_offset);
		cmdio_proc_line(saved_pline);
    }

    if (!qSar.present() || sar_mode)
    {
		if (fd)
		{
			size_t wn=fwrite(&f->buffer[-prologue_offset], 1, f->size+prologue_offset, fd);
			saved=wn==f->size+prologue_offset;
		}
    } else
    {
    	cmdio_pinfo("No SAR... no save");
    }

    f->written=1;

    return saved;
#if 0
    struct d_t
    {
        int16_t d[2];
    };
    d_t* p=(d_t*)(f->buffer);

    for(unsigned int r=0; r<n_rbin; ++r)
    {
        for(unsigned int pri=0; pri<n_pri; ++pri)
        {
            fprintf(fd, "%d,%d,", p[r+pri*n_rbin].d[0], p[r+pri*n_rbin].d[1]);
        }
        fprintf(fd, "0\n");
    }
#endif
}

DataWriter dw;

class RecCfgParse
{
public:
    json_t mem[2048];
    const json_t* json;

    //std::string lastError;

    const char* parse(char* str)   //Warning: str is modified by the parser
    {
        json = json_create( str, mem, sizeof mem / sizeof *mem );

        unsigned int max_size=2*1024*1024;    //1M

        unsigned int pingpong=0;
        unsigned int mbase=0;
        if (json==0)
        {
            return "Parsing Error";
        }

        json_t const* tmpObj = json_getProperty( json, "pingpong" );
        if (tmpObj && (JSON_TEXT == json_getType( tmpObj )))
        {
            const char* tmp=json_getValue(tmpObj);
            pingpong=strtoul(tmp, 0, 0);
        }
        cmdio_pinfo1("Cfg: pingpong=0x%X", pingpong);

        tmpObj = json_getProperty( json, "base" );
        if (tmpObj && (JSON_TEXT == json_getType( tmpObj )))
        {
            const char* tmp=json_getValue(tmpObj);
            mbase=strtoul(tmp, 0, 0);
        }
        tmpObj = json_getProperty( json, "max_size" );
        if (tmpObj && (JSON_TEXT == json_getType( tmpObj )))
        {
            const char* tmp=json_getValue(tmpObj);
            if (tmp)
            {
                errno=0;
                unsigned int s=strtoul(tmp, 0, 0);
                if (errno)
                {
                    max_size=s;
                }
            }
        }

        cmdio_pinfo1("Cfg: mbase=0x%X", mbase);

        json_t const* recObj = json_getProperty( json, "flows" );
        if ( !recObj || (JSON_ARRAY != json_getType( recObj )) ) {
            return "flows object not found";
        }

        json_t const* recList=recObj;
        json_t const* rec_point;

        for( rec_point = json_getChild( recList ); rec_point != 0; rec_point = json_getSibling( rec_point ) )
        {
            if ( JSON_OBJ == json_getType( rec_point ) )
            {
                char const* name = json_getPropertyValue( rec_point, "name" );
                char const* str_base= json_getPropertyValue( rec_point, "base" );
                char const* format= json_getPropertyValue( rec_point, "format" );
                char const* str_size= json_getPropertyValue( rec_point, "size");
                //char const* str_maxsize= json_getPropertyValue( rec_point, "maxsize");
                char const* str_rows= json_getPropertyValue( rec_point, "rows@");
                char const* str_cols= json_getPropertyValue( rec_point, "cols@");
                char const* str_batch= json_getPropertyValue( rec_point, "batch@");
                char const* str_pingpong= json_getPropertyValue( rec_point, "pingpong");
                char const* str_tt= json_getPropertyValue( rec_point, "tag@");
                char const* str_tstamp= json_getPropertyValue( rec_point, "timestamp@");

                unsigned int base=strtoul(str_base, 0, 0);
                if (str_base[0]=='+')
                    base+=mbase;

                unsigned int size=max_size;
                if (str_size)
                {
                    char* tail;
                    size=strtoul(str_size, &tail, 0);
                    if (tail && ((*tail=='K') || (*tail=='k')))
                        size*=1024;
                    else if (tail && ((*tail=='M') || (*tail=='m')))
                        size*=(1024*1024);
                }

                unsigned int flow_pp=pingpong;
                if (str_pingpong)
                {
                    flow_pp=strtoul(str_pingpong, 0, 0);
                }

                //for(unsigned int n=0; n<=flow_pp; n+=flow_pp)
                {
                    if (format==0)
                        format="c16";
                    if (strcmp(format, "H")==0)
                    {
                        fs.addFlow(new Flow(1, name, 0, base, flow_pp, size, &hobs));
                        hobs.bpos=strtol(str_batch, 0, 0);
                        hobs.rpos=strtol(str_rows, 0, 0);
                        hobs.cpos=strtol(str_cols, 0, 0);
                        hobs.tpos=strtol(str_tt, 0, 0);
                        hobs.timepos=hobs.bpos+64;
                        if (str_tstamp)
                        {
                            unsigned int tmp=strtol(str_tstamp, 0, 0);
                            if (str_tstamp[0]=='+')
                                hobs.timepos=hobs.bpos+tmp;
                            else
                                hobs.timepos=tmp;
                        }
                        swhobs.bpos=hobs.bpos;
                        swhobs.rpos=hobs.rpos;
                        swhobs.cpos=hobs.cpos;
                        swhobs.tpos=hobs.tpos;
                        swhobs.timepos=hobs.timepos;

                        cmdio_pinfo1("Flow: %s b=0x%X, f=%s, s=%u, b@=%u, r@=%u, c@=%u", name, base, format, size, hobs.bpos, hobs.rpos, hobs.cpos);
                    }
                    else
                    {
                        unsigned int wm=1;
                        if (strcmp(format, "SW")==0)
                            wm=0;
                        if (size==0)
                            size=2048*256*4;
                        Flow* newflow=new Flow(wm, name, 0, base, flow_pp, size, &obs);
                        fs.addFlow(newflow);
                        cmdio_pinfo1("Flow: %s b=0x%X (+0x%X), f=%s, s=%u ", name, base, flow_pp, format, size);
                        //if (strcmp(format, "c16"))
                        //	newflow->isSignal=true;

                    }
                    //base+=flow_pp;
                    //if (flow_pp==0)
                    //    break;
                }
            }
        }
        return 0;

    }
};

extern const char* json_str_default;

class DataReader
{
public:
    FD_T fd; //FILE* fd;
    //HANDLE* wFd;

    char base_name[1024];
    char current_name[1024];
    char f_ext[512];
    char inname[512];

    int nf_start;
    int nf_end;
    int nf;
    int nf_count;

    int concatenate;
    int processed_files;
    long pos;
    unsigned long long total_size;
    long f_size;

    unsigned int s_timestamp;
    unsigned int s_batch;
    unsigned int s_tt;
    bool first_close;

    unsigned int c_year=0;
    unsigned int c_month=0;
    unsigned int c_day=0;
    unsigned int c_hour=0;
    unsigned int c_minutes=0;
    unsigned int c_seconds=0;

    bool start(const char* fname, unsigned int cat)
    {
        nf_start=0;
        nf_end=nf_start+1;

        strcpy(base_name, fname);
        strcpy(current_name, fname);

        gtl_extract_extension(f_ext, fname);

        concatenate=cat;
        nf=0;

        if (concatenate)
        {
            int max=concatenate;
            if (max<0)
                max=100000;
            if (max==0)
                max=1;

            char* index_pos=strrchr(fname, '_');
            if (index_pos)
            {
                nf_start=strtol((index_pos+1), 0, 0);
                int i=0;
                for(const char* pp=fname; pp<=index_pos; ++pp)
                {
                    base_name[i]=*pp;
                    ++i;
                    base_name[i]=0;
                }
                nf_end=nf_start+(max);
            }
            cmdio_pinfo("Process: %s -> %d:%d", base_name, nf_start, nf_end);
        }
        processed_files=0;
        nf_count=0;
        nf=nf_start;
        pos=0;
        return open();
    }

    void close()
    {
        if (fd)
        {
            if (!first_close)
            {
                s_batch=hobs.start_batch;
                s_timestamp=hobs.start_timestamp;
                s_tt=hobs.start_tt;
                first_close=true;
            }
            rep_time_of_day tod(s_timestamp);

            cmdio_pinfo("batches: %lu..%lu  %u:%02u:%02u", s_batch, hobs.last_batch, tod.h, tod.m, tod.s);
            {
                s_batch=hobs.last_batch;
                s_timestamp=hobs.last_timestamp;
                s_tt=hobs.last_tt;
            }
            fr_close(fd); //fclose(fd);

            if (qFixDate.present() && s_timestamp)
            {
            	cmdio_pinfo("fix date: %u-%u-%u  %u:%u:%u", c_day, c_month, c_year, tod.h, tod.m, tod.s);
            	fix_date(current_name, c_year, c_month, c_day, tod.h, tod.m, tod.s);
            }
        }
    }

    bool open(bool no_info=false)
    {
        if (concatenate)
        {
            sprintf(current_name, "%s%d.%s", base_name, nf, f_ext);

        }
        //if (nf>=nf_end)
        //    return false;

        if (nf<nf_end)
        {
            gtl_extract_name(inname, current_name);
            gc_set_file_name(inname);
            cmdio_proc_file(inname);
            cmdio_proc_line(0);

            if (qFixDate.present())
            {
                //dr.close();
                //char* p=strchr(dr.iname, '_')
                int n=sscanf(inname, "_%u-%u-%u-%u-%u-%u_", &c_year, &c_month, &c_day, &c_hour, &c_minutes, &c_seconds);
                //cmdio_pinfo("fixing to: %u-%u-%u  %u:%u:%u", c_day, c_month, c_year, c_hour, c_minutes, c_seconds);
                if (n!=6)
                {
                    cmdio_perror("Invalid filename format %s", base_name);
                    exit(0);
                }
#if 0
                bool ok=fix_date(current_name, c_year, c_month, c_day, c_hour, c_minutes, c_seconds);
				if (!ok)
				{
					cmdio_oserror("fix date");
					exit(0);
				}
#endif
            }

            fd=fr_open(current_name); //fopen(current_name, "rb");
            //setvbuf(fd, 0, _IOFBF, 1*1024*1024);

            hobs.new_file=true;
        }

        if ((fd==0) || (nf>=nf_end))
        {
            //cmdio_proc_file(inname);
            cmdio_proc_line(0);
            if (concatenate)
            {
                if (processed_files<=0)
                {
                    cmdio_perror("processed %d files", 0);
                }
                else
                    cmdio_psuccess("%d..%d: processed %d files", nf_start, nf_start+(processed_files-1), processed_files);
            }
            else if (processed_files<=0)
                cmdio_perror("[%d] Source: %s: not found!", nf_count, current_name);
            return false;
        }
        auto size=fr_size(fd);
        f_size=size;
        total_size+=f_size;
#if 0
        fseek(fd, 0, SEEK_END);
        auto size=ftell(fd);
        total_size+=size;
        f_size=size;
        fseek(fd, 0, SEEK_SET);
#endif
        ++nf;
        ++nf_count;
        if (no_info)
            cmdio_pinfo("[%d] processing", nf_count);
        else
            cmdio_pinfo("[%d] size = %lu", nf_count, size);
        pos=fr_ftell(fd); //ftell(fd);

        return true;
    }

    void skipToNext()
    {
        cmdio_pinfo("skip to next");
    	close();
    	++processed_files;
    	open();
    }

    bool read(void* p, unsigned int size)
    {
        if (!fd)
            return false;

        char* c=reinterpret_cast<char*>(p);

        pos=fr_ftell(fd); //ftell(fd);
        cmdio_proc_line(pos);

        if (pos>f_size)
        {
            cmdio_perror("pos beyond EOF (%lu)", f_size);
            return false;
        }


        size_t n=fr_read(fd, c, size); //fread(c, 1, size, fd);

        if (n<size)
        {
            size_t remain=size-n;
            close();
            ++processed_files;
            bool ok=open();
            if (ok)
            {
                size_t n1=fr_read(fd, &c[n], remain); //fread(&c[n], 1, remain, fd);
                if (n1==remain)
                {
                    cmdio_pinfo1("partial read %u=%u+%u", size, n, n1);
                    return true;
                }
            }
            return false;
        }
        else
            return true;
    }
    bool backward(int n)
    {
        long backsize=1*(4*4);
        if (pos>backsize)
        {
            long bpos=pos-backsize;
            fr_seek(fd, bpos); //fseek(fd, bpos, SEEK_SET);
            pos=fr_ftell(fd); //ftell(fd);
            cmdio_proc_line(pos);
            cmdio_pwarning("Back retry: 0x%lX (-0x%X)...", pos, backsize);
            return true;
        }
        return false;
    }
};

static DataReader dr;

//static bool fix_date(const char* current_name, unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minutes, unsigned int seconds);

extern int vsave_frame_rate;
extern const char* vsave_filename;

#include <windows.h>

static volatile bool please_break;

static BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
    switch (dwCtrlType)
    {
    case CTRL_C_EVENT:
        printf("[Ctrl]+C\n");
        please_break = true;
        // Signal is handled - don't pass it on to the next handler
        return TRUE;
    default:
        // Pass signal on to the next handler
        return FALSE;
    }
}

//static void d1553_gpx_close();


static char outname[2048];

const char* mk_fname(const char* prefix, const char* ext)
{
	static char buffer[1024];
	sprintf(buffer, "%s-%s.%s", prefix, outname, ext);
	return buffer;
}

int main(int argc, char** argv)
{
    std::map<unsigned long, unsigned int> unrecognized_map;


    activeHOBS=&fwhobs;

    const char* json_default="recnv_cfg.json";

    gtl_filename_t pgname;
    gtl_extract_name(pgname, argv[0]);

    char pname[512];
    gtl_extract_node(pname, pgname);
    cmdio_initialize(pname, cmdio_info);
    cmdio_byebye_mode(cmdio_auto_byebye);

    vidopt.onVersion();

    int board=0;
    if (strcmp(pname, "g_reconvdfe")==0)
    {
    	cmdio_psuccess("Autoset option for DFE playback");
    	board=1;
    }
    else if (strcmp(pname, "g_reconvdsp")==0)
    {
    	cmdio_psuccess("Autoset option for DSP playback");
    	board=2;
    }

    if (board)
    {
    	qVideoShow.onPresent();
    	qVideoShow.value(0);
    	qVideoOut.onPresent();
    	qVideoFrame.onPresent();
    	qVideoFrame.value(-1);

    	if (board==1)
    	{
    		qFWH.onPresent();
    		qNoSignal.onPresent();
    	}
    }

    if (!vidopt.parseParams(argc, argv))
      cmdio_exit(1);

    const char* jname=qCfgFile.value();

    unsigned int max_batches=qMaxBatch.present() ? qMaxBatch.value() : 1000000;

    if (qGps.present())
    {
    	if (qGps.hasvalue())
    		gps_save_mode=qGps.value();
    	else
    		gps_save_mode=1;
    }

    if (qCfgFile.present())
    {
        jname=qCfgFile.value();
    }
    else if (gtl_is_readable(json_default))
        jname=json_default;

    char* jstr=0;//json_str_default;
    if (jname)
    {
        FILE* jfd=fopen(jname, "r");
        fseek(jfd, 0, SEEK_END);
        auto jsize=ftell(jfd);
        fseek(jfd, 0, SEEK_SET);
        char* jstr_buffer=new char[jsize+2048];
        fread(jstr_buffer, 1, jsize, jfd);
        fclose(jfd);
        jstr=jstr_buffer;
    }
    else
    {
        unsigned int s=strlen(json_str_default)+2048;
        char* jstr_buffer=new char[s];
        memset(jstr_buffer, 0, s);
        strcpy(jstr_buffer, json_str_default);
        jstr=jstr_buffer;
        jname="<default>";
    }

    {
        cmdio_pinfo("Configuration file: %s", jname);

        RecCfgParse j;

        const char* jres=j.parse(jstr);
        if (jres)
            cmdio_pfail("%s: JSON error: %s", jname, jres);
    }

    //return 0;
    gtl_extract_node(outname, pInFile.value());
    strcat(outname, "-cv.out");
    //const char* outname="recnv_out.txt";

    char video_fname[512];
    sprintf(video_fname, "vcapture_%s.avi", outname);
    vsave_filename=video_fname;

    if (qOutFile.present())
    {
        strcpy(outname,qOutFile.value());
    }

    if (qPP.present())
    {
    	pp_level=0;
    	if (qPP.hasvalue())
    	{
    		int v=qPP.value();
    		pp_level=v;
    	}
    }

    const char* fname=pInFile.value(); //"./test_sata_file_5.rec";

    srio_header_t hdr;
    //size_t n=0;
    unsigned int blocks=0;
    static unsigned char raw[4096*32*4];

    Flow* f=0;


    if (qRows.present())
        n_pri=qRows.value();
    if (qCols.present())
        n_rbin=qCols.value();


    if (!dr.start(fname, qConcatenate.present() ? (qConcatenate.hasvalue() ? qConcatenate.value() : -1) : 0))
    {
        cmdio_oserror("open source file");
        exit(1);
    }

#if 0
    if (qFixDate.present())
    {
        unsigned int year=0;
        unsigned int month=0;
        unsigned int day=0;
        unsigned int hour=0;
        unsigned int minutes=0;
        unsigned int seconds=0;
        //dr.close();
        //char* p=strchr(dr.iname, '_')
        int n=sscanf(dr.inname, "_%u-%u-%u-%u-%u-%u_", &year, &month, &day, &hour, &minutes, &seconds);
        cmdio_pinfo("fixing to: %u-%u-%u  %u:%u:%u", day, month, year, hour, minutes, seconds);
        if (n!=6)
        {
            cmdio_perror("Invalid filename format %s", dr.base_name);
            exit(0);
        }
        do
        {
            dr.close();
            bool ok=fix_date(dr.current_name, year, month, day, hour, minutes, seconds);
            if (!ok)
            {
                cmdio_oserror("fix date");
                exit(0);
            }
            ++dr.processed_files;
        } while(dr.open(true));

        exit(0);
    }
#endif

    if (qAnalyze.present())
    {

    }
    else
    {
      dw.fd=fopen64(outname, "wb");
      if (!dw.fd)
        cmdio_oserror("opening file %s", outname);
      else
      {
    	  setvbuf(dw.fd, 0, _IOFBF, 1*1024*1024);
      }
    }

    cmdio_psuccess("Destination: %s", outname);

    bool leading_incomplete_logger=false;

    int backward_retry=0;

    examine=qExtact.present() ? qExtact.value() : 0;

    show_video=(qVideoShow.isActive()) ? 1 : 0;
    save_video=(qVideoSave.isActive()) ? 1 : 0;
    if (save_video)
    	gc_save();

    if (show_video && qVideoShow.hasvalue())
    {
    	unsigned int tmp=qVideoShow.value();
    	video_sleep=tmp;
    }
    if (qVideoFrame.present())
    	vsave_frame_rate=qVideoFrame.value();


    SetConsoleCtrlHandler(HandlerRoutine, TRUE);

    fw_header=qFWH.present();

    if (!fw_header)
    	activeHOBS=&swhobs;

    postprocess_initialize(pp_level);

    if (qFixHR.present())
    {
    	postprocess_fixsar(true);
    }

    fix_date_only=qFixDate.present() && qAnalyze.present();

    //for(int nf=nf_start; nf<nf_end; ++nf_count)
    {
        bool first_header=true;


        while(dr.read(&hdr, sizeof hdr)) //(n=fread(&hdr, 1, sizeof hdr, fd))==(sizeof hdr))
        {
            if (please_break)
            	break;
            //cmdio_proc_line(pos);

            //if (((hdr.dst==0x81) || (hdr.dst==0xB3)) && (strncmp(hdr.marker, "[SA\x14T]", 6)==0))
            if (fix_date_only)
            {
            	if (hobs.start_timestamp)
            	{
            		dr.skipToNext();
            		hobs.resetTimestamp();
            		continue;
            	}
            }

            if (strncmp(hdr.marker, "[SA\x14T]", 6)==0)
            {
                if (first_header)
                {
                    cmdio_psuccess("First block found");
                    first_header=false;
                }

                //fread(&raw[0], 1, 256, fd);
                bool ok=dr.read(&raw[0], 256);
                if (!ok)
                    break;

                f=fs.lookup(hdr.address, hdr.src, raw);
                if (!f)
                {
                    auto u=unrecognized_map.find(hdr.address);
                    if (u==unrecognized_map.end())
                    {
                        unrecognized_map[hdr.address]=1;
                        cmdio_pwarning("????%u @%X: 0x%X", blocks, (unsigned int)dr.pos, hdr.address);
                    }
                    else
                        ++unrecognized_map[hdr.address];
                }
                //fread(&raw[0], 1, 256, fd);
                ++blocks;

                if (f)
                {
                    f->dst=hdr.dst;
                    f->addBlock(hdr.address, raw, dr.pos);
                }

                backward_retry=0;
            }
            else
            {
            	if (fix_date_only)
            		continue;

                if (first_header)
                {
                    if (leading_incomplete_logger)
                        cmdio_pinfo2("leading incomplete block");
                    else
                    {
                        cmdio_pinfo("leading incomplete block");
                        leading_incomplete_logger=true;
                    }
                }
                else
                {
                    if (qStop.present())
                    {
                        cmdio_pwarning("Incomplete block (0x%X)", dr.pos);
                        break;
                    }
                    if (backward_retry<14)
                    {
                        if (backward_retry<1)
                            cmdio_pwarning("Incomplete block (0x%X)", dr.pos);
                        ++backward_retry;
                        bool ok=dr.backward(1+backward_retry);
                        if (!ok)
                        {
                            cmdio_pfail("back retry error");
                            break;
                        }
                    }
                    else
                    {
                        cmdio_perror("broken recording?");
                        break;
                    }
                }
            }
            //pos=ftell(fd);
            if (hobs.batches>=max_batches)
                break;
        }
        //++nf;
        //sprintf(current_name, "%s%d.%s", base_name, nf, f_ext);
        //fclose(fd);
    }

    unsigned long long t_out=dw.close();
    //if (dw.fd) fclose(dw.fd);

    cmdio_proc_file(0);
    for(auto f: fs.flows)
    {
        if (f->size && (f->written==0))
            f->obs->flowFinalize(f);
    }
    for(auto f: fs.flows)
    {
        if (f->recorded)
            cmdio_psuccess("%s@%0X: %d (0x%X*0x%X +0x%X)", f->name, f->src, f->recorded, f->abase, f->rec_max_size, f->pp);
        else
            cmdio_pinfo1("%s@%0X: %d", f->name, f->src, f->recorded);
    }
    if (unrecognized_map.size())
    {
        cmdio_pwarning("Unrecognized flow: %u", unrecognized_map.size());
        for(auto f: unrecognized_map)
        {
            cmdio_pwarning("  @%0X: %u", f.first, f.second);
        }
    }
	postprocess_print_summary();

    {
        double m_in=dr.total_size;
        m_in=m_in/(1024*1024);
        double m_out=t_out;
        m_out=m_out/(1024*1024);
        unsigned int start_time=hobs.start_timestamp;
        unsigned int end_time=hobs.last_timestamp;
        unsigned int elapsed_time=(end_time-start_time);
        if (elapsed_time<=0)
            elapsed_time=1;
        float rate=m_in/(elapsed_time>0 ? elapsed_time : 1);
        cmdio_psuccess("processed %.fMib -> %.3fMib, %.3f Mib/sec", m_in, m_out, rate);
        rep_time_of_day s_tod(start_time);
        rep_time_of_day e_tod(end_time);
        cmdio_psuccess("  timestamp:  %u:%02u:%02u..%u:%02u:%02u=%lusec", s_tod.h, s_tod.m, s_tod.s, e_tod.h, e_tod.m, e_tod.s, elapsed_time);
        cmdio_psuccess("  timetag  :  %lu..%lu=%lu = %.3fsec", hobs.start_tt, hobs.last_tt, hobs.last_tt-hobs.start_tt, float(hobs.last_tt-hobs.start_tt)*64.0e-6);
        cmdio_psuccess("  batches  :  %lu..%lu=%lu", hobs.start_batch, hobs.last_batch, hobs.last_batch-hobs.start_batch);

    }

    gc_close();
    d1553_extract(0, gps_save_mode);
    cmdio_bye(0, 0);
    //cmdio_exit(0);
    //return 0;
}


#include <windows.h>

static bool fix_date(const char* current_name, unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minutes, unsigned int seconds, unsigned int fix_mask)
{
    HANDLE h=CreateFileA(current_name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
    if (h==INVALID_HANDLE_VALUE)
        return false;

    static FILETIME ft;
    static SYSTEMTIME st;
    static SYSTEMTIME st2;
    st.wYear=2000+year;
    st.wMonth=month;
    st.wDay=day;
    st.wHour=hour;
    st.wMinute=minutes;
    st.wSecond=seconds;
    TzSpecificLocalTimeToSystemTime(0, &st, &st2);
    BOOL ok=SystemTimeToFileTime(&st2, &ft);

    FILETIME* ft_lastWrite=fix_mask & 0x1 ? &ft : 0;
    FILETIME* ft_create=fix_mask & 0x2 ? &ft : 0;
    if (ok)
        ok=SetFileTime(h, ft_create, 0, ft_lastWrite); //&ft);
    CloseHandle(h);
    return ok;
}




///

struct timer_raw_if_t
{
    template<unsigned int S_, unsigned int E_> struct spare_t {uint32_t xxx[(E_-S_)/4];};

    enum {
        batch_parity=2,
            batch_odd=0,
            batch_even=1,

        max_prt=128,
        max_unique_prt=8,

        spare_timing=8
    };

    struct prt_fifo_t
    {
        uint32_t fifo[max_prt];
    };

    struct unique_prt_fifo_t
    {
        uint32_t fifo[max_unique_prt];
    };

    struct shift_registers_t
    {
        int32_t B_Filter;   //Batch DET,PC,Dispatcher, reset filters, PC: reset filters
        int32_t B_RF;       //?
        int32_t PT_DET;     //DET: load offset phase
        int32_t PC_Win;     //PC output window
        int32_t RX_SYNC;    //STC and NAGC
    };

    uint32_t tcr;
    uint32_t tpr;
    uint32_t tor;
    uint32_t stsr;

    uint16_t prt_num;
    uint16_t diff_prt_num;

    uint32_t spares__[3];

    shift_registers_t shift;

    uint32_t spare__[19-(3+5)];

    unique_prt_fifo_t aesa_delay[batch_parity];

    spare_t<0x0bf,0x100> spare0__;

    unique_prt_fifo_t exp_pulse1_delay[batch_parity];
    unique_prt_fifo_t exp_pulse2_delay[batch_parity];

    unique_prt_fifo_t pretrigger_det_delay[batch_parity];

    spare_t<0x01bf,0x200> spare1__;

    struct delay_with_period_t
    {
        unique_prt_fifo_t delay;
        unique_prt_fifo_t width;
        unique_prt_fifo_t period;

        spare_t<0x025f,0x280> spare__;
    };
    delay_with_period_t rx_synch[batch_parity];
    delay_with_period_t cf_synch[batch_parity];

    delay_with_period_t ew_synch[batch_parity];
    //delay_with_period_t ant_rxn_synch[batch_parity];

    struct delay_width_t
    {
        unique_prt_fifo_t delay;
        unique_prt_fifo_t width;
    };

    delay_width_t ant_rxn[batch_parity];

    delay_width_t down_blank[batch_parity];
    delay_width_t up_enable[batch_parity];

    delay_width_t blank_out[batch_parity];

    delay_width_t acq_det_window[batch_parity];
    delay_width_t acq_pc_window[batch_parity];

    delay_width_t provision[batch_parity][spare_timing];

    spare_t<0x0bff,0x1000> spare_pre_prt__;

    prt_fifo_t prt_fifo_odd;

    spare_t<0x011ff,0x1400> spare_pre_prt_even__;

    prt_fifo_t prt_fifo_even;
#if GRIFO_FW_TIMER_IF_DBG_AREA>0
    uint32_t dbg_marker_prog_end;
    uint32_t dbg_info;
    uint32_t dbg_sw_data[148];
#endif
};

struct grifo_fw_blob_header_t   //SW reserved, exacly 0x20 bytes
{
    enum {
        k_header_marker=0x12345678U

        //k_spare_size=8-7
    };

    uint32_t header_marker; //must be = k_header_marker
    uint32_t header_marker_bis;    //=~header_marker;

    uint32_t size; //sotal size including blob header and footer
    uint32_t work_size;
    uint32_t sub_marker;    //function depended, = header_marker if undefined
    uint32_t counter;
    uint32_t timetag;
    uint32_t sw_reserved;
};

struct grifo_timer_blob_t
{
	grifo_fw_blob_header_t hdr;
	timer_raw_if_t payload;
};

#include "AesaStream.h"

struct ext_sarr_dbg_data_t
{
    int wfId;

	float azimuth;
	float elevation;
	float range_m;
	float range_us;
	float lsn_dly_us;
	float lsn_len_us;
	float deramp_dly_us;
	unsigned int nrbin;
	float bchirp;
	float patch_len_us;
	float prt;

	unsigned int prf_ticks;
	unsigned int deramp_ticks;

	unsigned int spare[8];

	//SarLegacyMode::tracking_t trk;


	unsigned int spare_trailer[14-8];
};

static void timer_examine(Flow* f, void* af, void* aesax, void* ext, void* hdr_fix)
{
	grifo_timer_blob_t b;
	static FILE* tfd;
	static FILE* pfd;

	static unsigned int out_line=1;


	memcpy(&b, f->buffer, sizeof b);
	if ((b.hdr.header_marker==grifo_fw_blob_header_t::k_header_marker) && (b.hdr.sub_marker==0x54494D45))
	{
		if (tfd==0)
		{
			cmdio_psuccess("Timer blob found!");
			tfd=fopen(mk_fname("timer", "txt"), "w"); //"timer_data.txt", "w");
			pfd=fopen(mk_fname("ant_pos", "txt"), "w");
		}
	}
	else
	{
		cmdio_pwarning("Timer blob error: 0x%X, 0x%X", b.hdr.header_marker, b.hdr.sub_marker);
		return;
	}


	int prog_min_time=0;

	if (b.payload.prt_num==1)
		return;

	{
		unsigned int prt_n=b.payload.prt_num;
		if (b.payload.diff_prt_num>1)
			--prt_n;
		if (prt_n>127)
			prt_n=127;
		bool first_value=true;
		for(unsigned int i=1; i<prt_n+1; ++i)
		{
			unsigned prt=b.payload.prt_fifo_even.fifo[i];
			if (b.payload.aesa_delay[0].fifo[1]!=1048575)
			{
				int prog_time=b.payload.prt_fifo_even.fifo[0]; //2800;
				int dly=b.payload.aesa_delay[0].fifo[1];
				int pt=(prt-prog_time)-dly;
				if (first_value)
				{
					prog_min_time=pt;
					first_value=false;
				}
				else
				{
					if (pt<prog_min_time)
						prog_min_time=pt;
				}
				if ((prt-prog_time)<b.payload.aesa_delay[0].fifo[1])
				{
					//cmdio_pwarning("AESA prog too late: %u < %u + %u", prt, b.payload.aesa_delay[0].fifo[1], prog_time);
				}
				else
				{
					//unsigned long pt=(prt-prog_time)-b.payload.aesa_delay[0].fifo[1];
					//cmdio_pwarning("AESA prog time: %u %u < %u + %u", pt, prt, b.payload.aesa_delay[0].fifo[1], prog_time);
				}
			}
		}
	}
/*
	if (hdr_fix)
	{
		char* ext_sar=strstr((char*)hdr_fix, "EXT SARR");
		if (ext_sar)
		{
			ext_sarr_dbg_data_t* sar=(ext_sarr_dbg_data_t*)(&ext_sar[16]);

			sar->prf_ticks=b.payload
		}
	}
*/

	fprintf(tfd, "%32s;%11s;%8u;%5s;", cmdio_get_proc_file(0), time_of_mission, n_batch, lbl_mode ? lbl_mode : "?");

	if (aesax)
	{
		static raw_antenna_reply_buffer_t saved;

		const raw_antenna_reply_buffer_t* x=(raw_antenna_reply_buffer_t*)&(((uint32_t*)aesax)[36]);
		unsigned int delta=x->msg_count_errors-saved.msg_count_errors;
		unsigned int edelta=x->rxerr-saved.rxerr;
		unsigned int tdelta=x->timeouterr-saved.timeouterr;
		const char* errstr="OK";
		if (x->pri_err>=0)
		{
			cmdio_pwarning("(%u) AESA PRI Error: %d", out_line, x->pri_err);
			errstr="ERR";
		}

		fprintf(tfd, "%3s;%6u;%6u;%6u;%4d;%5d", errstr, delta, edelta, tdelta, x->pri_err, prog_min_time);

		saved=*x;
	}
	else
		fprintf(tfd, "   ;%6u;%6u;%6u;%4d;%5u", 0, 0, 0, 0, 0);

	fprintf(tfd, "%4u;%2u;%4u;",
			b.payload.prt_num, b.payload.diff_prt_num,
			b.payload.shift.RX_SYNC);

	unsigned int expected_aesa=0;

	fprintf(tfd, "__AESADLY;");

	for(int i=0; i<8; ++i)
	{
		fprintf(tfd, "%7u;",
				b.payload.aesa_delay[0].fifo[i]);
		if (b.payload.aesa_delay[0].fifo[i]!=1048575)
		{
			++expected_aesa;
		}
	}
	if (expected_aesa>1)
		expected_aesa=b.payload.prt_num;

	fprintf(tfd, "__ANTRXN;");

	for(int i=0; i<8; ++i)
	{
		fprintf(tfd, "%u2;%4u;",
				b.payload.ant_rxn[0].delay.fifo[i], b.payload.ant_rxn[0].width.fifo[i]);
	}

	fprintf(tfd, "__CFSYNC;");

	for(int i=0; i<8; ++i)
	{
		fprintf(tfd, "%7u;",
				b.payload.cf_synch[0].delay.fifo[i]);
	}

	fprintf(tfd, "__PRT;");

	fprintf(tfd, "%7u;", b.payload.prt_fifo_even.fifo[0]);

	unsigned int prt_min=1000000000;
	unsigned int prt_max=0;
	unsigned int prt_n=b.payload.prt_num;
	if (b.payload.diff_prt_num>1)
		--prt_n;
	if (prt_n>127)
		prt_n=127;
	for(unsigned int i=1; i<prt_n+1; ++i)
	{
		unsigned int prt=b.payload.prt_fifo_even.fifo[i];
		fprintf(tfd, "%7u;", prt);
		if (prt>prt_max)
			prt_max=prt;
		if (prt<prt_min)
			prt_min=prt;
		if (b.payload.aesa_delay[0].fifo[1]!=1048575)
		{
			unsigned int prog_time=b.payload.prt_fifo_even.fifo[0]; //2800;
			if ((prt-prog_time)<b.payload.aesa_delay[0].fifo[1])
			{
				cmdio_pwarning("(%u) AESA prog too late: %u < %u + %u", out_line, prt, b.payload.aesa_delay[0].fifo[1], prog_time);
			}
			else
			{
				//unsigned long pt=(prt-prog_time)-b.payload.aesa_delay[0].fifo[1];
				//cmdio_pwarning("AESA prog time: %u %u < %u + %u", pt, prt, b.payload.aesa_delay[0].fifo[1], prog_time);
			}
		}
	}
	if ((prt_n>1) && (prt_min!=prt_max))
	{
		//float v=(prt_max*100)/float(prt_min);
		//if (v>115)
		//	cmdio_pwarning("(%u) [%u] Jitter to big: %f %u..%u", out_line, prt_n, v, prt_min, prt_max);
	}


	//fprintf(tfd, "AESATX;");


	unsigned int aesa_frq_num=0;
	static unsigned int aesa_frq_list[2048];
	static struct aesa_pos_t
	{
	    float    tx_u;                          // u
	    float    tx_v;                          // v
	    float    rx_u;                          // u
	    float    rx_v;                          // v
	    float    guard_rx_u;                    // u
	    float    guard_rx_v;                    // v
	} aesa_pos[2048];

	memset(aesa_frq_list, 0, sizeof aesa_frq_list);

	if (af)
	{
		uint32_t* p=(uint32_t*)af;
		unsigned int n=0;

		uint8_t aesa_cmd=0;
		for(int i=11*4;i<(1024*2); ++i)
		{
			if (p[i]==0x5A5A5A5A)
			{
				if ((p[i+1]==0x1F028BU) || (p[i+1]==0x1F028AU))
				{
					AesaStream s(&p[i+2], 4096);
					aesa_cmd=s.pop();
					s.pop();
					s.pop();
					aesa_frq_list[aesa_frq_num]=s.pop16();
					s.pops16();
					aesa_pos[aesa_frq_num].tx_u=s.popuv(); //*BEAM_DIRECTION_FACTOR_LSB);
					aesa_pos[aesa_frq_num].tx_v=s.popuv(); //vp*BEAM_DIRECTION_FACTOR_LSB;
					aesa_pos[aesa_frq_num].rx_u=s.popuv();
					aesa_pos[aesa_frq_num].rx_v=s.popuv();
					aesa_pos[aesa_frq_num].guard_rx_u=s.popuv();
					aesa_pos[aesa_frq_num].guard_rx_v=s.popuv();

#if 0
					uint32_t v=p[i+3];
					unsigned int frq=((v & 0x00FF0000)>>16) |((v & 0x0000FF00));
					//fprintf(tfd, "%u;", frq);
					aesa_frq_list[aesa_frq_num]=frq;

					uint8_t* bp=(uint8_t*)&p[i+3];
					//int16_t* bw=(int16_t*)&bp[3];
					//bw=&bw[0];
					//int16_t up=bw[0];
					//int16_t vp=bw[1];
					int bindex=2;
					aesa_pos[aesa_frq_num].tx_u=aesabeam(&bp[bindex]); //*BEAM_DIRECTION_FACTOR_LSB);
					aesa_pos[aesa_frq_num].tx_v=aesabeam(&bp[bindex+2]); //vp*BEAM_DIRECTION_FACTOR_LSB;

					aesa_pos[aesa_frq_num].rx_u=aesabeam(&bp[bindex+4]);
					aesa_pos[aesa_frq_num].rx_v=aesabeam(&bp[bindex+6]);
					aesa_pos[aesa_frq_num].guard_rx_u=aesabeam(&bp[bindex+8]);
					aesa_pos[aesa_frq_num].guard_rx_v=aesabeam(&bp[bindex+10]);

#if 0
					aesa_pos[aesa_frq_num].rx_u=bw[2]*BEAM_DIRECTION_FACTOR_LSB;
					aesa_pos[aesa_frq_num].rx_v=bw[3]*BEAM_DIRECTION_FACTOR_LSB;
					aesa_pos[aesa_frq_num].guard_rx_u=bw[4]*BEAM_DIRECTION_FACTOR_LSB;
					aesa_pos[aesa_frq_num].guard_rx_v=bw[5]*BEAM_DIRECTION_FACTOR_LSB;
#endif
#endif
					++aesa_frq_num;
				}
				else if (p[i+1]==590469U)
				{
					//uint32_t v=p[i+3];
					//unsigned int frq=((v & 0x00FF0000)>>16) |((v & 0x0000FF00));
					//fprintf(tfd, "%u;", frq);
				}
				else
				{
					//fprintf(tfd, "??0x%X;", p[i+1]);
				}
				++n;
			}
			if (p[i]==0xA5A5A5A5)
				break;
		}
		unsigned int n2=n/2;
		if (n2!=expected_aesa)
		{
			cmdio_pwarning("(%u) Wrong AESA progr: %u -> %u (%u), cmd=0x%X", out_line, expected_aesa, n2, n, aesa_cmd);
		}

		//cmdio_pwarning("CMD 0x%X", ((unsigned int)aesa_cmd) & 0x0FF);

		fprintf(tfd, "__POS;%12.6f;%12.6f;", aesa_pos[0].tx_u, aesa_pos[0].tx_v);
		//fprintf(pfd, "__POS;%12.6f;%12.6f;%12.6f;%12.6f;%12.6f;%12.6f\n", aesa_pos[0].tx_u, aesa_pos[0].tx_v, aesa_pos[0].rx_u, aesa_pos[0].rx_v, aesa_pos[0].guard_rx_u, aesa_pos[0].guard_rx_v);
		fprintf(pfd, "%5u;%6u;%6u;_NAV;%12.6f;%12.6f;_ANT;%12.6f;%12.6f;_UV;%12.6f;%12.6f;__PP;%6u;%12.6f;%12.6f;__AESA;%12.6f;%12.6f;%12.6f;%12.6f;%12.6f;%12.6f;\n",
				aesa_frq_list[0],
				hdr_saved_info.tt,
				hdr_saved_info.ant_tt,
				saved_ant.navaz_rad, saved_ant.navel_rad,
				saved_ant.bodyaz_rad, saved_ant.bodyel_rad,
				saved_ant.urx_rad, saved_ant.vrx_rad, //saved_ant.uguardrx_rad, saved_ant.vguardrx_rad,

				hdr_saved_info.pe_tt, hdr_saved_info.pe.az, hdr_saved_info.pe.el,

				aesa_pos[0].tx_u, aesa_pos[0].tx_v, aesa_pos[0].rx_u, aesa_pos[0].rx_v, aesa_pos[0].guard_rx_u, aesa_pos[0].guard_rx_v);

		//fflush(pfd);
	}

	unsigned int synt_frq_num=0;
	static unsigned int synt_frq_list[2048];
	memset(synt_frq_list, 0, sizeof synt_frq_list);

	if (ext)
	{
		//fprintf(tfd, "__FRQ;");
		uint32_t* p=(uint32_t*)ext;
		for(int i=8; i<1024; ++i)
		{
			if (p[i]==0x5A5A5A5A)
			{
				unsigned int fid=p[i+1] & 0x0FF;
				if ((fid==0xA1) || (fid==0xA2))
				{
					unsigned int n=p[i+3];
					if (n<128)
					{
						uint32_t* fp=&p[i+4];
						for(unsigned int findex=0; findex<n; ++findex)
						{
							uint32_t frq=fp[findex];
							frq=(frq/10)*4; //100000)*4
							frq=frq/100000;
							frq=frq-3920;
							//fprintf(tfd, "%u;", frq);
							synt_frq_list[synt_frq_num]=frq;
							++synt_frq_num;
						}
					}
				}
				else
				{
					//fprintf(tfd, "FID=0x%X;", p[i+1]);
				}
			}
			if (p[i]==0xA5A5A5A5)
				break;
		}
	}

	unsigned int fn_max=synt_frq_num>aesa_frq_num ? synt_frq_num : aesa_frq_num;
	if (fn_max>1)
	{
		if (aesa_frq_num!=(synt_frq_num+1))
		{
			cmdio_pwarning("(%u) AESA/SYNT num frq error: %u != %u +1", out_line, aesa_frq_num, synt_frq_num);
		}
		else
			--fn_max;
	}

	fprintf(tfd, "__FRQ;");
	unsigned int first_frq_err_aesa=0;
	unsigned int first_frq_err_synt=0;
	unsigned int first_frq_err_num=0;
	unsigned int tot_frq_err_num=0;

	for(unsigned int i=0; i<fn_max; ++i)
	{
		bool ok=synt_frq_list[i]==aesa_frq_list[i];
		fprintf(tfd, "%c;%4u;%4u;", ok ? '=' : '!', aesa_frq_list[i], synt_frq_list[i]);
		if (!ok)
		{
			if (tot_frq_err_num==0)
			{
				first_frq_err_aesa=aesa_frq_list[i];
				first_frq_err_synt=synt_frq_list[i];
				first_frq_err_num=i;
			}
			++tot_frq_err_num;
			//cmdio_pwarning("(%u) AESA/SYNT frequency error: %u != %u", out_line, aesa_frq_list[i], synt_frq_list[i]);
		}
	}

	if (tot_frq_err_num)
	{
		cmdio_pwarning("(%u) AESA/SYNT FRQ Err %u: [%u]->%u != %u", out_line, tot_frq_err_num, first_frq_err_num, first_frq_err_aesa, first_frq_err_synt);

	}
	fprintf(tfd, "0\n");

	//fflush(tfd);
	++out_line;
}




//Asynch Read
static void ahead_read_file(void)
{

}
static void create_thread()
{
	 //_beginthread(ahead_read_file, 0, 0, 0);
}
