File doppler_channel_core.h¶
File List > doppler > doppler_channel > doppler_channel_core.h
Go to the documentation of this file
#ifndef DP_DOPPLER_CHANNEL_CORE_H
#define DP_DOPPLER_CHANNEL_CORE_H
#include <stdint.h>
#include "doppler/clib_common.h"
#include "doppler/dp_state.h"
#include "doppler/jm_perf.h"
#include "doppler/resamp/resamp_core.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DOPPLER_CHANNEL_STATE_MAGIC DP_FOURCC('D', 'P', 'C', 'H')
#define DOPPLER_CHANNEL_STATE_VERSION 2u
#define DOPPLER_CHANNEL_MAX_BLOCK 65536u
typedef struct {
double fs; /* receive sample rate, Hz */
double carrier_hz; /* RF carrier fc, Hz — drives the offset */
double doppler_ppm; /* d0, ppm of nominal */
double doppler_rate_ppm_s; /* d-dot, ppm/s */
resamp_state_t *rs; /* the dilation — never hand-rolled here */
/* Two separate clocks, on purpose. The resampler's per-sample rate
deviation is indexed by INPUT sample; the carrier phase is a function of
receive time, which is the OUTPUT sample index. They differ by the
dilation itself, so conflating them would fold a second copy of the
Doppler into the carrier. */
uint64_t n_in; /* input samples consumed */
uint64_t n_out; /* output samples produced */
double *ctrl; /* per-sample rate deviation scratch */
size_t ctrl_cap;
/* Profile mode (dp_doppler_channel_execute_profile). The carrier needs no
state of its own there: it is read off the resampler's position (see
dp_resamp_execute_ctrl_pos), which the resampler blob and the two
clocks already carry. What a profile does leave behind is the most
recent instantaneous d, the one thing the closed form can no longer
report -- hence these two, and the layout version. */
double prof_d; /* most recent instantaneous d (dimensionless) */
uint8_t profiled; /* a profile has driven this stream */
double *pos; /* per-output input position scratch (not state) */
size_t pos_cap;
} dp_doppler_channel_state_t;
static inline double
doppler_channel_excess(const dp_doppler_channel_state_t *s, double t)
{
return (s->doppler_ppm * t + 0.5 * s->doppler_rate_ppm_s * t * t) * 1e-6;
}
static inline double
doppler_channel_scale(const dp_doppler_channel_state_t *s, double t)
{
return 1.0 + (s->doppler_ppm + s->doppler_rate_ppm_s * t) * 1e-6;
}
static inline double
doppler_channel_phase(const dp_doppler_channel_state_t *s, double t)
{
return s->carrier_hz * doppler_channel_excess(s, t);
}
dp_doppler_channel_state_t *dp_doppler_channel_create(double fs, double carrier_hz, double doppler_ppm, double doppler_rate_ppm_s);
void dp_doppler_channel_destroy(dp_doppler_channel_state_t *state);
void dp_doppler_channel_reset(dp_doppler_channel_state_t *state);
size_t dp_doppler_channel_state_bytes(const dp_doppler_channel_state_t *state);
void dp_doppler_channel_get_state(const dp_doppler_channel_state_t *state, void *blob);
int dp_doppler_channel_set_state(dp_doppler_channel_state_t *state, const void *blob);
size_t dp_doppler_channel_execute_max_out(dp_doppler_channel_state_t *state);
size_t dp_doppler_channel_execute(dp_doppler_channel_state_t *state, const float _Complex *x, size_t x_len, float _Complex *out, size_t max_out);
size_t dp_doppler_channel_execute_profile_max_out(dp_doppler_channel_state_t *state, size_t n);
int64_t dp_doppler_channel_execute_profile(dp_doppler_channel_state_t *state, const float _Complex *x, size_t x_len, const double *ppm, size_t ppm_len, float _Complex *out, size_t max_out);
double dp_doppler_channel_get_elapsed_s(const dp_doppler_channel_state_t *state);
double dp_doppler_channel_get_offset_hz(const dp_doppler_channel_state_t *state);
double dp_doppler_channel_get_delay_samples(const dp_doppler_channel_state_t *state);
#ifdef __cplusplus
}
#endif
#endif /* DOPPLER_CHANNEL_CORE_H */