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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/// This module contains all of the struct definitions for the various types
/// we're pulling from OTDR files.
use serde::Serialize;

/// A BlockInfo struct contains information about a specific block later in the
/// file, and appears in the MapBlock
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
pub struct BlockInfo {
    /// Name of the block
    pub identifier: String,
    /// Revision number - major (3 digits), minor, cosmetic
    pub revision_number: u16,
    /// Size in bytes of the block
    pub size: i32
    
}

/// Every SOR file has a MapBlock which acts as a map to the file's contents
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
pub struct MapBlock {
    /// Revision number - major (3 digits), minor, cosmetic - for the file as a
    /// whole
    pub revision_number: u16, 
    /// Block size for the map block
    pub block_size: i32,
    /// Number of blocks in the file
    pub block_count: i16,
    /// Information on all the blocks in this file
    pub block_info: Vec<BlockInfo> 
}

/// The GeneralParametersBlock is mandatory for the format and contains 
/// test-identifying information as well as generic information about the test
/// being run such as the nominal wavelength
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
pub struct GeneralParametersBlock {
    /// Language code - EN, CN, JP, etc.
    pub language_code: String, 
    /// Cable identifier
    pub cable_id: String, 
    /// Fibre identifier
    pub fiber_id: String, 
    /// Fibre type - this is generally coded as the ITU-T standard definition,
    /// sans letters, e.g. 657, 655.
    pub fiber_type: i16, 
    /// Nominal test wavelength in nm
    pub nominal_wavelength: i16, 
    /// Start location for the test
    pub originating_location: String, 
    /// End location for the test
    pub terminating_location: String, 
    /// Cable code - free field
    pub cable_code: String, 
    ///  NC for new condition, RC for as-repaired, OT as something else
    pub current_data_flag: String, 
    /// User offset - This is essentially the launch lead length from the front 
    /// panel offset (provided in the fixed parameters block), in 100ps 
    /// increments
    pub user_offset: i32,
    /// This is the same as user_offset, but measured in 10x the distance units 
    /// in FixedParametersBlock.units_of_distance
    pub user_offset_distance: i32,
    /// Operator of the unit for the test
    pub operator: String,
    /// Free comment field
    pub comment: String,
}

/// Supplier parameters describe the OTDR unit itself, such as the optical 
/// module ID/serial number. Often this block also contains information about 
/// calibration dates in the "other" field.
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct SupplierParametersBlock {
    /// Manufacturer of the OTDR
    pub supplier_name: String,
    /// Mainframe model number
    pub otdr_mainframe_id: String,
    /// Mainframe serial number
    pub otdr_mainframe_sn: String,
    /// Optical module model number
    pub optical_module_id: String,
    /// Optical module serial number
    pub optical_module_sn: String,
    /// Software revision
    pub software_revision: String,
    /// Free text
    pub other: String,
}

