function [gpsEph,iono] = ReadRinexNav(fileName)
% [gpsEph,iono] = ReadRinexNav(fileName)
%
% Read GPS ephemeris and iono data from an ASCII formatted RINEX 2.10 Nav file.
% Input:
%    fileName - string containing name of RINEX formatted navigation data file
% Output:
% gpsEph: vector of ephemeris data, each element is an ephemeris structure 
%        structure order and orbit variable names follow RINEX 2.1 Table A4
%        clock variable names af0, af1, af2 follow IS GPS 200
% gpsEph(i).PRN     % SV PRN number
% gpsEph(i).Toc     % Time of clock (seconds)
% gpsEph(i).af0     % SV clock bias (seconds)
% gpsEph(i).af1     % SV clock drift (sec/sec)
% gpsEph(i).af2     % SV clock drift rate (sec/sec2)
% gpsEph(i).IODE    % Issue of data, ephemeris 
% gpsEph(i).Crs     % Sine harmonic correction to orbit radius (meters)
% gpsEph(i).Delta_n % Mean motion difference from computed value (radians/sec)
% gpsEph(i).M0      % Mean anomaly at reference time (radians)
% gpsEph(i).Cuc     % Cosine harmonic correction to argument of lat (radians)
% gpsEph(i).e       % Eccentricity (dimensionless)
% gpsEph(i).Cus     % Sine harmonic correction to argument of latitude (radians)
% gpsEph(i).Asqrt   % Square root of semi-major axis (meters^1/2)
% gpsEph(i).Toe     % Reference time of ephemeris (seconds)
% gpsEph(i).Cic     % Cosine harmonic correction to angle of inclination (radians)
% gpsEph(i).OMEGA   % Longitude of ascending node at weekly epoch (radians)
% gpsEph(i).Cis     % Sine harmonic correction to angle of inclination (radians)
% gpsEph(i).i0      % Inclination angle at reference time (radians)
% gpsEph(i).Crc	    % Cosine harmonic correction to the orbit radius (meters)
% gpsEph(i).omega	% Argument of perigee (radians)
% gpsEph(i).OMEGA_DOT% Rate of right ascension (radians/sec)
% gpsEph(i).IDOT	% Rate of inclination angle (radians/sec)
% gpsEph(i).codeL2  % codes on L2 channel 
% gpsEph(i).GPS_Week % GPS week (to go with Toe), (NOT Mod 1024)
% gpsEph(i).L2Pdata % L2 P data flag
% gpsEph(i).accuracy % SV user range accuracy (meters)
% gpsEph(i).health  % Satellite health
% gpsEph(i).TGD     % Group delay (seconds)
% gpsEph(i).IODC    % Issue of Data, Clock 
% gpsEph(i).ttx	    % Transmission time of message (seconds)
% gpsEph(i).Fit_interval %fit interval (hours), zero if not known
%
% iono: ionospheric parameter structure
%   iono.alpha = [alpha0, alpha1, alpha2, alpha3]
%	iono.beta =  [ beta0,  beta1,  beta2,  beta3]
% if iono data is not present in the Rinex file, iono is returned empty.

fidEph = fopen(fileName);
[numEph,numHdrLines] = countEph(fidEph);

%Now read from the begining again, looking for iono parameters
frewind(fidEph);
iono = readIono(fidEph,numHdrLines);

%initialize ephemeris structure array:
gpsEph = InitializeGpsEph;
gpsEph = repmat(gpsEph,1,numEph);

%now read each ephemeris into gpsEph(j)
%RINEX defines the format in terms of numbers of characters, so that's how we
%read it, e.g. "gpsEph(j).PRN   = str2num(line(1:2));" and so on
for j = 1:numEph
   line         = fgetl(fidEph);
   gpsEph(j).PRN   = str2num(line(1:2));
   %NOTE: we use str2num, not str2double, since str2num handles 'D' for exponent

   %% get Toc (Rinex gives this as UTC time yy,mm,dd,hh,mm,ss)
   year   = str2num(line(3:6));
   %convert year to a 4-digit year, this code is good to the year 2080.
   %From 2080 RINEX 2.1 is ambiguous and shouldnt be used, because is has a
   %2-digit year, and 100 years will have passed since the GPS Epoch.
   if year < 80,
       year = 2000+year;
   else
       year = 1900+year;
   end
   month  = str2num(line(7:9));
   day    = str2num(line(10:12));
   hour   = str2num(line(13:15));
   minute = str2num(line(16:18));
   second = str2num(line(19:22));
   %convert Toc to gpsTime
   gpsTime      = Utc2Gps([year,month,day,hour,minute,second]);
   gpsEph(j).Toc   = gpsTime(2);
   %% get all other parameters
   gpsEph(j).af0   = str2num(line(23:41));
   gpsEph(j).af1   = str2num(line(42:60));
   gpsEph(j).af2   = str2num(line(61:79));
   
   line = fgetl(fidEph);
   gpsEph(j).IODE  = str2num(line(4:22));
   gpsEph(j).Crs   = str2num(line(23:41));
   gpsEph(j).Delta_n = str2num(line(42:60));
   gpsEph(j).M0    = str2num(line(61:79));
   
   line = fgetl(fidEph);
   gpsEph(j).Cuc   = str2num(line(4:22));
   gpsEph(j).e     = str2num(line(23:41));
   gpsEph(j).Cus   = str2num(line(42:60));
   gpsEph(j).Asqrt = str2num(line(61:79));

   line=fgetl(fidEph);
   gpsEph(j).Toe   = str2num(line(4:22));
   gpsEph(j).Cic   = str2num(line(23:41));
   gpsEph(j).OMEGA = str2num(line(42:60));
   gpsEph(j).Cis   = str2num(line(61:79));

   line = fgetl(fidEph); 
   gpsEph(j).i0        =  str2num(line(4:22));
   gpsEph(j).Crc       = str2num(line(23:41));
   gpsEph(j).omega     = str2num(line(42:60));
   gpsEph(j).OMEGA_DOT = str2num(line(61:79));
   
   line = fgetl(fidEph);
   gpsEph(j).IDOT      = str2num(line(4:22));
   gpsEph(j).codeL2    = str2num(line(23:41));
   gpsEph(j).GPS_Week  = str2num(line(42:60));
   gpsEph(j).L2Pdata   = str2num(line(61:79));

   line = fgetl(fidEph);
   gpsEph(j).accuracy  = str2num(line(4:22));
   gpsEph(j).health    = str2num(line(23:41));
   gpsEph(j).TGD       = str2num(line(42:60));
   gpsEph(j).IODC      = str2num(line(61:79));
   
   line = fgetl(fidEph);
   gpsEph(j).ttx           = str2num(line(4:22));
   gpsEph(j).Fit_interval  = str2num(line(23:41));
