Skip to content
This repository was archived by the owner on Apr 13, 2021. It is now read-only.

Commit f51452e

Browse files
author
Pasi Miettinen
committed
Add GLO acquisition support
1 parent 09a118a commit f51452e

File tree

7 files changed

+242
-96
lines changed

7 files changed

+242
-96
lines changed

peregrine/acquisition.py

+93-37
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import numpy as np
1818
import pyfftw
1919
import cPickle
20-
import defaults
2120

2221
from include.generateCAcode import caCodes
22+
from include.generateGLOcode import GLOCode
23+
from peregrine.gps_constants import L1CA
24+
from peregrine.glo_constants import GLO_L1, glo_l1_step
2325

2426
import logging
2527
logger = logging.getLogger(__name__)
@@ -191,13 +193,15 @@ def interpolate(self, S_0, S_1, S_2, interpolation='gaussian'):
191193
192194
**Parabolic interpolation:**
193195
194-
.. math:: \Delta = \\frac{1}{2} \\frac{S[k+1] - S[k-1]}{2S[k] - S[k-1] - S[k+1]}
196+
.. math:: \Delta = \\frac{1}{2} \\frac{S[k+1] -
197+
S[k-1]}{2S[k] - S[k-1] - S[k+1]}
195198
196199
Where :math:`S[n]` is the magnitude of FFT bin :math:`n`.
197200
198201
**Gaussian interpolation:**
199202
200-
.. math:: \Delta = \\frac{1}{2} \\frac{\ln(S[k+1]) - \ln(S[k-1])}{2\ln(S[k]) - \ln(S[k-1]) - \ln(S[k+1])}
203+
.. math:: \Delta = \\frac{1}{2} \\frac{\ln(S[k+1]) -
204+
\ln(S[k-1])}{2\ln(S[k]) - \ln(S[k-1]) - \ln(S[k+1])}
201205
202206
The Gaussian interpolation method gives better results, especially when
203207
used with a Gaussian window function, at the expense of computational
@@ -342,6 +346,8 @@ def find_peak(self, freqs, results, interpolation='gaussian'):
342346
freq_index, cp_samples = np.unravel_index(results.argmax(),
343347
results.shape)
344348

