1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! RGB-sequential mode decoder — Scottie 1/2/DX and Martin 1/2.
//!
//! Both families use [`crate::modespec::ChannelLayout::RgbSequential`]:
//! three GBR channels per radio line, written to the image in-place
//! via `image.put_pixel`. The two families differ in **where the sync
//! pulse sits within a radio line**:
//!
//! - **Scottie** ([`crate::modespec::SyncPosition::Scottie`]): sync
//! sits between the B and R channels (mid-line). `find_sync`
//! applies a Scottie-specific correction so `skip_samples` lands
//! at line 0's start.
//! - **Martin** ([`crate::modespec::SyncPosition::LineStart`]): sync
//! at line start (standard SSTV convention); same as PD and Robot.
//!
//! ```text
//! Scottie line layout:
//! [septr][G pixels][septr][B pixels][SYNC][porch][R pixels]
//! ^ ^
//! | |
//! line start find_sync detects this
//! (mid-line — Scottie branch
//! in find_sync corrects skip)
//!
//! Martin line layout:
//! [SYNC][porch][G pixels][septr][B pixels][septr][R pixels]
//! ^
//! |
//! line start (sync at line start; standard PD/Robot path)
//! ```
//!
//! Translated from slowrx's `video.c:72-79` (Scottie `ChanStart`) and
//! the `video.c` "default" case (Martin/PD/Robot `ChanStart`). slowrx's
//! GBR storage convention (`video.c:440-444`) is bypassed — we write
//! RGB directly via [`crate::image::SstvImage::put_pixel`].
//!
//! See `NOTICE.md` for full slowrx attribution.
use crateModeSpec;
/// Decode one RGB-sequential radio line (Scottie or Martin) into
/// `image`. Per-channel start times are line-start-relative and
/// branch on `spec.sync_position`:
///
/// - [`crate::modespec::SyncPosition::Scottie`] — channels are
/// `[septr, 2·septr+chan_len, 2·septr+2·chan_len+sync+porch]` from
/// line start. The mid-line sync sits at `2·septr + 2·chan_len`;
/// `find_sync` corrects `skip_samples` to land at line 0's start
/// (slowrx C `sync.c:123-125`), so all three offsets stay positive.
/// - [`crate::modespec::SyncPosition::LineStart`] — channels are
/// `[sync+porch, sync+porch+chan_len+septr, sync+porch+2·chan_len+2·septr]`
/// from line start, all post-sync. `find_sync` uses the unmodified
/// PD/Robot formula. Same shape as Robot 72 with RGB instead of `YCrCb`.
///
/// In both cases RGB is composed in place via `image.put_pixel`; no
/// chroma side-buffer is needed (cf. R36/R24's `chroma_planes`).
///
/// `line_index` is the 0-based image row this radio line emits;
/// `line_seconds_offset` is `f64::from(line_index) * spec.line_seconds`
/// (un-rounded — the per-pixel time computation does the single
/// `round()` to match slowrx `video.c:140-142`).
pub