classdef radarIOPacketFileFread < radarIO
    %UNTITLED2 Summary of this class goes here
    %   Detailed explanation goes here

    properties
        %% Define constants to support acquiring data
        fd;
        Pathname;
        Filename;
    end

    methods
         %% class destructor ...
        function delete(obj)
            try
                obj.closeFile();
            catch
            end
        end

        %% Open File using Matlab file commands
        function obj = radarIOPacketFileFread(radarObj, varargin)
            % * WxMode_b -- boolean to override of the file name convention
            %   that identifies weather mode by a string with "Wx" in the
            %   filename.
            % * Filename - name of file to load, this function will prompt
            %   the operator to select a file(s) to process.
            obj=obj@radarIO(radarObj);
            obj.cipMetaData = radarObj.cipMetaData;
            obj.sourceRadar = radarObj;
            obj.configVals = radarObj.configVals;
            
            if isempty(varargin) || isempty(varargin{1}) 
                [aFilename, aPathname]= uigetfile('*.dat*',...
                    'Select Data-Recording file(s) to process',...
                    'multiselect','on');
            else
                aFilename = varargin{1};
            end
            if nargin > 2 && ~isempty(varargin{2})
                aPathname = varargin{2};
            end
            if nargin > 3
                printSwitch = varargin{3};
            else
                printSwitch = true;
            end
            if (isfloat(aFilename))
                error('No files selected');
            end
            if (ischar(aFilename))
                aFilename={aFilename}; % make it always a cell array...
            end
            %   handle Weather mode options...      
	    % WX workaround to replay.      
            if (contains(aFilename{1},'Wx')||contains(aFilename{1},'wx')||contains(aFilename{1},'.dat_w1')||contains(aFilename{1},'.dat_w2'))
                obj.cipMetaData.wxMode=true;
            end

            priMarker = 0;
            if (contains(aFilename{1},'.dat_u1')||contains(aFilename{1},'.dat_w1'))
                obj.cipMetaData.fileVer=1;
            elseif (contains(aFilename(1), '.dat_u2')||contains(aFilename(1),'dat_w2'))
                obj.cipMetaData.fileVer=2;
                priMarker = -30070;                     %0x8A8A
            end
            
            if (obj.cipMetaData.wxMode)
                % Wx mode differences... 120 PRIs / CIP
                mode=radChan.WxMode(true);
                obj.cipMetaData.numPris = obj.cipMetaData.numPrisWx;
                obj.cipMetaData.bandWidth=obj.cipMetaData.bandWidthWx;
                obj.cipMetaData.sampleRate_sps=obj.cipMetaData.sampleRateWx_sps;