349+
code_phase = float(cp_samples) / self.samples_per_chip
350+
345351
if freq_index > 1 and freq_index < len(freqs) - 1:
346352
delta = self.interpolate(
347353
results[freq_index - 1][cp_samples],
@@ -358,8 +364,6 @@ def find_peak(self, freqs, results, interpolation='gaussian'):
358364
else:
359365
freq = freqs[freq_index]
360366

361-
code_phase = float(cp_samples) / self.samples_per_chip
362-
363367
# Calculate SNR for the peak.
364368
results_mean = np.mean(results)
365369
if results_mean != 0:
@@ -370,7 +374,8 @@ def find_peak(self, freqs, results, interpolation='gaussian'):
370374
return (code_phase, freq, snr)
371375

372376
def acquisition(self,
373-
prns=range(32),
377+
prns=xrange(32),
378+
channels=[x - 7 for x in xrange(14)],
374379
doppler_priors=None,
375380
doppler_search=7000,
376381
doppler_step=None,
@@ -379,10 +384,10 @@ def acquisition(self,
379384
multi=True
380385
):
381386
"""
382-
Perform an acquisition for a given list of PRNs.
387+
Perform an acquisition for a given list of PRNs/channels.
383388
384-
Perform an acquisition for a given list of PRNs across a range of Doppler
385-
frequencies.
389+
Perform an acquisition for a given list of PRNs/channels across a range of
390+
Doppler frequencies.
386391
387392
This function returns :class:`AcquisitionResult` objects containing the
388393
location of the acquisition peak for PRNs that have an acquisition
@@ -394,8 +399,13 @@ def acquisition(self,
394399
395400
Parameters
396401
----------
402+
bandcode : optional
403+
String defining the acquisition code. Default: L1CA
404+
choices: L1CA, GLO_L1 (in gps_constants.py)
397405
prns : iterable, optional
398406
List of PRNs to acquire. Default: 0..31 (0-indexed)
407+
channels : iterable, optional
408+
List of channels to acquire. Default: -7..6
399409
doppler_prior: list of floats, optional
400410
List of expected Doppler frequencies in Hz (one per PRN). Search will be
401411
centered about these. If None, will search around 0 for all PRNs.
@@ -413,10 +423,11 @@ def acquisition(self,
413423
Returns
414424
-------
415425
out : [AcquisitionResult]
416-
A list of :class:`AcquisitionResult` objects, one per PRN in `prns`.
426+
A list of :class:`AcquisitionResult` objects, one per PRN in `prns` or
427+
channel in 'channels'.
417428
418429
"""
419-
logger.info("Acquisition starting")
430+
logger.info("Acquisition starting for " + self.signal)
420431
from peregrine.parallel_processing import parmap
421432

422433
# If the Doppler step is not specified, compute it from the coarse
@@ -428,9 +439,6 @@ def acquisition(self,
428439
# magnitude.
429440
doppler_step = self.sampling_freq / self.n_integrate
430441

431-
if doppler_priors is None:
432-
doppler_priors = np.zeros_like(prns)
433-
434442
if progress_bar_output == 'stdout':
435443
show_progress = True
436444
progress_fd = sys.stdout
@@ -446,33 +454,55 @@ def acquisition(self,
446454
show_progress = False
447455
logger.warning("show_progress = True but progressbar module not found.")
448456

457+
if self.signal == L1CA:
458+
input_len = len(prns)
459+
offset = 1
460+
pb_attr = progressbar.Attribute('prn', '(PRN: %02d)', '(PRN --)')
461+
if doppler_priors is None:
462+
doppler_priors = np.zeros_like(prns)
463+
else:
464+
input_len = len(channels)
465+
offset = 0
466+
pb_attr = progressbar.Attribute('ch', '(CH: %02d)', '(CH --)')
467+
if doppler_priors is None:
468+
doppler_priors = np.zeros_like(channels)
469+
449470
# Setup our progress bar if we need it
450471
if show_progress and not multi:
451472
widgets = [' Acquisition ',
452-
progressbar.Attribute('prn', '(PRN: %02d)', '(PRN --)'), ' ',
473+
pb_attr, ' ',
453474
progressbar.Percentage(), ' ',
454475
progressbar.ETA(), ' ',
455476
progressbar.Bar()]
456477
pbar = progressbar.ProgressBar(widgets=widgets,
457-
maxval=int(len(prns) *
458-
(2 * doppler_search / doppler_step + 1)),
478+
maxval=int(input_len *
479+
(2 * doppler_search / doppler_step + 1)),
459480
fd=progress_fd)
460481
pbar.start()
461482
else:
462483
pbar = None
463484

464485
def do_acq(n):
465-
prn = prns[n]
486+
if self.signal == L1CA:
487+
prn = prns[n]
488+
code = caCodes[prn]
489+
int_f = self.IF
490+
attr = {'prn': prn + 1}
491+
else:
492+
ch = channels[n]
493+
code = GLOCode
494+
int_f = self.IF + ch * glo_l1_step
495+
attr = {'ch': ch}
466496
doppler_prior = doppler_priors[n]
467497
freqs = np.arange(doppler_prior - doppler_search,
468-
doppler_prior + doppler_search, doppler_step) + self.IF
498+
doppler_prior + doppler_search, doppler_step) + int_f
469499
if pbar:
470500
def progress_callback(freq_num, num_freqs):
471-
pbar.update(n * len(freqs) + freq_num, attr={'prn': prn + 1})
501+
pbar.update(n * len(freqs) + freq_num, attr=attr)
472502
else:
473503
progress_callback = None
474504

475-
coarse_results = self.acquire(caCodes[prn], freqs,
505+
coarse_results = self.acquire(code, freqs,
476506
progress_callback=progress_callback)
477507

478508
code_phase, carr_freq, snr = self.find_peak(freqs, coarse_results,
@@ -485,13 +515,22 @@ def progress_callback(freq_num, num_freqs):
485515
status = 'A'
486516

487517
# Save properties of the detected satellite signal
488-
acq_result = AcquisitionResult(prn,
489-
carr_freq,
490-
carr_freq - self.IF,
491-
code_phase,
492-
snr,
493-
status,
494-
self.signal)
518+
if self.signal == L1CA:
519+
acq_result = AcquisitionResult(prn,
520+
carr_freq,
521+
carr_freq - int_f,
522+
code_phase,
523+
snr,
524+
status,
525+
L1CA)
526+
else:
527+
acq_result = GloAcquisitionResult(ch,
528+
carr_freq,
529+
carr_freq - int_f,
530+
code_phase,
531+
snr,
532+
status,
533+
GLO_L1)
495534

496535
# If the acquisition was successful, log it
497536
if (snr > threshold):
@@ -501,9 +540,9 @@ def progress_callback(freq_num, num_freqs):
501540

502541
if multi:
503542
acq_results = parmap(
504-
do_acq, range(len(prns)), show_progress=show_progress)
543+
do_acq, xrange(input_len), show_progress=show_progress)
505544
else:
506-
acq_results = map(do_acq, range(len(prns)))
545+
acq_results = map(do_acq, xrange(input_len))
507546

508547
# Acquisition is finished
509548

@@ -512,9 +551,11 @@ def progress_callback(freq_num, num_freqs):
512551
pbar.finish()
513552

514553
logger.info("Acquisition finished")
515-
acquired_prns = [ar.prn + 1 for ar in acq_results if ar.status == 'A']
516-
logger.info("Acquired %d satellites, PRNs: %s.",
517-
len(acquired_prns), acquired_prns)
554+
acq = [ar.prn + offset for ar in acq_results if ar.status == 'A']
555+
if self.signal == L1CA:
556+
logger.info("Acquired %d satellites, PRNs: %s.", len(acq), acq)
557+
else:
558+
logger.info("Acquired %d channels: %s.", len(acq), acq)
518559

519560
return acq_results
520561

@@ -531,7 +572,7 @@ def save_wisdom(self, wisdom_file=DEFAULT_WISDOM_FILE):
531572
pyfftw.export_wisdom(), f, protocol=cPickle.HIGHEST_PROTOCOL)
532573

533574

534-
class AcquisitionResult:
575+
class AcquisitionResult(object):
535576
"""
536577
Stores the acquisition parameters of a single satellite.
537578
@@ -560,7 +601,7 @@ class AcquisitionResult:
560601
"""
561602

562603
__slots__ = ('prn', 'carr_freq', 'doppler',
563-
'code_phase', 'snr', 'status', 'signal')
604+
'code_phase', 'snr', 'status', 'signal', 'sample_index')
564605

565606
def __init__(self, prn, carr_freq, doppler, code_phase, snr, status, signal,
566607
sample_index=0):
@@ -574,7 +615,7 @@ def __init__(self, prn, carr_freq, doppler, code_phase, snr, status, signal,
574615
self.sample_index = sample_index
575616

576617
def __str__(self):
577-
return "PRN %2d (%s) SNR %6.2f @ CP %6.1f, %+8.2f Hz %s" % \
618+
return "PRN %2d (%s) SNR %6.2f @ CP %6.3f, %+8.2f Hz %s" % \
578619
(self.prn + 1, self.signal, self.snr, self.code_phase,
579620
self.doppler, self.status)
580621

@@ -615,6 +656,20 @@ def _equal(self, other):
615656
return True
616657

617658

659+
class GloAcquisitionResult(AcquisitionResult):
660+
661+
def __init__(self, channel, carr_freq, doppler, code_phase, snr, status,
662+
signal, sample_index=0):
663+
super(GloAcquisitionResult, self).__init__(channel, carr_freq, doppler,
664+
code_phase, snr, status,
665+
signal, sample_index)
666+
667+
def __str__(self):
668+
return "CH %2d (%s) SNR %6.2f @ CP %6.3f, %+8.2f Hz %s" % \
669+
(self.prn, self.signal, self.snr, self.code_phase, self.doppler,
670+
self.status)
671+
672+
618673
def save_acq_results(filename, acq_results):
619674
"""
620675
Save a set of acquisition results to a file.
@@ -676,4 +731,5 @@ def print_scores(acq_results, pred, pred_dopp=None):
676731

677732
print "Found %d of %d, mean doppler error = %+5.0f Hz, mean abs err = %4.0f Hz, worst = %+5.0f Hz"\
678733
% (n_match, len(pred),
679-
sum_dopp_err / max(1, n_match), sum_abs_dopp_err / max(1, n_match), worst_dopp_err)
734+
sum_dopp_err / max(1, n_match), sum_abs_dopp_err /
735+
max(1, n_match), worst_dopp_err)

peregrine/defaults.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
ephemMaxAge = 4 * 3600.0 # Reject an ephemeris entry if older than this
1818

1919
# the size of the sample data block processed at a time
20-
processing_block_size = 20e6 # [samples]
20+
processing_block_size = 20e6 # [samples]
2121

2222
# original
2323
sample_channel_GPS_L1 = 0
@@ -75,18 +75,21 @@
7575
freq_profile_low_rate = {
7676
'GPS_L1_IF': 14.58e5,
7777
'GPS_L2_IF': 7.4e5,
78+
'GLO_L1_IF': 12e5,
7879
'sampling_freq': 24.84375e5}
7980

8081
# 'normal_rate' frequencies profile
8182
freq_profile_normal_rate = {
8283
'GPS_L1_IF': 14.58e6,
8384
'GPS_L2_IF': 7.4e6,
85+
'GLO_L1_IF': 12e6,
8486
'sampling_freq': 24.84375e6}
8587

86-
# 'normal_rate' frequencies profile
88+
# 'high_rate' frequencies profile
8789
freq_profile_high_rate = {
8890
'GPS_L1_IF': freq_profile_normal_rate['GPS_L1_IF'],
8991
'GPS_L2_IF': freq_profile_normal_rate['GPS_L2_IF'],
92+
'GLO_L1_IF': freq_profile_normal_rate['GLO_L1_IF'],
9093
'sampling_freq': 99.375e6}
9194

9295
L1CA_CHANNEL_BANDWIDTH_HZ = 1000

peregrine/glo_constants.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# -*- coding: utf-8 -*-
2+
from gps_constants import c
3+
# GLO system parameters
4+
glo_l1 = 1.602e9 # Hz
5+
glo_l2 = 1.246e9 # Hz
6+
glo_code_len = 511
7+
glo_chip_rate = 0.511e6 # Hz
8+
glo_l1_step = 0.5625e6 # Hz
9+
10+
glo_code_period = glo_code_len / glo_chip_rate
11+
glo_code_wavelength = glo_code_period * c
12+
13+
GLO_L1 = 'glo_l1'

peregrine/gps_constants.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
# Some fundamental constants have specific numeric definitions to ensure
44
# consistent results in curve fits:
5-
c = 2.99792458e8 # m/s
5+
c = 2.99792458e8 # m/s
66
pi = 3.1415926535898
77

88
# Physical parameters of the Earth
9-
earth_gm = 3.986005e14 # m^3/s^2 (WGS84 earth's gravitational constant)
10-
omegae_dot = 7.2921151467e-005 # rad/s (WGS84 earth rotation rate)
9+
earth_gm = 3.986005e14 # m^3/s^2 (WGS84 earth's gravitational constant)
10+
omegae_dot = 7.2921151467e-005 # rad/s (WGS84 earth rotation rate)
1111

1212
# GPS system parameters:
13-
l1 = 1.57542e9 # Hz
14-
l2 = 1.22760e9 # Hz
13+
l1 = 1.57542e9 # Hz
14+
l2 = 1.22760e9 # Hz
1515

16-
l1ca_chip_rate = 1.023e6 # Hz
16+
l1ca_chip_rate = 1.023e6 # Hz
1717
l1ca_code_length = 1023
1818
l1ca_code_period = l1ca_code_length / l1ca_chip_rate
1919
l1ca_code_wavelength = l1ca_code_period * c
2020

21-
l2c_chip_rate = 1.023e6 # Hz
21+
l2c_chip_rate = 1.023e6 # Hz
2222

23-
nominal_range = 26000e3 # m
23+
nominal_range = 26000e3 # m
2424

2525
L1CA = 'l1ca'
2626
L2C = 'l2c'

0 commit comments

Comments
 (0)