/// Fixed parameters block contains key information for interpreting the test 
/// data
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct FixedParametersBlock {
    /// Datestamp - unix epoch seconds, 32-bit. Remember not to do any OTDR 
    /// tests after 2038.
    pub date_time_stamp: u32,
    /// Units of distance - km, mt, ft, kf, mi, etc. Typically mt (in civilised 
    /// nations)
    pub units_of_distance: String,
    /// Actual wavelength used - normally the factory-calibrated wavelength in 
    /// nm, or nominal wavelength
    pub actual_wavelength: i16,
    /// Acquisition offset - this is the length of fibre from the OTDR port to 
    /// the first data point in the DataPoints, in 100ps increments
    pub acquisition_offset: i32,
    /// As acquisition_offset, but as 10x units_of_distance
    pub acquisition_offset_distance: i32,
    /// The total number of pulse widths used, if more than one pulse width's 
    /// results are stored in the file
    pub total_n_pulse_widths_used: i16,
    /// The list of pulse widths used, in nanoseconds
    pub pulse_widths_used: Vec<i16>,
    /// Data spacing, time taken to acquire 10,000 points in 100ps increments
    pub data_spacing: Vec<i32>,
    /// Number of points stored for each pulse width
    pub n_data_points_for_pulse_widths_used: Vec<i32>,
    /// Group index - the refractive index of the fibre, default to 146800 if 
    /// nothing supplied
    pub group_index: i32,
    /// Backscatter coefficient - 
    pub backscatter_coefficient: i16,
    /// Number of averages - the number of samples that were averaged to 
    /// generate the result - may be used instead of averaging time
    pub number_of_averages: i32,
    /// Averaging time - may be supplied instead of number of averages - in 
    /// seconds x 10
    pub averaging_time: u16,
    /// Acquisition range set by the tester to reach the end of the fibre - as 
    /// with other distance measurements, 100ps increments
    pub acquisition_range: i32,
    /// Acquisition range in 10x distance units, as an alternate to 
    /// acquisition_range
    pub acquisition_range_distance: i32,
    /// Front panel offset is the time taken, in 100ps increments, between the 
    /// front-end of the optical TRX and the front panel connector
    pub front_panel_offset: i32,
    /// Noise floor level - the lowest power level for which 98% of the noise 
    /// data lies below; 5-digit -dB value (e.g. 10200 = -10.2dB)
    pub noise_floor_level: u16,
    /// Scale factor for the noise floor level - defaults to 1
    pub noise_floor_scale_factor: i16,
    /// Attenuation in dB*1000 applied by the instrument if done by the 
    /// instrument
    pub power_offset_first_point: u16,
    /// The threshold in dB*1000 for a loss-type event; default 00200
    pub loss_threshold: u16,
    /// The threshold in -dB*1000 for reflectance events; default -55000
    pub reflectance_threshold: u16,
    /// The threshold in dB*1000 for the loss taken to detect the end of the 
    /// fibre; default 03000
    pub end_of_fibre_threshold: u16,
    /// Trace type - identifies if this is a standard one-way trace, a 
    /// bidirectional trace, reference trace, difference trace, or reversed 
    /// trace
    pub trace_type: String,
    /// Window coordinate for the upper right window corner
    pub window_coordinate_1: i32,
    /// Power coordinate for the upper right window corner
    pub window_coordinate_2: i32,
    /// Window coordinate for the lower left window corner
    pub window_coordinate_3: i32,
    /// Power coordinate for the lower left window corner
    pub window_coordinate_4: i32,
}

/// KeyEvents describe a single event along the fibre path detected by the OTDR
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct KeyEvent {
    /// Event number - this is from 0 to n
    pub event_number: i16,
    /// Event propogation time is the time in 100ps units from the front panel 
    /// to the event
    pub event_propogation_time: i32,
    /// The span loss in db/km (as a 5-digit value, i.e. dB*1000) for the fibre 
    /// entering the event
    pub attenuation_coefficient_lead_in_fiber: i16,
    /// Loss in dB*1000 for the event
    pub event_loss: i16,
    /// Reflectance in -dB*1000 for the event
    pub event_reflectance: i32,
    /// Code for the event is a 6-byte string:
    /// Byte 1:
    ///     0 = nonreflective, 1 = reflective, 2 = saturated reflective
    /// Byte 2:
    ///     A = added by user, M = moved by user, E = end of fibre, F = found 
    ///     by software, O = out of range, D = modified end of fibre
    /// Remaining bytes are the Landmark number if used - 9s otherwise
    pub event_code: String,
    /// Loss measurement technique - 2P for two point, LS for least squares, OT 
    /// for other
    pub loss_measurement_technique: String,
    /// Marker location - ML1 is the OTDR side for 2P/LS/OT measurements
    pub marker_location_1: i32,
    /// Marker location - ML2 is the OTDR side for LS measurements, and bounds 
    /// the event for 2P/OT
    pub marker_location_2: i32,
    /// Marker location - ML3 is on the far side for LS measurements, and empty 
    /// for 2P/OT
    pub marker_location_3: i32,
    /// Marker location - ML4 is on the far side for LS measurements, and empty 
    /// for 2P/OT
    pub marker_location_4: i32,
    /// Marker location - ML5 is the reflectance calculation position
    pub marker_location_5: i32,
    /// Free comment on the event
    pub comment: String,
}