%                 if (obj.cipMetaData.fileVer >= 1)
%                     obj.cipMetaData.numChannels = 2;
%                 end
                obj.configVals.normalizerType = obj.configVals.normalizerType_wx;
                obj.cipMetaData.pulseLength_s = obj.cipMetaData.pulseLengthWx_s;
                obj.sourceRadar.calcWaveformParams(obj);
            else
                mode=radChan.WxMode(false);
                obj.cipMetaData.numPris = obj.cipMetaData.numPrisUas;
                obj.cipMetaData.bandWidth=obj.cipMetaData.bandWidthUas;
                obj.cipMetaData.sampleRate_sps=obj.cipMetaData.sampleRateUas_sps;
                obj.configVals.normalizerType = obj.configVals.normalizerType_uas;
                obj.cipMetaData.pulseLength_s = obj.cipMetaData.pulseLengthUas_s;
                obj.cipMetaData = mpdrRadar.calcWaveformParams(obj);
            end

          
            obj.readSize = (obj.cipMetaData.numPris * obj.cipMetaData.numChannels * obj.iQ * obj.cipMetaData.priSize);

           
            if printSwitch
                fprintf("Processing data record file: \n(\'%s\',\'%s\')\n", aFilename{1},aPathname);
            end
            % read in the file...
            if ~exist('aPathname',"var")
                obj.fd = fopen(aFilename{1},'r'); % look for a file with the correct file name on the matlab path
            else
                obj.fd = fopen(fullfile(aPathname, aFilename{1}),'r');
            end
            if (obj.fd < 0)
                error('No valid file selected');
            end

            % open the first file...
            

            % calculate the read size: allow the some PRIs to be discarded and
            % add one to pad it out so we don't loose data at the end of the last PRI
            %TODO: fix so that getCIP can read more than one CIP...
            
            %initialReadSize = ((obj.discardPris * obj.cipMetaData.priSize)) * obj.cipMetaData.numChannels * obj.iQ;
            initialReadSize = 0;
            [~,~]=fread(obj.fd,initialReadSize,'int16');
            % do nothing with this initial data... it is just discarded.

            % loop until we detect the start of CIP marker
            obj.rangeCellSize=(obj.cipMetaData.numChannels*obj.iQ);
            startMark=ones(1,obj.rangeCellSize,'int16');
            ii=0;
            % search for the start mark, but only upto the length of two
            % PRIs
            sumPriMarkers = abs(obj.rangeCellSize * priMarker);
            while(sum(abs(startMark),'all')~=sumPriMarkers && ~feof(obj.fd) && ii <= obj.cipMetaData.priSize*2)
                [startMark,~]=fread(obj.fd,obj.rangeCellSize,'int16');
                ii=ii+1;
            end
            %fseek(obj.fd,-2*obj.rangeCellSize,'cof'); % include the start mark in the next read.... (bytes instead of words)
            % save this location for later reference
            obj.fileStartLoc = ftell(obj.fd);

            % figure out how large the file is in pri(s) and cips(s)...
            fseek(obj.fd,0,'eof');
            filesize=ftell(obj.fd);
            obj.cipMetaData.fileSizePris=floor((filesize-obj.fileStartLoc)/...
                (obj.cipMetaData.priSize*obj.cipMetaData.numChannels*obj.iQ*2+64)); % half a header per pri
            obj.cipMetaData.fileSizeCips=floor((filesize-obj.fileStartLoc)/(obj.readSize*2+128*80));
            if printSwitch
                videoCips=floor(obj.cipMetaData.fileSizePris/251); % todo calculate the magic number...
                %fprintf("file contains %d (%d) cips\n", obj.cipMetaData.fileSizeCips,videoCips);
                fprintf("file contains %d %1d-pulse cips (%d video cips), %1.1f sec\n", ...
                    obj.cipMetaData.fileSizeCips,obj.cipMetaData.numPris,videoCips, ...
                    obj.cipMetaData.priTime_s * obj.cipMetaData.fileSizePris);
            end

            fseek(obj.fd,obj.fileStartLoc,'bof');

            % find out the Rx/Tx start error by loading an PRI and Pulse
            % compressing it, finding the peak and determining the
            % difference from expected peak.
            priReadSize=obj.cipMetaData.priSize * obj.cipMetaData.numChannels * obj.iQ;
            [header,~]=fread(obj.fd,128,'uint8'); % we want to read 80*64 ints + cip size. for the full block.
            [iqdata,~]=fread(obj.fd,priReadSize,'int16');
            eof = feof(obj.fd);
            if ~eof
                %                     fprintf("ReadSize req: %d actual: %d\n",obj.readSize,lCount);
                cipData = reshape(iqdata,obj.iQ,[]);
                cipData = complex(cipData(1,:),cipData(2,:));
                cipData = reshape(cipData,...
                    obj.cipMetaData.numChannels,[]);
                % clear the PRI Start Mark
                cipData(:,1,:) = 0.0;
                LrPc = PulseCompressor(waveformTypes.LfmUpChirp,...
                    obj.cipMetaData.bandWidth(uint32(radChan.sumLr())),...
                    obj.cipMetaData.pulseLength_s(uint32(radChan.sumLr())),...
                    obj.cipMetaData.sampleRate_sps(uint32(radChan.sumLr())),...
                    obj.cipMetaData.priSize);

                % need to decimate when in weather mode....
                if obj.cipMetaData.wxMode && obj.cipMetaData.fileVer < 2
                    sumLr=uint32(radChan.sumLr());
                    cipData(sumLr,1:ceil(obj.cipMetaData.priSize/4),:)=...
                        cipData(sumLr,1:4:end,:);
                    obj.cipMetaData.sampleRate_sps = obj.cipMetaData.sampleRate_sps/4;
                end

                  chanToCheck =  abs(cipData(uint32(radChan.sumLr()),:));
                cipData(uint32(radChan.sumLr()),:) = LrPc.compress(cipData(uint32(radChan.sumLr()),:));

              
                [pks, locs ] = findpeaks(chanToCheck);

                [~,peakCell]=max(abs(cipData(uint32(radChan.sumLr()),...
                    obj.cipMetaData.cells.rangeZero(uint32(radChan.sumLr())):(obj.cipMetaData.cells.rangeZero(uint32(radChan.sumLr()))+...
                    obj.cipMetaData.maxRxTxBias))));

                obj.cipMetaData.cells.rxTxBiasCorr=15;
                %obj.cipMetaData.cells.rxTxBiasCorr=peakCell-1;

                fprintf("PACKET FILE I/O Rx/Tx Bias correction: %d\n",obj.cipMetaData.cells.rxTxBiasCorr);
                % todo: remove this...
