1pub mod analysis;
10
11use crate::constants::F_L1_HZ;
12use crate::tolerances::DOPPLER_GRID_EDGE_EPS_HZ;
13use crate::validate;
14
15pub const CA_CODE_LENGTH: usize = 1023;
17
18pub const CA_CHIP_RATE_HZ: f64 = 1_023_000.0;
20
21const TWO_PI: f64 = 2.0 * std::f64::consts::PI;
22const DEFAULT_DOPPLER_MIN_HZ: f64 = -2500.0;
23const DEFAULT_DOPPLER_MAX_HZ: f64 = 2500.0;
24const DEFAULT_DOPPLER_STEP_HZ: f64 = 500.0;
25const DEFAULT_SAMPLE_RATE_HZ: f64 = 2.046e6;
26const MAX_DOPPLER_BINS: usize = 4096;
27
28#[derive(Debug, Clone, PartialEq)]
30pub enum SignalError {
31 UnsupportedPrn(i64),
33 InvalidInput {
35 field: &'static str,
37 reason: &'static str,
39 },
40 EmptySamples,
42 TooShort,
44}
45
46impl core::fmt::Display for SignalError {
47 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48 match self {
49 Self::UnsupportedPrn(prn) => write!(f, "unsupported GPS C/A PRN {prn}"),
50 Self::InvalidInput { field, reason } => {
51 write!(f, "invalid signal input {field}: {reason}")
52 }
53 Self::EmptySamples => write!(f, "empty sample vector"),
54 Self::TooShort => write!(f, "sample vector shorter than one C/A code period"),
55 }
56 }
57}
58
59impl std::error::Error for SignalError {}
60
61#[derive(Debug, Clone, Copy, PartialEq)]
63pub struct IqSample {
64 pub i: f64,
66 pub q: f64,
68}
69
70impl IqSample {
71 pub const fn new(i: f64, q: f64) -> Self {
73 Self { i, q }
74 }
75
76 pub const fn real(i: f64) -> Self {
78 Self { i, q: 0.0 }
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq)]
84pub struct ReplicaOptions {
85 pub sample_rate_hz: f64,
87 pub num_samples: usize,
89 pub code_phase_chips: f64,
91 pub code_doppler_hz: f64,
93}
94
95impl ReplicaOptions {
96 pub fn one_code_period() -> Self {
98 let sample_rate_hz = DEFAULT_SAMPLE_RATE_HZ;
99 let integration_time_s = CA_CODE_LENGTH as f64 / CA_CHIP_RATE_HZ;
100 Self {
101 sample_rate_hz,
102 num_samples: (sample_rate_hz * integration_time_s).round() as usize,
103 code_phase_chips: 0.0,
104 code_doppler_hz: 0.0,
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq)]
111pub struct CorrelateOptions {
112 pub sample_rate_hz: f64,
114 pub doppler_hz: f64,
116 pub code_phase_chips: f64,
118 pub code_doppler_hz: f64,
120}
121
122impl Default for CorrelateOptions {
123 fn default() -> Self {
124 Self {
125 sample_rate_hz: DEFAULT_SAMPLE_RATE_HZ,
126 doppler_hz: 0.0,
127 code_phase_chips: 0.0,
128 code_doppler_hz: 0.0,
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq)]
135pub struct CorrelationResult {
136 pub i: f64,
138 pub q: f64,
140 pub power: f64,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq)]
146pub struct AcquisitionOptions {
147 pub sample_rate_hz: f64,
149 pub doppler_min_hz: f64,
151 pub doppler_max_hz: f64,
153 pub doppler_step_hz: f64,
155}
156
157impl Default for AcquisitionOptions {
158 fn default() -> Self {
159 Self {
160 sample_rate_hz: DEFAULT_SAMPLE_RATE_HZ,
161 doppler_min_hz: DEFAULT_DOPPLER_MIN_HZ,
162 doppler_max_hz: DEFAULT_DOPPLER_MAX_HZ,
163 doppler_step_hz: DEFAULT_DOPPLER_STEP_HZ,
164 }
165 }
166}
167
168#[derive(Debug, Clone, PartialEq)]
170pub struct AcquisitionGrid {
171 pub doppler_hz: Vec<f64>,
173 pub code_phase_bins: usize,
175 pub doppler_step_hz: f64,
177 pub samples_per_chip: f64,
179}
180
181#[derive(Debug, Clone, PartialEq)]
183pub struct AcquisitionResult {
184 pub code_phase_chips: f64,
186 pub doppler_hz: f64,
188 pub peak_metric: f64,
190 pub metric: f64,
192 pub peak_power: f64,
194 pub grid: AcquisitionGrid,
196}
197
198pub fn ca_code(prn: i64) -> Result<Vec<i8>, SignalError> {
200 let taps = phase_select(prn)?;
201 let raw = raw_code(taps);
202 Ok(raw.into_iter().map(|bit| 1 - 2 * bit as i8).collect())
203}
204
205pub fn ca_chip(prn: i64, index: i64) -> Result<i8, SignalError> {
207 let code = ca_code(prn)?;
208 let idx = index.rem_euclid(CA_CODE_LENGTH as i64) as usize;
209 Ok(code[idx])
210}
211
212pub fn autocorrelation(code: &[i8]) -> Vec<i32> {
214 (0..code.len())
215 .map(|lag| correlation_at_equal_len(code, code, lag as i64))
216 .collect()
217}
218
219pub fn cross_correlation(code_a: &[i8], code_b: &[i8]) -> Result<Vec<i32>, SignalError> {
221 if code_a.len() != code_b.len() {
222 return Err(invalid_signal_input("code_lengths", "length mismatch"));
223 }
224 Ok((0..code_a.len())
225 .map(|lag| correlation_at_equal_len(code_a, code_b, lag as i64))
226 .collect())
227}
228
229pub fn correlation_at(code_a: &[i8], code_b: &[i8], lag: i64) -> Result<i32, SignalError> {
231 if code_a.len() != code_b.len() {
232 return Err(invalid_signal_input("code_lengths", "length mismatch"));
233 }
234 validate_correlation_lag(code_a.len(), lag)?;
235 Ok(correlation_at_equal_len(code_a, code_b, lag))
236}
237
238fn correlation_at_equal_len(code_a: &[i8], code_b: &[i8], lag: i64) -> i32 {
239 let n = code_a.len() as i64;
240 let mut acc = 0_i32;
241 for (i, &chip_a) in code_a.iter().enumerate() {
242 let j = (i as i64 + lag).rem_euclid(n) as usize;
243 acc += i32::from(chip_a) * i32::from(code_b[j]);
244 }
245 acc
246}
247
248fn validate_correlation_lag(len: usize, lag: i64) -> Result<(), SignalError> {
249 if len == 0 || lag <= 0 {
250 return Ok(());
251 }
252 let max_index =
253 i64::try_from(len - 1).map_err(|_| invalid_signal_input("code_lengths", "out of range"))?;
254 if max_index > i64::MAX - lag {
255 return Err(invalid_signal_input("lag", "out of range"));
256 }
257 Ok(())
258}
259
260pub fn replica(prn: i64, options: ReplicaOptions) -> Result<Vec<i8>, SignalError> {
262 let sample_rate_hz = signal_positive_step(options.sample_rate_hz, "sample_rate_hz")?;
263 let code_phase_chips = signal_finite(options.code_phase_chips, "code_phase_chips")?;
264 let code_doppler_hz = signal_finite(options.code_doppler_hz, "code_doppler_hz")?;
265 let code = ca_code(prn)?;
266 Ok(sample_code(
267 &code,
268 options.num_samples,
269 sample_rate_hz,
270 code_phase_chips,
271 code_doppler_hz,
272 ))
273}
274
275pub fn correlate(
277 iq: &[IqSample],
278 prn: i64,
279 options: CorrelateOptions,
280) -> Result<CorrelationResult, SignalError> {
281 if iq.is_empty() {
282 return Err(SignalError::EmptySamples);
283 }
284 validate_iq_samples(iq, "samples")?;
285 let sample_rate_hz = signal_positive_step(options.sample_rate_hz, "sample_rate_hz")?;
286 let doppler_hz = signal_finite(options.doppler_hz, "doppler_hz")?;
287 let code_phase_chips = signal_finite(options.code_phase_chips, "code_phase_chips")?;
288 let code_doppler_hz = signal_finite(options.code_doppler_hz, "code_doppler_hz")?;
289 let code = ca_code(prn)?;
290 let sampled = sample_code(
291 &code,
292 iq.len(),
293 sample_rate_hz,
294 code_phase_chips,
295 code_doppler_hz,
296 );
297 let (i, q) = correlate_against(iq, &sampled, sample_rate_hz, doppler_hz)?;
298 let power = signal_finite(i * i + q * q, "correlation_power")?;
299 Ok(CorrelationResult { i, q, power })
300}
301
302pub fn correlate_against(
307 iq: &[IqSample],
308 code: &[i8],
309 fs: f64,
310 doppler_hz: f64,
311) -> Result<(f64, f64), SignalError> {
312 if iq.is_empty() {
313 return Err(SignalError::EmptySamples);
314 }
315 validate_iq_samples(iq, "samples")?;
316 if code.is_empty() {
317 return Err(invalid_signal_input("code", "empty"));
318 }
319 let fs = signal_positive_step(fs, "sample_rate_hz")?;
320 let doppler_hz = signal_finite(doppler_hz, "doppler_hz")?;
321 let w = TWO_PI * doppler_hz / fs;
322 let mut acc_i = 0.0;
323 let mut acc_q = 0.0;
324 for (n, (sample, &c)) in iq.iter().zip(code.iter()).enumerate() {
325 let theta = w * n as f64;
326 let cos = libm::cos(theta);
327 let sin = libm::sin(theta);
328 let cc = c as f64;
329 let di = (sample.i * cos + sample.q * sin) * cc;
330 let dq = (sample.q * cos - sample.i * sin) * cc;
331 acc_i += di;
332 acc_q += dq;
333 }
334 Ok((
335 signal_finite(acc_i, "correlation_i")?,
336 signal_finite(acc_q, "correlation_q")?,
337 ))
338}
339
340pub fn acquire(
342 samples: &[IqSample],
343 prn: i64,
344 options: AcquisitionOptions,
345) -> Result<AcquisitionResult, SignalError> {
346 if samples.is_empty() {
347 return Err(SignalError::EmptySamples);
348 }
349 validate_iq_samples(samples, "samples")?;
350 let sample_rate_hz = signal_positive_step(options.sample_rate_hz, "sample_rate_hz")?;
351 let doppler_min_hz = signal_finite(options.doppler_min_hz, "doppler_min_hz")?;
352 let doppler_max_hz = signal_finite(options.doppler_max_hz, "doppler_max_hz")?;
353 let doppler_step_hz = signal_positive_step(options.doppler_step_hz, "doppler_step_hz")?;
354 signal_range_order(doppler_min_hz, doppler_max_hz, "doppler_max_hz")?;
355 let options = AcquisitionOptions {
356 sample_rate_hz,
357 doppler_min_hz,
358 doppler_max_hz,
359 doppler_step_hz,
360 };
361
362 let samples_per_chip = options.sample_rate_hz / CA_CHIP_RATE_HZ;
363 let samples_per_code = (samples_per_chip * CA_CODE_LENGTH as f64).round() as usize;
364 if samples_per_code == 0 {
365 return Err(SignalError::InvalidInput {
366 field: "sample_rate_hz",
367 reason: "out of range",
368 });
369 }
370 if samples.len() < samples_per_code {
371 return Err(SignalError::TooShort);
372 }
373
374 let code = ca_code(prn)?;
375 do_acquire(samples, &code, options, samples_per_chip, samples_per_code)
376}
377
378pub fn coherent_loss(freq_error_hz: f64, integration_time_s: f64) -> Result<f64, SignalError> {
380 let freq_error_hz = signal_finite(freq_error_hz, "freq_error_hz")?;
381 let integration_time_s = signal_positive_step(integration_time_s, "integration_time_s")?;
382 let x = signal_finite(
383 std::f64::consts::PI * freq_error_hz * integration_time_s,
384 "coherent_loss",
385 )?;
386 if x == 0.0 {
387 Ok(1.0)
388 } else {
389 let s = libm::sin(x) / x;
390 signal_finite(s * s, "coherent_loss")
391 }
392}
393
394pub fn coherent_loss_db(freq_error_hz: f64, integration_time_s: f64) -> Result<f64, SignalError> {
396 let loss = coherent_loss(freq_error_hz, integration_time_s)?;
397 if loss <= 0.0 {
398 return Err(invalid_signal_input("coherent_loss_db", "out of range"));
399 }
400 let loss_db = 10.0 * libm::log10(loss);
401 if loss_db.is_finite() {
402 Ok(loss_db)
403 } else {
404 Err(invalid_signal_input("coherent_loss_db", "out of range"))
405 }
406}
407
408pub fn snr_post_db(cn0_dbhz: f64, integration_time_s: f64) -> Result<f64, SignalError> {
410 let cn0_dbhz = signal_finite(cn0_dbhz, "cn0_dbhz")?;
411 let integration_time_s = signal_positive_step(integration_time_s, "integration_time_s")?;
412 signal_finite(
413 cn0_dbhz + 10.0 * libm::log10(integration_time_s),
414 "snr_post_db",
415 )
416}
417
418fn do_acquire(
419 samples: &[IqSample],
420 code: &[i8],
421 options: AcquisitionOptions,
422 samples_per_chip: f64,
423 samples_per_code: usize,
424) -> Result<AcquisitionResult, SignalError> {
425 let doppler_bins = doppler_grid(
426 options.doppler_min_hz,
427 options.doppler_max_hz,
428 options.doppler_step_hz,
429 )?;
430
431 let record = &samples[..samples_per_code];
432 let base_code = sample_code(code, samples_per_code, options.sample_rate_hz, 0.0, 0.0);
433
434 let mut grid = Vec::with_capacity(doppler_bins.len());
435 for &d in &doppler_bins {
436 let wiped = carrier_wipeoff(record, options.sample_rate_hz, d);
437 validate_iq_samples(&wiped, "wiped samples")?;
438 let powers = code_phase_powers(&wiped, &base_code);
439 validate::finite_slice(&powers, "code phase powers").map_err(map_signal_input)?;
440 grid.push((d, powers));
441 }
442
443 let mut peak_power = -1.0;
444 let mut peak_doppler = 0.0;
445 let mut peak_offset = 0_usize;
446 for (d, powers) in &grid {
447 for (off, &p) in powers.iter().enumerate() {
448 if p > peak_power {
449 peak_power = p;
450 peak_doppler = *d;
451 peak_offset = off;
452 }
453 }
454 }
455
456 let metric = peak_to_mean_off_peak(&grid, peak_power, peak_doppler, peak_offset);
457 let code_phase_chips = peak_offset as f64 / samples_per_chip;
458
459 Ok(AcquisitionResult {
460 code_phase_chips,
461 doppler_hz: peak_doppler,
462 peak_metric: metric,
463 metric,
464 peak_power,
465 grid: AcquisitionGrid {
466 doppler_hz: doppler_bins,
467 code_phase_bins: samples_per_code,
468 doppler_step_hz: options.doppler_step_hz,
469 samples_per_chip,
470 },
471 })
472}
473
474fn phase_select(prn: i64) -> Result<(usize, usize), SignalError> {
475 match prn {
476 1 => Ok((2, 6)),
477 2 => Ok((3, 7)),
478 3 => Ok((4, 8)),
479 4 => Ok((5, 9)),
480 5 => Ok((1, 9)),
481 6 => Ok((2, 10)),
482 7 => Ok((1, 8)),
483 8 => Ok((2, 9)),
484 9 => Ok((3, 10)),
485 10 => Ok((2, 3)),
486 11 => Ok((3, 4)),
487 12 => Ok((5, 6)),
488 13 => Ok((6, 7)),
489 14 => Ok((7, 8)),
490 15 => Ok((8, 9)),
491 16 => Ok((9, 10)),
492 17 => Ok((1, 4)),
493 18 => Ok((2, 5)),
494 19 => Ok((3, 6)),
495 20 => Ok((4, 7)),
496 21 => Ok((5, 8)),
497 22 => Ok((6, 9)),
498 23 => Ok((1, 3)),
499 24 => Ok((4, 6)),
500 25 => Ok((5, 7)),
501 26 => Ok((6, 8)),
502 27 => Ok((7, 9)),
503 28 => Ok((8, 10)),
504 29 => Ok((1, 6)),
505 30 => Ok((2, 7)),
506 31 => Ok((3, 8)),
507 32 => Ok((4, 9)),
508 _ => Err(SignalError::UnsupportedPrn(prn)),
509 }
510}
511
512fn raw_code((tap_a, tap_b): (usize, usize)) -> Vec<u8> {
513 let mut g1 = [1_u8; 10];
514 let mut g2 = [1_u8; 10];
515 let mut chips = Vec::with_capacity(CA_CODE_LENGTH);
516
517 for _ in 0..CA_CODE_LENGTH {
518 let g1_out = g1[9];
519 let g2i = g2[tap_a - 1] ^ g2[tap_b - 1];
520 chips.push(g1_out ^ g2i);
521 step_g1(&mut g1);
522 step_g2(&mut g2);
523 }
524
525 chips
526}
527
528fn step_g1(g1: &mut [u8; 10]) {
529 let feedback = g1[2] ^ g1[9];
530 shift(g1, feedback);
531}
532
533fn step_g2(g2: &mut [u8; 10]) {
534 let feedback = g2[1] ^ g2[2] ^ g2[5] ^ g2[7] ^ g2[8] ^ g2[9];
535 shift(g2, feedback);
536}
537
538fn shift(reg: &mut [u8; 10], feedback: u8) {
539 for i in (1..reg.len()).rev() {
540 reg[i] = reg[i - 1];
541 }
542 reg[0] = feedback;
543}
544
545fn sample_code(code: &[i8], n: usize, fs: f64, code_phase: f64, code_doppler: f64) -> Vec<i8> {
546 let code_rate = CA_CHIP_RATE_HZ * (1.0 + code_doppler / F_L1_HZ);
547 let per_sample = code_rate / fs;
548 let len = code.len() as i64;
549
550 (0..n)
551 .map(|k| {
552 let pos = code_phase + k as f64 * per_sample;
553 let idx = (pos.floor() as i64).rem_euclid(len) as usize;
554 code[idx]
555 })
556 .collect()
557}
558
559fn carrier_wipeoff(iq: &[IqSample], fs: f64, doppler_hz: f64) -> Vec<IqSample> {
560 let w = TWO_PI * doppler_hz / fs;
561 iq.iter()
562 .enumerate()
563 .map(|(k, sample)| {
564 let theta = w * k as f64;
565 let cos = libm::cos(theta);
566 let sin = libm::sin(theta);
567 IqSample {
568 i: sample.i * cos + sample.q * sin,
569 q: sample.q * cos - sample.i * sin,
570 }
571 })
572 .collect()
573}
574
575fn code_phase_powers(wiped: &[IqSample], base_code: &[i8]) -> Vec<f64> {
576 let n = wiped.len();
577 (0..n)
578 .map(|offset| {
579 let mut i = 0.0;
580 let mut q = 0.0;
581 for k in 0..n {
582 let sample = wiped[k];
583 let c = base_code[(k + offset) % n] as f64;
584 i += sample.i * c;
585 q += sample.q * c;
586 }
587 i * i + q * q
588 })
589 .collect()
590}
591
592fn peak_to_mean_off_peak(
593 grid: &[(f64, Vec<f64>)],
594 peak_power: f64,
595 peak_doppler: f64,
596 peak_offset: usize,
597) -> f64 {
598 let n = grid.first().map_or(0, |(_, powers)| powers.len());
599 let mut sum = 0.0;
600 let mut count = 0_usize;
601
602 for (d, powers) in grid {
603 for (off, power) in powers.iter().enumerate() {
604 if *d == peak_doppler && abs_circular_diff(off, peak_offset, n) <= 1 {
605 continue;
606 }
607 sum += *power;
608 count += 1;
609 }
610 }
611
612 if count == 0 {
613 0.0
614 } else if sum <= 0.0 && peak_power > 0.0 {
615 1.0e12
616 } else if sum <= 0.0 {
617 0.0
618 } else {
619 peak_power / (sum / count as f64)
620 }
621}
622
623fn doppler_grid(dmin: f64, dmax: f64, dstep: f64) -> Result<Vec<f64>, SignalError> {
624 let dstep = signal_positive_step(dstep, "doppler_step_hz")?;
625 let last_bin_index = doppler_last_bin_index(dmin, dmax, dstep)?;
626 Ok((0..=last_bin_index)
627 .map(|k| dmin + k as f64 * dstep)
628 .filter(|d| *d <= dmax + DOPPLER_GRID_EDGE_EPS_HZ)
629 .collect())
630}
631
632fn doppler_last_bin_index(dmin: f64, dmax: f64, dstep: f64) -> Result<usize, SignalError> {
633 let last_bin_index = ((dmax - dmin) / dstep).round();
634 if !last_bin_index.is_finite() || last_bin_index < 0.0 {
635 return Err(invalid_signal_input("doppler_grid", "out of range"));
636 }
637 let bin_count = last_bin_index + 1.0;
638 if bin_count > MAX_DOPPLER_BINS as f64 {
639 return Err(invalid_signal_input("doppler_grid", "out of range"));
640 }
641 Ok(last_bin_index as usize)
642}
643
644fn signal_positive_step(x: f64, field: &'static str) -> Result<f64, SignalError> {
645 validate::positive_step(x, field).map_err(map_signal_input)
646}
647
648fn signal_finite(x: f64, field: &'static str) -> Result<f64, SignalError> {
649 validate::finite(x, field).map_err(map_signal_input)
650}
651
652fn signal_range_order(lo: f64, hi: f64, field: &'static str) -> Result<(), SignalError> {
653 validate::range_order(lo, hi, field).map_err(map_signal_input)
654}
655
656fn validate_iq_samples(samples: &[IqSample], field: &'static str) -> Result<(), SignalError> {
657 for sample in samples {
658 if !sample.i.is_finite() || !sample.q.is_finite() {
659 return Err(invalid_signal_input(field, "not finite"));
660 }
661 }
662 Ok(())
663}
664
665fn map_signal_input(error: validate::FieldError) -> SignalError {
666 invalid_signal_input(error.field(), error.reason())
667}
668
669fn invalid_signal_input(field: &'static str, reason: &'static str) -> SignalError {
670 SignalError::InvalidInput { field, reason }
671}
672
673fn abs_circular_diff(a: usize, b: usize, n: usize) -> usize {
674 let d = a.abs_diff(b) % n;
675 d.min(n - d)
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681
682 #[test]
683 fn unsupported_prn_is_tagged() {
684 assert_eq!(ca_code(33), Err(SignalError::UnsupportedPrn(33)));
685 assert_eq!(ca_chip(0, 0), Err(SignalError::UnsupportedPrn(0)));
686 }
687
688 #[test]
689 fn code_balance_and_correlation_shape_are_pinned() {
690 for prn in 1..=32 {
691 let code = ca_code(prn).unwrap();
692 assert_eq!(code.len(), CA_CODE_LENGTH);
693 assert_eq!(code.iter().filter(|&&chip| chip == -1).count(), 512);
694 assert_eq!(code.iter().filter(|&&chip| chip == 1).count(), 511);
695 assert_eq!(code.iter().map(|&chip| i32::from(chip)).sum::<i32>(), -1);
696 }
697
698 let code = ca_code(1).unwrap();
699 let corr = autocorrelation(&code);
700 assert_eq!(corr[0], 1023);
701 assert!(!corr[1..].contains(&1023));
702 let mut values = corr[1..].to_vec();
703 values.sort_unstable();
704 values.dedup();
705 assert_eq!(values, vec![-65, -1, 63]);
706 }
707
708 #[test]
709 fn loss_and_snr_primitives_are_deterministic() {
710 assert_eq!(
711 coherent_loss(0.0, 1.0e-3).unwrap().to_bits(),
712 1.0_f64.to_bits()
713 );
714 assert_eq!(
715 snr_post_db(40.0, 1.0e-3).unwrap().to_bits(),
716 10.0_f64.to_bits()
717 );
718 assert_eq!(
719 coherent_loss(f64::MAX, 1.0),
720 Err(invalid_signal_input("coherent_loss", "not finite"))
721 );
722 }
723
724 #[test]
725 fn correlation_rejects_nonfinite_derived_outputs() {
726 let samples = [IqSample::real(f64::MAX), IqSample::real(f64::MAX)];
727 let code = [1_i8, 1_i8];
728
729 assert_eq!(
730 correlate_against(&samples, &code, DEFAULT_SAMPLE_RATE_HZ, 0.0),
731 Err(invalid_signal_input("correlation_i", "not finite"))
732 );
733 assert_eq!(
734 correlate(
735 &samples[..1],
736 1,
737 CorrelateOptions {
738 sample_rate_hz: DEFAULT_SAMPLE_RATE_HZ,
739 doppler_hz: 0.0,
740 code_phase_chips: 0.0,
741 code_doppler_hz: 0.0,
742 }
743 ),
744 Err(invalid_signal_input("correlation_power", "not finite"))
745 );
746 }
747}