-
Notifications
You must be signed in to change notification settings - Fork 1
/
record.go
435 lines (384 loc) · 11.4 KB
/
record.go
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
package goasterix
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"io"
"math/bits"
"github.com/mokhtarimokhtar/goasterix/uap"
)
var (
// ErrDataFieldUnknown reports which ErrDatafield Unknown.
ErrDataFieldUnknown = errors.New("type of datafield not found")
)
type Record struct {
Cat uint8
Fspec []byte
Items []Item
}
func NewRecord() *Record {
return &Record{}
}
// Decode extracts a Record of asterix data block (only one record).
// An asterix data block can contain a or more records.
// It returns the number of bytes unread and fills the Record Struct(Fspec, Items array) in byte.
func (rec *Record) Decode(data []byte, stdUAP uap.StandardUAP) (unRead int, err error) {
rec.Cat = stdUAP.Category
rb := bytes.NewReader(data)
rec.Fspec, err = FspecReader(rb)
unRead = rb.Len()
if err != nil {
return unRead, err
}
frnIndex := FspecIndex(rec.Fspec)
offset := uint8(0) // offset shifts the index for a conditional UAP
for _, frn := range frnIndex {
uapItem := stdUAP.Items[frn-1-offset] // here the index corresponds to the FRN
item := NewItem(uapItem)
switch uapItem.Type {
case uap.Fixed:
tmp, err := FixedDataFieldReader(rb, uapItem.Fixed.Size)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.Fixed = &tmp
case uap.Extended:
tmp, err := ExtendedDataFieldReader(rb, uapItem.Extended.PrimarySize, uapItem.Extended.SecondarySize)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.Extended = &tmp
case uap.Explicit:
tmp, err := ExplicitDataFieldReader(rb)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.Explicit = &tmp
case uap.Repetitive:
tmp, err := RepetitiveDataFieldReader(rb, uapItem.Repetitive.SubItemSize)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.Repetitive = &tmp
case uap.Compound:
tmp, err := CompoundDataFieldReader(rb, uapItem.Compound)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.Compound = &tmp
case uap.SP:
tmp, err := SPAndREDataFieldReader(rb)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.SP = &tmp
case uap.RE:
tmp, err := SPAndREDataFieldReader(rb)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.RE = &tmp
case uap.RFS:
tmp, err := RFSDataFieldReader(rb, stdUAP.Items)
if err != nil {
unRead = rb.Len()
return unRead, err
}
item.RFS = &tmp
default:
err = ErrDataFieldUnknown
return unRead, err
}
unRead = rb.Len()
rec.Items = append(rec.Items, *item)
if uapItem.Conditional {
switch item.Meta.Type {
case uap.Fixed:
stdUAP.Items = selectUAPConditional(stdUAP.Category, item.Fixed.Data)
case uap.Extended:
stdUAP.Items = selectUAPConditional(stdUAP.Category, item.Extended.Primary)
}
offset = frn
}
}
return unRead, nil
}
// String returns a string(hex) representation of one asterix record (only existing items).
func (rec Record) String() []string {
var items []string
tmp := "FSPEC: " + hex.EncodeToString(rec.Fspec)
items = append(items, tmp)
for _, item := range rec.Items {
tmp := item.String()
items = append(items, tmp)
}
return items
}
// Payload returns a slice of byte for one asterix record.
func (rec Record) Payload() []byte {
var pd []byte
pd = append(pd, rec.Fspec...)
for _, item := range rec.Items {
pd = append(pd, item.Payload()...)
}
return pd
}
func selectUAPConditional(category uint8, field []byte) []uap.DataField {
var selectedUAP []uap.DataField
switch category {
case 1:
tmp := field[0] & 0x80 >> 7
if tmp == 1 {
selectedUAP = uap.Cat001TrackV12
} else {
selectedUAP = uap.Cat001PlotV12
}
case 26:
tmp := field[0] & 0x80 >> 7
if tmp == 1 {
selectedUAP = uap.Cat4TestTrack
} else {
selectedUAP = uap.Cat4TestPlot
}
}
return selectedUAP
}
// FspecReader returns a slice of FSPEC data record asterix.
func FspecReader(reader io.Reader) ([]byte, error) {
var fspec []byte
var err error
for {
var tmp uint8
err = binary.Read(reader, binary.BigEndian, &tmp)
if err != nil {
return nil, err
}
fspec = append(fspec, tmp)
if tmp&0x01 == 0 {
break
}
}
return fspec, err
}
// FspecIndex returns an array of uint8 corresponding to number FRN(Field Reference Number of Items).
// In other words, it transposes a fspec bits to an array FRNs.
// e.g. fspec = 1010 1010 => frnIndex = []uint8{1, 3, 5, 7}
func FspecIndex(fspec []byte) []uint8 {
var frnIndex []uint8
for j, val := range fspec {
for i := 0; i < 7; i++ {
frn := 7*j + i + 1
tmp := bits.RotateLeft8(val, i)
if tmp&0x80 != 0 {
frnIndex = append(frnIndex, uint8(frn))
}
}
}
return frnIndex
}
// FixedDataFieldReader extracts a number(nb) of bytes(size) and returns a slice of bytes(data of item).
// Fixed length Data Fields shall comprise a fixed number of octets.
func FixedDataFieldReader(rb *bytes.Reader, size uint8) (Fixed, error) {
var err error
item := Fixed{}
tmp := make([]byte, size)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return item, err
}
item.Data = tmp
return item, err
}
// ExtendedDataFieldReader extracts data item type Extended (FX: last bit = 1).
// primarySize parameter defines the Primary Subitem of extended field.
// secondarySize parameter defines the Secondary Subitem of extended field.
// Extended length Data Fields, being of a variable length, shall contain a primary part of predetermined length,
// immediately followed by a number of secondary parts, each of predetermined length.
// The presence of the next following secondary part shall be indicated by the setting to one of the
// Least Significant Bit (LSB) of the last octet of the preceding part (either the primary part or a secondary part).
// This bit which is reserved for that purpose is called the Field Extension Indicator (FX).
func ExtendedDataFieldReader(rb *bytes.Reader, primarySize uint8, secondarySize uint8) (Extended, error) {
var err error
item := Extended{}
tmp := make([]byte, primarySize)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return item, err
}
item.Primary = tmp
if tmp[primarySize-1]&0x01 != 0 {
for {
tmp := make([]byte, secondarySize)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return item, err
}
item.Secondary = append(item.Secondary, tmp...)
if tmp[secondarySize-1]&0x01 == 0 {
break
}
}
}
return item, err
}
// ExplicitDataFieldReader extracts a number of bytes define by the first byte.
// Explicit length Data Fields shall start with a one-octet length indicator giving
// the total field length in octets including the length indicator itself.
func ExplicitDataFieldReader(rb *bytes.Reader) (Explicit, error) {
var err error
item := Explicit{}
err = binary.Read(rb, binary.BigEndian, &item.Len)
if err != nil {
return item, err
}
tmp := make([]byte, item.Len-1)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return item, err
}
item.Data = tmp
return item, err
}
// RepetitiveDataFieldReader extracts data item type Repetitive(1+rep*N byte).
// The first byte is REP(factor), nb is the size of bytes to repetition.
// Repetitive Data Fields, being of a variable length, shall comprise a one-octet Field Repetition Indicator (REP)
// signalling the presence of N consecutive sub-fields each of the same pre-determined length.
func RepetitiveDataFieldReader(rb *bytes.Reader, SubItemSize uint8) (Repetitive, error) {
var err error
item := Repetitive{}
err = binary.Read(rb, binary.BigEndian, &item.Rep)
if err != nil {
return item, err
}
tmp := make([]byte, item.Rep*SubItemSize)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return item, err
}
item.Data = tmp
return item, err
}
// CompoundDataFieldReader
// Compound Data Fields, being of a variable length, shall comprise a primary subfield, followed by data subfields.
// The primary subfield determines the presence or absence of the subsequent data subfields. It comprises a first part
// of one octet extendable using the Field Extension (FX) mechanism.
// The definition, structure and format of the data subfields are part of the description of the relevant Compound Data
// Item. Data subfields shall be either fixed length, extended length, explicit length or repetitive, but not compound.
func CompoundDataFieldReader(rb *bytes.Reader, cp []uap.DataField) (Compound, error) {
var err error
items := Compound{}
items.Primary, err = FspecReader(rb)
if err != nil {
return items, err
}
frnIndex := FspecIndex(items.Primary)
for _, frn := range frnIndex {
uapItem := cp[frn-1]
item := NewItem(uapItem)
switch uapItem.Type {
case uap.Fixed:
tmp, err := FixedDataFieldReader(rb, uapItem.Fixed.Size)
if err != nil {
return items, err
}
item.Fixed = &tmp
items.Secondary = append(items.Secondary, *item)
case uap.Extended:
tmp, err := ExtendedDataFieldReader(rb, uapItem.Extended.PrimarySize, uapItem.Extended.SecondarySize)
if err != nil {
return items, err
}
item.Extended = &tmp
items.Secondary = append(items.Secondary, *item)
case uap.Explicit:
tmp, err := ExplicitDataFieldReader(rb)
if err != nil {
return items, err
}
item.Explicit = &tmp
items.Secondary = append(items.Secondary, *item)
case uap.Repetitive:
tmp, err := RepetitiveDataFieldReader(rb, uapItem.Repetitive.SubItemSize)
if err != nil {
return items, err
}
item.Repetitive = &tmp
items.Secondary = append(items.Secondary, *item)
default:
err = ErrDataFieldUnknown
return items, err
}
}
return items, err
}
// RFSDataFieldReader
// The RFS organised field is a collection of Data Fields which in
// contrast to the OFS organisation, can occur in any order.
// The RFS organised field shall be structured as follows:
// - the first octet provides the number, N, of Data Fields following;
// - N fields in any arbitrary order each consisting of a one-octet FRN immediately followed by the contents of the
// Data Item associated with the preceding FRN.
func RFSDataFieldReader(rb *bytes.Reader, items []uap.DataField) (RandomFieldSequencing, error) {
var err error
rfs := RandomFieldSequencing{}
// N is the total number of datafields
err = binary.Read(rb, binary.BigEndian, &rfs.N)
if err != nil {
return rfs, err
}
for i := uint8(0); i < rfs.N; i++ {
// retrieve random FRN
var frn uint8
err := binary.Read(rb, binary.BigEndian, &frn)
if err != nil {
return rfs, err
}
for _, field := range items {
if frn == field.FRN {
rf := new(RandomField)
rf.FRN = frn
// todo: work just for Fixed datafield use case
tmp := make([]byte, field.Fixed.Size)
err := binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return rfs, err
}
item := NewItem(field)
fixed := Fixed{}
fixed.Data = tmp
item.Fixed = &fixed
rf.Field = *item
rfs.Sequence = append(rfs.Sequence, *rf)
}
}
}
return rfs, err
}
// SPAndREDataFieldReader extracts returns a slice
// ref. EUROCONTROL-SPEC-0149 2.4
// 4.3.5 Non-Standard Data Fields:
// Reserved Expansion Data
// Field Special Purpose field
func SPAndREDataFieldReader(rb *bytes.Reader) (SPandREField, error) {
var err error
sp := SPandREField{}
err = binary.Read(rb, binary.BigEndian, &sp.Len)
if err != nil {
return sp, err
}
tmp := make([]byte, sp.Len-1)
err = binary.Read(rb, binary.BigEndian, &tmp)
if err != nil {
return sp, err
}
sp.Data = tmp
return sp, err
}