├── LICENSE ├── README.md ├── acquisition ├── norm_acq.m ├── norm_acq_parcode.m └── weak_acq_optimized_DBZP.m ├── cfg ├── config_sdr_params.m └── print_string.m ├── data_in ├── caCode.mat └── data_file_list.txt ├── file_NTLab_Bands_GPS_GLONASS_L12_ch_1_frame_3_algo_norm_acq_parcode_2021-12-12-17-25-26.mat ├── inc ├── calc_loof_coeff.m ├── check_phase.m ├── ephemeris.m ├── invert.m ├── make_ca_table.m ├── nav_party_chk.m ├── pre_run.m ├── show_channel_status.m ├── sky_plot.m └── twos_comp_2_dec.m ├── init.m ├── plots ├── figure_scripts │ ├── List_of_figures_and_instructions.pdf │ ├── Scurve.m │ ├── Tri.m │ ├── boc.m │ ├── ca_codes.m │ ├── discr.m │ ├── env.m │ ├── fig41.m │ ├── fig47.m │ ├── figure_7_dll.m │ ├── figure_7_pll.m │ ├── geoid.m │ ├── gps_sig.m │ ├── m_discr.m │ ├── multi_env_boc.m │ ├── plot1.m │ ├── plot_parallel_code_search.m │ ├── plot_parallel_freq_search.m │ ├── psd_boc.m │ ├── sincfig.m │ ├── sw.mp │ ├── winkel4.m │ └── winkel8.m ├── plot_acquisition.m ├── plot_acquisition_results.m ├── plot_delay_doppler_map_cpp.m ├── plot_navigation.m ├── plot_tracking.m ├── probe_data.m └── save_acquisition_results.m ├── post_processing ├── post_process.m ├── post_process_norm_acq_parcode.m ├── post_process_weak_acq_dbzp.m └── post_processing.m ├── pre_processing ├── calc_pseudo_ranges.m ├── ddm_processing.m ├── ddm_processing_woodbine.m ├── delay_doppler_map.m ├── find_preambles.m ├── gen_ca_code.m ├── post_navigation.m ├── pre_proc_norm_acq_parcode.m ├── pre_proc_weak_acq_dbzp.m ├── pre_process.m └── read_file_data.m ├── processing └── process.m ├── tracking └── tracking.m └── util ├── calc_sin.m ├── cart_2_geo.m ├── cart_2_utm.m ├── check_t.m ├── clen_sin.m ├── conv_dbzp_form.m ├── create_data_file.m ├── e_r_corr.m ├── find_utm_zone.m ├── geo_2_cart.m ├── least_square_post.m ├── sat_pos.m ├── sizeof.m ├── to_geod.m ├── topocent.m └── tropo_corr.m /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 JohnBagshaw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast_GNSS_ReceiverMATLAB 2 | A fast and highly sensitive GNSS receiver with a significant capability of detecting very weak GNSS signals (reflected signals) [MATLAB version] 3 | 4 | Here are the instructions for a successful run: 5 | 6 | • Create two folders; ‘mat’ and ‘data’ 7 | 8 | • In the ‘data’ folder create two new folders; ‘data_in’ and ‘data_out’ (or move the 'data_in' folder to the 'data' folder if already created) 9 | 10 | • Move all downloaded files/folders to the ‘mat’ folder except these three: 11 | 12 | o caCode.mat 13 | 14 | o data_file_list.txt 15 | 16 | o NTLab .bin file or other datasets 17 | 18 | • Move the three exception files mentioned above to the ‘data_in’ folder 19 | 20 | • Check and modify parameter settings in config_sdr_params.m file in the ‘cfg’ folder 21 | 22 | • Check the data_file_list.txt and add the input data files 23 | 24 | • Run the init.m file 25 | -------------------------------------------------------------------------------- /acquisition/norm_acq.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function acqResults = norm_acq(sdrParams, ppData, rxFrameData) 10 | %%% This function runs acquisition algorithm known as 11 | % 'Parallel Code Search Acquisition'. The minimum processing 12 | % block is for 1ms and this function takes care to give 13 | % results for coherentIntegrationInterval results. Results is a 14 | % Nd x N matrix containing complex correlation matrix for Nd doppler bins 15 | % and N samples per code. It also contains relevant stats used for 16 | % otimizations for examples number of samples averaged etc. 17 | % acqResults.ddMap; 18 | % acqResults.stats; 19 | 20 | optimizeOption = ppData.optimizeOption ; 21 | tic; 22 | 23 | if optimizeOption == 1 24 | 25 | % Parameters 26 | averFactor = ppData.averFactor; 27 | prnList = sdrParams.sysParams.acqSatelliteList; 28 | numPrns = length(prnList); 29 | numBlocks = sdrParams.sysParams.coherentProcessingTimeMS; 30 | currFile = sdrParams.stateParams.numFilesProcessed+1; 31 | numCodeSamples = sdrParams.dataFileParamsList{currFile}.samplingFreqHz * 1e-3; 32 | numDopplerSamples = floor(sdrParams.sysParams.acqDopplerBwKhz * 1e3 / ... 33 | sdrParams.sysParams.acqDopplerResHz) + 1; 34 | 35 | rxDataStartIdx = 1; 36 | 37 | % Define buffer for correlation matrix 38 | delayDopplerCohMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 39 | circCorrOutMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 40 | 41 | % Iterate over each block to do the processing. 42 | for blIdx=1:numBlocks 43 | 44 | 45 | % Multiply carrier signal with block data. 46 | blockData = rxFrameData(rxDataStartIdx:rxDataStartIdx+numCodeSamples-1) .*... 47 | ppData.dopplerFreqExp; 48 | 49 | % Take average of consecutive samples. 50 | blockDataN = zeros(numDopplerSamples, numCodeSamples/averFactor); 51 | for dbin=1:numDopplerSamples 52 | blockVec = blockData(dbin, :); 53 | blockVec = reshape(blockVec, averFactor, numCodeSamples/averFactor); 54 | blockDataN(dbin, :) = mean(blockVec, 1); 55 | end 56 | if 0 57 | blockData = permute(reshape(blockData, numDopplerSamples, averFactor, ... 58 | numCodeSamples/averFactor), [1,3,2]); 59 | blockData = mean(blockData, 3); 60 | end 61 | 62 | 63 | % Do the correaltion for 32 PRn iterations with each 64 | % circular correlation using FFT 65 | for prn=1:length(prnList) 66 | blockDataModFd = fft(blockDataN, [], 2); 67 | circCorrOutMatFd = blockDataModFd .* ppData.caCodesTable(prn,:); 68 | circCorrOutMat(prn,:,:) = ifft(circCorrOutMatFd, [], 2); 69 | end 70 | 71 | % Add result to correlation matrix 72 | delayDopplerCohMat = delayDopplerCohMat + abs(circCorrOutMat).^2; 73 | rxDataStartIdx = rxDataStartIdx + numCodeSamples; 74 | 75 | 76 | end 77 | acqResults.ddMap = delayDopplerCohMat; 78 | acqResults.averFactor = averFactor; 79 | acqResults.dopplerResHz = ppData.dopplerResHz; 80 | 81 | elseif optimizeOption == 2 82 | 83 | 84 | % Parameters 85 | 86 | averFactor = ppData.averFactor; 87 | prnList = sdrParams.sysParams.acqSatelliteList; 88 | numPrns = length(prnList); 89 | numBlocks = sdrParams.sysParams.coherentProcessingTimeMS; 90 | currFile = sdrParams.stateParams.numFilesProcessed+1; 91 | numCodeSamples = sdrParams.dataFileParamsList{currFile}.samplingFreqHz * 1e-3; 92 | numDopplerSamples = ppData.numDopplerSamples; 93 | rxDataStartIdx = 1; 94 | 95 | % Define buffer for correlation matrix 96 | delayDopplerCohMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 97 | circCorrOutMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 98 | 99 | % Iterate over each block to do the processing. 100 | for blIdx=1:numBlocks 101 | 102 | blockData = rxFrameData(rxDataStartIdx:rxDataStartIdx+numCodeSamples-1); 103 | 104 | % Multiply carrier signal with block data. 105 | blockData = blockData .* ppData.dopplerFreqInitExp; 106 | blockData = reshape(blockData, averFactor, numCodeSamples/averFactor); 107 | blockData = mean(blockData, 1); 108 | blockData = fft(blockData); 109 | 110 | blockDataN = zeros(numDopplerSamples, numCodeSamples/averFactor); 111 | if 1 112 | shiftFactor = ppData.shiftFactor; 113 | sh=0; 114 | for dbin=1:numDopplerSamples 115 | blockDataN(dbin, :) = circshift(blockData, sh); 116 | sh = floor(shiftFactor * dbin); 117 | end 118 | else 119 | % r=blockData; 120 | % c=circshift(fliplr(blockData), 1); 121 | % c = c(1:numDopplerSamples); 122 | % blockDataN = toeplitz(c, r); 123 | 124 | numCols = numCodeSamples/averFactor; 125 | blockDataN = repmat(blockData, numDopplerSamples, 1); 126 | blockDataN = ifft(fft(blockDataN,[],2) .* ... 127 | exp(-2i*pi/numCols*(0:numDopplerSamples-1)'*... 128 | (0:numCols-1)) ,[],2); 129 | 130 | end 131 | 132 | 133 | % Do the correaltion for 32 PRn iterations with each 134 | % circular correlation using FFT 135 | for prn=1:length(prnList) 136 | circCorrOutMatFd = blockDataN .* ppData.caCodesTable(prn,:); 137 | circCorrOutMat(prn,:,:) = ifft(circCorrOutMatFd, [], 2); 138 | end 139 | % Add result to correlation matrix 140 | delayDopplerCohMat = delayDopplerCohMat + abs(circCorrOutMat).^2; 141 | rxDataStartIdx = rxDataStartIdx + numCodeSamples; 142 | end 143 | 144 | acqResults.ddMap = delayDopplerCohMat; 145 | acqResults.averFactor = averFactor; 146 | acqResults.numCodeSamples = numCodeSamples; 147 | acqResults.numDopplerBins = numDopplerSamples; 148 | acqResults.dopplerResHz = ppData.dopplerResHz; 149 | end 150 | end 151 | 152 | -------------------------------------------------------------------------------- /acquisition/norm_acq_parcode.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function acqResults = norm_acq_parcode(sdrParams, ppData, rxFrameData) 10 | %%% This function runs acquisition algorithm known as 11 | % 'Parallel Code Search Acquisition'. The minimum processing 12 | % block is for 1ms and this function takes care to give 13 | % results for coherentIntegrationInterval results. Results is a 14 | % Nd x N matrix containing complex correlation matrix for Nd doppler bins 15 | % and N samples per code. It also contains relevant stats used for 16 | % optimizations for examples number of samples averaged etc. 17 | % acqResults.ddMap; 18 | % acqResults.stats; 19 | 20 | optimizeOption = ppData.optimizeOption ; 21 | tic; 22 | 23 | if optimizeOption == 1 24 | 25 | % Parameters 26 | averFactor = ppData.averFactor; 27 | prnList = sdrParams.sysParams.acqSatelliteList; 28 | numPrns = length(prnList); 29 | numBlocks = sdrParams.sysParams.coherentProcessingTimeMS; 30 | currFile = sdrParams.stateParams.numFilesProcessed+1; 31 | numCodeSamples = sdrParams.dataFileParamsList{currFile}.samplingFreqHz * 1e-3; 32 | numDopplerSamples = floor(sdrParams.sysParams.acqDopplerBwKhz * 1e3 / ... 33 | sdrParams.sysParams.acqDopplerResHz) + 1; 34 | 35 | rxDataStartIdx = 1; 36 | 37 | % Define buffer for correlation matrix 38 | delayDopplerCohMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 39 | circCorrOutMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 40 | 41 | % Iterate over each block to do the processing. 42 | for blIdx=1:numBlocks 43 | 44 | 45 | % Multiply carrier signal with block data. 46 | blockData = rxFrameData(rxDataStartIdx:rxDataStartIdx+numCodeSamples-1) .*... 47 | ppData.dopplerFreqExp; 48 | 49 | % Take average of consecutive samples. 50 | blockDataN = zeros(numDopplerSamples, numCodeSamples/averFactor); 51 | for dbin=1:numDopplerSamples 52 | blockVec = blockData(dbin, :); 53 | blockVec = reshape(blockVec, averFactor, numCodeSamples/averFactor); 54 | blockDataN(dbin, :) = mean(blockVec, 1); 55 | end 56 | if 0 57 | blockData = permute(reshape(blockData, numDopplerSamples, averFactor, ... 58 | numCodeSamples/averFactor), [1,3,2]); 59 | blockData = mean(blockData, 3); 60 | end 61 | 62 | 63 | % Do the correaltion for 32 PRn iterations with each 64 | % circular correlation using FFT 65 | for prn=1:length(prnList) 66 | blockDataModFd = fft(blockDataN, [], 2); 67 | circCorrOutMatFd = blockDataModFd .* ppData.caCodesTable(prn,:); 68 | circCorrOutMat(prn,:,:) = ifft(circCorrOutMatFd, [], 2); 69 | end 70 | 71 | % Add result to correlation matrix 72 | delayDopplerCohMat = delayDopplerCohMat + abs(circCorrOutMat).^2; 73 | rxDataStartIdx = rxDataStartIdx + numCodeSamples; 74 | 75 | 76 | end 77 | acqResults.ddMap = delayDopplerCohMat; 78 | acqResults.averFactor = averFactor; 79 | acqResults.dopplerResHz = ppData.dopplerResHz; 80 | 81 | elseif optimizeOption == 2 82 | 83 | 84 | % Parameters 85 | 86 | averFactor = ppData.averFactor; 87 | prnList = sdrParams.sysParams.acqSatelliteList; 88 | numPrns = length(prnList); 89 | numBlocks = sdrParams.sysParams.coherentProcessingTimeMS; 90 | currFile = sdrParams.stateParams.numFilesProcessed+1; 91 | numCodeSamples = sdrParams.dataFileParamsList{currFile}.samplingFreqHz * 1e-3; 92 | numDopplerSamples = ppData.numDopplerSamples; 93 | rxDataStartIdx = 1; 94 | 95 | % Define buffer for correlation matrix 96 | delayDopplerCohMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 97 | circCorrOutMat = zeros(numPrns, numDopplerSamples, numCodeSamples/averFactor); 98 | 99 | % Iterate over each block to do the processing. 100 | for blIdx=1:numBlocks 101 | 102 | blockData = rxFrameData(rxDataStartIdx:rxDataStartIdx+numCodeSamples-1); 103 | 104 | % Multiply carrier signal with block data. 105 | blockData = blockData .* ppData.dopplerFreqInitExp; 106 | blockData = reshape(blockData, averFactor, numCodeSamples/averFactor); 107 | blockData = mean(blockData, 1); 108 | blockData = fft(blockData); 109 | 110 | blockDataN = zeros(numDopplerSamples, numCodeSamples/averFactor); 111 | if 1 112 | shiftFactor = ppData.shiftFactor; 113 | sh=0; 114 | for dbin=1:numDopplerSamples 115 | blockDataN(dbin, :) = circshift(blockData, sh); 116 | sh = floor(shiftFactor * dbin); 117 | end 118 | else 119 | % r=blockData; 120 | % c=circshift(fliplr(blockData), 1); 121 | % c = c(1:numDopplerSamples); 122 | % blockDataN = toeplitz(c, r); 123 | 124 | numCols = numCodeSamples/averFactor; 125 | blockDataN = repmat(blockData, numDopplerSamples, 1); 126 | blockDataN = ifft(fft(blockDataN,[],2) .* ... 127 | exp(-2i*pi/numCols*(0:numDopplerSamples-1)'*... 128 | (0:numCols-1)) ,[],2); 129 | 130 | end 131 | 132 | 133 | % Do the correaltion for 32 PRn iterations with each 134 | % circular correlation using FFT 135 | for prn=1:length(prnList) 136 | circCorrOutMatFd = blockDataN .* ppData.caCodesTable(prn,:); 137 | circCorrOutMat(prn,:,:) = ifft(circCorrOutMatFd, [], 2); 138 | end 139 | % Add result to correlation matrix 140 | delayDopplerCohMat = delayDopplerCohMat + abs(circCorrOutMat).^2; 141 | rxDataStartIdx = rxDataStartIdx + numCodeSamples; 142 | end 143 | 144 | acqResults.ddMap = delayDopplerCohMat; 145 | acqResults.averFactor = averFactor; 146 | acqResults.numCodeSamples = numCodeSamples; 147 | acqResults.numDopplerBins = numDopplerSamples; 148 | acqResults.dopplerResHz = ppData.dopplerResHz; 149 | end 150 | end 151 | 152 | -------------------------------------------------------------------------------- /acquisition/weak_acq_optimized_DBZP.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function acqResults = weak_acq_optimized_DBZP(sdrParams, ppData, rxFrameData) 10 | %%% This function runs acquisition algorithm known as 11 | % 'Double block zero padding weak acquisition'. The minimum processing 12 | % block is for 1ms and this function takes care to give 13 | % results for coherentIntegrationInterval results. Results is a 14 | % Nd x N matrix containing complex correlation matrix for Nd doppler bins 15 | % and N samples per coherent processing interval divided by averaging factor. 16 | % It also contains relevant stats used for otimizations for examples number 17 | % of samples averaged etc. 18 | % acqResults.ddMap; 19 | % acqResults.stats; 20 | 21 | 22 | %%% Parameters 23 | 24 | caCodesTable = ppData.caCodesTable; 25 | dopplerFreqExp = ppData.dopplerFreqExp; 26 | numBlocks = ppData.numBlocks; 27 | numSamplesPerBlock = ppData.numSamplesPerBlock; 28 | averFactor = ppData.averFactor; 29 | prnList = sdrParams.sysParams.acqSatelliteList; 30 | currFile = sdrParams.stateParams.numFilesProcessed+1; 31 | samplingFreqHz = sdrParams.dataFileParamsList{currFile}.samplingFreqHz; 32 | numPrns = length(prnList); 33 | numDopplerSamples = numBlocks; 34 | numCohIntMs = sdrParams.sysParams.coherentProcessingTimeMS; 35 | 36 | dopplerDftInterpFactor = 4; 37 | numDopplerFftBins = dopplerDftInterpFactor*numDopplerSamples; 38 | 39 | % Define buffer for correlation matrix 40 | circCorrPartial = zeros(numSamplesPerBlock*numDopplerSamples, numDopplerSamples); 41 | circCorrOutMat = zeros(numPrns, numSamplesPerBlock*numDopplerSamples/numCohIntMs, numDopplerFftBins); 42 | 43 | 44 | %%% Convert signal to baseband. 45 | dataBB = rxFrameData .* dopplerFreqExp; 46 | 47 | 48 | %%% Average the input signal 49 | dataBB = reshape(dataBB, averFactor, length(dataBB)/averFactor); 50 | dataBB = mean(dataBB, 1); 51 | 52 | %%% Convert signal to DBZP format matix. 53 | dataBB = reshape(dataBB, numSamplesPerBlock, numBlocks); 54 | dataBB = [dataBB; circshift(dataBB, -1, 2)]; 55 | 56 | % dataBB = repmat(dataBB, 1, numBlocks); 57 | % r=1:numBlocks; 58 | % c=circshift(fliplr(1:numBlocks), 1); 59 | % matIdx = toeplitz(c, r)'; 60 | % permIdx = matIdx(:); 61 | % caCodeDbzpPerm = squeeze(caCodesTable(prnIdx, :, permIdx)); 62 | % parCorrOutput = ifft(fft(dataBB) .* conj(fft(caCodeDbzpPerm))); 63 | % parCorrOutput = parCorrOutput(1:numSamplesPerBlock, :); 64 | % parCorrOutput = mat2cell(parCorrOutput, [numSamplesPerBlock], [53*ones(1, 53)])'; 65 | % parCorrOutput = cell2mat(parCorrOutput); 66 | 67 | %%% Perform search for all listed PRN numbers .. 68 | for prnIdx = 1:numPrns 69 | 70 | %%% Generate local PRN C/A code 71 | 72 | %%% generate code for current PRN 73 | caCode = squeeze(caCodesTable(prnIdx, :, :)); 74 | 75 | %%% Iterate over each permuation 76 | for permIdx = 1:numBlocks 77 | 78 | %%% Generate Permuted DBZP matrix 79 | permMap = circshift(1:numBlocks, permIdx-1, 2); 80 | caCodeDbzpedPerm = caCode(:, permMap); 81 | 82 | %%% Partial correlation using FFT 83 | parCorrOutput = ifft(fft(dataBB) .* conj(fft(caCodeDbzpedPerm))); 84 | 85 | %%% Put partial correlation results in the buffer 86 | 87 | circCorrPartial((permIdx-1)*numSamplesPerBlock+1:permIdx*numSamplesPerBlock, :) = ... 88 | parCorrOutput(1:numSamplesPerBlock, :); 89 | end 90 | 91 | %%% FFT across the columsn (doppler bins) 92 | 93 | dftCircCorrPartial = abs(fftshift(fft(circCorrPartial, numDopplerFftBins, 2), 2)).^2; 94 | dftCircCorrPartialCellArr = mat2cell(dftCircCorrPartial, ... 95 | size(dftCircCorrPartial, 1)/numCohIntMs*ones(1, numCohIntMs), size(dftCircCorrPartial, 2)); 96 | 97 | dftCircCorrPartial = zeros(size(dftCircCorrPartialCellArr{1})); 98 | for m=1:numCohIntMs 99 | dftCircCorrPartial = dftCircCorrPartial + dftCircCorrPartialCellArr{m}; 100 | end 101 | circCorrOutMat(prnIdx, :, :) = dftCircCorrPartial; 102 | end 103 | 104 | % Pack results in the output. 105 | acqResults.ddMap = circCorrOutMat; 106 | acqResults.averFactor = averFactor; 107 | acqResults.numSamplesPerBlock = numSamplesPerBlock; 108 | acqResults.numBlocks = numBlocks/numCohIntMs; 109 | acqResults.numDopplerBins = numDopplerSamples; 110 | acqResults.dopplerResHz = (samplingFreqHz/averFactor/numSamplesPerBlock) / numDopplerFftBins; 111 | acqResults.numDopplerFftBins = numDopplerFftBins; 112 | 113 | end 114 | 115 | 116 | -------------------------------------------------------------------------------- /cfg/config_sdr_params.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function sdrParams = config_sdr_params() 10 | %%% This function is responsible for creating global 11 | % and per file settings. This is run once per data 12 | % file, stream and 13 | 14 | print_string("Initializing SDR parameters."); 15 | 16 | %%% Receiver system parameters 17 | 18 | % Users can specify most important parameters here. 19 | % TODO: Move these important user settings to settings.txt input file. 20 | sdrParams.sysParams.acqAlgosList = {'norm_acq_parcode', ... 21 | 'weak_acq_dbzp',... 22 | % 'weak_acq_dbzp' ,... 23 | % Add more... e.g. 24 | % 'weak_acquisition_hb',... 25 | }; 26 | 27 | sdrParams.sysParams.coherentProcessingTimeMS = 1; 28 | sdrParams.sysParams.incoherentProcessingTimeMS = 2; 29 | 30 | if sdrParams.sysParams.coherentProcessingTimeMS > 10 31 | sdrParams.sysParams.coherentProcessingTimeMS = 10; % Cannot be greater than 10ms. 32 | end 33 | 34 | if sdrParams.sysParams.incoherentProcessingTimeMS < sdrParams.sysParams.coherentProcessingTimeMS 35 | error("Incoherent processing time must be greater than coherent processing time."); 36 | end 37 | 38 | % System parameters 39 | sdrParams.sysParams.c = 299792458; % The speed of light, [m/s] 40 | sdrParams.sysParams.startOffset = 68.025; %[ms] Initial sign. travel time 41 | sdrParams.sysParams.caCodeChipRateHz = 1.023e6; % Code frequency basis [Hz] 42 | sdrParams.sysParams.minSamplingFreqHz = sdrParams.sysParams.caCodeChipRateHz * 2; 43 | sdrParams.sysParams.sampleInterpOrder = 2; 44 | 45 | % Acquisition Parameters 46 | sdrParams.sysParams.numberOfChannels = 8; % Number of channels to be used for signal processing 47 | sdrParams.sysParams.skipNumberOfBytes = 0; % Move the starting point of processing. 48 | sdrParams.sysParams.skipAcquisition = 0; % Skips acquisition in the script postProcessing.m if set to 1 49 | sdrParams.sysParams.acqSatelliteList = 1:32; % List of satellites to look for. Some satellites can be excluded to speed up acquisition [PRN numbers] 50 | sdrParams.sysParams.acqDopplerBwKhz = 50; % Band around IF to search for satellite signal. Depends on max Doppler 51 | sdrParams.sysParams.acqDopplerResHz = 1000; % Doppler resolution 52 | sdrParams.sysParams.acqThreshold = 2.5; % Threshold for the signal presence decision rule 53 | sdrParams.sysParams.numAcqSatellites = 8; % Keep record of strongest 8 satellites. 54 | 55 | % Tracking Parameters 56 | sdrParams.sysParams.msToProcess = 36; % [ms] 57 | sdrParams.sysParams.dllDampingRatio = 0.7; % Code tracking loop parameters 58 | sdrParams.sysParams.dllNoiseBandwidth = 2; %[Hz] 59 | sdrParams.sysParams.dllCorrelatorSpacing = 0.5; %[chips] 60 | sdrParams.sysParams.pllDampingRatio = 0.7; % Carrier tracking loop parameters 61 | sdrParams.sysParams.pllNoiseBandwidth = 25; %[Hz] 62 | 63 | %%% Navigation solution settings 64 | sdrParams.sysParams.navSolPeriod = 500; % Period for calculating pseudoranges and position [ms] 65 | sdrParams.sysParams.elevationMask = 10; % Elevation mask to exclude signals from satellites at low elevation[degrees 0 - 90] 66 | sdrParams.sysParams.useTropCorr = 1; % % Enable/dissable use of tropospheric correction [0 - Off, 1 - On] 67 | sdrParams.sysParams.truePosition.E = nan; % True position of the antenna in UTM system (if known). Otherwise enter 68 | sdrParams.sysParams.truePosition.N = nan; % all NaN's and mean position will be used as a reference . 69 | sdrParams.sysParams.truePosition.U = nan; 70 | 71 | %%% Data parameters 72 | sdrParams.stateParams.dataPathIn = '..\data\data_in\'; 73 | sdrParams.stateParams.dataPathOut = '..\data\data_out\'; 74 | dataInfoFileName = 'data_file_list.txt'; 75 | if ~(exist([sdrParams.stateParams.dataPathIn, ... 76 | dataInfoFileName], 'file') == 2) 77 | error(['Data list file is not present. Please define a ',... 78 | 'data_file_list.txt file in ~\..\data folder']); 79 | end 80 | 81 | fileNameStr = fileread([sdrParams.stateParams.dataPathIn, ... 82 | dataInfoFileName]); % Read data file names form data list file. 83 | sdrParams.stateParams.fileNames = regexp(fileNameStr, '\r\n|\r|\n', 'split'); 84 | 85 | for fileName=sdrParams.stateParams.fileNames 86 | 87 | % Check if file exists in the path. 88 | if exist([sdrParams.stateParams.dataPathIn, fileName{1}], 'file') == 2 89 | 90 | %sdrParams.dataParams.fileDataParams; 91 | dataParams = []; 92 | % TODO: Convert formats to JSON. 93 | switch fileName{1} 94 | case 'Woodbine_47a' 95 | dataParams.intermFreqHz = 10e6; % Intermediate frequency 96 | dataParams.samplingFreqHz = 40e6; % Sampling frequency 97 | dataParams.dataType = 'int16'; % Data type used to store one sample 98 | dataParams.isBasebandSignal = 1; 99 | dataParams.totalChannels = 1; 100 | dataParams.selectedChannel = 1; 101 | 102 | case 'GPS_and_GIOVE_A-NN-fs16_3676-if4_1304.bin' 103 | dataParams.intermFreqHz = 4.1304e6; % Intermediate frequency 104 | dataParams.samplingFreqHz = 16.367e6; % Sampling frequency 105 | dataParams.dataType = 'int8'; % Data type used to store one sample 106 | dataParams.isBasebandSignal = 0; 107 | dataParams.totalChannels = 1; 108 | dataParams.selectedChannel = 1; 109 | 110 | case 'GPSdata-DiscreteComponents-fs38_192-if9_55.bin' 111 | dataParams.intermFreqHz = 9.548e6; % Intermediate frequency 112 | dataParams.samplingFreqHz = 38.192e6; % Sampling frequency 113 | dataParams.dataType = 'int8'; % Data type used to store one sample 114 | dataParams.isBasebandSignal = 0; 115 | dataParams.totalChannels = 1; 116 | dataParams.selectedChannel = 1; 117 | 118 | case 'NTLab_Bands_GPS_GLONASS_L12.bin' 119 | dataParams.intermFreqHz = (1590-1575.42)*1e6; % Intermediate frequency 120 | dataParams.samplingFreqHz = 53e6; % Sampling frequency 121 | dataParams.dataType = 'ubit2'; % Data type used to store one sample 122 | dataParams.isBasebandSignal = 0; 123 | dataParams.totalChannels = 4; 124 | dataParams.selectedChannel = 4; % set to -1 for all channels 125 | 126 | case 'Woodbine_47a_1.bin' 127 | dataParams.intermFreqHz = 9.548e6; % Intermediate frequency 128 | dataParams.samplingFreqHz = 38.192e6; % Sampling frequency 129 | dataParams.dataType = 'int8'; % Data type used to store one sample 130 | dataParams.isBasebandSignal = 0; 131 | dataParams.totalChannels = 1; 132 | dataParams.selectedChannel = 1; 133 | 134 | case 'Woodbine_47a_2.bin' 135 | dataParams.intermFreqHz = 9.548e6; % Intermediate frequency 136 | dataParams.samplingFreqHz = 38.192e6; % Sampling frequency 137 | dataParams.dataType = 'int8'; % Data type used to store one sample 138 | dataParams.isBasebandSignal = 0; 139 | dataParams.totalChannels = 1; 140 | dataParams.selectedChannel = 1; 141 | 142 | otherwise 143 | error("Listed file is not recognized by SDR. If newer, please add to init_settings or json format."); 144 | end 145 | 146 | if isfield(sdrParams, 'dataFileParamsList') 147 | sdrParams.dataFileParamsList{end+1, :} = dataParams; 148 | else 149 | sdrParams.dataFileParamsList = {dataParams}; 150 | end 151 | 152 | else 153 | error([fileName, ' is not present in ', ... 154 | sdrParams.sysParams.dataPathIn, '.']); 155 | end 156 | end 157 | 158 | %%% State Params 159 | sdrParams.stateParams.numFilesProcessed = 0; % Set processed files to zero 160 | sdrParams.stateParams.numFilesToProcess = length(sdrParams.stateParams.fileNames); % number of total data sets to process 161 | 162 | sdrParams.stateParams.currFrameNum = 0; 163 | sdrParams.stateParams.numTotalFrames = sdrParams.sysParams.incoherentProcessingTimeMS / ... 164 | sdrParams.sysParams.coherentProcessingTimeMS; 165 | 166 | if floor(sdrParams.stateParams.numTotalFrames) ~= sdrParams.stateParams.numTotalFrames 167 | error('Number of incoherent frames of data not integer'); 168 | end 169 | 170 | 171 | 172 | 173 | %%% end. 174 | -------------------------------------------------------------------------------- /cfg/print_string.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function print_string(str) 10 | %%% Script to print string on console given the module ID 11 | 12 | global printDebugInfo; 13 | global swName; 14 | 15 | if printDebugInfo 16 | % https://www.mathworks.com/matlabcentral/answers/9907- 17 | % is-there-a-function-that-retrieves-the-filename-of-the-current-script#answer_13613 18 | S = dbstack(); 19 | callerPath = which(S(2).name); 20 | callerLineNum = S(2).line; 21 | 22 | callerPathSpltd = regexp(callerPath, '\', 'split'); 23 | sD = 1; 24 | while ~isequal(callerPathSpltd{sD}, 'mat') 25 | sD = sD + 1; 26 | end 27 | 28 | callerName = callerPathSpltd{end}; 29 | 30 | callerRelPath = '~\'; 31 | while sD < length(callerPathSpltd) 32 | callerRelPath = [callerRelPath, callerPathSpltd{sD}, '\']; 33 | sD = sD + 1; 34 | end 35 | 36 | callerInfo = ['path: ', callerRelPath, ', file: ', callerName, ', line: ', num2str(callerLineNum)]; 37 | fprintf("%s : %-20s :> %s (%s)\n", swName, getAllFuncNames(S), str, callerInfo); 38 | else 39 | S = dbstack(); 40 | fprintf("%s : %-20s :> %s \n", swName, getAllFuncNames(S), str); 41 | 42 | end 43 | end 44 | 45 | function recurScrptName = getAllFuncNames(S) 46 | 47 | sD = length(S); 48 | recurScrptName = S(sD).name; 49 | sD = sD - 1; 50 | while sD > 1 51 | recurScrptName = [recurScrptName, '->', S(sD).name]; 52 | sD = sD - 1; 53 | end 54 | end 55 | 56 | -------------------------------------------------------------------------------- /data_in/caCode.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnBagshaw/Fast_GNSS_ReceiverMATLAB/55882581c43cea1f3a4546d3c6ab70840da5c6e4/data_in/caCode.mat -------------------------------------------------------------------------------- /data_in/data_file_list.txt: -------------------------------------------------------------------------------- 1 | NTLab_Bands_GPS_GLONASS_L12.bin 2 | -------------------------------------------------------------------------------- /file_NTLab_Bands_GPS_GLONASS_L12_ch_1_frame_3_algo_norm_acq_parcode_2021-12-12-17-25-26.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnBagshaw/Fast_GNSS_ReceiverMATLAB/55882581c43cea1f3a4546d3c6ab70840da5c6e4/file_NTLab_Bands_GPS_GLONASS_L12_ch_1_frame_3_algo_norm_acq_parcode_2021-12-12-17-25-26.mat -------------------------------------------------------------------------------- /inc/calc_loof_coeff.m: -------------------------------------------------------------------------------- 1 | function [tau1, tau2] = calc_loof_coeff(LBW, zeta, k) 2 | %Function finds loop coefficients. The coefficients are used then in PLL-s 3 | %and DLL-s. 4 | % 5 | %[tau1, tau2] = calcLoopCoef(LBW, zeta, k) 6 | % 7 | % Inputs: 8 | % LBW - Loop noise bandwidth 9 | % zeta - Damping ratio 10 | % k - Loop gain 11 | % 12 | % Outputs: 13 | % tau1, tau2 - Loop filter coefficients 14 | 15 | %-------------------------------------------------------------------------- 16 | % SoftGNSS v3.0 17 | % 18 | % Copyright (C) Darius Plausinaitis and Dennis M. Akos 19 | % Written by Darius Plausinaitis and Dennis M. Akos 20 | %-------------------------------------------------------------------------- 21 | %This program is free software; you can redistribute it and/or 22 | %modify it under the terms of the GNU General Public License 23 | %as published by the Free Software Foundation; either version 2 24 | %of the License, or (at your option) any later version. 25 | % 26 | %This program is distributed in the hope that it will be useful, 27 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | %GNU General Public License for more details. 30 | % 31 | %You should have received a copy of the GNU General Public License 32 | %along with this program; if not, write to the Free Software 33 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 34 | %USA. 35 | %-------------------------------------------------------------------------- 36 | 37 | %CVS record: 38 | %$Id: calcLoopCoef.m,v 1.1.2.2 2006/08/14 11:38:22 dpl Exp $ 39 | 40 | % Solve natural frequency 41 | Wn = LBW*8*zeta / (4*zeta.^2 + 1); 42 | 43 | % solve for t1 & t2 44 | tau1 = k / (Wn * Wn); 45 | tau2 = 2.0 * zeta / Wn; 46 | -------------------------------------------------------------------------------- /inc/check_phase.m: -------------------------------------------------------------------------------- 1 | function word = check_phase(word, D30Star) 2 | %Checks the parity of the supplied 30bit word. 3 | %The last parity bit of the previous word is used for the calculation. 4 | %A note on the procedure is supplied by the GPS standard positioning 5 | %service signal specification. 6 | % 7 | %word = checkPhase(word, D30Star) 8 | % 9 | % Inputs: 10 | % word - an array with 30 bit long word from the navigation 11 | % message (a character array, must contain only '0' or 12 | % '1'). 13 | % D30Star - the last bit of the previous word (char type). 14 | % 15 | % Outputs: 16 | % word - word with corrected polarity of the data bits 17 | % (character array). 18 | 19 | %-------------------------------------------------------------------------- 20 | % SoftGNSS v3.0 21 | % 22 | % Written by Darius Plausinaitis and Dennis M. Akos 23 | %-------------------------------------------------------------------------- 24 | %This program is free software; you can redistribute it and/or 25 | %modify it under the terms of the GNU General Public License 26 | %as published by the Free Software Foundation; either version 2 27 | %of the License, or (at your option) any later version. 28 | % 29 | %This program is distributed in the hope that it will be useful, 30 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | %GNU General Public License for more details. 33 | % 34 | %You should have received a copy of the GNU General Public License 35 | %along with this program; if not, write to the Free Software 36 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 37 | %USA. 38 | %-------------------------------------------------------------------------- 39 | 40 | % CVS record: 41 | % $Id: checkPhase.m,v 1.1.2.4 2006/08/14 11:38:22 dpl Exp $ 42 | 43 | if D30Star == '1' 44 | % Data bits must be inverted 45 | word(1:24) = invert(word(1:24)); 46 | end 47 | -------------------------------------------------------------------------------- /inc/ephemeris.m: -------------------------------------------------------------------------------- 1 | function [eph, TOW] = ephemeris(bits, D30Star) 2 | %Function decodes ephemerides and TOW from the given bit stream. The stream 3 | %(array) in the parameter BITS must contain 1500 bits. The first element in 4 | %the array must be the first bit of a subframe. The subframe ID of the 5 | %first subframe in the array is not important. 6 | % 7 | %Function does not check parity! 8 | % 9 | %[eph, TOW] = ephemeris(bits, D30Star) 10 | % 11 | % Inputs: 12 | % bits - bits of the navigation messages (5 subframes). 13 | % Type is character array and it must contain only 14 | % characters '0' or '1'. 15 | % D30Star - The last bit of the previous nav-word. Refer to the 16 | % GPS interface control document ICD (IS-GPS-200D) for 17 | % more details on the parity checking. Parameter type is 18 | % char. It must contain only characters '0' or '1'. 19 | % Outputs: 20 | % TOW - Time Of Week (TOW) of the first sub-frame in the bit 21 | % stream (in seconds) 22 | % eph - SV ephemeris 23 | 24 | %-------------------------------------------------------------------------- 25 | % SoftGNSS v3.0 26 | % 27 | % Copyright (C) Darius Plausinaitis and Kristin Larson 28 | % Written by Darius Plausinaitis and Kristin Larson 29 | %-------------------------------------------------------------------------- 30 | %This program is free software; you can redistribute it and/or 31 | %modify it under the terms of the GNU General Public License 32 | %as published by the Free Software Foundation; either version 2 33 | %of the License, or (at your option) any later version. 34 | % 35 | %This program is distributed in the hope that it will be useful, 36 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 37 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 38 | %GNU General Public License for more details. 39 | % 40 | %You should have received a copy of the GNU General Public License 41 | %along with this program; if not, write to the Free Software 42 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 43 | %USA. 44 | %-------------------------------------------------------------------------- 45 | 46 | %CVS record: 47 | %$Id: ephemeris.m,v 1.1.2.7 2006/08/14 11:38:22 dpl Exp $ 48 | 49 | 50 | %% Check if there is enough data ========================================== 51 | if length(bits) < 1500 52 | error('The parameter BITS must contain 1500 bits!'); 53 | end 54 | 55 | %% Check if the parameters are strings ==================================== 56 | if ~ischar(bits) 57 | error('The parameter BITS must be a character array!'); 58 | end 59 | 60 | if ~ischar(D30Star) 61 | error('The parameter D30Star must be a char!'); 62 | end 63 | 64 | % Pi used in the GPS coordinate system 65 | gpsPi = 3.1415926535898; 66 | 67 | %% Decode all 5 sub-frames ================================================ 68 | for i = 1:5 69 | 70 | %--- "Cut" one sub-frame's bits --------------------------------------- 71 | subframe = bits(300*(i-1)+1 : 300*i); 72 | 73 | %--- Correct polarity of the data bits in all 10 words ---------------- 74 | for j = 1:10 75 | [subframe(30*(j-1)+1 : 30*j)] = ... 76 | checkPhase(subframe(30*(j-1)+1 : 30*j), D30Star); 77 | 78 | D30Star = subframe(30*j); 79 | end 80 | 81 | %--- Decode the sub-frame id ------------------------------------------ 82 | % For more details on sub-frame contents please refer to GPS IS. 83 | subframeID = bin2dec(subframe(50:52)); 84 | 85 | %--- Decode sub-frame based on the sub-frames id ---------------------- 86 | % The task is to select the necessary bits and convert them to decimal 87 | % numbers. For more details on sub-frame contents please refer to GPS 88 | % ICD (IS-GPS-200D). 89 | switch subframeID 90 | case 1 %--- It is subframe 1 ------------------------------------- 91 | % It contains WN, SV clock corrections, health and accuracy 92 | eph.weekNumber = bin2dec(subframe(61:70)) + 1024; 93 | eph.accuracy = bin2dec(subframe(73:76)); 94 | eph.health = bin2dec(subframe(77:82)); 95 | eph.T_GD = twosComp2dec(subframe(197:204)) * 2^(-31); 96 | eph.IODC = bin2dec([subframe(83:84) subframe(197:204)]); 97 | eph.t_oc = bin2dec(subframe(219:234)) * 2^4; 98 | eph.a_f2 = twosComp2dec(subframe(241:248)) * 2^(-55); 99 | eph.a_f1 = twosComp2dec(subframe(249:264)) * 2^(-43); 100 | eph.a_f0 = twosComp2dec(subframe(271:292)) * 2^(-31); 101 | 102 | case 2 %--- It is subframe 2 ------------------------------------- 103 | % It contains first part of ephemeris parameters 104 | eph.IODE_sf2 = bin2dec(subframe(61:68)); 105 | eph.C_rs = twosComp2dec(subframe(69: 84)) * 2^(-5); 106 | eph.deltan = ... 107 | twosComp2dec(subframe(91:106)) * 2^(-43) * gpsPi; 108 | eph.M_0 = ... 109 | twosComp2dec([subframe(107:114) subframe(121:144)]) ... 110 | * 2^(-31) * gpsPi; 111 | eph.C_uc = twosComp2dec(subframe(151:166)) * 2^(-29); 112 | eph.e = ... 113 | bin2dec([subframe(167:174) subframe(181:204)]) ... 114 | * 2^(-33); 115 | eph.C_us = twosComp2dec(subframe(211:226)) * 2^(-29); 116 | eph.sqrtA = ... 117 | bin2dec([subframe(227:234) subframe(241:264)]) ... 118 | * 2^(-19); 119 | eph.t_oe = bin2dec(subframe(271:286)) * 2^4; 120 | 121 | case 3 %--- It is subframe 3 ------------------------------------- 122 | % It contains second part of ephemeris parameters 123 | eph.C_ic = twosComp2dec(subframe(61:76)) * 2^(-29); 124 | eph.omega_0 = ... 125 | twosComp2dec([subframe(77:84) subframe(91:114)]) ... 126 | * 2^(-31) * gpsPi; 127 | eph.C_is = twosComp2dec(subframe(121:136)) * 2^(-29); 128 | eph.i_0 = ... 129 | twosComp2dec([subframe(137:144) subframe(151:174)]) ... 130 | * 2^(-31) * gpsPi; 131 | eph.C_rc = twosComp2dec(subframe(181:196)) * 2^(-5); 132 | eph.omega = ... 133 | twosComp2dec([subframe(197:204) subframe(211:234)]) ... 134 | * 2^(-31) * gpsPi; 135 | eph.omegaDot = twosComp2dec(subframe(241:264)) * 2^(-43) * gpsPi; 136 | eph.IODE_sf3 = bin2dec(subframe(271:278)); 137 | eph.iDot = twosComp2dec(subframe(279:292)) * 2^(-43) * gpsPi; 138 | 139 | case 4 %--- It is subframe 4 ------------------------------------- 140 | % Almanac, ionospheric model, UTC parameters. 141 | % SV health (PRN: 25-32). 142 | % Not decoded at the moment. 143 | 144 | case 5 %--- It is subframe 5 ------------------------------------- 145 | % SV almanac and health (PRN: 1-24). 146 | % Almanac reference week number and time. 147 | % Not decoded at the moment. 148 | 149 | end % switch subframeID ... 150 | 151 | end % for all 5 sub-frames ... 152 | 153 | %% Compute the time of week (TOW) of the first sub-frames in the array ==== 154 | % Also correct the TOW. The transmitted TOW is actual TOW of the next 155 | % subframe and we need the TOW of the first subframe in this data block 156 | % (the variable subframe at this point contains bits of the last subframe). 157 | TOW = bin2dec(subframe(31:47)) * 6 - 30; 158 | -------------------------------------------------------------------------------- /inc/invert.m: -------------------------------------------------------------------------------- 1 | function result = invert(data) 2 | % Inverts the binary input-string so that 0 becomes 1 and 1 becomes 0. 3 | % 4 | %result = invert(data) 5 | 6 | %-------------------------------------------------------------------------- 7 | % SoftGNSS v3.0 8 | % 9 | % Written by Darius Plausinaitis, Kristin Larson and Dennis M. Akos 10 | %-------------------------------------------------------------------------- 11 | %This program is free software; you can redistribute it and/or 12 | %modify it under the terms of the GNU General Public License 13 | %as published by the Free Software Foundation; either version 2 14 | %of the License, or (at your option) any later version. 15 | % 16 | %This program is distributed in the hope that it will be useful, 17 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | %GNU General Public License for more details. 20 | % 21 | %You should have received a copy of the GNU General Public License 22 | %along with this program; if not, write to the Free Software 23 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 24 | %USA. 25 | %-------------------------------------------------------------------------- 26 | 27 | % CVS record: 28 | % $Id: invert.m,v 1.1.2.4 2006/08/14 11:38:22 dpl Exp $ 29 | 30 | dataLength = length(data); 31 | temp(1:dataLength) = '1'; 32 | 33 | invertMask = bin2dec(char(temp)); 34 | 35 | result = dec2bin(bitxor(bin2dec(data), invertMask), dataLength); -------------------------------------------------------------------------------- /inc/make_ca_table.m: -------------------------------------------------------------------------------- 1 | function caCodesTable = make_ca_table(settings, seqLen) 2 | %Function generates CA codes for all 32 satellites based on the settings 3 | %provided in the structure "settings". The codes are digitized at the 4 | %sampling frequency specified in the settings structure. 5 | %One row in the "caCodesTable" is one C/A code. The row number is the PRN 6 | %number of the C/A code. 7 | % 8 | %caCodesTable = makeCaTable(settings) 9 | % 10 | % Inputs: 11 | % settings - receiver settings 12 | % Outputs: 13 | % caCodesTable - an array of arrays (matrix) containing C/A codes 14 | % for all satellite PRN-s 15 | 16 | %-------------------------------------------------------------------------- 17 | % SoftGNSS v3.0 18 | % 19 | % Copyright (C) Darius Plausinaitis 20 | % Written by Darius Plausinaitis 21 | % Based on Peter Rinder and Nicolaj Bertelsen 22 | %-------------------------------------------------------------------------- 23 | %This program is free software; you can redistribute it and/or 24 | %modify it under the terms of the GNU General Public License 25 | %as published by the Free Software Foundation; either version 2 26 | %of the License, or (at your option) any later version. 27 | % 28 | %This program is distributed in the hope that it will be useful, 29 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | %GNU General Public License for more details. 32 | % 33 | %You should have received a copy of the GNU General Public License 34 | %along with this program; if not, write to the Free Software 35 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 36 | %USA. 37 | %-------------------------------------------------------------------------- 38 | 39 | %CVS record: 40 | %$Id: makeCaTable.m,v 1.1.2.6 2006/08/14 11:38:22 dpl Exp $ 41 | 42 | %--- Find number of samples per spreading code ---------------------------- 43 | samplesPerCode = settings.dataParams.samplesPerCode; 44 | 45 | %--- Prepare the output matrix to speed up function ----------------------- 46 | caCodesTable = zeros(32, samplesPerCode); 47 | 48 | %--- Find time constants -------------------------------------------------- 49 | ts = 1/settings.samplingFreq; % Sampling period in sec 50 | tc = 1/settings.codeFreqBasis; % C/A chip period in sec 51 | 52 | %=== For all satellite PRN-s ... 53 | for PRN = 1:32 54 | %--- Generate CA code for given PRN ----------------------------------- 55 | caCode = gen_ca_code(PRN); 56 | 57 | %=== Digitizing ======================================================= 58 | 59 | %--- Make index array to read C/A code values ------------------------- 60 | % The length of the index array depends on the sampling frequency - 61 | % number of samples per millisecond (because one C/A code period is one 62 | % millisecond). 63 | codeValueIndex = ceil((ts * (1:samplesPerCode)) / tc); 64 | 65 | %--- Correct the last index (due to number rounding issues) ----------- 66 | codeValueIndex(end) = 1023; 67 | 68 | %--- Make the digitized version of the C/A code ----------------------- 69 | % The "upsampled" code is made by selecting values form the CA code 70 | % chip array (caCode) for the time instances of each sample. 71 | seqInd = mod(0:seqLen-1, samplesPerCode) + 1; 72 | caCodesTable(PRN, :) = caCode(codeValueIndex(seqInd)); 73 | 74 | end % for PRN = 1:32 75 | -------------------------------------------------------------------------------- /inc/nav_party_chk.m: -------------------------------------------------------------------------------- 1 | function status = nav_party_chk(ndat) 2 | % This function is called to compute and status the parity bits on GPS word. 3 | % Based on the flowchart in Figure 2-10 in the 2nd Edition of the GPS-SPS 4 | % Signal Spec. 5 | % 6 | %status = navPartyChk(ndat) 7 | % 8 | % Inputs: 9 | % ndat - an array (1x32) of 32 bits represent a GPS navigation 10 | % word which is 30 bits plus two previous bits used in 11 | % the parity calculation (-2 -1 0 1 2 ... 28 29) 12 | % 13 | % Outputs: 14 | % status - the test value which equals EITHER +1 or -1 if parity 15 | % PASSED or 0 if parity fails. The +1 means bits #1-24 16 | % of the current word have the correct polarity, while -1 17 | % means the bits #1-24 of the current word must be 18 | % inverted. 19 | 20 | %-------------------------------------------------------------------------- 21 | % SoftGNSS v3.0 22 | % 23 | % Written by Darius Plausinaitis, Kristin Larson 24 | %-------------------------------------------------------------------------- 25 | %This program is free software; you can redistribute it and/or 26 | %modify it under the terms of the GNU General Public License 27 | %as published by the Free Software Foundation; either version 2 28 | %of the License, or (at your option) any later version. 29 | % 30 | %This program is distributed in the hope that it will be useful, 31 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | %GNU General Public License for more details. 34 | % 35 | %You should have received a copy of the GNU General Public License 36 | %along with this program; if not, write to the Free Software 37 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 38 | %USA. 39 | %-------------------------------------------------------------------------- 40 | 41 | % CVS record: 42 | % $Id: navPartyChk.m,v 1.1.2.5 2006/08/14 11:38:22 dpl Exp $ 43 | 44 | % In order to accomplish the exclusive or operation using multiplication 45 | % this program represents a '0' with a '-1' and a '1' with a '1' so that 46 | % the exclusive or table holds true for common data operations 47 | % 48 | % a b xor a b product 49 | % -------------- ----------------- 50 | % 0 0 1 -1 -1 1 51 | % 0 1 0 -1 1 -1 52 | % 1 0 0 1 -1 -1 53 | % 1 1 1 1 1 1 54 | 55 | %--- Check if the data bits must be inverted ------------------------------ 56 | if (ndat(2) ~= 1) 57 | ndat(3:26)= -1 .* ndat(3:26); % Also could just negate 58 | end 59 | 60 | %--- Calculate 6 parity bits ---------------------------------------------- 61 | % The elements of the ndat array correspond to the bits showed in the table 62 | % 20-XIV (ICD-200C document) in the following way: 63 | % The first element in the ndat is the D29* bit and the second - D30*. 64 | % The elements 3 - 26 are bits d1-d24 in the table. 65 | % The elements 27 - 32 in the ndat array are the received bits D25-D30. 66 | % The array "parity" contains the computed D25-D30 (parity) bits. 67 | 68 | parity(1) = ndat(1) * ndat(3) * ndat(4) * ndat(5) * ndat(7) * ... 69 | ndat(8) * ndat(12) * ndat(13) * ndat(14) * ndat(15) * ... 70 | ndat(16) * ndat(19) * ndat(20) * ndat(22) * ndat(25); 71 | 72 | parity(2) = ndat(2) * ndat(4) * ndat(5) * ndat(6) * ndat(8) * ... 73 | ndat(9) * ndat(13) * ndat(14) * ndat(15) * ndat(16) * ... 74 | ndat(17) * ndat(20) * ndat(21) * ndat(23) * ndat(26); 75 | 76 | parity(3) = ndat(1) * ndat(3) * ndat(5) * ndat(6) * ndat(7) * ... 77 | ndat(9) * ndat(10) * ndat(14) * ndat(15) * ndat(16) * ... 78 | ndat(17) * ndat(18) * ndat(21) * ndat(22) * ndat(24); 79 | 80 | parity(4) = ndat(2) * ndat(4) * ndat(6) * ndat(7) * ndat(8) * ... 81 | ndat(10) * ndat(11) * ndat(15) * ndat(16) * ndat(17) * ... 82 | ndat(18) * ndat(19) * ndat(22) * ndat(23) * ndat(25); 83 | 84 | parity(5) = ndat(2) * ndat(3) * ndat(5) * ndat(7) * ndat(8) * ... 85 | ndat(9) * ndat(11) * ndat(12) * ndat(16) * ndat(17) * ... 86 | ndat(18) * ndat(19) * ndat(20) * ndat(23) * ndat(24) * ... 87 | ndat(26); 88 | 89 | parity(6) = ndat(1) * ndat(5) * ndat(7) * ndat(8) * ndat(10) * ... 90 | ndat(11) * ndat(12) * ndat(13) * ndat(15) * ndat(17) * ... 91 | ndat(21) * ndat(24) * ndat(25) * ndat(26); 92 | 93 | %--- Compare if the received parity is equal the calculated parity -------- 94 | if ((sum(parity == ndat(27:32))) == 6) 95 | 96 | % Parity is OK. Function output is -1 or 1 depending if the data bits 97 | % must be inverted or not. The "ndat(2)" is D30* bit - the last bit of 98 | % previous subframe. 99 | status = -1 * ndat(2); 100 | else 101 | % Parity failure 102 | status = 0; 103 | end 104 | -------------------------------------------------------------------------------- /inc/pre_run.m: -------------------------------------------------------------------------------- 1 | function [channel] = pre_run(acqResults, settings) 2 | %Function initializes tracking channels from acquisition data. The acquired 3 | %signals are sorted according to the signal strength. This function can be 4 | %modified to use other satellite selection algorithms or to introduce 5 | %acquired signal properties offsets for testing purposes. 6 | % 7 | %[channel] = preRun(acqResults, settings) 8 | % 9 | % Inputs: 10 | % acqResults - results from acquisition. 11 | % settings - receiver settings 12 | % 13 | % Outputs: 14 | % channel - structure contains information for each channel (like 15 | % properties of the tracked signal, channel status etc.). 16 | 17 | %-------------------------------------------------------------------------- 18 | % SoftGNSS v3.0 19 | % 20 | % Copyright (C) Darius Plausinaitis 21 | % Written by Darius Plausinaitis 22 | % Based on Peter Rinder and Nicolaj Bertelsen 23 | %-------------------------------------------------------------------------- 24 | %This program is free software; you can redistribute it and/or 25 | %modify it under the terms of the GNU General Public License 26 | %as published by the Free Software Foundation; either version 2 27 | %of the License, or (at your option) any later version. 28 | % 29 | %This program is distributed in the hope that it will be useful, 30 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | %GNU General Public License for more details. 33 | % 34 | %You should have received a copy of the GNU General Public License 35 | %along with this program; if not, write to the Free Software 36 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 37 | %USA. 38 | %-------------------------------------------------------------------------- 39 | 40 | %CVS record: 41 | %$Id: preRun.m,v 1.8.2.20 2006/08/14 11:38:22 dpl Exp $ 42 | 43 | %% Initialize all channels ================================================ 44 | channel = []; % Clear, create the structure 45 | 46 | channel.PRN = 0; % PRN number of the tracked satellite 47 | channel.acquiredFreq = 0; % Used as the center frequency of the NCO 48 | channel.codePhase = 0; % Position of the C/A start 49 | 50 | channel.status = '-'; % Mode/status of the tracking channel 51 | % "-" - "off" - no signal to track 52 | % "T" - Tracking state 53 | 54 | %--- Copy initial data to all channels ------------------------------------ 55 | channel = repmat(channel, 1, settings.numberOfChannels); 56 | 57 | %% Copy acquisition results =============================================== 58 | 59 | %--- Sort peaks to find strongest signals, keep the peak index information 60 | [junk, PRNindexes] = sort(acqResults.peakMetric, 2, 'descend'); 61 | 62 | %--- Load information about each satellite -------------------------------- 63 | % Maximum number of initialized channels is number of detected signals, but 64 | % not more as the number of channels specified in the settings. 65 | for ii = 1:min([settings.numberOfChannels, sum(acqResults.carrFreq > 0)]) 66 | channel(ii).PRN = PRNindexes(ii); 67 | channel(ii).acquiredFreq = acqResults.carrFreq(PRNindexes(ii)); 68 | channel(ii).codePhase = acqResults.codePhase(PRNindexes(ii)); 69 | 70 | % Set tracking into mode (there can be more modes if needed e.g. pull-in) 71 | channel(ii).status = 'T'; 72 | end 73 | -------------------------------------------------------------------------------- /inc/show_channel_status.m: -------------------------------------------------------------------------------- 1 | function show_channel_status(channel, settings) 2 | %Prints the status of all channels in a table. 3 | % 4 | %show_channel_status(channel, settings) 5 | % 6 | % Inputs: 7 | % channel - data for each channel. It is used to initialize and 8 | % at the processing of the signal (tracking part). 9 | % settings - receiver settings 10 | 11 | %-------------------------------------------------------------------------- 12 | % SoftGNSS v3.0 13 | % 14 | % Copyright (C) Peter Rinder and Nicolaj Bertelsen 15 | % Written by Peter Rinder Nicolaj Bertelsen and Darius Plausinaitis 16 | % Based on Peter Rinder and Nicolaj Bertelsen 17 | %-------------------------------------------------------------------------- 18 | %This program is free software; you can redistribute it and/or 19 | %modify it under the terms of the GNU General Public License 20 | %as published by the Free Software Foundation; either version 2 21 | %of the License, or (at your option) any later version. 22 | % 23 | %This program is distributed in the hope that it will be useful, 24 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | %GNU General Public License for more details. 27 | % 28 | %You should have received a copy of the GNU General Public License 29 | %along with this program; if not, write to the Free Software 30 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 31 | %USA. 32 | %-------------------------------------------------------------------------- 33 | 34 | %CVS record: 35 | %$Id: showChannelStatus.m,v 1.4.2.8 2006/08/14 11:38:22 dpl Exp $ 36 | 37 | fprintf('\n*=========*=====*===============*===========*=============*========*\n'); 38 | fprintf( '| Channel | PRN | Frequency | Doppler | Code Offset | Status |\n'); 39 | fprintf( '*=========*=====*===============*===========*=============*========*\n'); 40 | 41 | for channelNr = 1 : settings.numberOfChannels 42 | if (channel(channelNr).status ~= '-') 43 | fprintf('| %2d | %3d | %2.5e | %5.0f | %6d | %1s |\n', ... 44 | channelNr, ... 45 | channel(channelNr).PRN, ... 46 | channel(channelNr).acquiredFreq, ... 47 | channel(channelNr).acquiredFreq - settings.IF, ... 48 | channel(channelNr).codePhase, ... 49 | channel(channelNr).status); 50 | else 51 | fprintf('| %2d | --- | ------------ | ----- | ------ | Off |\n', ... 52 | channelNr); 53 | end 54 | end 55 | 56 | fprintf('*=========*=====*===============*===========*=============*========*\n\n'); 57 | -------------------------------------------------------------------------------- /inc/sky_plot.m: -------------------------------------------------------------------------------- 1 | function hpol = sky_plot(varargin) 2 | %Function plots "sky view" from the receiver perspective. 3 | % 4 | %h = skyPlot(AZ, EL, PRN, line_style) 5 | % 6 | % Inputs: 7 | % AZ - contains satellite azimuth angles. It is a 2D 8 | % matrix. One line contains data of one satellite. 9 | % The columns are the calculated azimuth values. 10 | % EL - contains satellite elevation angles. It is a 2D 11 | % matrix. One line contains data of one satellite. 12 | % The columns are the calculated elevations. 13 | % PRN - a row vector containing PRN numbers of the 14 | % satellites. 15 | % line_style - line style of the plot. The same style will be 16 | % used to plot all satellite positions (including 17 | % color). 18 | % Outputs: 19 | % h - handle to the plot 20 | 21 | %-------------------------------------------------------------------------- 22 | % SoftGNSS v3.0 23 | % 24 | % Copyright (C) Darius Plausinaitis and Kristin Larson 25 | % Written by Darius Plausinaitis and Kristin Larson 26 | %-------------------------------------------------------------------------- 27 | %This program is free software; you can redistribute it and/or 28 | %modify it under the terms of the GNU General Public License 29 | %as published by the Free Software Foundation; either version 2 30 | %of the License, or (at your option) any later version. 31 | % 32 | %This program is distributed in the hope that it will be useful, 33 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35 | %GNU General Public License for more details. 36 | % 37 | %You should have received a copy of the GNU General Public License 38 | %along with this program; if not, write to the Free Software 39 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 40 | %USA. 41 | %-------------------------------------------------------------------------- 42 | 43 | %CVS record: 44 | %$Id: skyPlot.m,v 1.1.2.5 2006/08/18 11:41:57 dpl Exp $ 45 | 46 | %% Check arguments and sort them ========================================== 47 | [hAxis, args, nargs] = axescheck(varargin{:}); 48 | 49 | if nargs < 3 || nargs > 4 50 | error('Requires 3 or 4 data arguments.') 51 | elseif nargs == 3 52 | [az, el, prn] = deal(args{1:3}); 53 | line_style = 'auto'; 54 | else 55 | [az, el, prn, line_style] = deal(args{1:4}); 56 | end 57 | 58 | if ischar(az) || ischar(el) || ischar(prn) 59 | error('AZ and EL must be numeric.'); 60 | end 61 | 62 | if ~isequal(size(az), size(el)) 63 | error('AZ and EL must be same size.'); 64 | end 65 | 66 | %% Prepare axis =========================================================== 67 | hAxis = newplot(hAxis); 68 | 69 | %--- Get x-axis text color so grid is in same color ----------------------- 70 | tc = get(hAxis, 'xcolor'); 71 | 72 | hold(hAxis, 'on'); 73 | 74 | %--- Plot white background ------------------------------------------------ 75 | rectangle('position', [-90, -90, 180, 180], ... 76 | 'Curvature', [1 1], ... 77 | 'facecolor', 'white', ... 78 | 'edgecolor', tc); 79 | 80 | %% Plot spokes ============================================================ 81 | 82 | %--- Find spoke angles ---------------------------------------------------- 83 | % Only 6 lines are needed to divide circle into 12 parts 84 | th = (1:6) * 2*pi / 12; 85 | 86 | %--- Convert spoke end point coordinate to Cartesian system --------------- 87 | cst = cos(th); snt = sin(th); 88 | cs = [cst; -cst]; 89 | sn = [snt; -snt]; 90 | 91 | %--- Plot the spoke lines ------------------------------------------------- 92 | line(90*sn, 90*cs, 'linestyle', ':', 'color', tc, 'linewidth', 0.5, ... 93 | 'handlevisibility', 'off'); 94 | 95 | %% Annotate spokes in degrees ============================================= 96 | rt = 1.1 * 90; 97 | 98 | for i = 1:max(size(th)) 99 | 100 | %--- Write text in the first half of the plot ------------------------- 101 | text(rt*snt(i), rt*cst(i), int2str(i*30), ... 102 | 'horizontalalignment', 'center', 'handlevisibility', 'off'); 103 | 104 | if i == max(size(th)) 105 | loc = int2str(0); 106 | else 107 | loc = int2str(180 + i*30); 108 | end 109 | 110 | %--- Write text in the opposite half of the plot ---------------------- 111 | text(-rt*snt(i), -rt*cst(i), loc, ... 112 | 'handlevisibility', 'off', 'horizontalalignment', 'center'); 113 | end 114 | 115 | %% Plot elevation grid ==================================================== 116 | 117 | %--- Define a "unit" radius circle ---------------------------------------- 118 | th = 0 : pi/50 : 2*pi; 119 | xunit = cos(th); 120 | yunit = sin(th); 121 | 122 | %--- Plot elevation grid lines and tick text ------------------------------ 123 | for elevation = 0 : 15 : 90 124 | elevationSpherical = 90*cos((pi/180) * elevation); 125 | 126 | line(yunit * elevationSpherical, xunit * elevationSpherical, ... 127 | 'lineStyle', ':', 'color', tc, 'linewidth', 0.5, ... 128 | 'handlevisibility', 'off'); 129 | 130 | text(0, elevationSpherical, num2str(elevation), ... 131 | 'BackgroundColor', 'white', 'horizontalalignment','center', ... 132 | 'handlevisibility', 'off'); 133 | end 134 | 135 | %--- Set view to 2-D ------------------------------------------------------ 136 | view(0, 90); 137 | 138 | %--- Set axis limits ------------------------------------------------------ 139 | %save some space for the title 140 | axis([-95 95 -90 101]); 141 | 142 | %% Transform elevation angle to a distance to the center of the plot ------ 143 | elSpherical = 90*cos(el * pi/180); 144 | 145 | %--- Transform data to Cartesian coordinates ------------------------------ 146 | yy = elSpherical .* cos(az * pi/180); 147 | xx = elSpherical .* sin(az * pi/180); 148 | 149 | %% Plot data on top of the grid =========================================== 150 | 151 | if strcmp(line_style, 'auto') 152 | %--- Plot with "default" line style ----------------------------------- 153 | hpol = plot(hAxis, xx', yy', '.-'); 154 | else 155 | %--- Plot with user specified line style ------------------------------ 156 | % The same line style and color will be used for all satellites 157 | hpol = plot(hAxis, xx', yy', line_style); 158 | end 159 | 160 | %--- Mark the last position of the satellite ------------------------------ 161 | plot(hAxis, xx(:,end)', yy(:,end)', 'o', 'MarkerSize', 7); 162 | 163 | %--- Place satellite PRN numbers at the latest position ------------------- 164 | for i = 1:length(prn) 165 | if(prn(i) ~= 0) 166 | % The empthy space is used to place the text a side of the last 167 | % point. This solution results in constant offset even if a zoom 168 | % is used. 169 | text(xx(i, end), yy(i, end), [' ', int2str(prn(i))], 'color', 'b'); 170 | end 171 | end 172 | 173 | %--- Make sure both axis have the same data aspect ratio ------------------ 174 | axis(hAxis, 'equal'); 175 | 176 | %--- Switch off the standard Cartesian axis ------------------------------- 177 | axis(hAxis, 'off'); 178 | -------------------------------------------------------------------------------- /inc/twos_comp_2_dec.m: -------------------------------------------------------------------------------- 1 | function intNumber = twos_comp_2_dec(binaryNumber) 2 | % TWOSCOMP2DEC(binaryNumber) Converts a two's-complement binary number 3 | % BINNUMBER (in Matlab it is a string type), represented as a row vector of 4 | % zeros and ones, to an integer. 5 | % 6 | %intNumber = twosComp2dec(binaryNumber) 7 | 8 | %-------------------------------------------------------------------------- 9 | % SoftGNSS v3.0 10 | % 11 | % Copyright (C) Darius Plausinaitis 12 | % Written by Darius Plausinaitis 13 | %-------------------------------------------------------------------------- 14 | %This program is free software; you can redistribute it and/or 15 | %modify it under the terms of the GNU General Public License 16 | %as published by the Free Software Foundation; either version 2 17 | %of the License, or (at your option) any later version. 18 | % 19 | %This program is distributed in the hope that it will be useful, 20 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | %GNU General Public License for more details. 23 | % 24 | %You should have received a copy of the GNU General Public License 25 | %along with this program; if not, write to the Free Software 26 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 27 | %USA. 28 | %-------------------------------------------------------------------------- 29 | 30 | % CVS record: 31 | % $Id: twosComp2dec.m,v 1.1.2.4 2006/08/14 11:38:22 dpl Exp $ 32 | 33 | %--- Check if the input is string ----------------------------------------- 34 | if ~isstr(binaryNumber) 35 | error('Input must be a string.') 36 | end 37 | 38 | %--- Convert from binary form to a decimal number ------------------------- 39 | intNumber = bin2dec(binaryNumber); 40 | 41 | %--- If the number was negative, then correct the result ------------------ 42 | if binaryNumber(1) == '1' 43 | intNumber = intNumber - 2^size(binaryNumber, 2); 44 | end 45 | -------------------------------------------------------------------------------- /init.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | %%%================== GNSS SDR ===========================%%% 10 | % 11 | % @brief: Software Defined GNSS Receiver Software Model 12 | % @date: 11-19-2021 13 | %%%=======================================================%%% 14 | 15 | %%% Clean up the environment first 16 | clear; close all; clc; clear init_settings; 17 | 18 | %%% Formats 19 | format ('compact'); 20 | format ('long', 'g'); 21 | 22 | %%% Global variables 23 | global printDebugInfo; 24 | global swName; 25 | swName = 'JohnBagshawGNSS_SDR_v0.1'; 26 | printDebugInfo = 0; 27 | 28 | %%% Add all folders and sub folders in current path. 29 | addpath(genpath('.')); 30 | 31 | %%% Print startup 32 | print_string("==============================================================="); 33 | print_string(['Welcome! MATLAB Reference Software-Defined Radio: ', swName]); 34 | print_string("==============================================================="); 35 | 36 | % Do parameter configuration. 37 | sdrParams = config_sdr_params(); 38 | 39 | 40 | % Do multiple data stream processing (offline) 41 | % TODO: support real-time. 42 | 43 | while sdrParams.stateParams.numFilesProcessed < ... 44 | sdrParams.stateParams.numFilesToProcess 45 | 46 | print_string('-----------------------------------------------------------------'); 47 | print_string(['Data processing started for file: ',... 48 | sdrParams.stateParams.fileNames{sdrParams.stateParams.numFilesProcessed+1}]); 49 | print_string('-----------------------------------------------------------------'); 50 | 51 | 52 | % Reset frame counter 53 | sdrParams.stateParams.currFrameNum = 0; 54 | 55 | %%% pre-processing per input data file. 56 | [ppData, rxData] = pre_process(sdrParams); 57 | 58 | %%% Data frame processing 59 | while sdrParams.stateParams.currFrameNum < ... 60 | sdrParams.stateParams.numTotalFrames 61 | 62 | %%% Processing 63 | % Process function is based on processing of data frames per 64 | % currently processing file. 65 | processResults = process(sdrParams, ppData, rxData); 66 | 67 | %%% post-processing 68 | % Post processing is done for data frames with one time final 69 | % global post processing managed internally. 70 | postProcessResults = post_process(sdrParams, rxData, processResults); 71 | 72 | sdrParams.stateParams.currFrameNum = ... 73 | sdrParams.stateParams.currFrameNum + 1; 74 | end 75 | 76 | 77 | %%% Plotting the results. 78 | plot_acquisition_results(sdrParams, postProcessResults); 79 | 80 | 81 | %%% Save results to configured file format. 82 | save_acquisition_resutls(sdrParams, postProcessResults); 83 | 84 | 85 | print_string('-----------------------------------------------------------------'); 86 | print_string(['Data processing completed for file: ',... 87 | sdrParams.stateParams.fileNames{sdrParams.stateParams.numFilesProcessed+1}]); 88 | print_string('-----------------------------------------------------------------'); 89 | 90 | % Next file. 91 | sdrParams.stateParams.numFilesProcessed = ... 92 | sdrParams.stateParams.numFilesProcessed + 1; 93 | 94 | end 95 | print_string("============================"); 96 | print_string("Program Completed. Good Bye!"); 97 | print_string("============================"); 98 | 99 | -------------------------------------------------------------------------------- /plots/figure_scripts/List_of_figures_and_instructions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnBagshaw/Fast_GNSS_ReceiverMATLAB/55882581c43cea1f3a4546d3c6ab70840da5c6e4/plots/figure_scripts/List_of_figures_and_instructions.pdf -------------------------------------------------------------------------------- /plots/figure_scripts/Scurve.m: -------------------------------------------------------------------------------- 1 | function s_curve() 2 | % SCURVE.M, Henrik Have Lindberg, 2006.03.16 3 | 4 | %CVS record: 5 | %$Id: Scurve.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 6 | 7 | cp = -2:0.01:2; 8 | 9 | % CA code 10 | 11 | corr = fxcorr(cp); 12 | 13 | figure(1) 14 | plot(cp,corr); 15 | hold on 16 | 17 | spacing = 0.5; 18 | 19 | EmL = fxcorr(cp+spacing/2)-fxcorr(cp-spacing/2); 20 | 21 | plot(cp,EmL); 22 | hold off 23 | axis([-2,2,-1.55, 1.55]) 24 | xlabel('Code Offset [chips]'); 25 | ylabel('Discriminator output/correlation'); 26 | legend ('Correlation','Discriminator\newline output'); 27 | %postPlot(get(gca,'children'), 1.5); 28 | 29 | 30 | % BOC(1,1) code 31 | 32 | corrBOC11 = fxcorrBOC11(cp); 33 | 34 | figure(2) 35 | plot(cp,corrBOC11) 36 | hold on 37 | 38 | EmLBOC11 = fxcorrBOC11(cp+spacing/2)-fxcorrBOC11(cp-spacing/2); 39 | 40 | plot(cp,EmLBOC11); 41 | hold off 42 | axis([-2,2,-1.55, 1.55]) 43 | xlabel('Code Offset [chips]'); 44 | ylabel('Discriminator output/correlation'); 45 | legend ('Correlation','Discriminator\newline output'); 46 | %postPlot(get(gca,'children'), 1.5); 47 | 48 | % fxcorr.m, HHL, 2006.03.16 49 | % 50 | % just CA code for now... 51 | 52 | function correlation = fxcorr(codephase) 53 | 54 | abs_codephase = abs(codephase); 55 | 56 | for i=1:length(codephase) 57 | if abs_codephase(i)<1, 58 | correlation(i) = abs(1-abs_codephase(i)); 59 | else 60 | correlation(i) = 0; 61 | end 62 | end 63 | 64 | 65 | % fxcorrBOC11.m, HHL, 2006.03.16 66 | % 67 | % just BOC(1,1) code for now... 68 | 69 | function correlation = fxcorrBOC11(codephase) 70 | 71 | abs_codephase = abs(codephase); 72 | 73 | for i=1:length(codephase) 74 | if abs_codephase(i)<1, 75 | k = ceil(2*1*abs_codephase(i)); 76 | correlation(i) = ((-1)^(k+1))*(-k^2+3*k-1-(5-2*k)*abs_codephase(i)); 77 | else 78 | correlation(i) = 0; 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /plots/figure_scripts/Tri.m: -------------------------------------------------------------------------------- 1 | function tri = Tri(x) 2 | %-------------------------------------------------------------------------- 3 | % Written by Darius Plausinaitis 4 | %-------------------------------------------------------------------------- 5 | 6 | %CVS record: 7 | %$Id: Tri.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 8 | tri = (1 - abs(x)) .* ((1 - abs(x)) > 0); 9 | -------------------------------------------------------------------------------- /plots/figure_scripts/boc.m: -------------------------------------------------------------------------------- 1 | % Demonstration of BOC modulation principle 2 | 3 | % February 13, 2006 4 | 5 | % Written by Kai Borre and Darius Plasinaitis 6 | phi = linspace(0,12,600); 7 | 8 | figure(1); 9 | %Spreading Code 10 | chips = [ones(1, 400), -1 * ones(1, 200)]; 11 | % plot(phi, data +7) 12 | plot(phi, chips +10) 13 | hold on 14 | 15 | %Sub-carrier 16 | subCarrier = square(phi*pi); 17 | % plot(phi, subCarrier+4) 18 | plot(phi, subCarrier +7) 19 | 20 | %BOC signal, no carrier 21 | plot(phi, (subCarrier.*chips) +4) 22 | 23 | %Carrier wave 24 | carrier = sin(phi*4*pi); 25 | plot(phi, carrier+1) 26 | 27 | %Modulated Carrier Phase 28 | signal = carrier.* subCarrier .* chips; 29 | plot(phi, signal - 2) 30 | axis([-1 13 -4 11]) 31 | axis off 32 | hold off 33 | 34 | print -deps boc 35 | %%%%%%%%%%%%%% boc.m %%%%%%%%%%%%%%%%%%%% 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /plots/figure_scripts/ca_codes.m: -------------------------------------------------------------------------------- 1 | clear all 2 | 3 | % ***** Generates C/A codes ***** 4 | PRN_1 = PRNgen(1); % SVN 1 5 | PRN_2 = PRNgen(2); % SVN 2 6 | 7 | PRN1 = [PRN_1';PRN_1']; 8 | PRN2 = [PRN_2';PRN_2']; 9 | 10 | % ***** Calculate auto- and cross-correlation ***** 11 | for n=1:1023 12 | Corr11(n) = PRN1(1:1023)'*PRN1(n:1022+n); 13 | Corr12(n) = PRN1(1:1023)'*PRN2(n:1022+n); 14 | end 15 | 16 | figure(1) 17 | subplot(121) 18 | plot(Corr11); 19 | axis([-50 1100 -100 1100]); 20 | %title('Autocorrelation of a PRN sequence'); 21 | set(gca,'Fontsize',16) 22 | ylabel('\itr_{kk}','Fontsize',16) 23 | xlabel('Lag','Fontsize',16) 24 | box off 25 | 26 | 27 | subplot(122) 28 | plot(Corr12); 29 | axis([-50 1100 -100 1100]); 30 | %title('Cross-correlation between two PRN sequences'); 31 | set(gca,'Fontsize',16) 32 | ylabel('\itr_{ik}','Fontsize',16) 33 | xlabel('Lag','Fontsize',16) 34 | box off 35 | 36 | print -deps2 ca-code-correlation 37 | %%%%%%%%%%%%%%%% end ca_codes.m %%%%%%%%%%%%%%% 38 | -------------------------------------------------------------------------------- /plots/figure_scripts/discr.m: -------------------------------------------------------------------------------- 1 | %Correlation and Discriminator functions 2 | 3 | % Kai Borre, February 12, 2006 4 | 5 | syms t 6 | 7 | T = 1; 8 | d = 1; 9 | 10 | delta = 1; 11 | figure(1); 12 | hold on 13 | t1 = ezplot(R(-1+t+d/2,T)); 14 | t2 = ezplot(R(-1+t+delta+d/2,T)); 15 | t3 = ezplot(-R(-1+t+d/2,T)+R(-1+t+delta+d/2,T)); 16 | line([-1.6 1.6], [0 0]) 17 | xlabel('Time Delay [chip]','FontSize',24) 18 | ylabel('Correlation','FontSize',24) 19 | set(t3,'color','k','linewidth',1.5) 20 | axis([-1.6 1.6 -.5 1.5]) 21 | axis equal 22 | set(gca,'FontSize',24) 23 | title('') 24 | hold off 25 | print -deps2 corrd1 26 | 27 | delta = .5; 28 | figure(2); 29 | hold on 30 | ezplot(R(-.75+t+d/2,T)); 31 | ezplot(R(-.75+t+delta+d/2,T)); 32 | t32 = ezplot(-R(-.75+t+d/2,T)+R(-.75+t+delta+d/2,T)); 33 | line([-1.5 1.5], [0 0]) 34 | xlabel('Time Delay [chip]','Fontsize',24) 35 | ylabel('Correlation','Fontsize',24) 36 | set(t32,'color','k','linewidth',1.5) 37 | axis([-1.5 1.5 -.5 1.5]) 38 | axis equal 39 | set(gca,'FontSize',24); 40 | title('') 41 | hold off 42 | print -deps2 corrd2 43 | 44 | figure(3); 45 | hold on 46 | h1 = ezplot(R(t+d/2,T) - R(t-d/2,T)); % coherent discriminator 47 | set(h1,'color','k','linewidth',1) 48 | h2 = ezplot((R(t+d/2,T))^2 - (R(t-d/2,T))^2); % non-coherent product discriminato 49 | set(h2,'color','k','linewidth',1,'linestyle','--') 50 | xlabel('Time Delay [chip]','FontSize',18) 51 | title('') 52 | axis equal 53 | set(gca,'FontSize',18); 54 | grid on 55 | print -deps2 discr 56 | 57 | %%%%%%%%%%%%%%%% discr.m %%%%%%%%%%%%%%%%%% 58 | -------------------------------------------------------------------------------- /plots/figure_scripts/env.m: -------------------------------------------------------------------------------- 1 | %Multipath envelope, 2 | % delta is geometric multipath and t is multipath error 3 | % Implementation of equation (180) and (18) in 4 | % Bernd Eissfeller (1997): Ein dynamisches Fehlermodell 5 | % f\"ur GPS Autokorrelationsempf\"anger. Universit\"at der 6 | % Bundeswehr M\"unchen, Heft 55 7 | 8 | % Kai Borre, February 27, 2005 9 | 10 | syms delta t 11 | 12 | T = 300; %1; % chip length 13 | alpha = .5; % signal-to-multipath ratio (SMR) 14 | 15 | d = 300; %1; % correlator spacing 16 | h1 = ezplot( R(t-d/2,T) - R(t+d/2,T) + alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 17 | hold on 18 | h2 = ezplot( R(t-d/2,T) - R(t+d/2,T) - alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 19 | set([h1, h2],'linewidth',1.8') 20 | 21 | d = 150; %.5; % changed correlator spacing 22 | h3 = ezplot( R(t-d/2,T) - R(t+d/2,T) + alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 23 | h4 = ezplot( R(t-d/2,T) - R(t+d/2,T) - alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 24 | set([h3,h4], 'linewidth',1.3) 25 | 26 | 27 | d = 30; %.1; % changed correlator spacing 28 | h5 = ezplot( R(t-d/2,T) - R(t+d/2,T) + alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 29 | h6 = ezplot( R(t-d/2,T) - R(t+d/2,T) - alpha*( R(t-delta-d/2,T)- R(t-delta+d/2,T) ), [0,500,-80,80]); 30 | set([h5,h6], 'linewidth',.8) 31 | 32 | title('') 33 | ylabel('Ranging error {\it\tau} [m]','fontsize',18) 34 | xlabel('Multipath delay {\it\delta} [m]','fontsize',18) 35 | hold off 36 | legend([h1, h3, h5],'{\itd} =1.0','{\itd} = 0.5','{\itd} = 0.1') 37 | set(gca,'Fontsize',18) 38 | box off 39 | legend('boxoff') 40 | 41 | print -deps2 env 42 | %%%%%%%%%%%%%%%%%%% env.m %%%%%%%%%%%%%%% 43 | -------------------------------------------------------------------------------- /plots/figure_scripts/fig41.m: -------------------------------------------------------------------------------- 1 | % Frequency domain depiction of the GPS signal and thermal noise 2 | % power 3 | 4 | %Written by Kai Borre, October 1, 2005 5 | 6 | f0 = 1.023e6; 7 | delta_f = linspace(-15e6, 15e6, 999); % difference with respect to f0 8 | noise_floor = 2*randn(1,length(delta_f))+db(2*1e-6); 9 | 10 | figure(1); 11 | h1 = plot(delta_f.*pi/2, db( (sinc(pi*delta_f/(2*f0))).^2./f0),... 12 | 'linewidth', 1.5,'linestyle','-'); 13 | hold on 14 | h2 = plot(delta_f.*pi/2, noise_floor,'linewidth',1.5,'linestyle','--'); 15 | hleg = legend('GPS C/A','Noise floor, 2 MHz BW'); 16 | set(gca,'XTick',[-5*1e6 -1e6 0 1e6 5*1e6]) 17 | set(gca,'XTickLabel',{'-5','-1','0','1','5'}) 18 | axis([-5*f0 5*f0 -190 -80]); 19 | set(gca,'FontSize',16) 20 | hold off 21 | ylabel('Power [dBm]') 22 | xlabel('Frequency [MHz]') 23 | box off 24 | legend('boxoff') 25 | %title('Center Frequency 1575.42 MHz') 26 | 27 | print -deps2 fig4_1 28 | %%%%%%%%%%%%%%%%%%%%%% end fig41.m %%%%%%%%%%%%%%% 29 | 30 | 31 | -------------------------------------------------------------------------------- /plots/figure_scripts/fig47.m: -------------------------------------------------------------------------------- 1 | % Frequency domain depiction of the GPS signal and thermal noise 2 | % power 3 | 4 | %Written by Kai Borre, October 1, 2005 5 | 6 | f0 = 1.023e6; 7 | delta_f = linspace(-15e6, 15e6, 999); % difference with respect to f0 8 | noise_floor = 2*randn(1,length(delta_f))+db(2*1e-6); 9 | 10 | % Interpolation for graph for RF filter 11 | x = [-3*f0 0 3*f0]; 12 | y = [-140 -115 -140]; 13 | xi = f0*(-3:.5:3); 14 | yi = interp1(x,y,xi,'cubic'); 15 | 16 | figure(1); 17 | h1 = plot(delta_f.*pi/2, db( (sinc(pi*delta_f/(2*f0))).^2./f0),... 18 | 'linewidth',1.5,'linestyle','-'); 19 | hold on 20 | h2 = plot(delta_f.*pi/2, noise_floor,'linewidth',... 21 | 1.5,'linestyle','--'); 22 | h3 = plot(xi,yi,':','linewidth',1); 23 | legend('GPS C/A','Noise floor, 2 MHz BW','RF filter') 24 | plot([0 3*f0], [-120 -120],'linewidth',1,'linestyle',':'); 25 | text(3.1*f0,-120,'{\it\approx -n} dBm'); 26 | text(3.1*f0,-126,'(> -120 dBm)'); 27 | set(gca,'XTick',[-5*1e6 -1e6 0 1e6 5*1e6]) 28 | set(gca,'XTickLabel',{'-5','-1','0','1','5'}) 29 | axis([-5*f0 5*f0 -190 -70]); 30 | set(gca,'FontSize',16) 31 | hold off 32 | ylabel('Power [dBm]') 33 | xlabel('Frequency [MHz]') 34 | %title('Center Frequency 1575.42 MHz') 35 | box off 36 | legend('boxoff') 37 | 38 | print -deps2 fig4_7 39 | %%%%%%%%%%%%%%%%%%%%%% end fig47.m %%%%%%%%%%%%%%% 40 | 41 | 42 | -------------------------------------------------------------------------------- /plots/figure_scripts/figure_7_dll.m: -------------------------------------------------------------------------------- 1 | %-------------------------------------------------------------------------- 2 | % Copyright (C) Darius Plausinaitis 3 | % Written by Darius Plausinaitis 4 | %-------------------------------------------------------------------------- 5 | 6 | % CVS record: 7 | % $Id: figure_7_dll.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 8 | 9 | 10 | t = linspace(-1.5, 1.5, 400); 11 | 12 | delay = 0.5; 13 | 14 | coherent = Tri(t-delay) - Tri(t+delay); 15 | power = Tri(t-delay).^2 - Tri(t+delay).^2; 16 | normPower = (Tri(t-delay).^2 - Tri(t+delay).^2) ./ (Tri(t-delay).^2 + Tri(t+delay).^2); 17 | dotProduct = Tri(t) .* (Tri(t-delay) - Tri(t+delay)); 18 | 19 | %Plot results. The sign of results is inverted to have a "mirrored version" 20 | %of the results. This is because in the code negative t values mean advance 21 | %in time, while negative values in x axis mean delay of the code. 22 | plot(t, -dotProduct); 23 | hold on 24 | plot(t, -normPower); 25 | plot(t, -power); 26 | plot(t, -coherent); 27 | hold off 28 | 29 | legend('Dot product', 'Normalized power', 'Power', 'Coherent'); 30 | xlabel('True offset of the two codes [chips]'); 31 | ylabel('DLL discriminator output'); 32 | 33 | postPlot(get(gca, 'children'), 1); 34 | 35 | axis ([-1.5, 1.5, -1.1, 1.1]); -------------------------------------------------------------------------------- /plots/figure_scripts/figure_7_pll.m: -------------------------------------------------------------------------------- 1 | %-------------------------------------------------------------------------- 2 | % Copyright (C) Darius Plausinaitis 3 | % Written by Darius Plausinaitis 4 | %-------------------------------------------------------------------------- 5 | 6 | % CVS record: 7 | % $Id: figure_7_pll.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 8 | 9 | t = linspace(-180, 180, 400); 10 | phi = linspace(-pi, pi, 400); 11 | 12 | delay = 0.5; 13 | 14 | arctan=atan(sin(phi)./cos(phi)); 15 | dotProduct = sin(phi).*cos(phi); 16 | signProduct = sin(phi).*sign(cos(phi)); 17 | % arctan2=atan2(sin(phi),cos(phi)); 18 | 19 | plot(t, arctan * (180/pi)); 20 | hold on 21 | plot(t, signProduct * (180/pi)); 22 | plot(t, dotProduct * (180/pi)); 23 | % plot(t, arctan2 * (180/pi)); 24 | hold off 25 | 26 | legend('arctan({\itQ}/{\itI})', 'sign({\itI})*{\itQ}', '{\itI}*{\itQ}'); 27 | xlabel('True phase error [\circ]'); 28 | ylabel('Discriminator output [\circ]') 29 | postPlot(get(gca, 'children'), 1); 30 | axis ([-180, 180, -100, 100]); 31 | -------------------------------------------------------------------------------- /plots/figure_scripts/geoid.m: -------------------------------------------------------------------------------- 1 | % Script for Figure 14.6: The WGS84 geoid 2 | % The data in heights can be found in Table 6.1 in 3 | % Department of Defense World Geodetic System 1984 4 | % Its Definition and Relationships with Local Geodetic Systems. 5 | % DMA Technical Report, Second Edition, 1 September 1991 6 | 7 | % We have changed sign of heights(12,8) to get a contour map 8 | % similar to that officially published. 9 | 10 | %Kai Borre 06-22-97 11 | %Copyright (c) by kai Borre 12 | %$Revision: 1.0 $ $Date: 1997/09/22 $ 13 | 14 | heights = [ ... 15 | 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13,... 16 | 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13; 17 | 33 34 28 23 17 13 9 4 4 1 -2 -2 0 2 3 2 1 1,... 18 | 3 1 -2 -3 -3 -3 -1 3 1 5 9 11 19 27 31 34 33 34; 19 | 51 43 29 20 12 5 -2 -10 -14 -12 -10 -14 -12 -6 -2 3 6 4,... 20 | 2 2 1 -1 -3 -7 -14 -24 -27 -25 -19 3 24 37 47 60 61 58; 21 | 47 41 21 18 14 7 -3 -22 -29 -32 -32 -26 -15 -2 13 17 19 6,... 22 | 2 9 17 10 13 1 -14 -30 -39 -46 -42 -21 6 29 49 65 60 57; 23 | 47 48 42 28 12 -10 -19 -33 -43 -42 -43 -29 -2 17 23 22 6 2,... 24 | -8 8 8 1 -11 -19 -16 -18 -22 -35 -40 -26 -12 24 45 63 62 59; 25 | 52 48 35 40 33 -9 -28 -39 -48 -59 -50 -28 3 23 37 18 -1 -11,... 26 | -12 -10 -13 -20 -31 -34 -21 -16 -26 -34 -33 -35 -26 2 33 59 52 51; 27 | 36 28 29 17 12 -20 -15 -40 -33 -34 -34 -28 7 29 43 20 4 -6,... 28 | -7 -5 -8 -15 -28 -40 -42 -29 -22 -26 -32 -51 -40 -17 17 31 34 44; 29 | 31 26 15 6 1 -29 -44 -61 -67 -59 -36 -11 21 39 49 39 22 10,... 30 | 5 10 7 -7 -23 -39 -47 -34 -9 -10 -20 -45 -48 -32 -9 17 25 31; 31 | 22 23 2 -3 -7 -36 -59 -90 -95 -63 -24 12 53 60 58 46 36 26,... 32 | 13 12 11 2 -11 -28 -38 -29 -10 3 1 -11 -41 -42 -16 3 17 33; 33 | 18 12 -13 -9 -28 -49 -62 -89 -102 -63 -9 33 58 73 74 63 50 32,... 34 | 22 16 17 13 1 -12 -23 -20 -14 -3 14 10 -15 -27 -18 3 12 20; 35 | 12 13 -2 -14 -25 -32 -38 -60 -75 -63 -26 0 35 52 68 76 64 52,... 36 | 36 22 11 6 -1 -8 -10 -8 -11 -9 1 32 4 -18 -13 -9 4 14; 37 | 17 23 21 8 -9 -10 -11 -20 -40 -47 -45 -25 5 23 45 58 57 63,... 38 | 51 27 10 0 -9 -11 -5 -2 -3 -1 9 35 20 -5 -6 -5 0 13; 39 | 22 27 34 29 14 15 15 7 -9 -25 -37 -39 -23 -14 15 33 34 45,... 40 | 46 22 5 -2 -8 -13 -10 -7 -4 1 9 32 16 4 -8 4 12 15; 41 | 18 26 31 33 39 41 30 24 13 -2 -20 -32 -33 -27 -14 -2 5 20,... 42 | 21 6 1 -7 -12 -12 -12 -10 -7 -1 8 23 15 -2 -6 6 21 24; 43 | 25 26 34 39 45 45 38 39 28 13 -1 -15 -22 -22 -18 -15 -14 -10,... 44 | -15 -18 -18 -16 -17 -15 -10 -10 -8 -2 6 14 13 3 3 10 20 27; 45 | 16 19 25 30 35 35 33 30 27 10 -2 -14 -23 -30 -33 -29 -35 -43,... 46 | -45 -43 -37 -32 -30 -26 -23 -22 -16 -10 -2 10 20 20 21 24 22 17; 47 | 16 16 17 21 20 26 26 22 16 10 -1 -16 -29 -36 -46 -55 -54 -59,... 48 | -61 -60 -61 -55 -49 -44 -38 -31 -25 -16 -6 1 4 5 4 2 6 12; 49 | -4 -1 1 4 4 6 5 4 2 -6 -15 -24 -33 -40 -48 -50 -53 -52,... 50 | -53 -54 -55 -52 -48 -42 -38 -38 -29 -26 -26 -24 -23 -21 -19 -16 -12 -8; 51 | -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30,... 52 | -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30 -30]; 53 | 54 | e = exist('geoid.eps'); 55 | if e ~= 0, delete('geoid.eps'), end 56 | 57 | heights = [heights heights(:,1)]; 58 | height = flipud(heights); 59 | [X,Y] = meshgrid(0:10:360,0:10:180); 60 | Z = height; 61 | [XI,YI]= meshgrid(0:360,0:180); 62 | ZI = interp2(X,Y,Z,XI,YI,'cubic'); 63 | 64 | % contour of continents 65 | set(gca,'visible','off','nextplot','add',... 66 | 'plotboxaspectratio',[1 .5 1]); 67 | load topo 68 | to = ones(size(topo)); % all sea 69 | to(find(topo > 0)) = 2; % sea to land 70 | colormap([1 1 1; .8 .8 .8]); % white for sea, and gray for land 71 | image(to); 72 | axis xy 73 | axis image 74 | %[c,ht] = contour(topo,[0 0]); 75 | % set(ht,'linewidth',1.5); 76 | 77 | % contour of geoid 78 | [c,hand]= contour(ZI); 79 | clabel(c,hand); 80 | 81 | print geoid -deps 82 | %%%%%%%%%%%%% end geoid.m %%%%%%%%%%%%%%%%%%%%%% 83 | -------------------------------------------------------------------------------- /plots/figure_scripts/gps_sig.m: -------------------------------------------------------------------------------- 1 | % Demonstration of BPSK modulation 2 | % and demodulation 3 | 4 | % Kai Borre February 26, 2005 5 | % Edited March 11, 2006; June 3, 2006 6 | 7 | PRN_1 = PRNgen(1); % SVN 1 8 | len = 25; 9 | code = PRN_1(1:len); % first 25 chips for PRN1 10 | 11 | x = zeros(1,len); % creating a rectangular graph 12 | y = x; 13 | t = 1:len; 14 | x(1) = .5; 15 | y(1) = code(1); 16 | s = 2; 17 | for i = 2:len 18 | if code(i) ~= code(i-1) 19 | x(s) = t(i)+.5; 20 | x(s+1) = x(s); 21 | y(s) = code(i-1); 22 | y(s+1) = code(i); 23 | s = s+2; 24 | end 25 | end 26 | 27 | x2 = [.5 18.5 18.5 len-.5]; % navigation data 28 | y2 = [1 1 -1 -1]; 29 | 30 | navy = y; % Chip times nav 31 | ch = find(x>=18.5); 32 | navy(ch) = -navy(ch); 33 | 34 | x3 = x; 35 | x3(16:17) = []; % crude corrections 36 | navy3 = navy; 37 | navy3(16:17) = []; 38 | 39 | phi = linspace(0,len,10*len); % carrier wave 40 | ycar = sin(phi*2*pi); 41 | code1 = [ 1 code(1:end-1)]; 42 | code1(19:25) = -code1(19:25); % crude correction 43 | phim = ycar.*kron(code1,ones(1,10)); %phimod; %modulated carrier phase 44 | 45 | figure(1); 46 | plot(x,y+4, x2, y2+1, x3,-navy3-2,... 47 | phi+0.5,ycar-5, phi+0.5,phim-8,'linewidth',1) 48 | axis([-5 len+5 -9.5 5.5]) 49 | axis off 50 | 51 | print -deps2 gps_sig 52 | 53 | figure(2); 54 | plot(phi+0.5,phim-2, x3,navy3-5, phi+0.5,ycar-8,'linewidth',1); 55 | axis([-5 len+5 -9.5 5.5]) 56 | axis off 57 | 58 | print -deps2 gps_demod 59 | 60 | %%%%%%%%%%%%%%%%%% gps_sig.m %%%%%%%%%%%%% 61 | 62 | 63 | -------------------------------------------------------------------------------- /plots/figure_scripts/m_discr.m: -------------------------------------------------------------------------------- 1 | % Constructive and destructive multipath interference 2 | 3 | % Kai Borre, March 27, 2006 4 | 5 | syms delta t 6 | 7 | T = 1; % chip length 8 | d = 1; % correlator spacing 9 | alpha = .3; % amplitude of multipath signal 10 | 11 | figure(1); % constructive interference 12 | hold on 13 | h1 = ezplot( R(t-d/2,T)); 14 | h2 = ezplot(alpha*R(-.6+t-d/2,T)); 15 | h3 = ezplot(R(t-d/2,T)+alpha*R(-.6+t-d/2,T)); %- 16 | line([-1 2.5], [ 0 0]); 17 | set(h2,'linestyle', '--'); 18 | set(h3,'linewidth', 1.5); 19 | xlabel('Time delay [chip]','FontSize',24) 20 | set(gca,'FontSize',24) 21 | title(''); 22 | axis([-1 2.5 -.3 1.3]) 23 | hold off 24 | print -deps2 m_discr1 25 | 26 | figure(2); % destructive interference 27 | hold on 28 | h1 = ezplot( R(t-d/2,T)); 29 | h2 = ezplot(-alpha*R(-.6+t-d/2,T)); 30 | h3 = ezplot(R(t-d/2,T)-alpha*R(-.6+t-d/2,T)); %+ 31 | line([-1 2.5], [ 0 0]); 32 | set(h2,'linestyle', '--'); 33 | set(h3,'linewidth', 1.5); 34 | xlabel('Time delay [chip]','FontSize',24) 35 | set(gca,'FontSize',24) 36 | title(''); 37 | axis([-1 2.5 -1 1.3]) 38 | hold off 39 | print -deps2 m_discr2 40 | 41 | %%%%%%%%%%%%%%%%%%%%% end m_discr.m %%%%%%%%%%%% 42 | -------------------------------------------------------------------------------- /plots/figure_scripts/multi_env_boc.m: -------------------------------------------------------------------------------- 1 | function multi_env_boc() 2 | %-------------------------------------------------------------------------- 3 | % Copyright (C) Darius Plausinaitis and Kai Borre 4 | % Written by Darius Plausinaitis and Kai Borre 5 | %-------------------------------------------------------------------------- 6 | 7 | % CVS record: 8 | % $Id: multEnvBOC.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 9 | 10 | maxDelay = 500; %[m] 11 | maxOffset = 80; %[m] 12 | 13 | lcode = 299.792458; %code length in meters 14 | 15 | alpha = 0.5; 16 | 17 | d = 1; %chip spacing 18 | 19 | h(1) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, -alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 20 | 21 | hold on 22 | 23 | h(2) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 24 | 25 | d = 0.5; %chip spacing 26 | 27 | h(3) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, -alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 28 | h(4) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 29 | 30 | d = 0.1; %chip spacing 31 | 32 | h(5) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, -alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 33 | h(6) = ezplot(@(delay, offset)multipathBOC(offset/lcode, delay/lcode, alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 34 | 35 | 36 | h(7) = ezplot(@(delay, offset)multipathRec(offset/lcode, delay/lcode, -alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 37 | % hold on 38 | h(8) = ezplot(@(delay, offset)multipathRec(offset/lcode, delay/lcode, alpha, d), [0, maxDelay, -maxOffset, maxOffset]); 39 | 40 | hold off 41 | 42 | set(h(1:2), 'LineStyle', '-') 43 | set(h(3:4), 'LineStyle', '--') 44 | set(h(5:6), 'LineStyle', '-.') 45 | set(h(7:8), 'LineStyle', ':') 46 | set(h, 'LineWidth', 1) 47 | 48 | xlabel('Multipath delay \delta [m]') 49 | ylabel('Ranging error \tau [m]') 50 | title('') 51 | box off 52 | legend(h([1,3,5, 7]),'{\itd} = 1','{\itd} = 0.5', '{\itd} = 0.1', '{\itd} = 0.1 (C/A)') 53 | legend boxoff 54 | 55 | function correlation = multipathRec(offset, delay, alpha, d) 56 | 57 | d = d/2; 58 | 59 | earlyCorrelator = (Tri(offset - d) + alpha * Tri(offset - d - delay)); 60 | lateCorrelator = (Tri(offset + d) + alpha * Tri(offset + d - delay)); 61 | 62 | correlation = earlyCorrelator - lateCorrelator; 63 | 64 | function correlation = multipathBOC(offset, delay, alpha, d) 65 | 66 | n=2; 67 | 68 | d = d/2; 69 | 70 | earlyCorrelator = (acf_boc(offset - d, n, 1) + alpha * acf_boc(offset - d - delay, n, 1)); 71 | lateCorrelator = (acf_boc(offset + d, n, 1) + alpha * acf_boc(offset + d - delay, n, 1)); 72 | 73 | correlation = earlyCorrelator - lateCorrelator; 74 | 75 | function result = acf_boc(x, n, sin_type) 76 | 77 | result = 0; 78 | 79 | sum = 0; 80 | sign = -1; 81 | r = -1; 82 | 83 | x = x * n * 2.0; 84 | 85 | if (sin_type) r = 1.0; end; 86 | if (mod(n, 2)) sign = 1; end 87 | 88 | for k=1:n-1 89 | sum = sum + sign * (2.0*k*Tri(x-2.0*(n-k)) + r * Tri(x-2.0*(n-k)-1.0) + 2.0 * k * Tri(x+2.0*(n-k)) + r*Tri(x+2.0*(n-k)+1.0)); 90 | sign= -sign; 91 | end 92 | 93 | sum = sum + sign * (2.0*n*Tri(x) + r * Tri(x-1.0) + r * Tri(x +1.0)); 94 | 95 | result = sum/(2*n); 96 | -------------------------------------------------------------------------------- /plots/figure_scripts/plot1.m: -------------------------------------------------------------------------------- 1 | % Generates a stem plot showing the ACF 2 | % for a Gold sequnce 3 | 4 | % Kai Borre, February 12, 2006 5 | 6 | ca_codes 7 | close all 8 | 9 | figure(1) 10 | pl = stem(0:51,[0 Corr11(1:50) 0],'.'); 11 | axis off 12 | set(get(pl,'Baseline'),'linestyle','-') 13 | set(pl,'Markersize',4,'linewidth',1.5) 14 | axis tight 15 | 16 | print -deps2 prn50 17 | %%%%%%%%%%%%%%%%%%%% 18 | 19 | 20 | -------------------------------------------------------------------------------- /plots/figure_scripts/plot_parallel_code_search.m: -------------------------------------------------------------------------------- 1 | %function plotParalCodeSearch(results, frqBins, PRN) 2 | 3 | % The variable "results" is taken from the function "acquisition.m". The 4 | % function is stopped at the point where script has to decide if there is 5 | % the GNSS signal or not (if statement). 6 | % 7 | % This script can be used to plot results of serial search algorithm. The 8 | % serial search source code is not included. 9 | 10 | %-------------------------------------------------------------------------- 11 | % Copyright (C) Darius Plausinaitis 12 | % Written by Darius Plausinaitis 13 | % Based on Peter Rinder and Nicolaj Bertelsen 14 | %-------------------------------------------------------------------------- 15 | 16 | %CVS record: 17 | %$Id: plotParalCodeSearch.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 18 | 19 | [xsize, ysize] = size(results); 20 | 21 | s=surf(1:ysize, frqBins(1:xsize)/1e6, results); 22 | set(s,'EdgeColor','none','Facecolor','interp'); 23 | %xlabel('Code phase\newline [chips]'); 24 | xlabel('Code phase\newline [samples]'); 25 | ylabel('Frequency\newline [MHz]'); 26 | zlabel('Magnitude'); 27 | text=sprintf('PRN %i', PRN); 28 | title(text); 29 | axis tight; 30 | 31 | % view(az, el); 32 | % colormap(fig69colormap); 33 | 34 | % fig69colormap = colormap; 35 | % [az, el] = view; -------------------------------------------------------------------------------- /plots/figure_scripts/plot_parallel_freq_search.m: -------------------------------------------------------------------------------- 1 | % This script plots results of parallel frequency search. The source code 2 | % is not included. The search algorithm requires a lot of computational 3 | % resources and produces a great amount of data, therefore only a smaller 4 | % portion of total results is plotted: 5 | % - the frequency region 5-15 MHz is a subset of the full region 0-19 MHz 6 | % - the code search region is 1-500 chips instead of 1-1023. 7 | 8 | %-------------------------------------------------------------------------- 9 | % Copyright (C) Darius Plausinaitis 10 | % Written by Darius Plausinaitis 11 | % Based on Peter Rinder and Nicolaj Bertelsen 12 | %-------------------------------------------------------------------------- 13 | 14 | %CVS record: 15 | %$Id: plotParalFreqSearch.m,v 1.1.2.2 2006/08/18 12:05:02 dpl Exp $ 16 | 17 | s=surf(5.001:0.001:15.000, 1:500, result); 18 | set(s,'EdgeColor','none'); 19 | ylabel('Code phase\newline [chips]'); 20 | xlabel('Frequency\newline [MHz]'); 21 | zlabel('Magnitude'); 22 | text=sprintf('PRN %i',svnum); 23 | title(text); 24 | ha = gca(); 25 | zlim(ha, myZlim); 26 | axis tight 27 | view(-49,25); 28 | set(ha, 'zminortick', 'on'); 29 | % caxis(ha, myCaxis) 30 | % colormap(ha, myColormap); 31 | 32 | % This is a trick to replace the data inside of the plot. It does not 33 | % update any of the plot properties (axes, limits, view angle etc.). 34 | % 35 | % set(get(gca,'children'),'zdata',result2) 36 | -------------------------------------------------------------------------------- /plots/figure_scripts/psd_boc.m: -------------------------------------------------------------------------------- 1 | % Power PSpectral Density for Galileo BOC(1,1) and GPS C/A 2 | 3 | %Written by Kai Borre, February 14, 2005 4 | 5 | f0 = 1.023e6; 6 | alpha = 1; 7 | beta = 1; 8 | if rem(alpha/beta,2) == 0, 9 | even = 'true'; 10 | else 11 | even = 'false'; 12 | end 13 | 14 | delta_f = linspace(-15e6,15e6,999); % difference with respect to L1 15 | fs = alpha*f0; 16 | fc = beta*f0; 17 | n = 2*fs/fc; 18 | r = rem(n,2); 19 | if r == 0, even = 'true'; end 20 | if even 21 | G = fc* ( tan(pi*delta_f/(2*fs)).*sin(pi*delta_f/fc)./(pi*delta_f+eps) ).^2; 22 | else 23 | G = fc* ( tan(pi*delta_f/(2*fs)).*cos(pi*delta_f/fc)./(pi*delta_f+eps) ).^2; 24 | end 25 | 26 | figure(1); 27 | h1 = semilogy(delta_f,G, 'linewidth',1); % BOC 28 | hold on 29 | h2 = semilogy(delta_f.*pi/2, (sinc(pi*delta_f/(2*fc))).^2./fc,... 30 | 'linewidth', 1,'linestyle','-.'); % GPS 31 | set(gca,'XTick',[-6*1e6 -4*1e6 -2*1e6 0 2*1e6 4*1e6 6*1e6]) 32 | set(gca,'XTickLabel',{'-6','-4','-2','0','2','4','6'}) 33 | 34 | legend('Galileo BOC(1,1)','GPS C/A') 35 | xlabel('Frequency [MHz]','FontSize',18) 36 | ylabel('Power','Fontsize',18) 37 | %title('Center Frequency 1575.42 MHz','FontSize',18) 38 | axis([-6e6 6e6 1e-10 1e-5]); 39 | box off 40 | legend('boxoff') 41 | hold off 42 | set(gca,'FontSize',18) 43 | 44 | print -deps2 psd_boc 45 | %%%%%%%%%%%%%%%%%%%%%% end psd_boc.m %%%%%%%%%%%%%%% 46 | 47 | 48 | -------------------------------------------------------------------------------- /plots/figure_scripts/sincfig.m: -------------------------------------------------------------------------------- 1 | %plot of sinc 2 | % For Figure 1.3 in S/W book 3 | 4 | 5 | t = linspace(-8,8,500); 6 | figure(1); 7 | plot(abs(sinc(t)),'linewidth',2) 8 | hold on 9 | plot([0 500],[0 0]); 10 | axis off 11 | print -deps swsinc1 12 | 13 | figure(2); 14 | plot((sinc(t)).^2,'linewidth',2) 15 | hold on 16 | plot([0 500],[0 0]); 17 | axis off 18 | print -deps swsinc2 19 | 20 | %%%%%%%%%%%%%%%%% 21 | -------------------------------------------------------------------------------- /plots/figure_scripts/winkel4.m: -------------------------------------------------------------------------------- 1 | % The ACF for the BOC signal as function of delay. 2 | % Eq. (2.68) and Figure 2.27 in Jon Winkel (2002). 3 | % The infinite bandwidth and bandlimited cases 4 | 5 | % Written by Kai Borre, February 26, 2005 6 | 7 | no = 300; 8 | t = linspace(-5,5,no); 9 | Tc = 1; 10 | Y = zeros(3,no); 11 | n = 1; 12 | k = 0; 13 | 14 | % infinite bandwidth, b = infty 15 | s = zeros(1,no); 16 | s = ( 2*R(t./Tc-2*k,1) - R(t./Tc-2*k-1,1) -R(t./Tc-2*k+1,1)); 17 | Y(n,:) = s/2; 18 | 19 | % bandlimited cases, b = 1 and 0.5 20 | for i = 1:2 21 | if i == 1, b =1; else b = 0.5; end; 22 | s = ( 2.*R_BL(t./Tc-2.*k,b) - R_BL(t./Tc-2.*k-1,b) -R_BL(t./Tc-2.*k+1,b)); 23 | Y(i+1,:) = s/2; 24 | end 25 | 26 | h1 = plot(t/2,Y(1,:)'); 27 | hold on 28 | h2 = plot(t/2,Y(2,:)'); 29 | h3 = plot(t/2,Y(3,:)'); 30 | axis([-2.2 2.2 -.7 1.1]) 31 | set(h1,'linestyle','-'); 32 | set(h2,'linestyle','--'); 33 | set(h3,'linestyle','-.'); 34 | xlabel('Delay [chip]','fontsize',18) 35 | ylabel('Normalized amplitude','fontsize',18) 36 | legend('{\itb} = \infty', '{\itb} = 1', '{\itb} = 0.5') 37 | hold off 38 | box off 39 | legend('boxoff') 40 | set(gca,'FontSize',18) 41 | 42 | print -deps2 acf_bocl 43 | %%%%%%%%%%%% winkel4.m %%%%%%%%%%%%%% 44 | 45 | -------------------------------------------------------------------------------- /plots/figure_scripts/winkel8.m: -------------------------------------------------------------------------------- 1 | % R_X(tau) as given by Nunes & Sousa & Leitao (2004). 2 | 3 | %Written by Kai Borre 4 | % February 26, 2005 5 | 6 | Tc = 1; 7 | t = linspace(-1,1,1000); 8 | 9 | for p = [1,2,4] 10 | k = ceil(2*p.*abs(t)/Tc); 11 | f = (-1).^(k+1); 12 | R(p,:) = f.*((-k.^2+2*k.*p+k-p)/p - (4*p-2.*k+1).*(abs(t)./Tc)); 13 | end 14 | 15 | h1 = plot(t,R(1,:)); 16 | hold on 17 | h2 = plot(t,R(2,:)); 18 | h4 = plot(t,R(4,:)); 19 | set(h1,'linestyle','-','linewidth',1) 20 | set(h2,'linestyle','--','linewidth',1) 21 | set(h4,'linestyle','-.','linewidth',1) 22 | xlabel( 'Delay [chip]','fontsize',18) 23 | ylabel('Correlation','fontsize',18) 24 | legend('{\itp} = 1', '{\itp} = 2','{\itp} = 4') 25 | set(gca,'FontSize',18) 26 | axis([-1.2 1.2 -0.9 1.1]) 27 | box off 28 | legend('boxoff') 29 | hold off 30 | 31 | print -deps fig3-3 32 | %%%%%%%%%%%% end winkel8.m %%%%%%%% 33 | -------------------------------------------------------------------------------- /plots/plot_acquisition.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function plot_acquisition(acqResults) 10 | 11 | figure(101); 12 | 13 | hAxes = newplot(); 14 | 15 | bar(hAxes, acqResults.peakMetric); 16 | 17 | title (hAxes, 'Acquisition results'); 18 | xlabel(hAxes, 'PRN number (no bar - SV is not in the acquisition list)'); 19 | ylabel(hAxes, 'Acquisition Metric'); 20 | 21 | oldAxis = axis(hAxes); 22 | axis (hAxes, [0, 33, 0, oldAxis(4)]); 23 | set (hAxes, 'XMinorTick', 'on'); 24 | set (hAxes, 'YGrid', 'on'); 25 | 26 | %% Mark acquired signals ================================================== 27 | 28 | acquiredSignals = acqResults.peakMetric .* (acqResults.carrFreq > 0); 29 | 30 | hold(hAxes, 'on'); 31 | bar (hAxes, acquiredSignals, 'FaceColor', [0 0.8 0]); 32 | hold(hAxes, 'off'); 33 | 34 | legend(hAxes, 'Not acquired signals', 'Acquired signals'); 35 | -------------------------------------------------------------------------------- /plots/plot_acquisition_results.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function plot_acquisition_results(sdrParams, postProcessResults) 10 | % SUMMAR for plotting different plots for comparison 11 | % of performance, code profiling and accuracy of results. 12 | 13 | 14 | %%% Extract coherent frame data. 15 | currFileNum = sdrParams.stateParams.numFilesProcessed + 1; 16 | dataParams = sdrParams.dataFileParamsList{currFileNum}; 17 | 18 | channelId = 1; 19 | if dataParams.selectedChannel ~= -1 20 | numChannels = 1; 21 | channelId = dataParams.selectedChannel; 22 | else 23 | numChannels = dataParams.totalChannels; 24 | channelId = 1:dataParams.totalChannels; 25 | end 26 | 27 | acqAlgoList = sdrParams.sysParams.acqAlgosList; 28 | numAcqAlgos = length(acqAlgoList); 29 | 30 | for chIdx=1:numChannels 31 | for acqAlgoIdx=1:numAcqAlgos 32 | 33 | acqResults = postProcessResults.acqResults{chIdx, acqAlgoIdx}.acqResults; 34 | if ~isempty(acqResults) 35 | 36 | print_string(['Plotting bar graph fpr ', num2str(chIdx),... 37 | '/', num2str(numChannels), ' for algorithm ', acqAlgoList{acqAlgoIdx}]); 38 | 39 | % Bar plot for peak metric bar graph of acquired satellites 40 | plot_acq_peak_metric_bars(sdrParams, postProcessResults, chIdx, channelId, acqAlgoIdx); 41 | 42 | % DDM plots 43 | plot_acq_ddm_map(sdrParams, postProcessResults, chIdx, channelId, acqAlgoIdx); 44 | 45 | end 46 | end 47 | end 48 | end 49 | 50 | function plot_acq_peak_metric_bars(sdrParams, postProcessResults, chIdx, channelId, algoIdx) 51 | 52 | acqResults = postProcessResults.acqResults{chIdx, algoIdx}.acqResults.chAlgoAcqResults; 53 | nacqPrnPeakMetric = postProcessResults.acqResults{chIdx, algoIdx}.acqResults.nacqStats.nacqPrnPeakMetric; 54 | nacqPrnList = postProcessResults.acqResults{chIdx, algoIdx}.acqResults.nacqStats.nacqPrnList; 55 | numAcqSatellites = length(acqResults); 56 | currFileNum = sdrParams.stateParams.numFilesProcessed + 1; 57 | currFrameNum = sdrParams.stateParams.currFrameNum; 58 | numTotalFrames = sdrParams.stateParams.numTotalFrames; 59 | acqAlgoList = sdrParams.sysParams.acqAlgosList; 60 | 61 | 62 | % prepare fields 63 | titleStr = sprintf('Acquisition Results. Total Frames %d\n', numTotalFrames); 64 | channelStr = sprintf('Data channel: %d\n', channelId(chIdx)); 65 | acqAlgoStr = sprintf('Acquisition Algorithm: %s', acqAlgoList{algoIdx}); 66 | 67 | acqPeakMetric = []; 68 | acqPeakMetricLoc = []; 69 | for aprnIdx=1:numAcqSatellites 70 | acqPeakMetric = [acqPeakMetric, acqResults{aprnIdx}.peakMetric]; 71 | acqPeakMetricLoc = [acqPeakMetricLoc, acqResults{aprnIdx}.satellitePrn]; 72 | end 73 | peakMetric = [acqPeakMetric, nacqPrnPeakMetric]; 74 | peakMetricLoc = [acqPeakMetricLoc, nacqPrnList]; 75 | 76 | figWindowTitle = sprintf('Acquisition Bar Graph. File#%d, Data_channel#%d, Algo#%d, Frame#%d', ... 77 | currFileNum, chIdx, algoIdx, currFrameNum); 78 | figure('NumberTitle', 'off', 'Name', figWindowTitle); 79 | 80 | ylimit = max(acqPeakMetric)*1.5; 81 | hAxes = newplot(); 82 | hAxes.NextPlot = 'replaceall'; 83 | bar(hAxes, peakMetricLoc, peakMetric); 84 | title (hAxes, [titleStr, channelStr, acqAlgoStr], 'Interpreter', 'none'); 85 | xlabel(hAxes, 'PRN number (no bar - SV is not in the acquisition list)'); 86 | ylabel(hAxes, 'Acquisition Metric'); 87 | hAxes.YLim = [0, ylimit]; % Set ylimit to 20 for easier eyeball comparison of plots. 88 | 89 | oldAxis = axis(hAxes); 90 | axis (hAxes, [0, 33, 0, oldAxis(4)]); 91 | set (hAxes, 'XMinorTick', 'on'); 92 | set (hAxes, 'YGrid', 'on'); 93 | 94 | % Mark acquired signals 95 | qualifiedSignals = peakMetric .* (peakMetric > sdrParams.sysParams.acqThreshold); 96 | hold(hAxes, 'on'); 97 | bar (hAxes, peakMetricLoc, qualifiedSignals, 'FaceColor', [0 0.8 0]); 98 | 99 | acquiredSignals = peakMetric .* ismember(peakMetric, acqPeakMetric); 100 | bar (hAxes, peakMetricLoc, acquiredSignals, ... 101 | 'FaceColor',[0 0.8 0],'EdgeColor',[0 .9 .9],'LineWidth',1.5); 102 | legend(hAxes, ... 103 | 'Not acquired signals', ... 104 | 'Acquired signals', ... 105 | 'Selected signals', ... 106 | 'AutoUpdate', 'off'); 107 | 108 | axisPos = hAxes.Position; 109 | leftBottomPointX = axisPos(1); 110 | leftBottomPointY = axisPos(2); 111 | rightTopX = axisPos(3)+axisPos(1); 112 | rightTopY = axisPos(4)+axisPos(2); 113 | plotWidth = rightTopX; 114 | plotHeight = rightTopY; 115 | 116 | x = [leftBottomPointX + plotWidth * 0.8, leftBottomPointX + plotWidth * 0.8 ]; 117 | y = [leftBottomPointY + plotHeight * 0.6, leftBottomPointY + ... 118 | (sdrParams.sysParams.acqThreshold/ylimit)*axisPos(4)]; 119 | annotation('textarrow',x,y,'String','Threshold ', 'Units','normalized', 'Color', 'r'); 120 | plot(0:33, sdrParams.sysParams.acqThreshold*ones(1, 34), 'r--'); 121 | hold(hAxes, 'off'); 122 | end 123 | 124 | 125 | function plot_acq_ddm_map(sdrParams, postProcessResults, chIdx, channelId, algoIdx) 126 | 127 | acqResults = postProcessResults.acqResults{chIdx, algoIdx}.acqResults.chAlgoAcqResults; 128 | numAcqSatellites = length(acqResults); 129 | currFileNum = sdrParams.stateParams.numFilesProcessed + 1; 130 | numTotalFrames = sdrParams.stateParams.numTotalFrames; 131 | acqAlgoList = sdrParams.sysParams.acqAlgosList; 132 | 133 | 134 | % prepare fields 135 | 136 | for aprnIdx=1:numAcqSatellites 137 | 138 | figWindowTitle = sprintf('Acquisition Delay Doppler Map. File#%d, Data_channel#%d, Algo#%d, Frames#%d', ... 139 | currFileNum, chIdx, algoIdx, numTotalFrames); 140 | 141 | figure('NumberTitle', 'off', 'Name', figWindowTitle); 142 | 143 | dopplerShiftAxis = acqResults{aprnIdx}.dopplerShiftAxis; 144 | codeDelayAxis = acqResults{aprnIdx}.codeDelayAxis; 145 | 146 | 147 | mesh(codeDelayAxis, dopplerShiftAxis, acqResults{aprnIdx}.ddm); 148 | 149 | 150 | titleStr = sprintf('Acquisition Delay Doppler Map\n '); 151 | channelStr = sprintf('Data channel: %d\n', channelId(chIdx)); 152 | acqAlgoStr = sprintf('Acquisition Algorithm: %s\n', acqAlgoList{algoIdx}); 153 | prnStr = sprintf('PRN : %d', acqResults{aprnIdx}.satellitePrn); 154 | 155 | 156 | title ([titleStr, channelStr, acqAlgoStr, prnStr], 'Interpreter', 'none'); 157 | zlabel('Correlation Magnitude'); 158 | ylabel('Doppler Shift (Hz)'); 159 | xlabel('Code Phase (samples)'); 160 | 161 | end 162 | 163 | end 164 | -------------------------------------------------------------------------------- /plots/plot_delay_doppler_map_cpp.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | clear; %close all; 10 | % For C++ 11 | fileName = ['NTLab_Bands_GPS_GLONASS_L12']; 12 | load(['../GNSS_SDR_C++/',fileName,'_ddm_mat.mat']); 13 | load(['../GNSS_SDR_C++/',fileName,'_cpp.mat']); 14 | 15 | %For matlab 16 | % load('results_sim0_18.mat'); 17 | % load('acqResults_sim0.mat'); 18 | 19 | PRN = 8; 20 | frequencyBinIndex = frequencyBinIndex + 1; 21 | codePhaseLast = codePhaseLast + 1; 22 | results = reshape(results, samplesPerCode, numberOfFrqBins)'; 23 | 24 | ts = 1 / samplingFreq; 25 | figure(1025) 26 | subplot(2,1,1) 27 | mesh(results) 28 | xlabel('Code phase [chips]') 29 | ylabel('Frequency [MHz]') 30 | title('correlation Map') 31 | 32 | subplot(2,1,2) 33 | imagesc(results) 34 | colorbar 35 | 36 | figddm = figure(1026); 37 | subplot(2,1,1) 38 | 39 | dopplerAxis = frequencyBinIndex-20:frequencyBinIndex+20; 40 | dopplerAxis(find(dopplerAxis < 1)) = []; 41 | dopplerAxis(find(dopplerAxis > numberOfFrqBins)) = []; 42 | 43 | delayAxis = codePhaseLast-50:codePhaseLast+100; 44 | delayAxis(find(delayAxis < 1)) = []; 45 | delayAxis(find(delayAxis > samplesPerCode)) = []; 46 | 47 | DopplerY = frqBins(dopplerAxis)-carrFreq(PRN); 48 | TotalDelaysec = ((1:samplesPerCode)-codePhaseLast)*ts*1e9; 49 | DelayX = TotalDelaysec(delayAxis); 50 | [Xax,Yax]=meshgrid(DelayX,DopplerY); 51 | DDM = results(dopplerAxis,delayAxis); 52 | 53 | 54 | mesh(Xax,Yax,DDM) 55 | xlabel('Delay axis [ns]') 56 | ylabel('Doppler axis [Hz]') 57 | title('Delay Doppler Map') 58 | box on 59 | 60 | subplot(2,1,2) 61 | imagesc(DelayX,DopplerY,DDM) 62 | colorbar 63 | xlabel('Delay axis [ns]') 64 | ylabel('Doppler axis [Hz]') 65 | title('Delay Doppler Map'); 66 | -------------------------------------------------------------------------------- /plots/plot_navigation.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function plot_navigation(navSolutions, settings) 10 | %Functions plots variations of coordinates over time and a 3D position 11 | %plot. It plots receiver coordinates in UTM system or coordinate offsets if 12 | %the true UTM receiver coordinates are provided. 13 | % 14 | %plotNavigation(navSolutions, settings) 15 | % 16 | % Inputs: 17 | % navSolutions - Results from navigation solution function. It 18 | % contains measured pseudoranges and receiver 19 | % coordinates. 20 | % settings - Receiver settings. The true receiver coordinates 21 | % are contained in this structure. 22 | 23 | 24 | 25 | %% Plot results in the necessary data exists ============================== 26 | if (~isempty(navSolutions)) 27 | 28 | %% If reference position is not provided, then set reference position 29 | %% to the average postion 30 | if isnan(settings.truePosition.E) || isnan(settings.truePosition.N) ... 31 | || isnan(settings.truePosition.U) 32 | 33 | %=== Compute mean values ========================================== 34 | % Remove NaN-s or the output of the function MEAN will be NaN. 35 | refCoord.E = mean(navSolutions.E(~isnan(navSolutions.E))); 36 | refCoord.N = mean(navSolutions.N(~isnan(navSolutions.N))); 37 | refCoord.U = mean(navSolutions.U(~isnan(navSolutions.U))); 38 | 39 | %Also convert geodetic coordinates to deg:min:sec vector format 40 | meanLongitude = dms2mat(deg2dms(... 41 | mean(navSolutions.longitude(~isnan(navSolutions.longitude)))), -5); 42 | meanLatitude = dms2mat(deg2dms(... 43 | mean(navSolutions.latitude(~isnan(navSolutions.latitude)))), -5); 44 | 45 | refPointLgText = ['Mean Position\newline Lat: ', ... 46 | num2str(meanLatitude(1)), '{\circ}', ... 47 | num2str(meanLatitude(2)), '{\prime}', ... 48 | num2str(meanLatitude(3)), '{\prime}{\prime}', ... 49 | '\newline Lng: ', ... 50 | num2str(meanLongitude(1)), '{\circ}', ... 51 | num2str(meanLongitude(2)), '{\prime}', ... 52 | num2str(meanLongitude(3)), '{\prime}{\prime}', ... 53 | '\newline Hgt: ', ... 54 | num2str(mean(navSolutions.height(~isnan(navSolutions.height))), '%+6.1f')]; 55 | else 56 | refPointLgText = 'Reference Position'; 57 | refCoord.E = settings.truePosition.E; 58 | refCoord.N = settings.truePosition.N; 59 | refCoord.U = settings.truePosition.U; 60 | end 61 | 62 | figureNumber = 300; 63 | % The 300 is chosen for more convenient handling of the open 64 | % figure windows, when many figures are closed and reopened. Figures 65 | % drawn or opened by the user, will not be "overwritten" by this 66 | % function if the auto numbering is not used. 67 | 68 | %=== Select (or create) and clear the figure ========================== 69 | figure(figureNumber); 70 | clf (figureNumber); 71 | set (figureNumber, 'Name', 'Navigation solutions'); 72 | 73 | %--- Draw axes -------------------------------------------------------- 74 | handles(1, 1) = subplot(4, 2, 1 : 4); 75 | handles(3, 1) = subplot(4, 2, [5, 7]); 76 | handles(3, 2) = subplot(4, 2, [6, 8]); 77 | 78 | %% Plot all figures ======================================================= 79 | 80 | %--- Coordinate differences in UTM system ----------------------------- 81 | plot(handles(1, 1), [(navSolutions.E - refCoord.E)', ... 82 | (navSolutions.N - refCoord.N)',... 83 | (navSolutions.U - refCoord.U)']); 84 | 85 | title (handles(1, 1), 'Coordinates variations in UTM system'); 86 | legend(handles(1, 1), 'E', 'N', 'U'); 87 | xlabel(handles(1, 1), ['Measurement period: ', ... 88 | num2str(settings.navSolPeriod), 'ms']); 89 | ylabel(handles(1, 1), 'Variations (m)'); 90 | grid (handles(1, 1)); 91 | axis (handles(1, 1), 'tight'); 92 | 93 | %--- Position plot in UTM system -------------------------------------- 94 | plot3 (handles(3, 1), navSolutions.E - refCoord.E, ... 95 | navSolutions.N - refCoord.N, ... 96 | navSolutions.U - refCoord.U, '+'); 97 | hold (handles(3, 1), 'on'); 98 | %Plot the reference point 99 | plot3 (handles(3, 1), 0, 0, 0, 'r+', 'LineWidth', 1.5, 'MarkerSize', 10); 100 | hold (handles(3, 1), 'off'); 101 | 102 | view (handles(3, 1), 0, 90); 103 | axis (handles(3, 1), 'equal'); 104 | grid (handles(3, 1), 'minor'); 105 | 106 | legend(handles(3, 1), 'Measurements', refPointLgText); 107 | 108 | title (handles(3, 1), 'Positions in UTM system (3D plot)'); 109 | xlabel(handles(3, 1), 'East (m)'); 110 | ylabel(handles(3, 1), 'North (m)'); 111 | zlabel(handles(3, 1), 'Upping (m)'); 112 | 113 | %--- Satellite sky plot ----------------------------------------------- 114 | skyPlot(handles(3, 2), ... 115 | navSolutions.channel.az, ... 116 | navSolutions.channel.el, ... 117 | navSolutions.channel.PRN(:, 1)); 118 | 119 | title (handles(3, 2), ['Sky plot (mean PDOP: ', ... 120 | num2str(mean(navSolutions.DOP(2,:))), ')']); 121 | 122 | else 123 | disp('plotNavigation: No navigation data to plot.'); 124 | end % if (~isempty(navSolutions)) 125 | -------------------------------------------------------------------------------- /plots/plot_tracking.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function plot_tracking(channelList, trackResults, settings) 10 | %This function plots the tracking results for the given channel list. 11 | % 12 | %plotTracking(channelList, trackResults, settings) 13 | % 14 | % Inputs: 15 | % channelList - list of channels to be plotted. 16 | % trackResults - tracking results from the tracking function. 17 | % settings - receiver settings. 18 | 19 | 20 | % Protection - if the list contains incorrect channel numbers 21 | channelList = intersect(channelList, 1:settings.numberOfChannels); 22 | 23 | %=== For all listed channels ============================================== 24 | for channelNr = channelList 25 | 26 | %% Select (or create) and clear the figure ================================ 27 | % The number 200 is added just for more convenient handling of the open 28 | % figure windows, when many figures are closed and reopened. 29 | % Figures drawn or opened by the user, will not be "overwritten" by 30 | % this function. 31 | 32 | figure(channelNr +200); 33 | clf(channelNr +200); 34 | set(channelNr +200, 'Name', ['Channel ', num2str(channelNr), ... 35 | ' (PRN ', ... 36 | num2str(trackResults(channelNr).PRN), ... 37 | ') results']); 38 | 39 | %% Draw axes ============================================================== 40 | % Row 1 41 | handles(1, 1) = subplot(3, 3, 1); 42 | handles(1, 2) = subplot(3, 3, [2 3]); 43 | % Row 2 44 | handles(2, 1) = subplot(3, 3, 4); 45 | handles(2, 2) = subplot(3, 3, [5 6]); 46 | % Row 3 47 | handles(3, 1) = subplot(3, 3, 7); 48 | handles(3, 2) = subplot(3, 3, 8); 49 | handles(3, 3) = subplot(3, 3, 9); 50 | 51 | %% Plot all figures ======================================================= 52 | 53 | timeAxisInSeconds = (1:settings.msToProcess)/1000; 54 | 55 | %----- Discrete-Time Scatter Plot --------------------------------- 56 | plot(handles(1, 1), trackResults(channelNr).I_P,... 57 | trackResults(channelNr).Q_P, ... 58 | '.'); 59 | 60 | grid (handles(1, 1)); 61 | axis (handles(1, 1), 'equal'); 62 | title (handles(1, 1), 'Discrete-Time Scatter Plot'); 63 | xlabel(handles(1, 1), 'I prompt'); 64 | ylabel(handles(1, 1), 'Q prompt'); 65 | 66 | %----- Nav bits --------------------------------------------------- 67 | plot (handles(1, 2), timeAxisInSeconds, ... 68 | trackResults(channelNr).I_P); 69 | 70 | grid (handles(1, 2)); 71 | title (handles(1, 2), 'Bits of the navigation message'); 72 | xlabel(handles(1, 2), 'Time (s)'); 73 | axis (handles(1, 2), 'tight'); 74 | 75 | %----- PLL discriminator unfiltered-------------------------------- 76 | plot (handles(2, 1), timeAxisInSeconds, ... 77 | trackResults(channelNr).pllDiscr, 'r'); 78 | 79 | grid (handles(2, 1)); 80 | axis (handles(2, 1), 'tight'); 81 | xlabel(handles(2, 1), 'Time (s)'); 82 | ylabel(handles(2, 1), 'Amplitude'); 83 | title (handles(2, 1), 'Raw PLL discriminator'); 84 | 85 | %----- Correlation ------------------------------------------------ 86 | plot(handles(2, 2), timeAxisInSeconds, ... 87 | [sqrt(trackResults(channelNr).I_E.^2 + ... 88 | trackResults(channelNr).Q_E.^2)', ... 89 | sqrt(trackResults(channelNr).I_P.^2 + ... 90 | trackResults(channelNr).Q_P.^2)', ... 91 | sqrt(trackResults(channelNr).I_L.^2 + ... 92 | trackResults(channelNr).Q_L.^2)'], ... 93 | '-*'); 94 | 95 | grid (handles(2, 2)); 96 | title (handles(2, 2), 'Correlation results'); 97 | xlabel(handles(2, 2), 'Time (s)'); 98 | axis (handles(2, 2), 'tight'); 99 | 100 | hLegend = legend(handles(2, 2), '$\sqrt{I_{E}^2 + Q_{E}^2}$', ... 101 | '$\sqrt{I_{P}^2 + Q_{P}^2}$', ... 102 | '$\sqrt{I_{L}^2 + Q_{L}^2}$'); 103 | 104 | %set interpreter from tex to latex. This will draw \sqrt correctly 105 | set(hLegend, 'Interpreter', 'Latex'); 106 | 107 | %----- PLL discriminator filtered---------------------------------- 108 | plot (handles(3, 1), timeAxisInSeconds, ... 109 | trackResults(channelNr).pllDiscrFilt, 'b'); 110 | 111 | grid (handles(3, 1)); 112 | axis (handles(3, 1), 'tight'); 113 | xlabel(handles(3, 1), 'Time (s)'); 114 | ylabel(handles(3, 1), 'Amplitude'); 115 | title (handles(3, 1), 'Filtered PLL discriminator'); 116 | 117 | %----- DLL discriminator unfiltered-------------------------------- 118 | plot (handles(3, 2), timeAxisInSeconds, ... 119 | trackResults(channelNr).dllDiscr, 'r'); 120 | 121 | grid (handles(3, 2)); 122 | axis (handles(3, 2), 'tight'); 123 | xlabel(handles(3, 2), 'Time (s)'); 124 | ylabel(handles(3, 2), 'Amplitude'); 125 | title (handles(3, 2), 'Raw DLL discriminator'); 126 | 127 | %----- DLL discriminator filtered---------------------------------- 128 | plot (handles(3, 3), timeAxisInSeconds, ... 129 | trackResults(channelNr).dllDiscrFilt, 'b'); 130 | 131 | grid (handles(3, 3)); 132 | axis (handles(3, 3), 'tight'); 133 | xlabel(handles(3, 3), 'Time (s)'); 134 | ylabel(handles(3, 3), 'Amplitude'); 135 | title (handles(3, 3), 'Filtered DLL discriminator'); 136 | 137 | end % for channelNr = channelList 138 | -------------------------------------------------------------------------------- /plots/probe_data.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function probe_data(varargin) 10 | 11 | 12 | %%% Check the number of arguments 13 | if (nargin == 1) 14 | settings = deal(varargin{1}); 15 | fileNameStr = settings.fileName; 16 | elseif (nargin == 2) 17 | [fileNameStr, settings] = deal(varargin{1:2}); 18 | if ~ischar(fileNameStr) 19 | error('File name must be a string'); 20 | end 21 | else 22 | error('Incorect number of arguments'); 23 | end 24 | 25 | %%% Generate plot of raw data 26 | [fid, message] = fopen(fileNameStr, 'rb'); 27 | settings.fileID = fid; 28 | if (fid > 0) 29 | % Move the starting point of processing. Can be used to start the 30 | % signal processing at any point in the data record (e.g. for long 31 | % records). 32 | %fseek(fid, settings.skipNumberOfBytes, 'bof'); 33 | fseek(fid,0, 'bof'); 34 | 35 | % Read 10ms of signal 36 | [tmpdata, count] = fread(fid, [1, 10*settings.samplesPerCode], settings.dataType); 37 | fclose(fid); 38 | 39 | % convert the signal to symbols from bits 40 | % TODO: this moves to pre-processing for 41 | 42 | channelNum = 4; 43 | numChannels = 4; 44 | tmpdata = int32(tmpdata(channelNum:numChannels:end)); 45 | data = double(1 + 2 * idivide(tmpdata, 2) .* (1 - 2 * mod(tmpdata, 2))); 46 | 47 | if (count < 10*settings.samplesPerCode) 48 | % The file is to short 49 | error('Could not read enough data from the data file.'); 50 | end 51 | 52 | %%% Initialization 53 | figure(100); 54 | clf(100); 55 | timeScale = 0 : 1/settings.samplingFreq : 5e-3; 56 | 57 | %%% Time domain plot 58 | subplot(2, 2, 1); plot(1000 * timeScale(1:round(settings.samplesPerCode/50)), ... 59 | data(1:round(settings.samplesPerCode/50))); 60 | axis tight; grid on; 61 | title ('Time domain plot'); 62 | xlabel('Time (ms)'); 63 | ylabel('Amplitude'); 64 | ylim([-5 5]) 65 | 66 | %%% Frequency domain plot 67 | subplot(2,2,2); pwelch(data-mean(data), 16384, ... 68 | 1024, 2048, settings.samplingFreq/1e6,'centered'); 69 | 70 | axis tight; grid on; 71 | title ('Frequency domain plot'); 72 | xlabel('Frequency (MHz)'); ylabel('Magnitude'); 73 | 74 | %%% Histogram 75 | subplot(2, 2, 3.5); hist(data, -128:128) 76 | dmax = max(abs(data)) + 1; 77 | axis tight; 78 | adata = axis; 79 | axis([-dmax dmax adata(3) adata(4)]); 80 | grid on; 81 | title ('Histogram'); 82 | xlabel('Bin'); ylabel('Number in bin'); 83 | else 84 | %%% Error while opening the data file 85 | error('Unable to read file %s: %s.', fileNameStr, message); 86 | end % if (fid > 0) 87 | -------------------------------------------------------------------------------- /plots/save_acquisition_results.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function save_acquisition_results(sdrParams, postProcessResults) 10 | %%% 11 | 12 | %%% Extract coherent frame data. 13 | currFileNum = sdrParams.stateParams.numFilesProcessed + 1; 14 | fileName = sdrParams.stateParams.fileNames{currFileNum}; 15 | dataParams = sdrParams.dataFileParamsList{currFileNum}; 16 | 17 | channelId = 1; 18 | if dataParams.selectedChannel ~= -1 19 | numChannels = 1; 20 | channelId = dataParams.selectedChannel; 21 | else 22 | numChannels = dataParams.totalChannels; 23 | channelId = 1:dataParams.totalChannels; 24 | end 25 | 26 | acqAlgoList = sdrParams.sysParams.acqAlgosList; 27 | numAcqAlgos = length(acqAlgoList); 28 | 29 | print_string('====Displaying Acquisition Results: Start===='); 30 | for chIdx=1:numChannels 31 | for acqAlgoIdx=1:numAcqAlgos 32 | print_string(sprintf('(File: %s, Channel: %d, Algorithm: %s)', ... 33 | fileName, chIdx, acqAlgoList{acqAlgoIdx})); 34 | 35 | acqResults = postProcessResults.acqResults{chIdx, acqAlgoIdx}.acqResults; 36 | if ~isempty(acqResults) 37 | 38 | chAlgoAcqResults = acqResults.chAlgoAcqResults; 39 | 40 | %%% Save 41 | sFileName = sprintf('file_%s_ch_%d_frame_%d_algo_%s_%s.mat', ... 42 | fileName(1:end-4),... 43 | chIdx,... 44 | sdrParams.stateParams.currFrameNum+1, ... 45 | acqAlgoList{acqAlgoIdx},... 46 | datestr(now, 'yyyy-mm-dd-HH-MM-SS')); 47 | 48 | % clean dir 49 | 50 | if currFileNum == 1 && ... 51 | chIdx == 1 && ... 52 | sdrParams.stateParams.currFrameNum == 0 &&... 53 | acqAlgoIdx == 1 54 | 55 | 56 | % Check to make sure that folder actually exists. Warn user if it doesn't. 57 | if ~isdir(sdrParams.stateParams.dataPathOut) 58 | errorMessage = sprintf('Error: The following folder does not exist:\n%s', ... 59 | sdrParams.stateParams.dataPathOut); 60 | uiwait(warndlg(errorMessage)); 61 | return; 62 | end 63 | % Get a list of all files in the folder with the desired file name pattern. 64 | filePattern = fullfile(sdrParams.stateParams.dataPathOut, '*.*'); 65 | theFiles = dir(filePattern); 66 | for k = 3 : length(theFiles) 67 | baseFileName = theFiles(k).name; 68 | fullFileName = fullfile(sdrParams.stateParams.dataPathOut, baseFileName); 69 | print_string(sprintf('Now deleting %s', fullFileName)); 70 | delete(fullFileName); 71 | end 72 | end 73 | % save 74 | save([sdrParams.stateParams.dataPathOut, sFileName], 'acqResults'); 75 | 76 | 77 | 78 | %%% Dump to console 79 | for prn=1:length(chAlgoAcqResults) 80 | print_string(sprintf('(PRN: %3d, Acq_Metric: %8s, Code: %5d, Frequency: %s)', ... 81 | chAlgoAcqResults{prn}.satellitePrn,... 82 | num2str(chAlgoAcqResults{prn}.peakMetric),... 83 | chAlgoAcqResults{prn}.codeDelay,... 84 | num2str(chAlgoAcqResults{prn}.dopplerShiftHz))); 85 | end 86 | else 87 | print_string('No satellites found.'); 88 | end 89 | end 90 | end 91 | print_string('====Displaying Acquisition Results: End===='); 92 | end 93 | -------------------------------------------------------------------------------- /post_processing/post_process.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function [ postProcessResuts ] = post_process(sdrParams, rxData, ... 10 | processing_results... 11 | ) 12 | % SUMMARY Post processes correlation for 1 frame of coherent processing interval. 13 | % This function receives processing results for configured number of 14 | % satellites for 1 coherent processing interval and converts correlation 15 | % delay doppler profile to code delay and doppler shift parameters as well 16 | % as refines them through advanced signal processing techniques. It stores 17 | % results for one incoheren processing interval frame and then outputs them 18 | % either to plotting functions or dumps to files. 19 | 20 | postProcessResuts = struct(); 21 | 22 | currFileNum = sdrParams.stateParams.numFilesProcessed+1; 23 | dataFileParams = sdrParams.dataFileParamsList{currFileNum}; 24 | numChannels = dataFileParams.totalChannels; 25 | selectedChannel = dataFileParams.selectedChannel; 26 | acqAlgos = sdrParams.sysParams.acqAlgosList; 27 | numAcqAlgos = length(acqAlgos); 28 | 29 | if selectedChannel ~= -1 30 | numChannels = 1; 31 | end 32 | 33 | %%% Define buffer for incoherent integration for all data channels 34 | % and all acquisition algorithms types. 35 | 36 | persistent ddMapBuffer; 37 | if isempty(ddMapBuffer) 38 | ddMapBuffer = cell(numChannels, numAcqAlgos); 39 | end 40 | 41 | % Define data buffer here that are static 42 | acqResults = cell(numChannels, numAcqAlgos); 43 | 44 | 45 | for chIdx = 1:numChannels 46 | for algoIdx = 1:numAcqAlgos 47 | 48 | %%% Add the delay doppler map for multiple incoherent integration 49 | % intervals. 50 | 51 | if isempty(ddMapBuffer) || ... 52 | sdrParams.stateParams.currFrameNum == 0 53 | ddMapBuffer{chIdx, algoIdx}.ddMapBuffer = processing_results{chIdx, algoIdx}.ddMap; 54 | else 55 | ddMapBuffer{chIdx, algoIdx}.ddMapBuffer = ddMapBuffer{chIdx, algoIdx}.ddMapBuffer + ... 56 | processing_results{chIdx, algoIdx}.ddMap; 57 | end 58 | 59 | 60 | print_string(sprintf('Post processing for frame: %d/%d, channel: %d/%d, algorithm: %s', ... 61 | sdrParams.stateParams.currFrameNum+1, ... 62 | sdrParams.stateParams.numTotalFrames, ... 63 | chIdx, ... 64 | numChannels, ... 65 | acqAlgos{algoIdx}... 66 | )); 67 | 68 | 69 | %%% Do post-processing for each algorithm. 70 | switch(acqAlgos{algoIdx}) 71 | case 'norm_acq_parcode' 72 | chAlgoAcqResults = post_process_norm_acq_parcode(... 73 | sdrParams, ... 74 | dataFileParams, ... 75 | processing_results{chIdx, algoIdx}, ... 76 | rxData{chIdx}, ... 77 | ddMapBuffer{chIdx, algoIdx}.ddMapBuffer... 78 | ); 79 | case 'weak_acq_dbzp' 80 | chAlgoAcqResults = post_process_weak_acq_dbzp( ... 81 | sdrParams, ... 82 | dataFileParams, ... 83 | processing_results{chIdx, algoIdx}, ... 84 | rxData{chIdx}, ... 85 | ddMapBuffer{chIdx, algoIdx}.ddMapBuffer... 86 | ); 87 | otherwise 88 | error('Acquisition algorithm not identified.'); 89 | end 90 | 91 | acqResults{chIdx, algoIdx}.acqResults = chAlgoAcqResults; 92 | end 93 | 94 | % Save results as well as spit out. 95 | 96 | end 97 | 98 | 99 | %%% visualizations of different algorithms 100 | % plot_acquisition(acqResults); 101 | postProcessResuts.acqResults = acqResults; 102 | 103 | end 104 | -------------------------------------------------------------------------------- /post_processing/post_process_norm_acq_parcode.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function acqResults = post_process_norm_acq_parcode(... 10 | sdrParams, ... 11 | dataFileParams, ... 12 | processing_results, ... 13 | rxData, ... 14 | delayDopplerMapAcq... 15 | ) 16 | 17 | %%% Get Parameters from results. 18 | averFactor = processing_results.averFactor; 19 | numCodeSamples = processing_results.numCodeSamples; 20 | numDopplerSamples = processing_results.numDopplerBins; 21 | dopplerResHz = processing_results.dopplerResHz; 22 | 23 | satelliteList = sdrParams.sysParams.acqSatelliteList; 24 | maxNumAcqSatellites = sdrParams.sysParams.numAcqSatellites; 25 | numPrns = length(satelliteList); 26 | 27 | chipRateHz = sdrParams.sysParams.caCodeChipRateHz; 28 | numChipsPerMs = floor(chipRateHz * 1e-3); 29 | 30 | if size(delayDopplerMapAcq, 1) ~= length(satelliteList) 31 | error('The number of returned correlation maps does not equal number of satellites.'); 32 | end 33 | 34 | %%% Find coarse results 35 | 36 | peakValueList = zeros(1, numPrns); 37 | peakMetricList = zeros(1, numPrns); 38 | peakLocList = zeros(1, numPrns); 39 | 40 | excludeBinRangePeakAtZero = [10:floor(numCodeSamples/averFactor)-10]; 41 | 42 | % Check if a satellite should be acquired 43 | % if its peak metric is greater than threshold. 44 | 45 | for prnIdx = 1:numPrns 46 | delayDopplerMapAcqPrn = squeeze(delayDopplerMapAcq(prnIdx, :, :)); 47 | [peakValue, peakLoc] = max(delayDopplerMapAcqPrn(:)); 48 | 49 | if length(peakValue) > 1 50 | peakValue = peakValue(1); 51 | peakLoc = peakLoc(1); 52 | end 53 | 54 | % Calculate peak metric for all satellite signals 55 | dopplerShiftInt = mod(peakLoc-1, numDopplerSamples)+1; 56 | codeDelay = floor(peakLoc/numDopplerSamples); 57 | excludeBinRange = mod(excludeBinRangePeakAtZero + codeDelay+1,... 58 | numCodeSamples/averFactor)+1; 59 | nonPeakCorrProfile = delayDopplerMapAcqPrn(dopplerShiftInt, excludeBinRange); 60 | secondMaxPeak = max(nonPeakCorrProfile); 61 | peakMetric = peakValue/secondMaxPeak; 62 | 63 | % Fill in the list of peak Metric, peak value and locations. 64 | peakLocList(:, prnIdx) = peakLoc; 65 | peakValueList(:, prnIdx) = peakValue; 66 | peakMetricList(:, prnIdx) = peakMetric; 67 | end 68 | 69 | % Identify acquired and non-acquired satellites. 70 | acqPrnIdx = (peakMetricList>sdrParams.sysParams.acqThreshold); 71 | nacqPrnIdx = ~acqPrnIdx; 72 | 73 | aPrnList = satelliteList(acqPrnIdx); 74 | aprnLocList = peakLocList(acqPrnIdx); 75 | aprnPeakMetricList = peakMetricList(acqPrnIdx); 76 | 77 | nonAcqPeakMetricList = peakMetricList(nacqPrnIdx); 78 | naprnList = satelliteList(nacqPrnIdx); 79 | 80 | acqPrnCount = length(aPrnList); 81 | 82 | %%% Refine results for acquired PRNs 83 | acqResults = struct([]); 84 | 85 | if acqPrnCount 86 | 87 | % if acquired satellites are greater than required, choose strongest. 88 | if acqPrnCount > maxNumAcqSatellites 89 | 90 | % Sort the acquired satellite signals according to peak strength 91 | [sortedAprnPeakMetricList, ~] = ... 92 | sort(aprnPeakMetricList, 'descend'); 93 | 94 | acqPrnIdx = ismember(aprnPeakMetricList, ... 95 | sortedAprnPeakMetricList(1:maxNumAcqSatellites)); 96 | nacqPrnIdx = ~acqPrnIdx; 97 | 98 | % Choose maxNumAcqSatellites 99 | 100 | nonAcqPeakMetricList = [nonAcqPeakMetricList, aprnPeakMetricList(nacqPrnIdx)]; 101 | naprnList = [naprnList, aPrnList(nacqPrnIdx)]; 102 | 103 | aPrnList = aPrnList(acqPrnIdx); 104 | aprnLocList = aprnLocList(acqPrnIdx); 105 | aprnPeakMetricList = aprnPeakMetricList(acqPrnIdx); 106 | 107 | 108 | acqPrnCount = maxNumAcqSatellites; 109 | end 110 | 111 | print_string([num2str(acqPrnCount), ' satellites acquired for ', ... 112 | num2str(sdrParams.stateParams.currFrameNum+1),... 113 | '/', num2str(sdrParams.stateParams.numTotalFrames), '.']); 114 | 115 | % Resize if size is not ok. 116 | chAlgoAcqResults = cell(1, acqPrnCount); 117 | for aprnIdx = 1:acqPrnCount 118 | 119 | prnIdx = aPrnList(aprnIdx); 120 | location = aprnLocList(aprnIdx); 121 | 122 | % Coarse code and doppler delay. 123 | 124 | dopplerShiftInt = mod(location-1, numDopplerSamples)+1; 125 | codeDelay = floor(location/numDopplerSamples); 126 | dopplerSpectrum = squeeze(delayDopplerMapAcq(prnIdx, 1:numDopplerSamples, codeDelay+1)); 127 | 128 | 129 | % First perform fine frequency estimation using quadratic 130 | % interpolation 131 | 132 | maxValArrIdx = mod(dopplerShiftInt-2:dopplerShiftInt, numDopplerSamples) + 1; 133 | maxValArr = sqrt(dopplerSpectrum(maxValArrIdx)); 134 | doppleShiftError = 0.5*(maxValArr(1) - maxValArr(3)) / ... 135 | (maxValArr(1) - 2*maxValArr(2)+maxValArr(3)); 136 | dopplerShiftInt = dopplerShiftInt - floor(numDopplerSamples/2)-1; 137 | dopplerShiftHz = (dopplerShiftInt + doppleShiftError) * dopplerResHz; 138 | dopplerShiftAxis = ((0:numDopplerSamples-1) - floor(numDopplerSamples/2))* dopplerResHz; 139 | 140 | % Refine code delay results. 141 | ifFreqEst = dataFileParams.intermFreqHz + dopplerShiftHz; 142 | 143 | dopplerShiftHz = 0; 144 | codeDelayError = 0; 145 | dataIdx = (codeDelay*averFactor+1):(codeDelay*averFactor+1)+numCodeSamples-1; 146 | 147 | if dataIdx(end) <= length(rxData) 148 | 149 | dopplerFreqExp = exp(2i * pi * ifFreqEst * ... 150 | (0:numCodeSamples-1)/(dataFileParams.samplingFreqHz)); 151 | 152 | rxDataMs = rxData(dataIdx) .* dopplerFreqExp; 153 | 154 | caCodeMappingInd = floor((0:numCodeSamples-1)*(chipRateHz / dataFileParams.samplingFreqHz)) + 1; 155 | caCodeMappingInd(caCodeMappingInd == 0) = 1; 156 | caCodeMappingInd(caCodeMappingInd > numChipsPerMs) = numChipsPerMs; 157 | caCode = gen_ca_code(sdrParams.stateParams.dataPathIn, prnIdx); 158 | caCode = caCode(caCodeMappingInd); 159 | 160 | [corrOut, lags] = xcorr(rxDataMs, caCode, ceil(averFactor/2)); 161 | [~, maxIdx] = max(abs(corrOut)); 162 | if length(maxIdx) > 1 163 | maxIdx = maxIdx(1); 164 | end 165 | codeDelayError = lags(maxIdx); 166 | 167 | % Refine doppler frequency agains. 168 | 169 | rxDataMs = rxData(dataIdx) .* caCode; 170 | rxDataMs = rxDataMs .* dopplerFreqExp; 171 | 172 | acqDopplerBW = sdrParams.sysParams.acqDopplerBwKhz * 1e3; 173 | fftNumPts = 8*2^(nextpow2(length(rxDataMs))); 174 | deltaF = dataFileParams.samplingFreqHz/fftNumPts; 175 | pbins = ceil(0.5 * acqDopplerBW / deltaF); 176 | faxis = 0.5*fftNumPts-pbins:pbins+0.5*fftNumPts; 177 | fftFreqBins = (faxis - floor(0.5*fftNumPts)-1) * deltaF; 178 | 179 | fftxc = abs(fftshift(fft(rxDataMs, fftNumPts))); 180 | fftxc = fftxc(faxis); 181 | [~, fftMaxIndex] = max(fftxc); 182 | 183 | maxValArrIdx = fftMaxIndex-1:fftMaxIndex+1; 184 | maxValArr = sqrt(fftxc(maxValArrIdx)); 185 | 186 | % Apply quadratic interpolation again. 187 | 188 | doppleShiftError1 = 0.5*(maxValArr(1) - maxValArr(3)) / ... 189 | (maxValArr(1) - 2*maxValArr(2)+maxValArr(3)); 190 | dopplerShiftHz = fftFreqBins(fftMaxIndex) + doppleShiftError1*deltaF; 191 | 192 | end 193 | 194 | codeDelay = codeDelayError + codeDelay * averFactor + 1; 195 | ifFreqEst = ifFreqEst - dopplerShiftHz; 196 | 197 | % Save results per PRN 198 | chAlgoAcqResults{aprnIdx}.dopplerShiftHz = dopplerShiftHz; 199 | chAlgoAcqResults{aprnIdx}.codeDelay = codeDelay; 200 | chAlgoAcqResults{aprnIdx}.peakMetric = aprnPeakMetricList(aprnIdx); 201 | chAlgoAcqResults{aprnIdx}.dopplerShiftHz = ifFreqEst; 202 | chAlgoAcqResults{aprnIdx}.satellitePrn = prnIdx; 203 | chAlgoAcqResults{aprnIdx}.ddm = squeeze(delayDopplerMapAcq(prnIdx, :, :)); 204 | chAlgoAcqResults{aprnIdx}.dopplerShiftAxis=dopplerShiftAxis; 205 | chAlgoAcqResults{aprnIdx}.codeDelayAxis = 0:averFactor:numCodeSamples-1; 206 | 207 | end 208 | 209 | acqResults(1).chAlgoAcqResults = chAlgoAcqResults; 210 | acqResults(1).nacqStats.nacqPrnList = naprnList; 211 | acqResults(1).nacqStats.nacqPrnPeakMetric = nonAcqPeakMetricList; 212 | end 213 | end 214 | -------------------------------------------------------------------------------- /post_processing/post_processing.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | % Script postProcessing.m processes the raw signal from the specified data 10 | % file (in settings) operating on blocks of 37 seconds of data. 11 | % 12 | % First it runs acquisition code identifying the satellites in the file, 13 | % then the code and carrier for each of the satellites are tracked, storing 14 | % the 1msec accumulations. After processing all satellites in the 37 sec 15 | % data block, then postNavigation is called. It calculates pseudoranges 16 | % and attempts a position solutions. At the end plots are made for that 17 | % block of data. 18 | 19 | % THE SCRIPT "RECIPE" 20 | % 21 | % The purpose of this script is to combine all parts of the software 22 | % receiver. 23 | % 24 | % 1.1) Open the data file for the processing and seek to desired point. 25 | % 26 | % 2.1) Acquire satellites 27 | % 28 | % 3.1) Initialize channels (preRun.m). 29 | % 3.2) Pass the channel structure and the file identifier to the tracking 30 | % function. It will read and process the data. The tracking results are 31 | % stored in the trackResults structure. The results can be accessed this 32 | % way (the results are stored each millisecond): 33 | % trackResults(channelNumber).XXX(fromMillisecond : toMillisecond), where 34 | % XXX is a field name of the result (e.g. I_P, codePhase etc.) 35 | % 36 | % 4) Pass tracking results to the navigation solution function. It will 37 | % decode navigation messages, find satellite positions, measure 38 | % pseudoranges and find receiver position. 39 | % 40 | % 5) Plot the results. 41 | %% Initialization ========================================================= 42 | disp ('Starting processing...'); 43 | 44 | [fid, message] = fopen(settings.fileName, 'rb', 'ieee-le'); 45 | 46 | %If success, then process the data 47 | if (fid > 0) 48 | 49 | % Move the starting point of processing. Can be used to start the 50 | % signal processing at any point in the data record (e.g. good for long 51 | % records or for signal processing in blocks). 52 | fseek(fid, settings.skipNumberOfBytes, 'bof'); 53 | % Do acquisition if it is not disabled in settings or if the variable 54 | % acqResults does not exist. 55 | 56 | if ((settings.skipAcquisition == 0) || ~exist('acqResults', 'var')) 57 | 58 | % Read data for acquisition. 59 | fseek(fid, settings.skipNumberOfBytes, 'bof'); 60 | tmpdata = fread(fid, settings.dataExtractLen, settings.dataType)'; 61 | 62 | tmpdata(tmpdata==1) = -1; 63 | tmpdata(tmpdata==3) = -3; 64 | tmpdata(tmpdata==0) = 1; 65 | tmpdata(tmpdata==2) = 3; 66 | 67 | L = floor((length(tmpdata)/4)); 68 | 69 | Data4I = double(tmpdata(1:4:4*(L))); 70 | Data3I = double(tmpdata(2:4:4*(L))); 71 | Data2I = double(tmpdata(3:4:4*(L))); 72 | Data1I = double(tmpdata(4:4:4*(L))); 73 | 74 | data = Data1I; 75 | 76 | if settings.modulationRequired 77 | dataLen = settings.dataExtractLen / 2; 78 | phasePoints = 2 * pi * settings.IF / settings.samplingFreq * (0 : dataLen-1); 79 | data = data(1:2:end) .* cos(phasePoints) - data(2:2:end) .* sin(phasePoints); 80 | elseif settings.demodulationRequired 81 | phasePoints = (0 : settings.dataExtractLen/4-1) * 2 * pi * settings.IF / settings.samplingFreq; 82 | data = data .* cos(phasePoints) + 1i * data .* sin(phasePoints); 83 | end 84 | 85 | disp (' Acquiring satellites...'); 86 | 87 | if isequal(settings.acquisitionType, 'normalAcquisition') 88 | acqResults = acquisition(data, settings); 89 | elseif isequal(settings.acquisitionType, 'weakAcquisition') 90 | acqResults = weak_acquisition(data, settings); 91 | elseif isequal(settings.acquisitionType, 'halfBitAcquisition') 92 | acqResults = half_bit_acquisition(data, settings, settings.dataExtractLen); 93 | end 94 | 95 | end 96 | plotAcquisition(acqResults); 97 | 98 | %% Initialize channels and prepare for the run ============================ 99 | 100 | % Start further processing only if a GNSS signal was acquired (the 101 | % field FREQUENCY will be set to 0 for all not acquired signals) 102 | if (any(acqResults.carrFreq)) 103 | channel = preRun(acqResults, settings); 104 | showChannelStatus(channel, settings); 105 | else 106 | % No satellites to track, exit 107 | disp('No GNSS signals detected, signal processing finished.'); 108 | trackResults = []; 109 | return; 110 | end 111 | 112 | %% Track the signal ======================================================= 113 | startTime = now; 114 | disp ([' Tracking started at ', datestr(startTime)]); 115 | 116 | % Process all channels for given data block 117 | % [trackResults, channel] = tracking(fid, channel, settings); 118 | 119 | % Close the data file 120 | % fclose(fid); 121 | disp([' Tracking is over (elapsed time ', datestr(now - startTime, 13), ')']) 122 | 123 | % Auto save the acquisition & tracking results to a file to allow 124 | % running the positioning solution afterwards. 125 | % disp(' Saving Acq & Tracking results to file "trackingResults.mat"') 126 | % [filepath, name, ext]=fileparts(settings.fileName); 127 | % save(fullfile('.','..','..', [name, '_mat.mat']), 'trackResults', 'settings', 'acqResults', 'channel'); 128 | 129 | %% Calculate navigation solutions ========================================= 130 | % disp(' Calculating navigation solutions...'); 131 | % navSolutions = postNavigation(trackResults, settings); 132 | % 133 | % disp(' Processing is complete for this data block'); 134 | % 135 | % %% Plot all results =================================================== 136 | % disp (' Ploting results...'); 137 | % if settings.plotTracking 138 | % plotTracking(1:settings.numberOfChannels, trackResults, settings); 139 | % end 140 | 141 | % plotNavigation(navSolutions, settings); 142 | 143 | disp('Post processing of the signal is over.'); 144 | 145 | else 146 | % Error while opening the data file. 147 | error('Unable to read file %s: %s.', settings.fileName, message); 148 | end % if (fid > 0) 149 | -------------------------------------------------------------------------------- /pre_processing/calc_pseudo_ranges.m: -------------------------------------------------------------------------------- 1 | function [pseudoranges] = calc_pseudo_ranges(trackResults, ... 2 | msOfTheSignal, ... 3 | channelList, settings) 4 | %calculatePseudoranges finds relative pseudoranges for all satellites 5 | %listed in CHANNELLIST at the specified millisecond of the processed 6 | %signal. The pseudoranges contain unknown receiver clock offset. It can be 7 | %found by the least squares position search procedure. 8 | % 9 | %[pseudoranges] = calculatePseudoranges(trackResults, msOfTheSignal, ... 10 | % channelList, settings) 11 | % 12 | % Inputs: 13 | % trackResults - output from the tracking function 14 | % msOfTheSignal - pseudorange measurement point (millisecond) in 15 | % the trackResults structure 16 | % channelList - list of channels to be processed 17 | % settings - receiver settings 18 | % 19 | % Outputs: 20 | % pseudoranges - relative pseudoranges to the satellites. 21 | 22 | %-------------------------------------------------------------------------- 23 | % SoftGNSS v3.0 24 | % 25 | % Copyright (C) Darius Plausinaitis 26 | % Written by Darius Plausinaitis 27 | % Based on Peter Rinder and Nicolaj Bertelsen 28 | %-------------------------------------------------------------------------- 29 | % 30 | %This program is free software; you can redistribute it and/or 31 | %modify it under the terms of the GNU General Public License 32 | %as published by the Free Software Foundation; either version 2 33 | %of the License, or (at your option) any later version. 34 | % 35 | %This program is distributed in the hope that it will be useful, 36 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 37 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 38 | %GNU General Public License for more details. 39 | % 40 | %You should have received a copy of the GNU General Public License 41 | %along with this program; if not, write to the Free Software 42 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 43 | %USA. 44 | %-------------------------------------------------------------------------- 45 | 46 | % CVS record: 47 | % $Id: calculatePseudoranges.m,v 1.1.2.18 2006/08/09 17:20:11 dpl Exp $ 48 | 49 | %--- Set initial travel time to infinity ---------------------------------- 50 | % Later in the code a shortest pseudorange will be selected. Therefore 51 | % pseudoranges from non-tracking channels must be the longest - e.g. 52 | % infinite. 53 | travelTime = inf(1, settings.numberOfChannels); 54 | 55 | % Find number of samples per spreading code 56 | samplesPerCode = round(settings.samplingFreq / ... 57 | (settings.codeFreqBasis / settings.codeLength)); 58 | 59 | %--- For all channels in the list ... 60 | for channelNr = channelList 61 | 62 | %--- Compute the travel times ----------------------------------------- 63 | travelTime(channelNr) = ... 64 | trackResults(channelNr).absoluteSample(msOfTheSignal(channelNr)) / samplesPerCode; 65 | end 66 | 67 | %--- Truncate the travelTime and compute pseudoranges --------------------- 68 | minimum = floor(min(travelTime)); 69 | travelTime = travelTime - minimum + settings.startOffset; 70 | 71 | %--- Convert travel time to a distance ------------------------------------ 72 | % The speed of light must be converted from meters per second to meters 73 | % per millisecond. 74 | pseudoranges = travelTime * (settings.c / 1000); 75 | -------------------------------------------------------------------------------- /pre_processing/ddm_processing.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | % Script postProcessing.m processes the raw signal from the specified data 10 | % file (in settings) operating on blocks of 37 seconds of data. 11 | % 12 | % First it runs acquisition code identifying the satellites in the file, 13 | % then the code and carrier for each of the satellites are tracked, storing 14 | % the 1msec accumulations. After processing all satellites in the 37 sec 15 | % data block, then postNavigation is called. It calculates pseudoranges 16 | % and attempts a position solutions. At the end plots are made for that 17 | % block of data. 18 | 19 | % THE SCRIPT "RECIPE" 20 | % 21 | % The purpose of this script is to combine all parts of the software 22 | % receiver. 23 | % 24 | % 1.1) Open the data file for the processing and seek to desired point. 25 | % 26 | % 2.1) Acquire satellites 27 | % 28 | % 3.1) Initialize channels (preRun.m). 29 | % 3.2) Pass the channel structure and the file identifier to the tracking 30 | % function. It will read and process the data. The tracking results are 31 | % stored in the trackResults structure. The results can be accessed this 32 | % way (the results are stored each millisecond): 33 | % trackResults(channelNumber).XXX(fromMillisecond : toMillisecond), where 34 | % XXX is a field name of the result (e.g. I_P, codePhase etc.) 35 | % 36 | % 4) Pass tracking results to the navigation solution function. It will 37 | % decode navigation messages, find satellite positions, measure 38 | % pseudoranges and find receiver position. 39 | % 40 | % 5) Plot the results. 41 | 42 | %% Initialization ========================================================= 43 | disp ('Starting processing...'); 44 | 45 | [fid, message] = fopen(settings.fileName, 'rb', 'ieee-le'); 46 | 47 | %If success, then process the data 48 | if (fid > 0) 49 | 50 | % Move the starting point of processing. Can be used to start the 51 | % signal processing at any point in the data record (e.g. good for long 52 | % records or for signal processing in blocks). 53 | fseek(fid, settings.skipNumberOfBytes, 'bof'); 54 | % Do acquisition if it is not disabled in settings or if the variable 55 | % acqResults does not exist. 56 | 57 | if ((settings.skipAcquisition == 0) || ~exist('acqResults', 'var')) 58 | 59 | % Read data for acquisition. 60 | fseek(fid, settings.skipNumberOfBytes, 'bof'); 61 | tmpdata = fread(fid, settings.dataExtractLen, settings.dataType)'; 62 | 63 | tmpdata(tmpdata==1) = -1; 64 | tmpdata(tmpdata==3) = -3; 65 | tmpdata(tmpdata==0) = 1; 66 | tmpdata(tmpdata==2) = 3; 67 | 68 | L = floor((length(tmpdata)/4)); 69 | 70 | Data4I = double(tmpdata(1:4:4*(L))); 71 | Data3I = double(tmpdata(2:4:4*(L))); 72 | Data2I = double(tmpdata(3:4:4*(L))); 73 | Data1I = double(tmpdata(4:4:4*(L))); 74 | 75 | data = Data1I; 76 | 77 | if settings.modulationRequired 78 | dataLen = settings.dataExtractLen / 2; 79 | phasePoints = 2 * pi * settings.IF / settings.samplingFreq * (0 : dataLen-1); 80 | data = data(1:2:end) .* cos(phasePoints) - data(2:2:end) .* sin(phasePoints); 81 | elseif settings.demodulationRequired 82 | phasePoints = (0 : settings.dataExtractLen/4-1) * 2 * pi * settings.IF / settings.samplingFreq; 83 | data = data .* cos(phasePoints) + 1i * data .* sin(phasePoints); 84 | end 85 | 86 | disp (' Acquiring satellites...'); 87 | 88 | if isequal(settings.acquisitionType, 'normalAcquisition') 89 | acqResults = acquisition(data, settings); 90 | elseif isequal(settings.acquisitionType, 'weakAcquisition') 91 | acqResults = weak_acquisition(data, settings); 92 | elseif isequal(settings.acquisitionType, 'halfBitAcquisition') 93 | acqResults = half_bit_acquisition(data, settings, settings.dataExtractLen); 94 | end 95 | 96 | end 97 | 98 | plot_acquisition(acqResults); 99 | 100 | %% Initialize channels and prepare for the run ============================ 101 | % Start further processing only if a GNSS signal was acquired (the 102 | % field FREQUENCY will be set to 0 for all not acquired signals) 103 | if (any(acqResults.carrFreq)) 104 | channel = pre_run(acqResults, settings); 105 | show_channel_status(channel, settings); 106 | else 107 | % No satellites to track, exit 108 | disp('No GNSS signals detected, signal processing finished.'); 109 | trackResults = []; 110 | return; 111 | end 112 | 113 | % Close the data file 114 | fclose(fid); 115 | else 116 | % Error while opening the data file. 117 | error('Unable to read file %s: %s.', settings.fileName, message); 118 | end % if (fid > 0) 119 | -------------------------------------------------------------------------------- /pre_processing/ddm_processing_woodbine.m: -------------------------------------------------------------------------------- 1 | % clear; close all; 2 | format ('compact'); 3 | format ('long', 'g'); 4 | %-------------------------------------------------------------- 5 | addpath include % The software receiver functions 6 | addpath geoFunctions % Position calculation related functions 7 | %-------------------------------------------------------------- 8 | GPSL1freq = 1575.42*power(10,6); 9 | 10 | Channel = 1; 11 | 12 | switch Channel 13 | case 1 14 | % NLHCP = load('./../../Woodbine_47a_ch1_1.mat'); 15 | SourceFilepath = sprintf('./../../Woodbine_47a_ch1_1.bin'); 16 | case 2 17 | NRHCP = load('./../../Woodbine_47a_ch2_1.mat'); 18 | SourceFilepath = sprintf('./../../Woodbine_47a_ch2_1.bin'); 19 | case 3 20 | ZRHCP = load('./../../Woodbine_cf7_ch1_1.mat'); 21 | SourceFilepath = sprintf('./../../Woodbine_cf7_ch1_1.bin'); 22 | end 23 | 24 | [~,SourceFilename,ext] = fileparts(SourceFilepath); 25 | settings = initSettings(SourceFilepath); 26 | samplesPerCode = round(settings.samplingFreq/(settings.codeFreqBasis / settings.codeLength)); 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | [fid, message] = fopen(settings.fileName, 'r','ieee-le'); 29 | fseek(fid, settings.skipNumberOfBytes, 'bof'); 30 | [Sample, count] = fread(fid,[1, samplesPerCode*1000*2], settings.dataType); 31 | fclose(fid); 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | I = Sample(1:2:end)/2048; 34 | Q = Sample(2:2:end)/2048; 35 | %-------------------------------------------------------------- 36 | ts = 1/settings.samplingFreq; % Sampling period in sec 37 | tc = 1/settings.codeFreqBasis; % C/A chip period in sec 38 | %-------------------------------------------------------------- 39 | DelayDopplerMap = zeros(11,21); 40 | DDMcollector = cell(100,32); 41 | SatelliteList = 1:32; 42 | %-------------------------------------------------------------- 43 | Delayaxis = -5:1:5; 44 | Doppleraxis = -5000:500:5000; 45 | DDMPhase = (0 : (samplesPerCode-1)) * 2 * pi * ts; 46 | 47 | k=2; 48 | %for k=1:10 49 | 50 | switch Channel 51 | case 1 52 | PRN = SatelliteList(NLHCP.acqResults(k).peakMetric>1.3); 53 | DopplerFreq = NLHCP.acqResults(k).carrFreq(PRN); 54 | Delay = NLHCP.acqResults(k).codePhase(PRN); 55 | case 2 56 | PRN = SatelliteList(NRHCP.acqResults(k).peakMetric>1.3); 57 | DopplerFreq = NRHCP.acqResults(k).carrFreq(PRN); 58 | Delay = NRHCP.acqResults(k).codePhase(PRN); 59 | case 3 60 | PRN = SatelliteList(ZRHCP.acqResults(k).peakMetric>1.3); 61 | DopplerFreq = ZRHCP.acqResults(k).carrFreq(PRN); 62 | Delay = ZRHCP.acqResults(k).codePhase(PRN); 63 | end 64 | 65 | %-------------------------------------------------------------- 66 | signal_i = I(samplesPerCode*(k-1)+1:samplesPerCode*k); 67 | signal_q = Q(samplesPerCode*(k-1)+1:samplesPerCode*k); 68 | RecSignal = signal_i+1i*signal_q; 69 | %RecSignal = signal_i-signal_q; 70 | %-------------------------------------------------------------- 71 | tmpDDMcollector = cell(1,32); 72 | 73 | for ll = 1:length(PRN) 74 | CAcode = generateCAcode(PRN(ll)); 75 | codeValueIndex = ceil((ts * (1:samplesPerCode)) / tc); 76 | codeValueIndex(end) = 1023; 77 | tmpCACodesTable= CAcode(codeValueIndex); 78 | 79 | for ii = 1:length(Delayaxis) 80 | for jj = 1:21 81 | CACodesTable = circshift(tmpCACodesTable,-(Delay(ll)-39*Delayaxis(ii))); 82 | %Y = sum(RecSignal.*CACodesTable.*(cos(DDMPhase.*(DopplerFreq(ll)-Doppleraxis(jj)))+1i*sin(DDMPhase.*(DopplerFreq(ll)-Doppleraxis(jj)))),'omitnan'); 83 | YY = sum(RecSignal.*CACodesTable.*(exp(-1i*DDMPhase.*(GPSL1freq+Doppleraxis(jj)))) ,'omitnan'); 84 | DelayDopplerMap(ii,jj) = (abs(YY))^2; 85 | end 86 | end 87 | 88 | tmpDDMcollector{PRN(ll)}=DelayDopplerMap; 89 | 90 | end 91 | 92 | DDMcollector(k,:) = tmpDDMcollector; 93 | 94 | fprintf('%d th acquistion results has processed \n',k); 95 | %end 96 | 97 | %save(sprintf('/Volumes/TOSHIBA/bladeRF/200223/1/Results/cf7_ch1/DDM_ZRHCP_1_sub.mat'),'DDMcollector'); 98 | 99 | max(max(DDMcollector{2, 3})) 100 | 101 | % figure(1) 102 | % imagesc(Doppleraxis,Delayaxis,DelayDopplerMap) 103 | % xlabel('\DeltaDoppler Frequency') 104 | % ylabel('Delay (chip)') 105 | -------------------------------------------------------------------------------- /pre_processing/find_preambles.m: -------------------------------------------------------------------------------- 1 | function [firstSubFrame, activeChnList] = find_preambles(trackResults, ... 2 | settings) 3 | % findPreambles finds the first preamble occurrence in the bit stream of 4 | % each channel. The preamble is verified by check of the spacing between 5 | % preambles (6sec) and parity checking of the first two words in a 6 | % subframe. At the same time function returns list of channels, that are in 7 | % tracking state and with valid preambles in the nav data stream. 8 | % 9 | %[firstSubFrame, activeChnList] = findPreambles(trackResults, settings) 10 | % 11 | % Inputs: 12 | % trackResults - output from the tracking function 13 | % settings - Receiver settings. 14 | % 15 | % Outputs: 16 | % firstSubframe - the array contains positions of the first 17 | % preamble in each channel. The position is ms count 18 | % since start of tracking. Corresponding value will 19 | % be set to 0 if no valid preambles were detected in 20 | % the channel. 21 | % activeChnList - list of channels containing valid preambles 22 | 23 | %-------------------------------------------------------------------------- 24 | % SoftGNSS v3.0 25 | % 26 | % Copyright (C) Darius Plausinaitis, Peter Rinder and Nicolaj Bertelsen 27 | % Written by Darius Plausinaitis, Peter Rinder and Nicolaj Bertelsen 28 | %-------------------------------------------------------------------------- 29 | % 30 | %This program is free software; you can redistribute it and/or 31 | %modify it under the terms of the GNU General Public License 32 | %as published by the Free Software Foundation; either version 2 33 | %of the License, or (at your option) any later version. 34 | % 35 | %This program is distributed in the hope that it will be useful, 36 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 37 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 38 | %GNU General Public License for more details. 39 | % 40 | %You should have received a copy of the GNU General Public License 41 | %along with this program; if not, write to the Free Software 42 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 43 | %USA. 44 | %-------------------------------------------------------------------------- 45 | 46 | % CVS record: 47 | % $Id: findPreambles.m,v 1.1.2.10 2006/08/14 11:38:22 dpl Exp $ 48 | 49 | % Preamble search can be delayed to a later point in the tracking results 50 | % to avoid noise due to tracking loop transients 51 | searchStartOffset = 0; 52 | 53 | %--- Initialize the firstSubFrame array ----------------------------------- 54 | firstSubFrame = zeros(1, settings.numberOfChannels); 55 | 56 | %--- Generate the preamble pattern ---------------------------------------- 57 | preamble_bits = [1 -1 -1 -1 1 -1 1 1]; 58 | 59 | % "Upsample" the preamble - make 20 vales per one bit. The preamble must be 60 | % found with precision of a sample. 61 | preamble_ms = kron(preamble_bits, ones(1, 20)); 62 | 63 | %--- Make a list of channels excluding not tracking channels -------------- 64 | activeChnList = find([trackResults.status] ~= '-'); 65 | 66 | %=== For all tracking channels ... 67 | for channelNr = activeChnList 68 | 69 | %% Correlate tracking output with preamble ================================ 70 | % Read output from tracking. It contains the navigation bits. The start 71 | % of record is skiped here to avoid tracking loop transients. 72 | bits = trackResults(channelNr).I_P(1 + searchStartOffset : end); 73 | 74 | % Now threshold the output and convert it to -1 and +1 75 | bits(bits > 0) = 1; 76 | bits(bits <= 0) = -1; 77 | 78 | % Correlate tracking output with the preamble 79 | tlmXcorrResult = xcorr(bits, preamble_ms); 80 | 81 | %% Find all starting points off all preamble like patterns ================ 82 | clear index 83 | clear index2 84 | 85 | xcorrLength = (length(tlmXcorrResult) + 1) /2; 86 | 87 | %--- Find at what index/ms the preambles start ------------------------ 88 | index = find(... 89 | abs(tlmXcorrResult(xcorrLength : xcorrLength * 2 - 1)) > 153)' + ... 90 | searchStartOffset; 91 | 92 | %% Analyze detected preamble like patterns ================================ 93 | for i = 1:size(index) % For each occurrence 94 | 95 | %--- Find distances in time between this occurrence and the rest of 96 | %preambles like patterns. If the distance is 6000 milliseconds (one 97 | %subframe), the do further verifications by validating the parities 98 | %of two GPS words 99 | 100 | index2 = index - index(i); 101 | 102 | if (~isempty(find(index2 == 6000))) 103 | 104 | %=== Re-read bit vales for preamble verification ============== 105 | % Preamble occurrence is verified by checking the parity of 106 | % the first two words in the subframe. Now it is assumed that 107 | % bit boundaries a known. Therefore the bit values over 20ms are 108 | % combined to increase receiver performance for noisy signals. 109 | % in Total 62 bits mast be read : 110 | % 2 bits from previous subframe are needed for parity checking; 111 | % 60 bits for the first two 30bit words (TLM and HOW words). 112 | % The index is pointing at the start of TLM word. 113 | bits = trackResults(channelNr).I_P(index(i)-40 : ... 114 | index(i) + 20 * 60 -1)'; 115 | 116 | %--- Combine the 20 values of each bit ------------------------ 117 | bits = reshape(bits, 20, (size(bits, 1) / 20)); 118 | bits = sum(bits); 119 | 120 | % Now threshold and make it -1 and +1 121 | bits(bits > 0) = 1; 122 | bits(bits <= 0) = -1; 123 | 124 | %--- Check the parity of the TLM and HOW words ---------------- 125 | if (navPartyChk(bits(1:32)) ~= 0) && ... 126 | (navPartyChk(bits(31:62)) ~= 0) 127 | % Parity was OK. Record the preamble start position. Skip 128 | % the rest of preamble pattern checking for this channel 129 | % and process next channel. 130 | 131 | firstSubFrame(channelNr) = index(i); 132 | break; 133 | end % if parity is OK ... 134 | 135 | end % if (~isempty(find(index2 == 6000))) 136 | end % for i = 1:size(index) 137 | 138 | % Exclude channel from the active channel list if no valid preamble was 139 | % detected 140 | if firstSubFrame(channelNr) == 0 141 | 142 | % Exclude channel from further processing. It does not contain any 143 | % valid preamble and therefore nothing more can be done for it. 144 | activeChnList = setdiff(activeChnList, channelNr); 145 | 146 | disp(['Could not find valid preambles in channel ', ... 147 | num2str(channelNr),'!']); 148 | end 149 | 150 | end % for channelNr = activeChnList 151 | -------------------------------------------------------------------------------- /pre_processing/gen_ca_code.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function [ caCode ] = gen_ca_code( filePath, prnList ) 10 | %GEN_CA_CODE Returns C/A code for specified PRN list. 11 | % Returns a MxN matrix where each row corresponds to 12 | % a PRN where M is length of input argument list and N 13 | % is equal to the number of chips (1023). 14 | fileName = [filePath, 'caCode.mat']; 15 | genCaCode = true; 16 | 17 | if exist(fileName, 'file') == 2 18 | caCode=load(fileName); 19 | if (length(prnList) == 1 && any(ismember(prnList, caCode.prnList))) || ... 20 | all(caCode.prnList == prnList) 21 | caCode=caCode.caCode(prnList, :); 22 | genCaCode = false; 23 | else 24 | genCaCode = true; 25 | end 26 | end 27 | 28 | if genCaCode 29 | 30 | %--- Make the code shift array. The shift depends on the PRN number ------- 31 | % The g2s vector holds the appropriate shift of the g2 code to generate 32 | % the C/A code (ex. for SV#19 - use a G2 shift of g2s(19) = 471) 33 | g2s = [ 5, 6, 7, 8, 17, 18, 139, 140, 141, 251, ... 34 | 252, 254, 255, 256, 257, 258, 469, 470, 471, 472, ... 35 | 473, 474, 509, 512, 513, 514, 515, 516, 859, 860, ... 36 | 861, 862 ... end of shifts for GPS satellites 37 | ... Shifts for the ground GPS transmitter are not included 38 | ... Shifts for EGNOS and WAAS satellites (true_PRN = PRN + 87) 39 | 145, 175, 52, 21, 237, 235, 886, 657, ... 40 | 634, 762, 355, 1012, 176, 603, 130, 359, 595, 68, ... 41 | 386]; 42 | 43 | 44 | % Parameters 45 | caCodeLength = 1023; 46 | numPrns = length(prnList); 47 | 48 | % C/A code buffer 49 | caCode = zeros(numPrns, caCodeLength); 50 | 51 | % Iterate over PRN list 52 | for prnIdx = 1:numPrns 53 | prn = prnList(prnIdx); 54 | % Generate all G1 & G2 signal chips 55 | g1 = zeros(1, caCodeLength); 56 | g2 = zeros(1, caCodeLength); 57 | reg1 = -1*ones(1, 10); 58 | reg2 = -1*ones(1, 10); 59 | for i=1:caCodeLength 60 | g1(i) = reg1(10); 61 | g2(i) = reg2(10); 62 | saveBitG1 = reg1(3)*reg1(10); 63 | saveBitG2 = reg2(2)*reg2(3)*reg2(6)*reg2(8)*reg2(9)*reg2(10); 64 | reg1 = circshift(reg1, 1); 65 | reg2 = circshift(reg2, 1); 66 | reg1(1) = saveBitG1; 67 | reg2(1) = saveBitG2; 68 | end 69 | 70 | % Form single sample C/A code by multiplying G1 and G2 71 | g2shift = g2s(prn); 72 | caCode(prnIdx, :) = -(g1 .* circshift(g2, g2shift)); 73 | end 74 | save(fileName, 'caCode', 'prnList'); 75 | 76 | end 77 | end 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /pre_processing/pre_proc_norm_acq_parcode.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function pprocSignals = pre_proc_norm_acq_parcode(sdrParams, caCode) 10 | 11 | optimizeOption = 2; % 2 for double efficiency 12 | if optimizeOption ~= 1 && optimizeOption ~= 2 13 | error('Only two optimization options are available for parallel code search algorithm.'); 14 | end 15 | 16 | %%% Parameters 17 | fileNum = sdrParams.stateParams.numFilesProcessed + 1; 18 | samplingFreqHz = sdrParams.dataFileParamsList{fileNum}.samplingFreqHz; 19 | intermFreqHz = sdrParams.dataFileParamsList{fileNum}.intermFreqHz; 20 | chipRateHz = sdrParams.sysParams.caCodeChipRateHz; 21 | acqDopplerBwKhz = sdrParams.sysParams.acqDopplerBwKhz; 22 | acqDopplerResHz = sdrParams.sysParams.acqDopplerResHz; 23 | 24 | samplesPerMs = samplingFreqHz * 1e-3; 25 | numChipsPerMs = chipRateHz * 1e-3; 26 | prnList = sdrParams.sysParams.acqSatelliteList; 27 | 28 | if floor(samplesPerMs) ~= samplesPerMs 29 | error('Number of samples per millisecond can''t have decimal point.'); 30 | end 31 | 32 | % Prepare per algorithm per block common signals 33 | 34 | %%% Determine maximum extent to which averaging can be done 35 | averFactor = [1]; 36 | temAverFactor = 1; 37 | while samplingFreqHz/temAverFactor > sdrParams.sysParams.minSamplingFreqHz 38 | temAverFactor=temAverFactor+1; 39 | goodAverFactor = samplesPerMs/temAverFactor; 40 | if floor(goodAverFactor) == goodAverFactor 41 | averFactor = [averFactor, temAverFactor]; 42 | end 43 | end 44 | 45 | averFactor = averFactor(end); 46 | 47 | %%% each PRN C/A code sequence frequency transform here. 48 | caCodeMappingInd = floor((0:samplesPerMs/averFactor-1) * ... 49 | (chipRateHz *averFactor/ samplingFreqHz)) + 1; 50 | 51 | caCodeMappingInd(caCodeMappingInd == 0) = 1; 52 | caCodeMappingInd(caCodeMappingInd > numChipsPerMs) = numChipsPerMs; 53 | caCodesTable = caCode(:, caCodeMappingInd); 54 | caCodesTable = conj(fft(caCodesTable, [], 2)); 55 | 56 | 57 | % Prepare baseband doppler modulated matrix of data 58 | if optimizeOption == 1 59 | numDopplerSamples = floor(acqDopplerBwKhz * 1e3 / acqDopplerResHz) + 1; 60 | intermFreqVec = intermFreqHz; 61 | dopplerFreqVec = acqDopplerResHz*(0:numDopplerSamples-1); 62 | dopplerFreqVec = dopplerFreqVec - median(dopplerFreqVec); % make it two sided 63 | iFfreqVec = intermFreqVec + dopplerFreqVec; 64 | dopplerFreqExp = exp(2i * pi * iFfreqVec' * (0:samplesPerMs-1)... 65 | /(samplingFreqHz)); 66 | 67 | pprocSignals.dopplerFreqExp = dopplerFreqExp; 68 | pprocSignals.dopplerResHz = acqDopplerResHz; 69 | 70 | elseif optimizeOption == 2 71 | 72 | % In this method, frequency shift is integer in terms of 1/1ms=1khz 73 | acqDopplerResHz = 1e3; % 1/0.001 for time duration of 1 millisecond 74 | numDopplerSamples = floor(acqDopplerBwKhz * 1e3 / acqDopplerResHz) + 1; 75 | iFfreqVec = intermFreqHz - acqDopplerBwKhz * 0.5e3; 76 | dopplerFreqInitExp = exp(2i * pi * iFfreqVec * (0:samplesPerMs-1)... 77 | /(samplingFreqHz)); 78 | dopplerFreqDeltaExp = exp(2i * pi * acqDopplerResHz * (0:samplesPerMs-1)... 79 | /(samplingFreqHz)); 80 | 81 | pprocSignals.shiftFactor = acqDopplerResHz * samplesPerMs / ... 82 | samplingFreqHz; 83 | pprocSignals.dopplerFreqInitExp = dopplerFreqInitExp; 84 | pprocSignals.dopplerFreqDeltaExp = dopplerFreqDeltaExp; 85 | pprocSignals.numDopplerSamples = numDopplerSamples; 86 | pprocSignals.dopplerResHz = acqDopplerResHz; 87 | 88 | end 89 | 90 | pprocSignals.caCodesTable = caCodesTable; 91 | pprocSignals.averFactor = averFactor; 92 | pprocSignals.optimizeOption = optimizeOption; 93 | 94 | 95 | end 96 | -------------------------------------------------------------------------------- /pre_processing/pre_proc_weak_acq_dbzp.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function pprocSignals = pre_proc_weak_acq_dbzp(sdrParams, caCode) 10 | 11 | %%% Parameters 12 | fileNum = sdrParams.stateParams.numFilesProcessed + 1; 13 | samplingFreqHz = sdrParams.dataFileParamsList{fileNum}.samplingFreqHz; 14 | intermFreqHz = sdrParams.dataFileParamsList{fileNum}.intermFreqHz; 15 | chipRateHz = sdrParams.sysParams.caCodeChipRateHz; 16 | acqDopplerBwKhz = sdrParams.sysParams.acqDopplerBwKhz; 17 | cpiTimeMs = sdrParams.sysParams.coherentProcessingTimeMS; 18 | 19 | samplesPerCpi = samplingFreqHz * cpiTimeMs *1e-3; 20 | samplesPerMs = samplingFreqHz * 1e-3; 21 | numChipsPerMs = chipRateHz * 1e-3; 22 | numCaCodeFolds = cpiTimeMs; 23 | prnList = sdrParams.sysParams.acqSatelliteList; 24 | 25 | if floor(samplesPerCpi) ~= samplesPerCpi 26 | error('Number of samples per coherent processing interval can''t have decimal point.'); 27 | end 28 | 29 | % Prepare per algorithm per block common signals 30 | 31 | %%% Determine maximum extent to which averaging can be done 32 | averFactor = [1]; 33 | temAverFactor = 1; 34 | while samplingFreqHz/temAverFactor > sdrParams.sysParams.minSamplingFreqHz 35 | temAverFactor=temAverFactor+1; 36 | goodAverFactor = samplesPerCpi/temAverFactor; 37 | if floor(goodAverFactor) == goodAverFactor 38 | averFactor = [averFactor, temAverFactor]; 39 | end 40 | end 41 | % choose highest dividing factor to maximize number of input samples 42 | % for one averaged output. 43 | averFactor = averFactor(end); 44 | 45 | 46 | %%% Find out integer number of samples per block and doppler bins 47 | 48 | numSamplesPerCpi = samplesPerCpi / averFactor; 49 | %number of doppler bins should be atleast 2/t_cpi 50 | minDopplerBins = round(acqDopplerBwKhz * cpiTimeMs); 51 | candidateNumDopplerBins = []; 52 | for ndb=minDopplerBins:minDopplerBins*2 53 | if floor(numSamplesPerCpi/ndb) == (numSamplesPerCpi/ndb) 54 | candidateNumDopplerBins = [candidateNumDopplerBins, ndb]; 55 | end 56 | end 57 | if isempty(candidateNumDopplerBins) 58 | error('Unable to find integer multiple of blockSize for DBZP.'); 59 | end 60 | numDopplerBins = min(candidateNumDopplerBins); 61 | numSamplesPerBlock = numSamplesPerCpi/numDopplerBins; 62 | 63 | 64 | 65 | %%% generate code for current PRN 66 | % Convert CA code to DBZP format. 67 | 68 | %%% Map to samples of 1ms 69 | caCodeSampleMap = floor((0:samplesPerMs/averFactor-1) * (numChipsPerMs*averFactor/samplesPerMs)) + 1; 70 | caCodeMapped = caCode(prnList, caCodeSampleMap); 71 | caCodeMapped = repmat(caCodeMapped, 1, numCaCodeFolds); 72 | 73 | numBlocks = samplesPerCpi/averFactor/numSamplesPerBlock; 74 | caCodeDbzpTable = zeros(length(prnList), 2*numSamplesPerBlock, numBlocks); 75 | for prnIdx=1:length(prnList) 76 | caCodePerPrn = reshape(caCodeMapped(prnIdx, :), numSamplesPerBlock, numBlocks); 77 | caCodePerPrn = [caCodePerPrn; zeros(numSamplesPerBlock, numBlocks)]; 78 | caCodeDbzpTable(prnIdx, :, :) = caCodePerPrn; 79 | end 80 | 81 | 82 | %%% Prepare baseband doppler modulated matrix of data 83 | 84 | dopplerFreqExp = exp(2i * pi * (intermFreqHz/samplingFreqHz) * (0:samplesPerCpi-1)); 85 | 86 | 87 | %%% Save preprocessing common signals and parameters to used in the 88 | % processing of DBZP algorithm. 89 | 90 | pprocSignals.caCodesTable = caCodeDbzpTable; 91 | pprocSignals.dopplerFreqExp = dopplerFreqExp; 92 | pprocSignals.numBlocks = numBlocks; 93 | pprocSignals.numSamplesPerBlock = numSamplesPerBlock; 94 | pprocSignals.averFactor = averFactor; 95 | 96 | end 97 | -------------------------------------------------------------------------------- /pre_processing/pre_process.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function [ acqAlgoPprocSignals, rxData] = pre_process(sdrParams) 10 | %%% pre_process is responsible for configuration of settings 11 | % and pre-conditioning of the input data. 12 | % This includes per acquisition algorithm parameters 13 | % and common signal preparations. 14 | 15 | %%% Initialize the settings 16 | 17 | % Read data from configured channels 18 | % from current file. 19 | rxData = read_file_data(sdrParams); 20 | 21 | 22 | print_string('Generating C/A code.'); 23 | 24 | % Gen/Read CA/Code 25 | caCodeTable = gen_ca_code(sdrParams.stateParams.dataPathIn, ... 26 | sdrParams.sysParams.acqSatelliteList); 27 | 28 | % Prepare signals for each algorithm 29 | 30 | acqAlgoPprocSignals = cell(0); 31 | acqAlgosList = sdrParams.sysParams.acqAlgosList; 32 | for acqIdx = 1:length(acqAlgosList) 33 | 34 | algoName = acqAlgosList{acqIdx}; 35 | 36 | switch algoName 37 | case 'norm_acq_parcode' 38 | print_string('Preprocessing: pre_proc_norm_acq_parcode()'); 39 | pprocSignals = pre_proc_norm_acq_parcode(sdrParams, caCodeTable); 40 | 41 | case 'weak_acq_dbzp' 42 | print_string('Preprocessing: Calling pre_proc_weak_acq_dbzp()'); 43 | pprocSignals = pre_proc_weak_acq_dbzp(sdrParams, caCodeTable); 44 | 45 | otherwise 46 | error("Acquisition algorithm is not recognized."); 47 | end 48 | 49 | acqAlgoPprocSignals{acqIdx} = pprocSignals; 50 | end 51 | 52 | end 53 | 54 | -------------------------------------------------------------------------------- /pre_processing/read_file_data.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function [ outData ] = read_file_data( sdrParams ) 10 | %%%READ_FILE_DATA Read input file data and converts to suitable format. 11 | % Reads input file data and returns a cell of dimension 12 | % of 1xM is number of channels in the data file. 13 | 14 | 15 | % Extract relevant parameters 16 | fileNum = sdrParams.stateParams.numFilesProcessed + 1; 17 | fileName = sdrParams.stateParams.fileNames{fileNum}; 18 | dataType = sdrParams.dataFileParamsList{fileNum}.dataType; 19 | samplingFreqHz = sdrParams.dataFileParamsList{fileNum}.samplingFreqHz; 20 | totalChannels = sdrParams.dataFileParamsList{fileNum}.totalChannels; 21 | selectedChannel = sdrParams.dataFileParamsList{fileNum}.selectedChannel; 22 | skipNumberOfBytes = sdrParams.sysParams.skipNumberOfBytes; 23 | 24 | % Define data buffer 25 | outData = cell(1); 26 | 27 | % Open file. 28 | currFileFullName = [sdrParams.stateParams.dataPathIn, fileName]; 29 | [fid, message] = fopen(currFileFullName, 'rb', 'ieee-le'); 30 | if fid <= 0 31 | % Error while opening the data file. 32 | error('Unable to read file %s: %s.', stateParams.currFileName, message); 33 | else 34 | 35 | % File opened successfully, read the contents. 36 | processDataDurMs = max([sdrParams.sysParams.coherentProcessingTimeMS, ... 37 | sdrParams.sysParams.incoherentProcessingTimeMS]); 38 | processDataDurSec = processDataDurMs * 1e-3; 39 | numSamples = floor(processDataDurSec * samplingFreqHz) * totalChannels; 40 | 41 | % Skip initial bytes 42 | fseek(fid, skipNumberOfBytes, 'bof'); 43 | 44 | % Read from data file. 45 | rawData = fread(fid, numSamples, dataType)'; 46 | 47 | % Check if sufficient data is present in the file. 48 | if length(rawData) < numSamples 49 | error('Configured processing interval data is not present in data file.'); 50 | end 51 | 52 | % Convert from bits to symbols if necessary 53 | % This is file specific. 54 | switch fileName 55 | case 'NTLab_Bands_GPS_GLONASS_L12.bin' 56 | mOneIdx = rawData == 1; 57 | mThreeIdx = rawData == 3; 58 | pOneIdx = rawData == 0; 59 | pThreeIdx = rawData == 2; 60 | 61 | rawData(pOneIdx) = 1; 62 | rawData(mOneIdx) = -1; 63 | rawData(pThreeIdx) = 3; 64 | rawData(mThreeIdx) = -3; 65 | 66 | if selectedChannel == -1 67 | % Read all data from all channel 68 | for chIdx=1:totalChannels 69 | print_string(['Reading input data for channel : ', ... 70 | num2str(chIdx), '/', ... 71 | num2str(totalChannels)]); 72 | outData{chIdx, :} = rawData(chIdx:totalChannels:end); 73 | end 74 | else 75 | % Read all data from selected channel 76 | print_string(['Reading input data for channel : ', ... 77 | num2str(selectedChannel), '/', ... 78 | num2str(selectedChannel)]); 79 | outData{1} = rawData(selectedChannel:totalChannels:end); 80 | end 81 | 82 | case 'GPS_and_GIOVE_A-NN-fs16_3676-if4_1304.bin' 83 | 84 | otherwise 85 | print_string('File data is assumed to be in symbols not bits.'); 86 | 87 | end 88 | end 89 | end 90 | 91 | 92 | -------------------------------------------------------------------------------- /processing/process.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Project Title: GNSS-R SDR 3 | % Author : John Bagshaw 4 | % Contact : jotshaw@yorku.ca 5 | % Supervisor : Prof.Sunil Bisnath 6 | % Institution : York University, Canada. 7 | %% 8 | 9 | function [ processResults] = process( sdrParams, ppData, rxData ) 10 | % SUMMARY Function to process coherent processing interval data. 11 | % This function takes in input Raw data, preprocessed common signals 12 | % and processes current frame (coherent processing interval for current 13 | % input data file. process_results returns cell of length C where C is 14 | % number of channels and each cell contains L cells each corresponding to 15 | % different algorithm and each each contains struct containing processing 16 | % results of delay doppler map and acquisition stats. 17 | 18 | 19 | 20 | %%% Extract coherent frame data. 21 | currFileNum = sdrParams.stateParams.numFilesProcessed + 1; 22 | dataParams = sdrParams.dataFileParamsList{currFileNum}; 23 | numSamplesPerMs = dataParams.samplingFreqHz * 1e-3; 24 | currFrameNum = sdrParams.stateParams.currFrameNum + 1; 25 | numTotalFrames = sdrParams.stateParams.numTotalFrames; 26 | numSamplesPerFrame = sdrParams.sysParams.coherentProcessingTimeMS * numSamplesPerMs; 27 | frameDataIndex = (currFrameNum-1)*numSamplesPerFrame + 1: ... 28 | currFrameNum * numSamplesPerFrame ; 29 | numChannels = length(rxData); 30 | acqAlgoList = sdrParams.sysParams.acqAlgosList; 31 | numAcqAlgos = length(acqAlgoList); 32 | processResults = cell(numChannels, numAcqAlgos); 33 | for chIdx=1:numChannels 34 | 35 | % Rx data for current frame 36 | % used for coherent integration 37 | % within the function. 38 | frameData = rxData{chIdx}(frameDataIndex); 39 | 40 | % Iterate over all algorithms and process the data. 41 | % These are core algorithms for acquisition. 42 | for acqAlgoIdx = 1:numAcqAlgos 43 | 44 | 45 | %%% Acquisition 46 | 47 | print_string(sprintf('Processing for frame: %d/%d, channel: %d/%d, algorithm: %s', ... 48 | sdrParams.stateParams.currFrameNum+1, ... 49 | sdrParams.stateParams.numTotalFrames, ... 50 | chIdx, ... 51 | numChannels, ... 52 | acqAlgoList{acqAlgoIdx}... 53 | )); 54 | 55 | 56 | clear( acqAlgoList{acqAlgoIdx} ); % Clear previous calls 57 | 58 | processResults{chIdx, acqAlgoIdx} = feval(acqAlgoList{acqAlgoIdx}, ... % Call acqusition function 59 | sdrParams, ... 60 | ppData{acqAlgoIdx}, ... 61 | frameData); 62 | 63 | 64 | %%% Tracking 65 | 66 | 67 | 68 | end 69 | end 70 | 71 | 72 | end 73 | 74 | 75 | -------------------------------------------------------------------------------- /util/calc_sin.m: -------------------------------------------------------------------------------- 1 | function [re, im] = calc_sin(ar, degree, arg_real, arg_imag) 2 | %Clenshaw summation of sinus with complex argument 3 | %[re, im] = clksin(ar, degree, arg_real, arg_imag); 4 | 5 | % Written by Kai Borre 6 | % December 20, 1995 7 | % 8 | % See also WGS2UTM or CART2UTM 9 | % 10 | % CVS record: 11 | % $Id: clksin.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 12 | %========================================================================== 13 | 14 | sin_arg_r = sin(arg_real); 15 | cos_arg_r = cos(arg_real); 16 | sinh_arg_i = sinh(arg_imag); 17 | cosh_arg_i = cosh(arg_imag); 18 | 19 | r = 2 * cos_arg_r * cosh_arg_i; 20 | i =-2 * sin_arg_r * sinh_arg_i; 21 | 22 | hr1 = 0; hr = 0; hi1 = 0; hi = 0; 23 | 24 | for t = degree : -1 : 1 25 | hr2 = hr1; 26 | hr1 = hr; 27 | hi2 = hi1; 28 | hi1 = hi; 29 | z = ar(t) + r*hr1 - i*hi - hr2; 30 | hi = i*hr1 + r*hi1 - hi2; 31 | hr = z; 32 | end 33 | 34 | r = sin_arg_r * cosh_arg_i; 35 | i = cos_arg_r * sinh_arg_i; 36 | 37 | re = r*hr - i*hi; 38 | im = r*hi + i*hr; 39 | -------------------------------------------------------------------------------- /util/cart_2_geo.m: -------------------------------------------------------------------------------- 1 | function [phi, lambda, h] = cart_2_geo(X, Y, Z, i) 2 | %CART2GEO Conversion of Cartesian coordinates (X,Y,Z) to geographical 3 | %coordinates (phi, lambda, h) on a selected reference ellipsoid. 4 | % 5 | %[phi, lambda, h] = cart2geo(X, Y, Z, i); 6 | % 7 | % Choices i of Reference Ellipsoid for Geographical Coordinates 8 | % 1. International Ellipsoid 1924 9 | % 2. International Ellipsoid 1967 10 | % 3. World Geodetic System 1972 11 | % 4. Geodetic Reference System 1980 12 | % 5. World Geodetic System 1984 13 | 14 | %Kai Borre 10-13-98 15 | %Copyright (c) by Kai Borre 16 | %Revision: 1.0 Date: 1998/10/23 17 | % 18 | % CVS record: 19 | % $Id: cart2geo.m,v 1.1.2.2 2006/08/22 13:45:59 dpl Exp $ 20 | %========================================================================== 21 | 22 | a = [6378388 6378160 6378135 6378137 6378137]; 23 | f = [1/297 1/298.247 1/298.26 1/298.257222101 1/298.257223563]; 24 | 25 | lambda = atan2(Y,X); 26 | ex2 = (2-f(i))*f(i)/((1-f(i))^2); 27 | c = a(i)*sqrt(1+ex2); 28 | phi = atan(Z/((sqrt(X^2+Y^2)*(1-(2-f(i)))*f(i)))); 29 | 30 | h = 0.1; oldh = 0; 31 | while abs(h-oldh) > 1.e-12 32 | oldh = h; 33 | N = c/sqrt(1+ex2*cos(phi)^2); 34 | phi = atan(Z/((sqrt(X^2+Y^2)*(1-(2-f(i))*f(i)*N/(N+h))))); 35 | h = sqrt(X^2+Y^2)/cos(phi)-N; 36 | end 37 | 38 | phi = phi*180/pi; 39 | % b = zeros(1,3); 40 | % b(1,1) = fix(phi); 41 | % b(2,1) = fix(rem(phi,b(1,1))*60); 42 | % b(3,1) = (phi-b(1,1)-b(1,2)/60)*3600; 43 | 44 | lambda = lambda*180/pi; 45 | % l = zeros(1,3); 46 | % l(1,1) = fix(lambda); 47 | % l(2,1) = fix(rem(lambda,l(1,1))*60); 48 | % l(3,1) = (lambda-l(1,1)-l(1,2)/60)*3600; 49 | 50 | %fprintf('\n phi =%3.0f %3.0f %8.5f',b(1),b(2),b(3)) 51 | %fprintf('\n lambda =%3.0f %3.0f %8.5f',l(1),l(2),l(3)) 52 | %fprintf('\n h =%14.3f\n',h) 53 | %%%%%%%%%%%%%% end cart2geo.m %%%%%%%%%%%%%%%%%%% 54 | -------------------------------------------------------------------------------- /util/cart_2_utm.m: -------------------------------------------------------------------------------- 1 | function [E, N, U] = cart_2_utm(X, Y, Z, zone) 2 | %CART2UTM Transformation of (X,Y,Z) to (N,E,U) in UTM, zone 'zone'. 3 | % 4 | %[E, N, U] = cart2utm(X, Y, Z, zone); 5 | % 6 | % Inputs: 7 | % X,Y,Z - Cartesian coordinates. Coordinates are referenced 8 | % with respect to the International Terrestrial Reference 9 | % Frame 1996 (ITRF96) 10 | % zone - UTM zone of the given position 11 | % 12 | % Outputs: 13 | % E, N, U - UTM coordinates (Easting, Northing, Uping) 14 | 15 | %Kai Borre -11-1994 16 | %Copyright (c) by Kai Borre 17 | % 18 | % CVS record: 19 | % $Id: cart2utm.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 20 | 21 | %This implementation is based upon 22 | %O. Andersson & K. Poder (1981) Koordinattransformationer 23 | % ved Geod\ae{}tisk Institut. Landinspekt\oe{}ren 24 | % Vol. 30: 552--571 and Vol. 31: 76 25 | % 26 | %An excellent, general reference (KW) is 27 | %R. Koenig & K.H. Weise (1951) Mathematische Grundlagen der 28 | % h\"oheren Geod\"asie und Kartographie. 29 | % Erster Band, Springer Verlag 30 | 31 | % Explanation of variables used: 32 | % f flattening of ellipsoid 33 | % a semi major axis in m 34 | % m0 1 - scale at central meridian; for UTM 0.0004 35 | % Q_n normalized meridian quadrant 36 | % E0 Easting of central meridian 37 | % L0 Longitude of central meridian 38 | % bg constants for ellipsoidal geogr. to spherical geogr. 39 | % gb constants for spherical geogr. to ellipsoidal geogr. 40 | % gtu constants for ellipsoidal N, E to spherical N, E 41 | % utg constants for spherical N, E to ellipoidal N, E 42 | % tolutm tolerance for utm, 1.2E-10*meridian quadrant 43 | % tolgeo tolerance for geographical, 0.00040 second of arc 44 | 45 | % B, L refer to latitude and longitude. Southern latitude is negative 46 | % International ellipsoid of 1924, valid for ED50 47 | 48 | a = 6378388; 49 | f = 1/297; 50 | ex2 = (2-f)*f / ((1-f)^2); 51 | c = a * sqrt(1+ex2); 52 | vec = [X; Y; Z-4.5]; 53 | alpha = .756e-6; 54 | R = [ 1 -alpha 0; 55 | alpha 1 0; 56 | 0 0 1]; 57 | trans = [89.5; 93.8; 127.6]; 58 | scale = 0.9999988; 59 | v = scale*R*vec + trans; % coordinate vector in ED50 60 | L = atan2(v(2), v(1)); 61 | N1 = 6395000; % preliminary value 62 | B = atan2(v(3)/((1-f)^2*N1), norm(v(1:2))/N1); % preliminary value 63 | U = 0.1; oldU = 0; 64 | 65 | while abs(U-oldU) > 1.e-4 66 | oldU = U; 67 | N1 = c/sqrt(1+ex2*(cos(B))^2); 68 | B = atan2(v(3)/((1-f)^2*N1+U), norm(v(1:2))/(N1+U) ); 69 | U = norm(v(1:2))/cos(B)-N1; 70 | end 71 | 72 | %Normalized meridian quadrant, KW p. 50 (96), p. 19 (38b), p. 5 (21) 73 | m0 = 0.0004; 74 | n = f / (2-f); 75 | m = n^2 * (1/4 + n*n/64); 76 | w = (a*(-n-m0+m*(1-m0))) / (1+n); 77 | Q_n = a + w; 78 | 79 | %Easting and longitude of central meridian 80 | E0 = 500000; 81 | L0 = (zone-30)*6 - 3; 82 | 83 | %Check tolerance for reverse transformation 84 | tolutm = pi/2 * 1.2e-10 * Q_n; 85 | tolgeo = 0.000040; 86 | 87 | %Coefficients of trigonometric series 88 | 89 | %ellipsoidal to spherical geographical, KW p. 186--187, (51)-(52) 90 | % bg[1] = n*(-2 + n*(2/3 + n*(4/3 + n*(-82/45)))); 91 | % bg[2] = n^2*(5/3 + n*(-16/15 + n*(-13/9))); 92 | % bg[3] = n^3*(-26/15 + n*34/21); 93 | % bg[4] = n^4*1237/630; 94 | 95 | %spherical to ellipsoidal geographical, KW p. 190--191, (61)-(62) 96 | % gb[1] = n*(2 + n*(-2/3 + n*(-2 + n*116/45))); 97 | % gb[2] = n^2*(7/3 + n*(-8/5 + n*(-227/45))); 98 | % gb[3] = n^3*(56/15 + n*(-136/35)); 99 | % gb[4] = n^4*4279/630; 100 | 101 | %spherical to ellipsoidal N, E, KW p. 196, (69) 102 | % gtu[1] = n*(1/2 + n*(-2/3 + n*(5/16 + n*41/180))); 103 | % gtu[2] = n^2*(13/48 + n*(-3/5 + n*557/1440)); 104 | % gtu[3] = n^3*(61/240 + n*(-103/140)); 105 | % gtu[4] = n^4*49561/161280; 106 | 107 | %ellipsoidal to spherical N, E, KW p. 194, (65) 108 | % utg[1] = n*(-1/2 + n*(2/3 + n*(-37/96 + n*1/360))); 109 | % utg[2] = n^2*(-1/48 + n*(-1/15 + n*437/1440)); 110 | % utg[3] = n^3*(-17/480 + n*37/840); 111 | % utg[4] = n^4*(-4397/161280); 112 | 113 | %With f = 1/297 we get 114 | 115 | bg = [-3.37077907e-3; 116 | 4.73444769e-6; 117 | -8.29914570e-9; 118 | 1.58785330e-11]; 119 | 120 | gb = [ 3.37077588e-3; 121 | 6.62769080e-6; 122 | 1.78718601e-8; 123 | 5.49266312e-11]; 124 | 125 | gtu = [ 8.41275991e-4; 126 | 7.67306686e-7; 127 | 1.21291230e-9; 128 | 2.48508228e-12]; 129 | 130 | utg = [-8.41276339e-4; 131 | -5.95619298e-8; 132 | -1.69485209e-10; 133 | -2.20473896e-13]; 134 | 135 | %Ellipsoidal latitude, longitude to spherical latitude, longitude 136 | neg_geo = 'FALSE'; 137 | 138 | if B < 0 139 | neg_geo = 'TRUE '; 140 | end 141 | 142 | Bg_r = abs(B); 143 | [res_clensin] = clsin(bg, 4, 2*Bg_r); 144 | Bg_r = Bg_r + res_clensin; 145 | L0 = L0*pi / 180; 146 | Lg_r = L - L0; 147 | 148 | %Spherical latitude, longitude to complementary spherical latitude 149 | % i.e. spherical N, E 150 | cos_BN = cos(Bg_r); 151 | Np = atan2(sin(Bg_r), cos(Lg_r)*cos_BN); 152 | Ep = atanh(sin(Lg_r) * cos_BN); 153 | 154 | %Spherical normalized N, E to ellipsoidal N, E 155 | Np = 2 * Np; 156 | Ep = 2 * Ep; 157 | [dN, dE] = clksin(gtu, 4, Np, Ep); 158 | Np = Np/2; 159 | Ep = Ep/2; 160 | Np = Np + dN; 161 | Ep = Ep + dE; 162 | N = Q_n * Np; 163 | E = Q_n*Ep + E0; 164 | 165 | if neg_geo == 'TRUE ' 166 | N = -N + 20000000; 167 | end; 168 | 169 | %%%%%%%%%%%%%%%%%%%% end cart2utm.m %%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /util/check_t.m: -------------------------------------------------------------------------------- 1 | function corrTime = check_t(time) 2 | %CHECK_T accounting for beginning or end of week crossover. 3 | % 4 | %corrTime = check_t(time); 5 | % 6 | % Inputs: 7 | % time - time in seconds 8 | % 9 | % Outputs: 10 | % corrTime - corrected time (seconds) 11 | 12 | %Kai Borre 04-01-96 13 | %Copyright (c) by Kai Borre 14 | % 15 | % CVS record: 16 | % $Id: check_t.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 17 | %========================================================================== 18 | 19 | half_week = 302400; % seconds 20 | 21 | corrTime = time; 22 | 23 | if time > half_week 24 | corrTime = time - 2*half_week; 25 | elseif time < -half_week 26 | corrTime = time + 2*half_week; 27 | end 28 | %%%%%%% end check_t.m %%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /util/clen_sin.m: -------------------------------------------------------------------------------- 1 | function result = clen_sin(ar, degree, argument) 2 | %Clenshaw summation of sinus of argument. 3 | % 4 | %result = clsin(ar, degree, argument); 5 | 6 | % Written by Kai Borre 7 | % December 20, 1995 8 | % 9 | % See also WGS2UTM or CART2UTM 10 | % 11 | % CVS record: 12 | % $Id: clsin.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 13 | %========================================================================== 14 | 15 | cos_arg = 2 * cos(argument); 16 | hr1 = 0; 17 | hr = 0; 18 | 19 | for t = degree : -1 : 1 20 | hr2 = hr1; 21 | hr1 = hr; 22 | hr = ar(t) + cos_arg*hr1 - hr2; 23 | end 24 | 25 | result = hr * sin(argument); 26 | %%%%%%%%%%%%%%%%%%%%%%% end clsin.m %%%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /util/conv_dbzp_form.m: -------------------------------------------------------------------------------- 1 | %% This function arrange linear data into DBZP form 2 | % where data_in is an 1xN array and output is 2k x (N/k) 3 | % and first half of each contains rearranged data while 4 | % second half contains zeros. 5 | 6 | function [ data_out ] = conv_dbzp_form( data_i, ... % input data 7 | k ... % block size to convert into 8 | ) 9 | 10 | data_out = reshape(data_i, k, length(data_i)/k); 11 | data_out = [data_out; zeros(size(data_out))]; 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /util/create_data_file.m: -------------------------------------------------------------------------------- 1 | data = dlmread('data.txt', ','); 2 | 3 | str = []; 4 | 5 | str = sprintf('%s\n', '{'); 6 | for i=1:100 7 | str = [str, sprintf('%s', '{')]; 8 | str = [str, sprintf('%d', data((i-1)*8+1))]; 9 | for j=(i-1)*8+2:i*8 10 | str = [str, sprintf(', %d', data(j))]; 11 | end 12 | str = [str, sprintf('%s, \n', '}')]; 13 | end 14 | str = [str, sprintf('%s\n', '};')] -------------------------------------------------------------------------------- /util/e_r_corr.m: -------------------------------------------------------------------------------- 1 | function X_sat_rot = e_r_corr(traveltime, X_sat) 2 | %E_R_CORR Returns rotated satellite ECEF coordinates due to Earth 3 | %rotation during signal travel time 4 | % 5 | %X_sat_rot = e_r_corr(traveltime, X_sat); 6 | % 7 | % Inputs: 8 | % travelTime - signal travel time 9 | % X_sat - satellite's ECEF coordinates 10 | % 11 | % Outputs: 12 | % X_sat_rot - rotated satellite's coordinates (ECEF) 13 | 14 | %Written by Kai Borre 15 | %Copyright (c) by Kai Borre 16 | % 17 | % CVS record: 18 | % $Id: e_r_corr.m,v 1.1.1.1.2.6 2006/08/22 13:45:59 dpl Exp $ 19 | %========================================================================== 20 | 21 | Omegae_dot = 7.292115147e-5; % rad/sec 22 | 23 | %--- Find rotation angle -------------------------------------------------- 24 | omegatau = Omegae_dot * traveltime; 25 | 26 | %--- Make a rotation matrix ----------------------------------------------- 27 | R3 = [ cos(omegatau) sin(omegatau) 0; 28 | -sin(omegatau) cos(omegatau) 0; 29 | 0 0 1]; 30 | 31 | %--- Do the rotation ------------------------------------------------------ 32 | X_sat_rot = R3 * X_sat; 33 | 34 | %%%%%%%% end e_r_corr.m %%%%%%%%%%%%%%%%%%%% -------------------------------------------------------------------------------- /util/find_utm_zone.m: -------------------------------------------------------------------------------- 1 | function utmZone = find_utm_zone(latitude, longitude) 2 | %Function finds the UTM zone number for given longitude and latitude. 3 | %The longitude value must be between -180 (180 degree West) and 180 (180 4 | %degree East) degree. The latitude must be within -80 (80 degree South) and 5 | %84 (84 degree North). 6 | % 7 | %utmZone = findUtmZone(latitude, longitude); 8 | % 9 | %Latitude and longitude must be in decimal degrees (e.g. 15.5 degrees not 10 | %15 deg 30 min). 11 | 12 | %-------------------------------------------------------------------------- 13 | % SoftGNSS v3.0 14 | % 15 | % Copyright (C) Darius Plausinaitis 16 | % Written by Darius Plausinaitis 17 | %-------------------------------------------------------------------------- 18 | %This program is free software; you can redistribute it and/or 19 | %modify it under the terms of the GNU General Public License 20 | %as published by the Free Software Foundation; either version 2 21 | %of the License, or (at your option) any later version. 22 | % 23 | %This program is distributed in the hope that it will be useful, 24 | %but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | %GNU General Public License for more details. 27 | % 28 | %You should have received a copy of the GNU General Public License 29 | %along with this program; if not, write to the Free Software 30 | %Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 31 | %USA. 32 | %========================================================================== 33 | 34 | %CVS record: 35 | %$Id: findUtmZone.m,v 1.1.2.2 2006/08/22 13:45:59 dpl Exp $ 36 | 37 | %% Check value bounds ===================================================== 38 | 39 | if ((longitude > 180) || (longitude < -180)) 40 | error('Longitude value exceeds limits (-180:180).'); 41 | end 42 | 43 | if ((latitude > 84) || (latitude < -80)) 44 | error('Latitude value exceeds limits (-80:84).'); 45 | end 46 | 47 | %% Find zone ============================================================== 48 | 49 | % Start at 180 deg west = -180 deg 50 | 51 | utmZone = fix((180 + longitude)/ 6) + 1; 52 | 53 | %% Correct zone numbers for particular areas ============================== 54 | 55 | if (latitude > 72) 56 | % Corrections for zones 31 33 35 37 57 | if ((longitude >= 0) && (longitude < 9)) 58 | utmZone = 31; 59 | elseif ((longitude >= 9) && (longitude < 21)) 60 | utmZone = 33; 61 | elseif ((longitude >= 21) && (longitude < 33)) 62 | utmZone = 35; 63 | elseif ((longitude >= 33) && (longitude < 42)) 64 | utmZone = 37; 65 | end 66 | 67 | elseif ((latitude >= 56) && (latitude < 64)) 68 | % Correction for zone 32 69 | if ((longitude >= 3) && (longitude < 12)) 70 | utmZone = 32; 71 | end 72 | end -------------------------------------------------------------------------------- /util/geo_2_cart.m: -------------------------------------------------------------------------------- 1 | function [X, Y, Z] = geo_2_cart(phi, lambda, h, i) 2 | %GEO2CART Conversion of geographical coordinates (phi, lambda, h) to 3 | %Cartesian coordinates (X, Y, Z). 4 | % 5 | %[X, Y, Z] = geo2cart(phi, lambda, h, i); 6 | % 7 | %Format for phi and lambda: [degrees minutes seconds]. 8 | %h, X, Y, and Z are in meters. 9 | % 10 | %Choices i of Reference Ellipsoid 11 | % 1. International Ellipsoid 1924 12 | % 2. International Ellipsoid 1967 13 | % 3. World Geodetic System 1972 14 | % 4. Geodetic Reference System 1980 15 | % 5. World Geodetic System 1984 16 | % 17 | % Inputs: 18 | % phi - geocentric latitude (format [degrees minutes seconds]) 19 | % lambda - geocentric longitude (format [degrees minutes seconds]) 20 | % h - height 21 | % i - reference ellipsoid type 22 | % 23 | % Outputs: 24 | % X, Y, Z - Cartesian coordinates (meters) 25 | 26 | %Kai Borre 10-13-98 27 | %Copyright (c) by Kai Borre 28 | % 29 | % CVS record: 30 | % $Id: geo2cart.m,v 1.1.2.7 2006/08/22 13:45:59 dpl Exp $ 31 | %========================================================================== 32 | 33 | b = phi(1) + phi(2)/60 + phi(3)/3600; 34 | b = b*pi / 180; 35 | l = lambda(1) + lambda(2)/60 + lambda(3)/3600; 36 | l = l*pi / 180; 37 | 38 | a = [6378388 6378160 6378135 6378137 6378137]; 39 | f = [1/297 1/298.247 1/298.26 1/298.257222101 1/298.257223563]; 40 | 41 | ex2 = (2-f(i))*f(i) / ((1-f(i))^2); 42 | c = a(i) * sqrt(1+ex2); 43 | N = c / sqrt(1 + ex2*cos(b)^2); 44 | 45 | X = (N+h) * cos(b) * cos(l); 46 | Y = (N+h) * cos(b) * sin(l); 47 | Z = ((1-f(i))^2*N + h) * sin(b); 48 | %%%%%%%%%%%%%% end geo2cart.m %%%%%%%%%%%%%%%%%%%%%%%% 49 | -------------------------------------------------------------------------------- /util/least_square_post.m: -------------------------------------------------------------------------------- 1 | function [pos, el, az, dop] = least_square_pos(satpos, obs, settings) 2 | %Function calculates the Least Square Solution. 3 | % 4 | %[pos, el, az, dop] = leastSquarePos(satpos, obs, settings); 5 | % 6 | % Inputs: 7 | % satpos - Satellites positions (in ECEF system: [X; Y; Z;] - 8 | % one column per satellite) 9 | % obs - Observations - the pseudorange measurements to each 10 | % satellite: 11 | % (e.g. [20000000 21000000 .... .... .... .... ....]) 12 | % settings - receiver settings 13 | % 14 | % Outputs: 15 | % pos - receiver position and receiver clock error 16 | % (in ECEF system: [X, Y, Z, dt]) 17 | % el - Satellites elevation angles (degrees) 18 | % az - Satellites azimuth angles (degrees) 19 | % dop - Dilutions Of Precision ([GDOP PDOP HDOP VDOP TDOP]) 20 | 21 | %-------------------------------------------------------------------------- 22 | % SoftGNSS v3.0 23 | %-------------------------------------------------------------------------- 24 | %Based on Kai Borre 25 | %Copyright (c) by Kai Borre 26 | %Updated by Darius Plausinaitis, Peter Rinder and Nicolaj Bertelsen 27 | % 28 | % CVS record: 29 | % $Id: leastSquarePos.m,v 1.1.2.12 2006/08/22 13:45:59 dpl Exp $ 30 | %========================================================================== 31 | 32 | %=== Initialization ======================================================= 33 | nmbOfIterations = 7; 34 | 35 | dtr = pi/180; 36 | pos = zeros(4, 1); 37 | X = satpos; 38 | nmbOfSatellites = size(satpos, 2); 39 | 40 | A = zeros(nmbOfSatellites, 4); 41 | omc = zeros(nmbOfSatellites, 1); 42 | az = zeros(1, nmbOfSatellites); 43 | el = az; 44 | 45 | %=== Iteratively find receiver position =================================== 46 | for iter = 1:nmbOfIterations 47 | 48 | for i = 1:nmbOfSatellites 49 | if iter == 1 50 | %--- Initialize variables at the first iteration -------------- 51 | Rot_X = X(:, i); 52 | trop = 2; 53 | else 54 | %--- Update equations ----------------------------------------- 55 | rho2 = (X(1, i) - pos(1))^2 + (X(2, i) - pos(2))^2 + ... 56 | (X(3, i) - pos(3))^2; 57 | traveltime = sqrt(rho2) / settings.c ; 58 | 59 | %--- Correct satellite position (do to earth rotation) -------- 60 | Rot_X = e_r_corr(traveltime, X(:, i)); 61 | 62 | %--- Find the elevation angel of the satellite ---------------- 63 | [az(i), el(i), dist] = topocent(pos(1:3, :), Rot_X - pos(1:3, :)); 64 | 65 | if (settings.useTropCorr == 1) 66 | %--- Calculate tropospheric correction -------------------- 67 | trop = tropo(sin(el(i) * dtr), ... 68 | 0.0, 1013.0, 293.0, 50.0, 0.0, 0.0, 0.0); 69 | else 70 | % Do not calculate or apply the tropospheric corrections 71 | trop = 0; 72 | end 73 | end % if iter == 1 ... ... else 74 | 75 | %--- Apply the corrections ---------------------------------------- 76 | omc(i) = (obs(i) - norm(Rot_X - pos(1:3), 'fro') - pos(4) - trop); 77 | 78 | %--- Construct the A matrix --------------------------------------- 79 | A(i, :) = [ (-(Rot_X(1) - pos(1))) / obs(i) ... 80 | (-(Rot_X(2) - pos(2))) / obs(i) ... 81 | (-(Rot_X(3) - pos(3))) / obs(i) ... 82 | 1 ]; 83 | end % for i = 1:nmbOfSatellites 84 | 85 | % These lines allow the code to exit gracefully in case of any errors 86 | if rank(A) ~= 4 87 | pos = zeros(1, 4); 88 | return 89 | end 90 | 91 | %--- Find position update --------------------------------------------- 92 | x = A \ omc; 93 | 94 | %--- Apply position update -------------------------------------------- 95 | pos = pos + x; 96 | 97 | end % for iter = 1:nmbOfIterations 98 | 99 | pos = pos'; 100 | 101 | %=== Calculate Dilution Of Precision ====================================== 102 | if nargout == 4 103 | %--- Initialize output ------------------------------------------------ 104 | dop = zeros(1, 5); 105 | 106 | %--- Calculate DOP ---------------------------------------------------- 107 | Q = inv(A'*A); 108 | 109 | dop(1) = sqrt(trace(Q)); % GDOP 110 | dop(2) = sqrt(Q(1,1) + Q(2,2) + Q(3,3)); % PDOP 111 | dop(3) = sqrt(Q(1,1) + Q(2,2)); % HDOP 112 | dop(4) = sqrt(Q(3,3)); % VDOP 113 | dop(5) = sqrt(Q(4,4)); % TDOP 114 | end 115 | -------------------------------------------------------------------------------- /util/sat_pos.m: -------------------------------------------------------------------------------- 1 | function [satPositions, satClkCorr] = sat_pos(transmitTime, prnList, ... 2 | eph, settings) 3 | %SATPOS Calculation of X,Y,Z satellites coordinates at TRANSMITTIME for 4 | %given ephemeris EPH. Coordinates are calculated for each satellite in the 5 | %list PRNLIST. 6 | %[satPositions, satClkCorr] = satpos(transmitTime, prnList, eph, settings); 7 | % 8 | % Inputs: 9 | % transmitTime - transmission time 10 | % prnList - list of PRN-s to be processed 11 | % eph - ephemeridies of satellites 12 | % settings - receiver settings 13 | % 14 | % Outputs: 15 | % satPositions - positions of satellites (in ECEF system [X; Y; Z;]) 16 | % satClkCorr - correction of satellites clocks 17 | 18 | %-------------------------------------------------------------------------- 19 | % SoftGNSS v3.0 20 | %-------------------------------------------------------------------------- 21 | %Based on Kai Borre 04-09-96 22 | %Copyright (c) by Kai Borre 23 | %Updated by Darius Plausinaitis, Peter Rinder and Nicolaj Bertelsen 24 | % 25 | % CVS record: 26 | % $Id: satpos.m,v 1.1.2.15 2006/08/22 13:45:59 dpl Exp $ 27 | 28 | %% Initialize constants =================================================== 29 | numOfSatellites = size(prnList, 2); 30 | 31 | % GPS constatns 32 | 33 | gpsPi = 3.1415926535898; % Pi used in the GPS coordinate 34 | % system 35 | 36 | %--- Constants for satellite position calculation ------------------------- 37 | Omegae_dot = 7.2921151467e-5; % Earth rotation rate, [rad/s] 38 | GM = 3.986005e14; % Earth's universal 39 | % gravitational parameter, 40 | % [m^3/s^2] 41 | F = -4.442807633e-10; % Constant, [sec/(meter)^(1/2)] 42 | 43 | %% Initialize results ===================================================== 44 | satClkCorr = zeros(1, numOfSatellites); 45 | satPositions = zeros(3, numOfSatellites); 46 | 47 | %% Process each satellite ================================================= 48 | 49 | for satNr = 1 : numOfSatellites 50 | 51 | prn = prnList(satNr); 52 | 53 | %% Find initial satellite clock correction -------------------------------- 54 | 55 | %--- Find time difference --------------------------------------------- 56 | dt = check_t(transmitTime - eph(prn).t_oc); 57 | 58 | %--- Calculate clock correction --------------------------------------- 59 | satClkCorr(satNr) = (eph(prn).a_f2 * dt + eph(prn).a_f1) * dt + ... 60 | eph(prn).a_f0 - ... 61 | eph(prn).T_GD; 62 | 63 | time = transmitTime - satClkCorr(satNr); 64 | 65 | %% Find satellite's position ---------------------------------------------- 66 | 67 | %Restore semi-major axis 68 | a = eph(prn).sqrtA * eph(prn).sqrtA; 69 | 70 | %Time correction 71 | tk = check_t(time - eph(prn).t_oe); 72 | 73 | %Initial mean motion 74 | n0 = sqrt(GM / a^3); 75 | %Mean motion 76 | n = n0 + eph(prn).deltan; 77 | 78 | %Mean anomaly 79 | M = eph(prn).M_0 + n * tk; 80 | %Reduce mean anomaly to between 0 and 360 deg 81 | M = rem(M + 2*gpsPi, 2*gpsPi); 82 | 83 | %Initial guess of eccentric anomaly 84 | E = M; 85 | 86 | %--- Iteratively compute eccentric anomaly ---------------------------- 87 | for ii = 1:10 88 | E_old = E; 89 | E = M + eph(prn).e * sin(E); 90 | dE = rem(E - E_old, 2*gpsPi); 91 | 92 | if abs(dE) < 1.e-12 93 | % Necessary precision is reached, exit from the loop 94 | break; 95 | end 96 | end 97 | 98 | %Reduce eccentric anomaly to between 0 and 360 deg 99 | E = rem(E + 2*gpsPi, 2*gpsPi); 100 | 101 | %Compute relativistic correction term 102 | dtr = F * eph(prn).e * eph(prn).sqrtA * sin(E); 103 | 104 | %Calculate the true anomaly 105 | nu = atan2(sqrt(1 - eph(prn).e^2) * sin(E), cos(E)-eph(prn).e); 106 | 107 | %Compute angle phi 108 | phi = nu + eph(prn).omega; 109 | %Reduce phi to between 0 and 360 deg 110 | phi = rem(phi, 2*gpsPi); 111 | 112 | %Correct argument of latitude 113 | u = phi + ... 114 | eph(prn).C_uc * cos(2*phi) + ... 115 | eph(prn).C_us * sin(2*phi); 116 | %Correct radius 117 | r = a * (1 - eph(prn).e*cos(E)) + ... 118 | eph(prn).C_rc * cos(2*phi) + ... 119 | eph(prn).C_rs * sin(2*phi); 120 | %Correct inclination 121 | i = eph(prn).i_0 + eph(prn).iDot * tk + ... 122 | eph(prn).C_ic * cos(2*phi) + ... 123 | eph(prn).C_is * sin(2*phi); 124 | 125 | %Compute the angle between the ascending node and the Greenwich meridian 126 | Omega = eph(prn).omega_0 + (eph(prn).omegaDot - Omegae_dot)*tk - ... 127 | Omegae_dot * eph(prn).t_oe; 128 | %Reduce to between 0 and 360 deg 129 | Omega = rem(Omega + 2*gpsPi, 2*gpsPi); 130 | 131 | %--- Compute satellite coordinates ------------------------------------ 132 | satPositions(1, satNr) = cos(u)*r * cos(Omega) - sin(u)*r * cos(i)*sin(Omega); 133 | satPositions(2, satNr) = cos(u)*r * sin(Omega) + sin(u)*r * cos(i)*cos(Omega); 134 | satPositions(3, satNr) = sin(u)*r * sin(i); 135 | 136 | 137 | %% Include relativistic correction in clock correction -------------------- 138 | satClkCorr(satNr) = (eph(prn).a_f2 * dt + eph(prn).a_f1) * dt + ... 139 | eph(prn).a_f0 - ... 140 | eph(prn).T_GD + dtr; 141 | 142 | end % for satNr = 1 : numOfSatellites 143 | -------------------------------------------------------------------------------- /util/sizeof.m: -------------------------------------------------------------------------------- 1 | function nBytes = sizeof(precision) 2 | %%% SIZEOF return the number of bytes of a builtin data type. 3 | error(nargchk(1, 1, nargin, 'struct')); 4 | try 5 | d = regexp(str, '\d*','Match'); 6 | nBits = str2num(d{1}); 7 | nBytes = ceil(nBits / 8); 8 | catch 9 | error('Unsupported class for finding size'); 10 | end 11 | -------------------------------------------------------------------------------- /util/to_geod.m: -------------------------------------------------------------------------------- 1 | function [dphi, dlambda, h] = to_geod(a, finv, X, Y, Z) 2 | %TOGEOD Subroutine to calculate geodetic coordinates latitude, longitude, 3 | % height given Cartesian coordinates X,Y,Z, and reference ellipsoid 4 | % values semi-major axis (a) and the inverse of flattening (finv). 5 | % 6 | %[dphi, dlambda, h] = togeod(a, finv, X, Y, Z); 7 | % 8 | % The units of linear parameters X,Y,Z,a must all agree (m,km,mi,ft,..etc) 9 | % The output units of angular quantities will be in decimal degrees 10 | % (15.5 degrees not 15 deg 30 min). The output units of h will be the 11 | % same as the units of X,Y,Z,a. 12 | % 13 | % Inputs: 14 | % a - semi-major axis of the reference ellipsoid 15 | % finv - inverse of flattening of the reference ellipsoid 16 | % X,Y,Z - Cartesian coordinates 17 | % 18 | % Outputs: 19 | % dphi - latitude 20 | % dlambda - longitude 21 | % h - height above reference ellipsoid 22 | 23 | % Copyright (C) 1987 C. Goad, Columbus, Ohio 24 | % Reprinted with permission of author, 1996 25 | % Fortran code translated into MATLAB 26 | % Kai Borre 03-30-96 27 | % 28 | % CVS record: 29 | % $Id: togeod.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 30 | %========================================================================== 31 | 32 | h = 0; 33 | tolsq = 1.e-10; 34 | maxit = 10; 35 | 36 | % compute radians-to-degree factor 37 | rtd = 180/pi; 38 | 39 | % compute square of eccentricity 40 | if finv < 1.e-20 41 | esq = 0; 42 | else 43 | esq = (2 - 1/finv) / finv; 44 | end 45 | 46 | oneesq = 1 - esq; 47 | 48 | % first guess 49 | % P is distance from spin axis 50 | P = sqrt(X^2+Y^2); 51 | % direct calculation of longitude 52 | 53 | if P > 1.e-20 54 | dlambda = atan2(Y,X) * rtd; 55 | else 56 | dlambda = 0; 57 | end 58 | 59 | if (dlambda < 0) 60 | dlambda = dlambda + 360; 61 | end 62 | 63 | % r is distance from origin (0,0,0) 64 | r = sqrt(P^2 + Z^2); 65 | 66 | if r > 1.e-20 67 | sinphi = Z/r; 68 | else 69 | sinphi = 0; 70 | end 71 | 72 | dphi = asin(sinphi); 73 | 74 | % initial value of height = distance from origin minus 75 | % approximate distance from origin to surface of ellipsoid 76 | if r < 1.e-20 77 | h = 0; 78 | return 79 | end 80 | 81 | h = r - a*(1-sinphi*sinphi/finv); 82 | 83 | % iterate 84 | for i = 1:maxit 85 | sinphi = sin(dphi); 86 | cosphi = cos(dphi); 87 | 88 | % compute radius of curvature in prime vertical direction 89 | N_phi = a/sqrt(1-esq*sinphi*sinphi); 90 | 91 | % compute residuals in P and Z 92 | dP = P - (N_phi + h) * cosphi; 93 | dZ = Z - (N_phi*oneesq + h) * sinphi; 94 | 95 | % update height and latitude 96 | h = h + (sinphi*dZ + cosphi*dP); 97 | dphi = dphi + (cosphi*dZ - sinphi*dP)/(N_phi + h); 98 | 99 | % test for convergence 100 | if (dP*dP + dZ*dZ < tolsq) 101 | break; 102 | end 103 | 104 | % Not Converged--Warn user 105 | if i == maxit 106 | fprintf([' Problem in TOGEOD, did not converge in %2.0f',... 107 | ' iterations\n'], i); 108 | end 109 | end % for i = 1:maxit 110 | 111 | dphi = dphi * rtd; 112 | %%%%%%%% end togeod.m %%%%%%%%%%%%%%%%%%%%%% 113 | -------------------------------------------------------------------------------- /util/topocent.m: -------------------------------------------------------------------------------- 1 | function [Az, El, D] = topocent(X, dx) 2 | %TOPOCENT Transformation of vector dx into topocentric coordinate 3 | % system with origin at X. 4 | % Both parameters are 3 by 1 vectors. 5 | % 6 | %[Az, El, D] = topocent(X, dx); 7 | % 8 | % Inputs: 9 | % X - vector origin corrdinates (in ECEF system [X; Y; Z;]) 10 | % dx - vector ([dX; dY; dZ;]). 11 | % 12 | % Outputs: 13 | % D - vector length. Units like units of the input 14 | % Az - azimuth from north positive clockwise, degrees 15 | % El - elevation angle, degrees 16 | 17 | %Kai Borre 11-24-96 18 | %Copyright (c) by Kai Borre 19 | % 20 | % CVS record: 21 | % $Id: topocent.m,v 1.1.1.1.2.4 2006/08/22 13:45:59 dpl Exp $ 22 | %========================================================================== 23 | 24 | dtr = pi/180; 25 | 26 | [phi, lambda, h] = togeod(6378137, 298.257223563, X(1), X(2), X(3)); 27 | 28 | cl = cos(lambda * dtr); 29 | sl = sin(lambda * dtr); 30 | cb = cos(phi * dtr); 31 | sb = sin(phi * dtr); 32 | 33 | F = [-sl -sb*cl cb*cl; 34 | cl -sb*sl cb*sl; 35 | 0 cb sb]; 36 | 37 | local_vector = F' * dx; 38 | E = local_vector(1); 39 | N = local_vector(2); 40 | U = local_vector(3); 41 | 42 | hor_dis = sqrt(E^2 + N^2); 43 | 44 | if hor_dis < 1.e-20 45 | Az = 0; 46 | El = 90; 47 | else 48 | Az = atan2(E, N)/dtr; 49 | El = atan2(U, hor_dis)/dtr; 50 | end 51 | 52 | if Az < 0 53 | Az = Az + 360; 54 | end 55 | 56 | D = sqrt(dx(1)^2 + dx(2)^2 + dx(3)^2); 57 | %%%%%%%%% end topocent.m %%%%%%%%% -------------------------------------------------------------------------------- /util/tropo_corr.m: -------------------------------------------------------------------------------- 1 | function ddr = tropo(sinel, hsta, p, tkel, hum, hp, htkel, hhum) 2 | %TROPO Calculation of tropospheric correction. 3 | % The range correction ddr in m is to be subtracted from 4 | % pseudo-ranges and carrier phases 5 | % 6 | %ddr = tropo(sinel, hsta, p, tkel, hum, hp, htkel, hhum); 7 | % 8 | % Inputs: 9 | % sinel - sin of elevation angle of satellite 10 | % hsta - height of station in km 11 | % p - atmospheric pressure in mb at height hp 12 | % tkel - surface temperature in degrees Kelvin at height htkel 13 | % hum - humidity in % at height hhum 14 | % hp - height of pressure measurement in km 15 | % htkel - height of temperature measurement in km 16 | % hhum - height of humidity measurement in km 17 | % 18 | % Outputs: 19 | % ddr - range correction (meters) 20 | % 21 | % Reference 22 | % Goad, C.C. & Goodman, L. (1974) A Modified Tropospheric 23 | % Refraction Correction Model. Paper presented at the 24 | % American Geophysical Union Annual Fall Meeting, San 25 | % Francisco, December 12-17 26 | 27 | % A Matlab reimplementation of a C code from driver. 28 | % Kai Borre 06-28-95 29 | % 30 | % CVS record: 31 | % $Id: tropo.m,v 1.1.1.1.2.4 2006/08/22 13:46:00 dpl Exp $ 32 | %========================================================================== 33 | 34 | a_e = 6378.137; % semi-major axis of earth ellipsoid 35 | b0 = 7.839257e-5; 36 | tlapse = -6.5; 37 | tkhum = tkel + tlapse*(hhum-htkel); 38 | atkel = 7.5*(tkhum-273.15) / (237.3+tkhum-273.15); 39 | e0 = 0.0611 * hum * 10^atkel; 40 | tksea = tkel - tlapse*htkel; 41 | em = -978.77 / (2.8704e6*tlapse*1.0e-5); 42 | tkelh = tksea + tlapse*hhum; 43 | e0sea = e0 * (tksea/tkelh)^(4*em); 44 | tkelp = tksea + tlapse*hp; 45 | psea = p * (tksea/tkelp)^em; 46 | 47 | if sinel < 0 48 | sinel = 0; 49 | end 50 | 51 | tropo = 0; 52 | done = 'FALSE'; 53 | refsea = 77.624e-6 / tksea; 54 | htop = 1.1385e-5 / refsea; 55 | refsea = refsea * psea; 56 | ref = refsea * ((htop-hsta)/htop)^4; 57 | 58 | while 1 59 | rtop = (a_e+htop)^2 - (a_e+hsta)^2*(1-sinel^2); 60 | 61 | % check to see if geometry is crazy 62 | if rtop < 0 63 | rtop = 0; 64 | end 65 | 66 | rtop = sqrt(rtop) - (a_e+hsta)*sinel; 67 | a = -sinel/(htop-hsta); 68 | b = -b0*(1-sinel^2) / (htop-hsta); 69 | rn = zeros(8,1); 70 | 71 | for i = 1:8 72 | rn(i) = rtop^(i+1); 73 | end 74 | 75 | alpha = [2*a, 2*a^2+4*b/3, a*(a^2+3*b),... 76 | a^4/5+2.4*a^2*b+1.2*b^2, 2*a*b*(a^2+3*b)/3,... 77 | b^2*(6*a^2+4*b)*1.428571e-1, 0, 0]; 78 | 79 | if b^2 > 1.0e-35 80 | alpha(7) = a*b^3/2; 81 | alpha(8) = b^4/9; 82 | end 83 | 84 | dr = rtop; 85 | dr = dr + alpha*rn; 86 | tropo = tropo + dr*ref*1000; 87 | 88 | if done == 'TRUE ' 89 | ddr = tropo; 90 | break; 91 | end 92 | 93 | done = 'TRUE '; 94 | refsea = (371900.0e-6/tksea-12.92e-6)/tksea; 95 | htop = 1.1385e-5 * (1255/tksea+0.05)/refsea; 96 | ref = refsea * e0sea * ((htop-hsta)/htop)^4; 97 | end; 98 | %%%%%%%%% end tropo.m %%%%%%%%%%%%%%%%%%% 99 | --------------------------------------------------------------------------------