Skip to content

Support CCSDS TDM Receive/Transmit frequencies#546

Open
ChristopherRabotin wants to merge 1 commit into
masterfrom
feature/support-tdm-frequency-export-14184810549512038715
Open

Support CCSDS TDM Receive/Transmit frequencies#546
ChristopherRabotin wants to merge 1 commit into
masterfrom
feature/support-tdm-frequency-export-14184810549512038715

Conversation

@ChristopherRabotin
Copy link
Copy Markdown
Member

Implemented full support for CCSDS TDM frequency data types (RECEIVE_FREQ, TRANSMIT_FREQ).
This includes simulation, sensitivity calculations for OD, and high-fidelity TDM export/import.
GroundStation now supports optional transmit frequency and turnaround ratio parameters.
TrackingDataArc provides new helper methods for batch conversion between Doppler (km/s) and frequency (Hz) units.

Fixes #392


PR created automatically by Jules for task 14184810549512038715 started by @ChristopherRabotin

…ies in TDM

This commit adds comprehensive support for RECEIVE_FREQ and TRANSMIT_FREQ
data types throughout the Nyx pipeline.

Key changes:
- Updated GroundStation to store transmit_freq_hz and turnaround_ratio.
- Implemented ReceiveFrequency simulation in GroundStation tracking device.
- Added sensitivity calculations for ReceiveFrequency in Orbit Determination.
- Enhanced TrackingDataArc with explicit conversion methods between Doppler
  and frequency data.
- Updated CCSDS TDM IO to preserve frequency data on import and correctly
  handle multipliers on export.
- Added integration tests for frequency data round-trip.

Co-authored-by: ChristopherRabotin <4823784+ChristopherRabotin@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@github-actions
Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit b0c361c):

https://nyx-rustdoc--pr546-feature-support-tdm-0u7fbv70.web.app

(expires Thu, 28 May 2026 17:00:45 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: d8e2a55934352d850c15d11866c39eb2d2e029be

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for transmit and receive frequency measurements within the ground station and orbit determination modules. Key changes include adding transmit_freq_hz and turnaround_ratio to the GroundStation struct, implementing frequency-based measurement simulation and sensitivity calculations, and enhancing CCSDS TDM I/O to preserve frequency data. Feedback highlights a discrepancy in the Doppler factor used in instantaneous measurements, inconsistencies in the Python constructor's default measurement types, and the need for more robust handling of one-way versus two-way tracking configurations in sensitivity and TDM export logic. Additionally, a suggestion was made to include zero-division checks during frequency-to-Doppler conversions.

{
let rho_dot = MeasurementType::Doppler.compute_one_way(aer, 0.0)?;
// One way: f_rx = f_tx * k * (1 - rho_dot / c)
let f_rx = f_tx * k * (1.0 - rho_dot / SPEED_OF_LIGHT_KM_S);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The frequency calculation in measure_instantaneous uses a factor of 1.0 for the Doppler shift, whereas measure (line 144) uses 2.0. Since GroundStation is defined as a two-way station (line 47 in mod.rs), measure_instantaneous should also use the factor of 2.0 to be consistent with two-way coherent tracking models.

Suggested change
let f_rx = f_tx * k * (1.0 - rho_dot / SPEED_OF_LIGHT_KM_S);
let f_rx = f_tx * k * (1.0 - 2.0 * rho_dot / SPEED_OF_LIGHT_KM_S);

terrain_mask: vec![],
terrain_mask_ignored: true,
},
measurement_types: IndexSet::new(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Python constructor py_new initializes measurement_types as an empty set, which differs from the Default implementation that includes Range and Doppler. It is better to maintain consistency across constructors to ensure that a ground station created via Python has the same default capabilities as one created in Rust.

Suggested change
measurement_types: IndexSet::new(),
measurement_types: {
let mut set = IndexSet::new();
set.insert(MeasurementType::Range);
set.insert(MeasurementType::Doppler);
set
},

let m22 = delta_v.y / ρ_km - ρ_dot_km_s * delta_r.y / ρ_km.powi(2);
let m23 = delta_v.z / ρ_km - ρ_dot_km_s * delta_r.z / ρ_km.powi(2);

let factor = -2.0 * f_tx * k / SPEED_OF_LIGHT_KM_S;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The factor 2.0 is hardcoded, assuming two-way tracking. While consistent with the current GroundStation definition, it will produce incorrect sensitivities if one-way frequency data is used. Consider making this factor dependent on the measurement configuration (e.g., based on the TDM PATH).

Comment on lines 464 to +474
let two_way_types = types
.iter()
.filter(|msr_type| msr_type.may_be_two_way())
.filter(|msr_type| {
msr_type.may_be_two_way()
|| [
MeasurementType::ReceiveFrequency,
MeasurementType::TransmitFrequency,
MeasurementType::TransmitFrequencyRate,
]
.contains(msr_type)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic forces all frequency-related measurement types into the two_way_types bucket. While common in deep space tracking, CCSDS TDM also supports one-way frequency data (e.g., PATH = 1,2). Forcing them into two-way will cause them to be exported with PATH = 1,2,1 and potentially incorrect multipliers in the data section. Consider checking the actual path configuration of the arc.

Comment on lines +430 to +435
if let Some(f_tx) = msr.data.get(&MeasurementType::TransmitFrequency) {
let doppler_shift_hz = f_tx * turnaround_ratio - f_rx;
let rho_dot_km_s = (doppler_shift_hz * SPEED_OF_LIGHT_KM_S)
/ (2.0 * f_tx * turnaround_ratio);
msr.push(MeasurementType::Doppler, rho_dot_km_s);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The conversion from frequency to Doppler does not check for division by zero if f_tx or turnaround_ratio are zero. Adding a check improves robustness against invalid or corrupted tracking data.

                if let Some(f_tx) = msr.data.get(&MeasurementType::TransmitFrequency) {
                    if f_tx.abs() > f64::EPSILON && turnaround_ratio.abs() > f64::EPSILON {
                        let doppler_shift_hz = f_tx * turnaround_ratio - f_rx;
                        let rho_dot_km_s = (doppler_shift_hz * SPEED_OF_LIGHT_KM_S)
                            / (2.0 * f_tx * turnaround_ratio);
                        msr.push(MeasurementType::Doppler, rho_dot_km_s);
                    }
                }

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 45.56962% with 86 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.31%. Comparing base (7bcdd51) to head (b0c361c).

Files with missing lines Patch % Lines
nyx-core/src/od/msr/sensitivity.rs 0.00% 42 Missing ⚠️
nyx-core/src/od/ground_station/trk_device.rs 22.58% 24 Missing ⚠️
nyx-core/src/od/msr/trackingdata/mod.rs 47.82% 12 Missing ⚠️
nyx-core/src/od/ground_station/mod.rs 85.71% 4 Missing ⚠️
nyx-core/src/od/msr/trackingdata/io_ccsds_tdm.rs 85.71% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #546      +/-   ##
==========================================
- Coverage   79.01%   78.31%   -0.71%     
==========================================
  Files         103      103              
  Lines       16645    16774     +129     
==========================================
- Hits        13152    13136      -16     
- Misses       3493     3638     +145     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support exporting simulated Doppler data as Receive/Transmit frequencies in TDM

1 participant