end
fclose(fidEph);

end %end of function ReadRinexNav
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [numEph,numHdrLines] = countEph(fidEph,fileName)
%utility function for ReadRinexNav
%Read past the header, and then read to the end, counting ephemerides:
numHdrLines = 0;
bFoundHeader = false;
while ~bFoundHeader  %Read past the header
    numHdrLines = numHdrLines+1;
    line = fgetl(fidEph);
    if ~ischar(line), break, end
    k = strfind(line,'END OF HEADER');
    if ~isempty(k),
        bFoundHeader = true;
        break
    end
end
if ~bFoundHeader
    error('Error reading file: %s\nExpected RINEX header not found',fileName);
end
%Now read to the end of the file
numEph = -1;
while 1
    numEph = numEph+1;
    line = fgetl(fidEph);
    if line == -1, 
        break;  
    elseif length(line)~=79
        %use this opportunity to check line is the right length
        %because in the rest of ReadRinexNav we depend on line being this length
        error('Incorrect line length encountered in RINEX file'); 
    end
end;
%check that we read the expected number of lines:
if rem(numEph,8)~=0
  error('Number of nav lines in %s should be divisible by 8',fileName);
end
numEph = numEph/8; %8 lines per ephemeris

end %end of function countEph
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function iono = readIono(fidEph,numHdrLines)
%utility function to read thru the header lines, and find iono parameters

iono = []; %return empty if iono not found
bIonoAlpha=false; bIonoBeta=false;

for i = 1:numHdrLines, 
    line = fgetl(fidEph); 
        % Look for iono parameters, and read them in
    if ~isempty(strfind(line,'ION ALPHA')) %line contains iono alpha parameters
        ii = strfind(line,'ION ALPHA');
        iono.alpha=str2num(line(1:ii-1));
        %If we have 4 parameters then we have the complete iono alpha
        bIonoAlpha = (length(iono.alpha)==4);
    end
    if ~isempty(strfind(line,'ION BETA'))%line contains the iono beta parameters
        ii = strfind(line,'ION BETA');
        iono.beta=str2num(line(1:ii-1));
        %If we have 4 parameters then we have the complete iono beta
        bIonoBeta = (length(iono.beta)==4);
    end
end

if ~(bIonoAlpha && bIonoBeta)
   %if we didn't get both alpha and beta iono correctly, then return empty iono
   iono=[];
end

end %end of function readIono
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function gpsEph = InitializeGpsEph
%utility function to initialize the ephemeris structure
gpsEph.PRN         = 0;
gpsEph.Toc         = 0;
gpsEph.af0         = 0;
gpsEph.af1         = 0;
gpsEph.af2         = 0;
gpsEph.IODE        = 0;
gpsEph.Crs         = 0;
gpsEph.Delta_n     = 0;
gpsEph.M0          = 0;
gpsEph.Cuc         = 0;
gpsEph.e           = 0;
gpsEph.Cus         = 0;
gpsEph.Asqrt       = 0;
gpsEph.Toe         = 0;
gpsEph.Cic         = 0;
gpsEph.OMEGA       = 0;
gpsEph.Cis         = 0;
gpsEph.i0          = 0;
gpsEph.Crc         = 0;
gpsEph.omega       = 0;
gpsEph.OMEGA_DOT   = 0;
gpsEph.IDOT        = 0;
gpsEph.codeL2      = 0;
gpsEph.GPS_Week    = 0;
gpsEph.L2Pdata     = 0;
gpsEph.accuracy    = 0;
gpsEph.health      = 0;
gpsEph.TGD         = 0;
gpsEph.IODC        = 0;
gpsEph.ttx         = 0;
gpsEph.Fit_interval= 0;

end %end of function InitializeEph
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% Copyright 2016 Google Inc.
% 
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
% 
%     http://www.apache.org/licenses/LICENSE-2.0
% 
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