%                 figure;
%                 plot(abs(cipData(uint32(radChan.sumLr()),:)));
%                 hold on;
%                 plot(obj.cipMetaData.cells.rangeZero(uint32(radChan.sumLr()))+peakCell-1,...
%                     abs(cipData(uint32(radChan.sumLr()),obj.cipMetaData.cells.rangeZero(uint32(radChan.sumLr()))+peakCell-1)),...
%                     "ro");
%     
%                 figure(22);
%                 plot(chanToCheck);
%                 hold on;
%                 plot(chanToCheck(locs),"or");
%                 hold off;

                % now that we have determined the run to run rx/tx jitter
                % recalculate the start point of the waveforms...
              obj.cipMetaData=mpdrRadar.calcWaveformParams(obj);

                % go back to include this Pri in the next collection set...
            end
            %fseek(obj.fd,-2*priReadSize-64,'cof'); % include the start mark in the next read.... (bytes instead of words)
            fseek(obj.fd,0,'bof');
            obj.Pathname = aPathname;
            obj.Filename = aFilename{1};
            obj.cipMetaData.Filename = aFilename{1};
            obj.playback=true;
        end

        function [cipData, cipMetaData, eof] = getCIP(obj,cipData,cipNum,cipTime_s)
            % This function returns a complex double data cube:
            %
            %     cipData(chan,range,pri). The subscripts are:
            %
            % * Chan -- radar channel being processed (use the radChan
            %           enum class access specific channels)
            % * Range -- range cells aligned to the first range cell of
            %            the first transmit pulse.  This is 1/2 Tau
            %            samples prior to Range Zero (R0) for the first
            %            pulse. The application needs to keep track of
            %            the number of pulses transmitted, and their
            %            respective R0, and their eclipsed regions.
            %            This range increment represents the fast time
            %            samples.
            % * Pri -- Pulse Repitition Peroiod, indexes into the
            %            different range data for the successive pulses
            %            of a pulse train.  This is the slow-time for
            %            the data cube.
            % The function takes the DataCube in as an argument to
            % elimintate the need for copies.
            %
            % optional argument cipNum -- can be used to step to a
            % particular cip in the file.  (only valid when processing
            % record file)
            %
            % optional argument cipTime -- can be used to step to the
            % nearest PRI within the file... this is to allow making videos
            % that match the the frame rate of a video camera (30 frames/
            % second) particular cip in the file. (only valid when
            % processing record file)
            %
            % The function also returns meta data describing the Cip
            % that is returned it contains the following infomation:
            %
            % * cipMetaData.sampleRate_sps -- sample rate -- in samples
            %   per second (Hz)
            % * cipMetaData.numChannels -- Number of channels (integer)
            % * cipMetaData.wxMode -- Boolian, true if the system is in
            %   Weather mode
            % * cipMetaData.PriLength_s -- Double, Length of the PRI in
            %   seconds (PriTime_s * sampleRate_sps must be integer)
            % * cipMetaData.pulseLength_s -- array of pulse lengths for
            %   each sub-pulse (MPDR: LR=1, SR=2)
            % * cipMetaData.numPris -- Number of PRIs / CIP
            % * cipMetaData.bandWidth -- waveform bandwidth in Hz
            % * cipMetaData.Filename -- filename being processed
            % * cipMetaData.cipTime_s -- time cip executed (Todo decide
            %   epoch (boot time, record file start?)
            % * cipMetaData.cipCenterAzimuth_d -- azimuth of beam
            %   center (yaw)
            % * cipMetaData.cipCenterElevation_d -- elevation of the
            %   beam center (pitch)
            % * cipMetaData.cipCenterRoll -- roll measurement due to
            %   antenna being out of level
            % * cipMetaData.cipCenterFrequency_hz
            % The funtion also returns EOF flag indicateing that the end of
            % file has been reached for data recorded CIPs.

            
            cipMetaData = obj.cipMetaData;
            
            % TODO: fill the following in...
            cipMetaData.cipCenterAzimuth_d = 0;
            cipMetaData.cipCenterElevation_d = 0;
            cipMetaData.cipCenterRoll = 0;

            if (obj.playback)
                % MPS.  'seeking' to CIP is not very helpful until I get
                % the incremnt to reflect the packet headers. 
%                 if exist('cipNum',"var") && ~isempty(cipNum)
%                     % seek the correct location in file prior to reading
%                     % the data.
%                     if cipNum > obj.cipMetaData.fileSizeCips
%                         warning('Request beyond end of Record file');
%                         cipNum = obj.cipMetaData.fileSizeCips-1;
%                         fseek(obj.fd,0,'eof');
%                     end
%                     % set the requested in both the current meta data and
%                     % in the radar Main object.
%                     cipMetaData.cipNum=cipNum;
%                     cipMetaData.cipTime_s=cipNum*cipMetaData.cip_s;
%                     %                         cipMetaData = obj.cipMetaData;
%                     obj.seekLoc=cipNum*(obj.readSize*2 + 128*80)+obj.fileStartLoc;
%                     status=fseek(obj.fd,obj.seekLoc,'bof');
%                     if status ~= 0
%                         error('cip selection failed %s',ferror);
%                     end
%                 elseif exist('cipTime_s',"var") && ~isempty(cipTime_s)
%                     % calculate the nearest PRI start
%                     reqPriNum= floor(cipTime_s/obj.cipMetaData.priTime_s);
%                     if reqPriNum >= obj.cipMetaData.fileSizePris
%                         warning('Request beyond end of Record file');
%                         fseek(obj.fd,0,'eof');
%                     else
%                         % set the requested in both the current meta data and
%                         % in the radar Main object.
%                         cipMetaData.cipTime_s =  cipTime_s;
%                         cipMetaData.cipNum = floor(reqPriNum/cipMetaData.priPerVideoFrame);
%                         %                         cipMetaData = obj.cipMetaData;
%                         PriSize=(obj.cipMetaData.numChannels * obj.iQ * obj.cipMetaData.priSize);
%                         obj.seekLoc=reqPriNum*PriSize*2+obj.fileStartLoc;
%                         status=fseek(obj.fd,obj.seekLoc,'bof');
%                         if status ~=0
%                             error('cip selection failed %s',ferror);
%                         end
% 
%                     end
%                 else
                    cipMetaData.cipNum=obj.cipMetaData.cipNum+1;
                    cipMetaData.cipTime_s=cipMetaData.cipNum*cipMetaData.cip_s;
               % end
                % accumulate iqdata
        
                iqdata = zeros(0,1,'single');
                packetcount = 0; 
                % two PRIs per packet. is fread 'size' in bytes or
                % requested type? MPS
                payloadSize = cipMetaData.priSize*8*2*2;
                %prisizeallchans = cipMetaData.priSize*8;

                % packet size: 27840
                % header size: 32
                % imu data size: 96
                % data size: 27712
             try   
                while( packetcount < cipMetaData.numPris/2)

                    header =fread(obj.fd,32,'*ubit8');
                    startpos = ftell(obj.fd);
                    flags = fread(obj.fd,2,'*uint32'); % NOTE:  These are BOOL in C++ which are 8 bits; compiler turned them into 4 bytes in struct. 
                    gpstime = fread(obj.fd,1,'*uint64');
                    start = fread(obj.fd,1,'*uint64');
                    systime =fread(obj.fd,1,'*uint64');
                    ypr =fread(obj.fd,3,'*single');
                    lle=fread(obj.fd,3,'*single');
                    endofimu = ftell(obj.fd);
                    [remainder,~]=fread(obj.fd,96-(endofimu-startpos),'*ubit8');
                    endofall = ftell(obj.fd);
                    locdiff = endofall - startpos;
%                     cipMetaData.cipCenterAzimuth_d = ypr(1); % yaw;  
%                     cipMetaData.cipCenterElevation_d = ypr(2);
%                     cipMetaData.senslat = lle(1);
%                     cipMetaData.senslon = lle(2);
                    if( packetcount == cipMetaData.numPris/4)

%                       if(round(ypr(2)) == 10.0)
%                         fprintf("%f, %f, %d\n",ypr(1),round(ypr(2)),systime)
%                       end

                      cipMetaData.cipCenterAzimuth_d = ypr(1); % yaw;  
                      %cipMetaData.cipCenterElevation_d = lle(3);
                      cipMetaData.cipCenterElevation_d = ypr(2);
                      cipMetaData.senslat = lle(1);
                      cipMetaData.senslon = lle(2);
                      cipMetaData.time = systime;
                    end
                    [iqdataChunk,~]=fread(obj.fd,payloadSize,'int16=>single');
                    %iqdata(anend*(pricount)-1)=iqdataChunk;
                    iqdata = cat(1,iqdata,iqdataChunk);
       
                    packetcount = packetcount+1;
                end
             catch
             end
                eof = feof(obj.fd);
                if ~eof
                    cipData = reshape(iqdata,obj.iQ,[]);
                    cipData = complex(cipData(1,:),cipData(2,:));
                    cipData = reshape(cipData,...
                        obj.cipMetaData.numChannels,...
                        obj.cipMetaData.priSize,[]);

                    if (obj.configVals.zeroTransmit)
                        % zero out the transmit interval...
                        cipData(:,1:cipMetaData.cells.procStart(1)+10,:) = 0.0;
                    else % only clear the pri start mark
                        cipData(:,1,:) = 0.0;
                    end

                    if obj.cipMetaData.wxMode && obj.cipMetaData.fileVer < 2
                        sumLr=uint32(radChan.sumLr());
                        cipData(sumLr,1:ceil(obj.cipMetaData.priSize/4),:)=...
                            cipData(sumLr,1:4:end,:);
                    end
                end
             
            else
                % call MEX read function...
            end
            obj.cipMetaData=cipMetaData;
        end

         %% get the meta data for the last CIP read (or default from config file)
        function cipMetaData=getCipMetaData(obj)
            cipMetaData = obj.cipMetaData;
        end

    end
end
