-
-
Notifications
You must be signed in to change notification settings - Fork 15
Adding label 16 AUTPOS decoding #340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { MessageDecoder } from '../MessageDecoder'; | ||
| import { Label_16_AUTPOS } from './Label_16_AUTPOS'; | ||
|
|
||
| describe('Label 16 AUTPOS', () => { | ||
| let plugin: Label_16_AUTPOS; | ||
| const message = { label: '16', text: '' }; | ||
|
|
||
| beforeEach(() => { | ||
| const decoder = new MessageDecoder(); | ||
| plugin = new Label_16_AUTPOS(decoder); | ||
| }); | ||
|
|
||
| test('matches qualifiers', () => { | ||
| expect(plugin.decode).toBeDefined(); | ||
| expect(plugin.name).toBe('label-16-autpos'); | ||
| expect(plugin.qualifiers).toBeDefined(); | ||
| expect(plugin.qualifiers()).toEqual({ | ||
| labels: ['16'], | ||
| preambles: [''], | ||
| }); | ||
| }); | ||
|
|
||
| test('matches redacted', () => { | ||
| message.text = | ||
| '283806/AUTPOS/LLD N400547 W0774954\r\n/ALT 12932/SAT ****\r\n/WND ******/TAT ****/TAS ****/CRZ ***\r\n/FOB 065120\r\n/DAT 260228/TIM 150742'; | ||
| const decodeResult = plugin.decode(message); | ||
|
|
||
| expect(decodeResult.decoded).toBe(true); | ||
| expect(decodeResult.decoder.decodeLevel).toBe('full'); | ||
| expect(decodeResult.raw.day).toBe(28); | ||
| expect(decodeResult.raw.flight_number).toBe('3806'); | ||
| expect(decodeResult.raw.position.latitude).toBeCloseTo(40.096, 3); | ||
| expect(decodeResult.raw.position.longitude).toBeCloseTo(-77.832, 3); | ||
| expect(decodeResult.raw.altitude).toBe(12932); | ||
| expect(decodeResult.raw.fuel_on_board).toBe(65120); | ||
| expect(decodeResult.raw.message_timestamp).toBe(1772291262); | ||
| expect(decodeResult.formatted.description).toBe('Position Report'); | ||
| expect(decodeResult.formatted.items.length).toBe(6); | ||
| }); | ||
|
|
||
| test('matches all values', () => { | ||
| message.text = | ||
| '289142/AUTPOS/LLD N395538 W0753341 \r\n/ALT 35000/SAT -057\r\n/WND 239065/TAT -027/TAS 476/CRZ 836\r\n/FOB 107600\r\n/DAT 260228/TIM 132714'; | ||
| const decodeResult = plugin.decode(message); | ||
| expect(decodeResult.decoded).toBe(true); | ||
| expect(decodeResult.decoder.decodeLevel).toBe('full'); | ||
| expect(decodeResult.raw.day).toBe(28); | ||
| expect(decodeResult.raw.flight_number).toBe('9142'); | ||
| expect(decodeResult.raw.position.latitude).toBeCloseTo(39.927, 3); | ||
| expect(decodeResult.raw.position.longitude).toBeCloseTo(-75.561, 3); | ||
| expect(decodeResult.raw.altitude).toBe(35000); | ||
| expect(decodeResult.raw.wind_data[0].windSpeed).toBe(65); | ||
| expect(decodeResult.raw.wind_data[0].windDirection).toBe(239); | ||
| expect(decodeResult.raw.total_air_temperature).toBe(-27); | ||
| expect(decodeResult.raw.airspeed).toBe(476); | ||
| expect(decodeResult.raw.mach).toBe(0.836); | ||
| expect(decodeResult.raw.fuel_on_board).toBe(107600); | ||
| expect(decodeResult.raw.message_timestamp).toBe(1772285234); | ||
| expect(decodeResult.formatted.items.length).toBe(11); | ||
| }); | ||
|
|
||
| test('does not match if wrong format', () => { | ||
| message.text = 'invalid AUTPOS message'; | ||
| const decodeResult = plugin.decode(message); | ||
| expect(decodeResult.decoded).toBe(false); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| import { DateTimeUtils } from '../DateTimeUtils'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { DecoderPlugin } from '../DecoderPlugin'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { DecodeResult, Message } from '../DecoderPluginInterface'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { CoordinateUtils } from '../utils/coordinate_utils'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { ResultFormatter } from '../utils/result_formatter'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export class Label_16_AUTPOS extends DecoderPlugin { | ||||||||||||||||||||||||||||||||||||||||||||||
| name = 'label-16-autpos'; | ||||||||||||||||||||||||||||||||||||||||||||||
| qualifiers() { | ||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||
| labels: ['16'], | ||||||||||||||||||||||||||||||||||||||||||||||
| preambles: [''], | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| decode(message: Message): DecodeResult { | ||||||||||||||||||||||||||||||||||||||||||||||
| let decodeResult = this.defaultResult(); | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.decoder.name = this.name; | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.message = message; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Regex to match the AUTPOS message | ||||||||||||||||||||||||||||||||||||||||||||||
| const regex = | ||||||||||||||||||||||||||||||||||||||||||||||
| /^(\d{6})\/AUTPOS\/LLD (N|S)(\d{2})(\d{2})(\d{2}) (E|W)(\d{3})(\d{2})(\d{2})\s*\r?\n\/ALT (\d+)\/SAT ([*\-\d]{4})\r?\n\/WND ([*\d]{3})([\*\d]{3})\/TAT ([*\-\d]{4})\/TAS ([*\d]{3,4})\/CRZ ([*\d]{3,4})\r?\n\/FOB (\d{6})\r?\n\/DAT (\d{6})\/TIM (\d{6})/; | ||||||||||||||||||||||||||||||||||||||||||||||
| const match = regex.exec(message.text); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!match) { | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.decoded = false; | ||||||||||||||||||||||||||||||||||||||||||||||
| return decodeResult; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.decoded = true; | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.decoder.decodeLevel = 'full'; | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult.formatted.description = 'Position Report'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Extract fields | ||||||||||||||||||||||||||||||||||||||||||||||
| const [ | ||||||||||||||||||||||||||||||||||||||||||||||
| , | ||||||||||||||||||||||||||||||||||||||||||||||
| flight, | ||||||||||||||||||||||||||||||||||||||||||||||
| latHem, | ||||||||||||||||||||||||||||||||||||||||||||||
| latDeg, | ||||||||||||||||||||||||||||||||||||||||||||||
| latMin, | ||||||||||||||||||||||||||||||||||||||||||||||
| latSec, | ||||||||||||||||||||||||||||||||||||||||||||||
| lonHem, | ||||||||||||||||||||||||||||||||||||||||||||||
| lonDeg, | ||||||||||||||||||||||||||||||||||||||||||||||
| lonMin, | ||||||||||||||||||||||||||||||||||||||||||||||
| lonSec, | ||||||||||||||||||||||||||||||||||||||||||||||
| altitude, | ||||||||||||||||||||||||||||||||||||||||||||||
| sat, | ||||||||||||||||||||||||||||||||||||||||||||||
| windDir, | ||||||||||||||||||||||||||||||||||||||||||||||
| windSpd, | ||||||||||||||||||||||||||||||||||||||||||||||
| tat, | ||||||||||||||||||||||||||||||||||||||||||||||
| tas, | ||||||||||||||||||||||||||||||||||||||||||||||
| crz, | ||||||||||||||||||||||||||||||||||||||||||||||
| fob, | ||||||||||||||||||||||||||||||||||||||||||||||
| dat, | ||||||||||||||||||||||||||||||||||||||||||||||
| tim, | ||||||||||||||||||||||||||||||||||||||||||||||
| ] = match; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.day(decodeResult, parseInt(flight.slice(0, 2), 10)); | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.flightNumber(decodeResult, flight.slice(2)); | ||||||||||||||||||||||||||||||||||||||||||||||
| let latitude = CoordinateUtils.dmsToDecimalDegrees( | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(latDeg, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(latMin, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(latSec, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (latHem === 'S') latitude = -latitude; | ||||||||||||||||||||||||||||||||||||||||||||||
| let longitude = CoordinateUtils.dmsToDecimalDegrees( | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(lonDeg, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(lonMin, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(lonSec, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (lonHem === 'W') longitude = -longitude; | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.position(decodeResult, { | ||||||||||||||||||||||||||||||||||||||||||||||
| latitude, | ||||||||||||||||||||||||||||||||||||||||||||||
| longitude, | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| const alt = parseInt(altitude, 10); | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.altitude(decodeResult, alt); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Parse wind | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!windSpd.includes('*') && !windDir.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.windData(decodeResult, [ | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| waypoint: { | ||||||||||||||||||||||||||||||||||||||||||||||
| name: 'POSITION', | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| flightLevel: Math.round(alt / 100), | ||||||||||||||||||||||||||||||||||||||||||||||
| windDirection: parseInt(windDir, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| windSpeed: parseInt(windSpd, 10), | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| ]); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (!tat.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.totalAirTemp(decodeResult, tat); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!tas.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.airspeed(decodeResult, parseInt(tas, 10)); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!sat.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.temperature(decodeResult, sat); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!crz.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.mach(decodeResult, parseInt(crz, 10) / 1000); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (!fob.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.currentFuel(decodeResult, parseInt(fob, 10)); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (!dat.includes('*') && !tim.includes('*')) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const yy = dat.slice(0, 2); | ||||||||||||||||||||||||||||||||||||||||||||||
| const mm = dat.slice(2, 4); | ||||||||||||||||||||||||||||||||||||||||||||||
| const dd = dat.slice(4, 6); | ||||||||||||||||||||||||||||||||||||||||||||||
| const mmddyy = `${mm}${dd}${yy}`; | ||||||||||||||||||||||||||||||||||||||||||||||
| ResultFormatter.timestamp( | ||||||||||||||||||||||||||||||||||||||||||||||
| decodeResult, | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTimeUtils.convertDateTimeToEpoch(tim, mmddyy), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+110
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Date format comment is incorrect; actual format appears to be YYMMDD. The comment states DAT is 📝 Proposed fix if (!dat.includes('*') && !tim.includes('*')) {
- // DAT is DDMMYY, TIM is HHMMSS
+ // DAT is YYMMDD, TIM is HHMMSS
// DateTimeUtils.convertDateTimeToEpoch expects MMDDYY
const yy = dat.slice(0, 2);
const mm = dat.slice(2, 4);
const dd = dat.slice(4, 6);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is incorrect. the code is corect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
However, if you feel the comment is fine as-is or prefer to keep it, that's perfectly okay. I'll defer to your judgment on this. 🧠 Learnings used |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| return decodeResult; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: airframesio/acars-decoder-typescript
Length of output: 331
🏁 Script executed:
Repository: airframesio/acars-decoder-typescript
Length of output: 487
🏁 Script executed:
Repository: airframesio/acars-decoder-typescript
Length of output: 611
🏁 Script executed:
Repository: airframesio/acars-decoder-typescript
Length of output: 1836
🏁 Script executed:
Repository: airframesio/acars-decoder-typescript
Length of output: 106
Plugin not exported from official plugins module.
The
Label_16_AUTPOSplugin is not exported fromlib/plugins/official.tsand therefore not registered inMessageDecoder.ts. The plugin class exists but won't be invoked when decoding messages through the mainMessageDecoder.decode()API. Add the export line tolib/plugins/official.tsalongside the other Label_16 variants (lines 11-14):🤖 Prompt for AI Agents