/// The last key event is as the KeyEvent, with some additional fields; see 
/// KeyEvent for the documentation of other fields
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct LastKeyEvent {
    pub event_number: i16,
    pub event_propogation_time: i32,
    pub attenuation_coefficient_lead_in_fiber: i16,
    pub event_loss: i16,
    pub event_reflectance: i32,
    pub event_code: String,
    pub loss_measurement_technique: String,
    pub marker_location_1: i32,
    pub marker_location_2: i32,
    pub marker_location_3: i32,
    pub marker_location_4: i32,
    pub marker_location_5: i32,
    pub comment: String,
    /// End to end loss is in dB*1000 and measures the loss between the two 
    /// markers defined below
    pub end_to_end_loss: i32,
    /// Start of the measurement span - typically user offset
    pub end_to_end_marker_position_1: i32,
    /// End of the measurement span - typically end of fibre event position
    pub end_to_end_marker_position_2: i32,
    /// Return loss in dB*1000 for the markers defined below
    pub optical_return_loss: u16,
    /// Start of the measurement span - typically user offset
    pub optical_return_loss_marker_position_1: i32,
    /// End of the measurement span - typically end of fibre event position
    pub optical_return_loss_marker_position_2: i32,
}

/// List of key events and a pointer to the last key event
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct KeyEvents {
    pub number_of_key_events: i16,
    pub key_events: Vec<KeyEvent>,
    pub last_key_event: LastKeyEvent,
}

/// Landmarks are a slightly esoteric feature not often used in SOR files for 
/// field test equipment. They act to relate OTDR events to real-world 
/// information such as WGS84 GPS data, known fibre MFDs, metre markers, etc
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct Landmark {
    pub landmark_number: i16,
    /// Landmark code identifies the landmark - see page 27 of the standard for 
    /// the list
    pub landmark_code: String,
    /// Location in 100ps from user offset to the landmark
    pub landmark_location: i32,
    pub related_event_number: i16,
    pub gps_longitude: i32,
    pub gps_latitude: i32,
    /// Fibre correction factor is the difference in 100*% between the optical 
    /// path and the cable length; otherwise known as heliax correction
    pub fiber_correction_factor_lead_in_fiber: i16,
    pub sheath_marker_entering_landmark: i32,
    pub sheath_marker_leaving_landmark: i32,
    pub units_of_sheath_marks_leaving_landmark: String,
    pub mode_field_diameter_leaving_landmark: i16,
    pub comment: String,
}

/// DataPointsAtScaleFactor is the struct that actually contains the data 
/// points of the measurements for a given scale factor
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct DataPointsAtScaleFactor {
    /// Number of points in this block
    pub n_points: i32,
    /// Scale factor for the data, as 1000*SF
    pub scale_factor: i16,
    /// Data points as dB*1000
    pub data: Vec<u16>,
}

/// DataPoints holds all the different datasets in this file - one per scale 
/// factor
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct DataPoints {
    pub number_of_data_points: i32,
    pub total_number_scale_factors_used: i16,
    pub scale_factors: Vec<DataPointsAtScaleFactor>,
}

/// LinkParameters are a bit esoteric and not often found in test equipment, 
/// more the likes of network management systems.
/// Contains a set of landmarks which describe the physical fibre path and may 
/// relate this to described KeyEvents
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct LinkParameters {
    pub number_of_landmarks: i16,
    pub landmarks: Vec<Landmark>,
}

/// ProprietaryBlock is a struct to contain third-party proprietary information.
/// This is mostly used for vendor-specific special sauce, extra data, extra 
/// analysis, etc.
/// otdrs extracts the header, and stores the data as an array of bytes.
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct ProprietaryBlock {
    pub header: String,
    pub data: Vec<u8>,
}

/// SORFile describes a full SOR file. All blocks except MapBlock are Option 
/// types as we cannot guarantee the parser will find them, but many blocks are 
/// in fact mandatory in the specification so compliant files will provide them.
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct SORFile {
    pub map: MapBlock,
    pub general_parameters: Option<GeneralParametersBlock>,
    pub supplier_parameters: Option<SupplierParametersBlock>,
    pub fixed_parameters: Option<FixedParametersBlock>,
    pub key_events: Option<KeyEvents>,
    pub link_parameters: Option<LinkParameters>,
    pub data_points: Option<DataPoints>,
    pub proprietary_blocks: Vec<ProprietaryBlock>,
}