# National Transportation Safety Board feed generator # Expects JSON data from a NTSB CAROL investigation data export, outputs xmltodict-compatible JSON # WARNING: Dates are locale-sensitive; the RSS feed might not generate correctly with another locale. # Convert an object to an HTML definition list def to_dl: if length > 0 then "
\([to_entries[]|select(.value != null)|"
\(.key)
\(.value)
"]|join(""))
" else null end; # Convert an array to a HTML unordered list def to_ul: if length > 0 then "" else null end; { "AL": "Alabama", "AK": "Alaska", "AZ": "Arizona", "AR": "Arkansas", "AO": "Atlantic Ocean", "CA": "California", "CB": "Caribbean Sea", "CO": "Colorado", "CT": "Connecticut", "DE": "Delaware", "DC": "District of Columbia", "FL": "Florida", "GA": "Georgia", "GM": "Gulf of Mexico", "HI": "Hawaii", "ID": "Idaho", "IL": "Illinois", "IN": "Indiana", "IA": "Iowa", "KS": "Kansas", "KY": "Kentucky", "LA": "Louisiana", "ME": "Maine", "MD": "Maryland", "MA": "Massachusetts", "MI": "Michigan", "MN": "Minnesota", "MS": "Mississippi", "MO": "Missouri", "MT": "Montana", "NE": "Nebraska", "NV": "Nevada", "NH": "New Hampshire", "NJ": "New Jersey", "NM": "New Mexico", "NY": "New York", "NC": "North Carolina", "ND": "North Dakota", "OH": "Ohio", "OK": "Oklahoma", "OR": "Oregon", "OF": "Other Foreign", "PO": "Pacific Ocean", "PA": "Pennsylvania", "PR": "Puerto Rico", "RI": "Rhode Island", "SC": "South Carolina", "SD": "South Dakota", "TN": "Tennessee", "TX": "Texas", "UT": "Utah", "VT": "Vermont", "VA": "Virginia", "WA": "Washington", "WV": "West Virginia", "WI": "Wisconsin", "WY": "Wyoming" } as $states | { "AF": "Afghanistan", "AL": "Albania", "AG": "Algeria", "AQ": "American Samoa", "AN": "Andorra", "AO": "Angola", "AV": "Anguilla", "AY": "Antarctica", "AC": "Antigua And Barbuda", "AR": "Argentina", "AM": "Armenia", "AA": "Aruba", "AT": "Ashmore And Cartier Islands", "AS": "Australia", "AU": "Austria", "AJ": "Azerbaijan", "BF": "Bahamas", "BA": "Bahrain", "FQ": "Baker Island", "BG": "Bangladesh", "BB": "Barbados", "BS": "Bassas Da India", "BO": "Belarus", "BE": "Belgium", "BH": "Belize", "BN": "Benin", "BD": "Bermuda", "BT": "Bhutan", "BL": "Bolivia", "BK": "Bosnia And Herzegovina", "BC": "Botswana", "BV": "Bouvet Island", "BR": "Brazil", "IO": "British Indian Ocean Territory", "VG": "British Virgin Islands", "BX": "Brunei", "BU": "Bulgaria", "UV": "Burkina", "BM": "Burma", "BY": "Burundi", "CB": "Cambodia", "CM": "Cameroon", "CA": "Canada", "CV": "Cape Verde", "CJ": "Cayman Islands", "CT": "Central African Republic", "CD": "Chad", "CI": "Chile", "CH": "China", "KT": "Christmas Island", "IP": "Clipperton Island", "CK": "Cocos (keeling) Islands", "CO": "Colombia", "CN": "Comoros", "CF": "Congo", "CW": "Cook Islands", "CR": "Coral Sea Islands", "CS": "Costa Rica", "IV": "Cote D'ivoire", "HR": "Croatia", "CU": "Cuba", "CUW": "Curacao", "CY": "Cyprus", "EZ": "Czech Republic", "KN": "Democratic People's Republic Of Korea", "DA": "Denmark", "DJ": "Djibouti", "DO": "Dominica", "DR": "Dominican Republic", "EC": "Ecuador", "EG": "Egypt", "ES": "El Salvador", "EK": "Equatorial Guinea", "ER": "Eritrea", "EN": "Estonia", "ET": "Ethiopia", "EU": "Europa Island", "FK": "Falkland Islands (islas Mal) as)", "FO": "Faroe Islands", "FM": "Federated States Of Micronesia", "FJ": "Fiji", "FI": "Finland", "FT": "Fr Terr Of Afars Issas", "FR": "France", "FG": "French Guiana", "FP": "French Polynesia", "FS": "French Southern And Antarctic Lands", "GB": "Gabon", "GA": "Gambia", "GZ": "Gaza Strip", "GG": "Georgia", "GE": "Germany", "GH": "Ghana", "GI": "Gibraltar", "GO": "Glorioso Islands", "GR": "Greece", "GL": "Greenland", "GJ": "Grenada", "GP": "Guadeloupe", "GT": "Guatemala", "GK": "Guernsey", "GV": "Guinea", "PU": "Guinea-bissau", "GY": "Guyana", "HA": "Haiti", "HM": "Heard Island And Mcdonald Islands", "HO": "Honduras", "HK": "Hong Kong", "HU": "Hungary", "IC": "Iceland", "IN": "India", "ID": "Indonesia", "IR": "Iran", "IZ": "Iraq", "EI": "Ireland", "IM": "Isle of Man", "IS": "Israel", "IT": "Italy", "IY": "Ivory Coast", "JM": "Jamaica", "JN": "Jan Mayen", "JA": "Japan", "JE": "Jersey", "JO": "Jordan", "JU": "Juan De Nova Island", "KZ": "Kazakhstan", "KE": "Kenya", "KQ": "Kingman Reef", "KR": "Kiribati", "KU": "Kuwait", "KG": "Kyrgyzstan", "LA": "Laos", "LG": "Latvia", "LE": "Lebanon", "LT": "Lesotho", "LI": "Liberia", "LY": "Libya", "LS": "Liechtenstein", "LH": "Lithuania", "LU": "Luxembourg", "MC": "Macau", "MK": "Macedonia", "MA": "Madagascar", "MI": "Malawi", "MY": "Malaysia", "MV": "Maldives", "ML": "Mali", "MT": "Malta", "MS": "Marshall Islands", "MB": "Martinique", "MR": "Mauritania", "MP": "Mauritius", "MF": "Mayotte", "MX": "Mexico", "MD": "Moldova", "MN": "Monaco", "MG": "Mongolia", "MW": "Montenegro", "MH": "Montserrat", "MO": "Morocco", "MZ": "Mozambique", "WA": "Namibia", "NR": "Nauru", "NP": "Nepal", "NL": "Netherlands", "NT": "Netherlands Antilles", "NC": "New Caledonia", "NZ": "New Zealand", "NU": "Nicaragua", "NG": "Niger", "NI": "Nigeria", "NE": "Niue", "NF": "Norfolk Island", "CQ": "Northern Mariana Islands", "NO": "Norway", "MU": "Oman", "PK": "Pakistan", "PW": "Palau", "PM": "Panama", "PP": "Papua New Guinea", "PF": "Paracel Islands", "PA": "Paraguay", "PE": "Peru", "RP": "Philippines", "PC": "Pitcairn Islands", "PL": "Poland", "PO": "Portugal", "QA": "Qatar", "KS": "Republic Of Korea", "RE": "Reunion", "RO": "Romania", "RS": "Russia", "RW": "Rwanda", "SM": "San Marino", "TP": "Sao Tome And Principe", "SA": "Saudi Arabia", "SG": "Senegal", "SR": "Serbia", "SE": "Seychelles", "SL": "Sierra Leone", "SN": "Singapore", "LO": "Slovakia", "SI": "Slovenia", "BP": "Solomon Islands", "SO": "Somalia", "SF": "South Africa", "SX": "South Georgia & S Sandwich Isl", "SP": "Spain", "PG": "Spratly Islands", "CE": "Sri Lanka", "SH": "St Helena", "SC": "St Kitts And Nevis", "ST": "St Lucia", "SB": "St Pierre And Miquelon", "VC": "St Vincent And The Grenadines", "SU": "Sudan", "NS": "Suriname", "SV": "Svalbard", "WZ": "Swaziland", "SW": "Sweden", "SZ": "Switzerland", "SY": "Syria", "TW": "Taiwan", "TI": "Tajikistan", "TZ": "Tanzania", "TH": "Thailand", "TO": "Togo", "TL": "Tokelau", "TN": "Tonga", "TD": "Trinidad And Tobago", "TE": "Tromelin Island", "PS": "Trust Terr Of Pacific Islands", "TS": "Tunisia", "TU": "Turkey", "TX": "Turkmenistan", "TK": "Turks And Caicos Islands", "TV": "Tuvalu", "UG": "Uganda", "UP": "Ukraine", "TC": "United Arab Emirates", "UK": "United Kingdom", "USA": "United States", "UN": "Unknown", "UY": "Uruguay", "UZ": "Uzbekistan", "NH": "Vanuatu", "VT": "Vatican City", "VE": "Venezuela", "VM": "Vietnam", "WQ": "Wake Island", "WF": "Wallis And Futuna", "WE": "West Bank", "WN": "West Indies", "WI": "Western Sahara", "WS": "Western Samoa", "YM": "Yemen", "CG": "Zaire", "ZA": "Zambia", "ZI": "Zimbabwe" } as $countries | { "AIR": "Airplane", "BALL": "Balloon", "BLIM": "Blimp", "CSF": "Commercial space flight", "GLI": "Glider", "GYRO": "Gyroplane", "HELI": "Helicopter", "PPAR": "Powered parachute", "PLFT": "Powered-lift", "RCKT": "Rocket", "ULTR": "Ultralight", "UNK": "Unknown", "UNMANNED": "Unmanned", "WSFT": "Weight-shift" } as $aircraft_categories | { "ARMF": "Armed Forces", "NUSC": "Non-U.S., commercial", "NUSN": "Non-U.S., non-commercial", "103": "Part 103: Ultralight", "107": "Part 107: Small UAS", "121": "Part 121: Air carrier", "125": "Part 125: 20+ pax,6000+ lbs", "129": "Part 129: Foreign", "133": "Part 133: Rotorcraft ext. load", "135": "Part 135: Air taxi & commuter", "137": "Part 137: Agricultural", "415": "Part 415: Commercial space flight", "431": "Part 431: Commercial space flight", "435": "Part 435: Commercial space flight", "437": "Part 437: Commercial space flight", "091K": "Part 91 subpart k: Fractional", "091": "Part 91: General aviation", "091F": "Part 91F: Special flt ops.", "PUBU": "Public aircraft", "UNK": "Unknown" } as $far_parts | { "MEDE": "Medical emergency", "DISC": "Discretionary", "ORGT": "Organizational" } as $air_medical_types | { "ELEC": "Electric", "GTFN": "Geared turbofan", "HR": "Hybrid rocket", "LR": "Liquid rocket", "NONE": "None", "REC": "Reciprocating", "SR": "Solid rocket", "TF": "Turbo fan", "TJ": "Turbo jet", "TP": "Turbo prop", "TS": "Turbo shaft", "UNK": "Unknown" } as $engine_types | { "AAPL": "Aerial application", "AOBV": "Aerial observation", "ADRP": "Air drop", "ASHO": "Air race/show", "BANT": "Banner tow", "BUS": "Business", "EXEC": "Executive/Corporate", "EXLD": "External load", "FERY": "Ferry", "FIRF": "Firefighting", "FLTS": "Flight test", "GLDT": "Glider tow", "INST": "Instructional", "OWRK": "Other work use", "PERS": "Personal", "POSI": "Positioning", "PUBU": "Public aircraft", "PUBF": "Public aircraft - federal", "PUBL": "Public aircraft - local", "PUBS": "Public aircraft - state", "SKYD": "Skydiving", "UNK": "Unknown" } as $flight_operation_types | { "CARG": "Cargo", "MAIL": "Mail contract only", "PAX": "Passenger", "PACA": "Passenger/Cargo", "UNK": "Unknown" } as $flight_service_types | { "DOM": "Domestic", "INT": "International" } as $flight_terminal_types | { "0": "Level 0: No Driving Automation", "1": "Level 1: Driver Assistance", "2": "Level 2: Partial Driving Automation", "3": "Level 3: Conditional Driving Automation", "4": "Level 4: High Driving Automation", "5": "Level 5: Full Driving Automation" } as $driving_automation_levels | { "ACC": "Accident", "INC": "Incident", "OCC": "Occurrence" } as $event_types | { # This list only includes aviation-related event codes, as codes are not officially documented and some # aviation codes can be found as the available values of the EventSequence.CICTTEventSOEGroup field # from CAROL's API. "000": "Unknown or undetermined", "090": "Abnormal runway contact", "091": "Tailstrike", "092": "Hard landing", "093": "Dragged wing/rotor/float/other", "094": "Landing gear collapse", "095": "Landing gear not configured", "096": "Nose over/nose down", "097": "Roll over", "100": "Air traffic event", "110": "Cabin safety event", "120": "Controlled flight into terr/obj (CFIT)", "130": "Emergency descent initiated", "140": "Engine shutdown", "150": "Fire/smoke (non-impact)", "160": "Explosion (non-impact)", "170": "Fire/smoke (post-impact)", "180": "Explosion (post-impact)", "190": "Fuel related", "191": "Fuel starvation", "192": "Fuel exhaustion", "193": "Fuel contamination", "194": "Wrong fuel", "200": "Ground collision", "210": "Structural icing", "220": "Low altitude operation/event", "230": "Loss of control on ground", "231": "Dynamic rollover", "232": "Ground resonance", "240": "Loss of control in flight", "241": "Aerodynamic stall/spin", "242": "Loss of tail rotor effectiveness", "243": "Retreating blade stall", "244": "Settling with power/vortex ring state", "245": "Mast bumping", "250": "Midair collision", "260": "Near midair/TCAS alert/loss of separation", "270": "Abrupt maneuver", "271": "Inflight upset", "280": "Navigation error", "281": "Course deviation", "282": "Altitude deviation", "283": "Airspace incursion", "284": "Wrong surface or wrong airport", "300": "Runway excursion", "310": "Wildlife encounter (non-bird)", "320": "Runway incursion veh/AC/person", "330": "Sys/Comp malf/fail (non-power)", "331": "Pressure/environ sys malf/fail", "332": "Electrical system malf/failure", "333": "Flight control sys malf/fail", "334": "Flight instrument malf/fail", "335": "Nav system malfunction/failure", "336": "Comm system malf/failure", "337": "Aircraft structural failure", "338": "Part(s) separation from AC", "340": "Powerplant sys/comp malf/fail", "341": "Loss of engine power (total)", "342": "Loss of engine power (partial)", "343": "Uncontained engine failure", "350": "Security/criminal event", "360": "Turbulence encounter", "361": "Aircraft wake turb encounter", "362": "Clear air turbulence encounter", "370": "Landing area undershoot", "380": "Landing area overshoot", "390": "Windshear or thunderstorm", "400": "Other weather encounter", "401": "VFR encounter with IMC", "402": "Loss of visual reference", "410": "Terrain avoidance alert", "420": "Collision avoidance alert", "430": "Stall warn/stick-shaker/pusher", "440": "Off-field or emergency landing", "441": "Ditching", "450": "Hazardous material leak/spill", "460": "Evacuation", "470": "Collision with terr/obj (non-CFIT)", "480": "External load event (Rotorcraft)", "850": "Medical event", "901": "Birdstrike" } as $event_codes | { "rss": { "@version": "2.0", "channel": { "title": "National Transportation Safety Board", "description": "Completed investigation reports from the National Transportation Safety Board of the United States.", "link": "https://www.ntsb.gov/", "language": "en-US", "pubDate": (now | strftime("%a, %d %b %Y %T %z")), "docs": "https://www.rssboard.org/rss-specification", "ttl": 1440, "generator": "ITSB", "item": [.[] | { "title": .cm_ntsbNum, "author": .cm_agency, "description": (({ "Unique identifier": .cm_mkey, "NTSB case number": .cm_ntsbNum, "Transportation mode": .cm_mode, "Investigation agency": .cm_agency, "Completion status": .cm_completionStatus, "Occurrence date": .cm_eventDate, "Publication date": .cm_originalPublishedDate, "Report type": .cm_mostRecentReportType, "Last updated": .cm_recentReportPublishDate, "Event type": (.cm_eventType | try ($event_types[.] // .) catch null), "Location": ( .cm_city + if .cm_state != null then ", \(.cm_state | $states[.] // .)" elif .cm_country != null then ", \(.cm_country | $countries[.] // .)" else "" end ), "Airport": (if .airportName != null then "\(.airportName) (\(.airportId))" else null end), "Weather conditions": (.accidentSiteCondition | if . == "VMC" then "Visual Meteorological Conditions" elif . == "IMC" then "Instrument Meteorological Conditions" else . end), "Injuries": "\(.cm_fatalInjuryCount) fatal, \(.cm_seriousInjuryCount) serious, \(.cm_minorInjuryCount) minor", "Pipeline operator": .pipelineOperator, "Pipeline type": .pipelineType, "Regulator type": .regulatorType, "Chemical released": .chemicalReleased, "Hazard class": .hazardClass, "Hazardous material operator": .hazmatOperator, "Hazardous material type": .hazmatType, "State of material": .stateOfMaterial, "Container type": .containerType, "Department of Transportation container specifications": .dotContainerSpec, "Competent authority": .competentAuthority, "Preliminary narrative": .prelimNarrative, "Factual narrative": .factualNarrative, "Analysis": .analysisNarrative, "Probable cause": .cm_probableCause, "Has safety recommendations": .cm_hasSafetyRec, } | to_dl) + ([ (.cm_vehicles // []) | to_entries[] | "

Vehicle \(.key + 1)

" + (.value | { "Aircraft category": (.aircraftCategory | try ($aircraft_categories[.] // .) catch null), "Make": .make, "Model": .model, "Amateur built": .amateurBuilt, "Engines": ( [ (.cm_engines // []) | group_by(.engineType)[] | "\(length) \(.[0].engineType | (try ($engine_types[.] // .) catch "Unknown"))" ] | join(", ") | if . == "" then null else . end ), "Registration number": .registrationNumber, "Registered owner": .registerOwner, "Operator": .operatorName, "Second pilot present": .secondPilotPresent, "Flight conducted under": (.regulationFlightConductedUnder | try ($far_parts[.] // .) catch null), "Flight operation type": (.flightOperationType | try ($flight_operation_types[.] // .) catch null), "Flight service type": (.flightServiceType | try ($flight_service_types[.] // .) catch null), "Flight terminal type": (.flightTerminalType | try ($flight_terminal_types[.] // .) catch null), "Medical flight type": (.airMedicalType | try ($air_medical_types[.] // .) catch null), "Scheduled flight": (.flightScheduledType | if . == "SCHD" then true elif . == "NSCH" then false else . end), "Commercial sightseeing flight": .revenueSightseeing, "Railroad name": .railroadName, "Equipment type": .equipmentType, "Train name": .trainName, "Train number": .trainNum, "Train type": .trainType, "Callsign": .callSign, "Total cars": .totalCars, "Total locomotive units": .totalLocomotiveUnits, "Trailing tons": .trailingTons, "Vessel name": .vesselName, "Vessel type": .vesselType, "IMO number": .imoNum, "Maritime Mobile Service Identity": .mmsi, "Port of registry": .portOfRegistry, "Safety Management Certificate number": .smcNum, "Classification society": .classificationSociety, "Flag state": .flagState, "Traffic unit name": .trafficUnitName, "Traffic unit type": .trafficUnitType, "Serial number": .SerialNumber, "Damage level": .DamageLevel, "Carrying hazardous materials": .carryingHazmat, "Has released hazardous materials": .releasedHazmat, "Units": ([ (.vehicles // [])[] | { "Type": .vehicleType, "Name": .vehicleName, "Make": .vehicleMake, "Model": .vehicleModel, "Year": .vehicleYear, "Engine type": .engineType, "Transmission type": .transmissionType, "Driving automation level": (.drivingAutomationLevel | try ($driving_automation_levels[.] // .) catch null), "Gross vehicle weight rating": .gvwr } | to_dl ] | to_ul), "Events": ([ (.cm_events // [])[] | ( ( # Try to use the known event codes to map to more specific names (try $event_codes[.cm_tier2Num] catch null) // (try $event_codes[.cm_tier1Num] catch null) # Fallback to the sometimes provided generic event group name // .cicttEventSOEGroup # Fallback to an error message // "Unknown event code \(.cm_eventCode)" ) + if .cm_isDefiningEvent then "" else " (non-defining event)" end ) ] | to_ul), "Findings": ([ (.cm_findings // [])[] | (.cm_findingText + if .cm_inPc then "" else " (not part of the probable cause)" end) ] | to_ul) } | to_dl) ] | join("")) | gsub("[\\x00-\\x1f]"; "") | gsub("\r"; "
")), "link": "https://data.ntsb.gov/carol-repgen/api/Aviation/ReportMain/GenerateNewestReport/\(.cm_mkey)/pdf", "guid": { "@isPermaLink": false, "#text": (.cm_mkey | tostring) }, "pubDate": ( # Prefer the most recent report publication date when available. # Both dates use different formats; the most recent report publication date has milliseconds # but the original publication date does not. # strptime does not support milliseconds, so we just cut the part after the seconds. (.cm_recentReportPublishDate // .cm_originalPublishedDate)[:19] | strptime("%Y-%m-%dT%H:%M:%S") | mktime | strftime("%a, %d %b %Y %T %z") ) }] } } }