Task 443: .NDK File Format

File Format Specifications for the .NDK File Format

The .NDK file format is an ASCII text-based format used by the Global Centroid-Moment-Tensor (GCMT) project to store earthquake moment tensor solutions. It is a compact format where each earthquake event is described in exactly five lines of 80 characters each. The format combines fixed-width fields (primarily in the first line) with space-separated fields in the subsequent lines. Moment tensor components are scaled by 10^exponent dyne-cm. The format supports variations based on inversion types, such as omitted errors for fixed depths. The source duration (half-duration) is not stored directly but can be derived empirically as 1.05 * (scalar_moment / 10^24)^(1/3) seconds.

  1. List of all the properties of this file format intrinsic to its file system:
  • Hypocenter reference catalog (string, e.g., "PDEW")
  • Origin year (integer, e.g., 1976)
  • Origin month (integer, e.g., 1)
  • Origin day (integer, e.g., 1)
  • Origin hour (integer, e.g., 1)
  • Origin minute (integer, e.g., 29)
  • Origin second (float, e.g., 53.4)
  • Latitude (float, degrees, signed)
  • Longitude (float, degrees, signed)
  • Depth (float, km)
  • mb magnitude (float)
  • ms magnitude (float)
  • Geographic region (string)
  • CMT event name (string, e.g., "C197601010129A")
  • Number of body wave stations (integer)
  • Number of body wave channels (integer)
  • Number of body wave components (integer)
  • Number of surface wave stations (integer)
  • Number of surface wave channels (integer)
  • Number of surface wave components (integer)
  • Number of mantle wave stations (integer)
  • Number of mantle wave channels (integer)
  • Number of mantle wave components (integer)
  • CMT type (integer, typically 1 for standard)
  • Inversion type (string, e.g., "FIX")
  • Centroid time offset (float, seconds relative to origin)
  • Centroid time offset error (float, seconds)
  • Centroid latitude (float, degrees)
  • Centroid latitude error (float, degrees)
  • Centroid longitude (float, degrees)
  • Centroid longitude error (float, degrees)
  • Centroid depth (float, km)
  • Centroid depth error (float, km; omitted if depth fixed)
  • Depth type (string, e.g., "FIXED")
  • Body wave depth (float, km)
  • Exponent (integer, for scaling moment tensor components)
  • Mrr (float, moment tensor component)
  • Mrr error (float)
  • Mtt (float, moment tensor component)
  • Mtt error (float)
  • Mpp (float, moment tensor component)
  • Mpp error (float)
  • Mrt (float, moment tensor component)
  • Mrt error (float)
  • Mrp (float, moment tensor component)
  • Mrp error (float)
  • Mtp (float, moment tensor component)
  • Mtp error (float)
  • Version code (string, e.g., "V10")
  • Principal axis 1 eigenvalue (float)
  • Principal axis 1 plunge (integer, degrees)
  • Principal axis 1 azimuth (integer, degrees)
  • Principal axis 2 eigenvalue (float)
  • Principal axis 2 plunge (integer, degrees)
  • Principal axis 2 azimuth (integer, degrees)
  • Principal axis 3 eigenvalue (float)
  • Principal axis 3 plunge (integer, degrees)
  • Principal axis 3 azimuth (integer, degrees)
  • Scalar moment (float, in 10^exponent dyne-cm)
  • Nodal plane 1 strike (integer, degrees)
  • Nodal plane 1 dip (integer, degrees)
  • Nodal plane 1 rake (integer, degrees)
  • Nodal plane 2 strike (integer, degrees)
  • Nodal plane 2 dip (integer, degrees)
  • Nodal plane 2 rake (integer, degrees)
  1. Two direct download links for files of format .NDK:
  1. Ghost blog embedded HTML JavaScript that allows a user to drag n drop a file of format .NDK and it will dump to screen all these properties:
.NDK File Parser
Drop .NDK file here
  1. Python class that can open any file of format .NDK and decode read and write and print to console all the properties from the above list:
class NDKFile:
    def __init__(self):
        self.events = []

    def read(self, filename):
        with open(filename, 'r') as f:
            lines = f.readlines()
        i = 0
        while i < len(lines):
            if i + 4 >= len(lines):
                break
            line1 = lines[i].strip()
            line2 = lines[i+1].split()
            line3 = lines[i+2].split()
            line4 = lines[i+3].split()
            line5 = lines[i+4].split()
            event = self._parse_event(line1, line2, line3, line4, line5)
            self.events.append(event)
            i += 5

    def _parse_event(self, line1, line2, line3, line4, line5):
        event = {}
        # Parse line 1 (fixed width)
        event['hyp_cat'] = line1[0:4].strip()
        event['year'] = int(line1[5:9])
        event['month'] = int(line1[10:12])
        event['day'] = int(line1[13:15])
        event['hour'] = int(line1[16:18])
        event['minute'] = int(line1[19:21])
        event['second'] = float(line1[22:27])
        event['lat'] = float(line1[27:34])
        event['lon'] = float(line1[34:42])
        event['depth'] = float(line1[42:48])
        event['mb'] = float(line1[48:52])
        event['ms'] = float(line1[52:56])
        event['region'] = line1[56:].strip()

        # Parse line 2
        event['event_name'] = line2[0]
        event['body_stations'] = int(line2[2])
        event['body_channels'] = int(line2[3])
        event['body_components'] = int(line2[4])
        event['surface_stations'] = int(line2[6])
        event['surface_channels'] = int(line2[7])
        event['surface_components'] = int(line2[8])
        event['mantle_stations'] = int(line2[10])
        event['mantle_channels'] = int(line2[11])
        event['mantle_components'] = int(line2[12])
        event['cmt_type'] = int(line2[14])
        event['inv_type'] = line2[15] if len(line2) > 15 else ''

        # Parse line 3
        event['centroid_time_offset'] = 0.0
        event['centroid_time_offset_err'] = 0.0
        event['centroid_lat'] = 0.0
        event['centroid_lat_err'] = 0.0
        event['centroid_lon'] = 0.0
        event['centroid_lon_err'] = 0.0
        event['centroid_depth'] = 0.0
        event['centroid_depth_err'] = 0.0
        event['depth_type'] = ''
        event['body_depth'] = 0.0
        if line3[0] == 'CENTROID:':
            event['centroid_time_offset'] = float(line3[1])
            event['centroid_time_offset_err'] = float(line3[2])
            event['centroid_lat'] = float(line3[3])
            event['centroid_lat_err'] = float(line3[4])
            event['centroid_lon'] = float(line3[5])
            event['centroid_lon_err'] = float(line3[6])
            event['centroid_depth'] = float(line3[7])
            idx = 8
            if line3[idx] in ['FIXED', 'FREE']:
                event['depth_type'] = line3[idx]
                idx += 1
            else:
                event['centroid_depth_err'] = float(line3[idx])
                idx += 1
                event['depth_type'] = line3[idx]
                idx += 1
            if len(line3) > idx and line3[idx] == 'BD:':
                event['body_depth'] = float(line3[idx+1])

        # Parse line 4
        event['exponent'] = int(line4[0])
        event['mrr'] = float(line4[1])
        event['mrr_err'] = float(line4[2])
        event['mtt'] = float(line4[3])
        event['mtt_err'] = float(line4[4])
        event['mpp'] = float(line4[5])
        event['mpp_err'] = float(line4[6])
        event['mrt'] = float(line4[7])
        event['mrt_err'] = float(line4[8])
        event['mrp'] = float(line4[9])
        event['mrp_err'] = float(line4[10])
        event['mtp'] = float(line4[11])
        event['mtp_err'] = float(line4[12])

        # Parse line 5
        event['version'] = line5[0]
        event['principal_axis1_eigenvalue'] = float(line5[1])
        event['principal_axis1_plunge'] = int(line5[2])
        event['principal_axis1_azimuth'] = int(line5[3])
        event['principal_axis2_eigenvalue'] = float(line5[4])
        event['principal_axis2_plunge'] = int(line5[5])
        event['principal_axis2_azimuth'] = int(line5[6])
        event['principal_axis3_eigenvalue'] = float(line5[7])
        event['principal_axis3_plunge'] = int(line5[8])
        event['principal_axis3_azimuth'] = int(line5[9])
        event['scalar_moment'] = float(line5[10])
        event['nodal_plane1_strike'] = int(line5[11])
        event['nodal_plane1_dip'] = int(line5[12])
        event['nodal_plane1_rake'] = int(line5[13])
        event['nodal_plane2_strike'] = int(line5[14])
        event['nodal_plane2_dip'] = int(line5[15])
        event['nodal_plane2_rake'] = int(line5[16])

        return event

    def print_properties(self):
        for event in self.events:
            print("Event:", event['event_name'])
            for key, value in event.items():
                print(f"{key}: {value}")
            print("\n")

    def write(self, filename):
        with open(filename, 'w') as f:
            for event in self.events:
                # Line 1: fixed width
                line1 = f"{event['hyp_cat']:>4}{event['year']:>5}{event['month']:>3}{event['day']:>3}{event['hour']:>3}{event['minute']:>3}{event['second']:>6.1f}{event['lat']:>8.2f}{event['lon']:>9.2f}{event['depth']:>6.1f}{event['mb']:>4.1f}{event['ms']:>4.1f} {event['region']}\n"
                f.write(line1)
                # Line 2: space separated
                line2 = f"{event['event_name']} BW: {event['body_stations']} {event['body_channels']} {event['body_components']} SW: {event['surface_stations']} {event['surface_channels']} {event['surface_components']} MW: {event['mantle_stations']} {event['mantle_channels']} {event['mantle_components']} TMTC: {event['cmt_type']} {event['inv_type']}\n"
                f.write(line2)
                # Line 3: space separated, with conditional depth err
                line3 = "CENTROID: "
                line3 += f"{event['centroid_time_offset']:4.1f} {event['centroid_time_offset_err']:3.1f} {event['centroid_lat']:6.2f} {event['centroid_lat_err']:4.2f} {event['centroid_lon']:7.2f} {event['centroid_lon_err']:4.2f} {event['centroid_depth']:4.1f} "
                if event['centroid_depth_err'] > 0:
                    line3 += f"{event['centroid_depth_err']:3.1f} "
                line3 += f"{event['depth_type']} BD: {event['body_depth']:4.1f}\n"
                f.write(line3)
                # Line 4: space separated
                line4 = f"{event['exponent']} {event['mrr']:5.2f} {event['mrr_err']:4.2f} {event['mtt']:5.2f} {event['mtt_err']:4.2f} {event['mpp']:5.2f} {event['mpp_err']:4.2f} {event['mrt']:5.2f} {event['mrt_err']:4.2f} {event['mrp']:5.2f} {event['mrp_err']:4.2f} {event['mtp']:5.2f} {event['mtp_err']:4.2f}\n"
                f.write(line4)
                # Line 5: space separated
                line5 = f"{event['version']} {event['principal_axis1_eigenvalue']:5.2f} {event['principal_axis1_plunge']:2} {event['principal_axis1_azimuth']:3} {event['principal_axis2_eigenvalue']:5.2f} {event['principal_axis2_plunge']:2} {event['principal_axis2_azimuth']:3} {event['principal_axis3_eigenvalue']:5.2f} {event['principal_axis3_plunge']:2} {event['principal_axis3_azimuth']:3} {event['scalar_moment']:5.2f} {event['nodal_plane1_strike']:3} {event['nodal_plane1_dip']:2} {event['nodal_plane1_rake']:4} {event['nodal_plane2_strike']:3} {event['nodal_plane2_dip']:2} {event['nodal_plane2_rake']:4}\n"
                f.write(line5)

# Example usage:
# ndk = NDKFile()
# ndk.read('example.ndk')
# ndk.print_properties()
# ndk.write('output.ndk')
  1. Java class that can open any file of format .NDK and decode read and write and print to console all the properties from the above list:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NDKFile {
    private List<Map<String, Object>> events = new ArrayList<>();

    public void read(String filename) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader(filename));
        String line;
        List<String> lines = new ArrayList<>();
        while ((line = br.readLine()) != null) {
            lines.add(line);
        }
        br.close();
        for (int i = 0; i < lines.size(); i += 5) {
            if (i + 4 >= lines.size()) break;
            String line1 = lines.get(i).trim();
            String[] line2 = lines.get(i+1).trim().split("\\s+");
            String[] line3 = lines.get(i+2).trim().split("\\s+");
            String[] line4 = lines.get(i+3).trim().split("\\s+");
            String[] line5 = lines.get(i+4).trim().split("\\s+");
            Map<String, Object> event = parseEvent(line1, line2, line3, line4, line5);
            events.add(event);
        }
    }

    private Map<String, Object> parseEvent(String line1, String[] line2, String[] line3, String[] line4, String[] line5) {
        Map<String, Object> event = new HashMap<>();
        // Parse line 1 (fixed width)
        event.put("hyp_cat", line1.substring(0,4).trim());
        event.put("year", Integer.parseInt(line1.substring(5,9).trim()));
        event.put("month", Integer.parseInt(line1.substring(10,12).trim()));
        event.put("day", Integer.parseInt(line1.substring(13,15).trim()));
        event.put("hour", Integer.parseInt(line1.substring(16,18).trim()));
        event.put("minute", Integer.parseInt(line1.substring(19,21).trim()));
        event.put("second", Double.parseDouble(line1.substring(22,27).trim()));
        event.put("lat", Double.parseDouble(line1.substring(27,34).trim()));
        event.put("lon", Double.parseDouble(line1.substring(34,42).trim()));
        event.put("depth", Double.parseDouble(line1.substring(42,48).trim()));
        event.put("mb", Double.parseDouble(line1.substring(48,52).trim()));
        event.put("ms", Double.parseDouble(line1.substring(52,56).trim()));
        event.put("region", line1.substring(56).trim());

        // Parse line 2
        event.put("event_name", line2[0]);
        event.put("body_stations", Integer.parseInt(line2[2]));
        event.put("body_channels", Integer.parseInt(line2[3]));
        event.put("body_components", Integer.parseInt(line2[4]));
        event.put("surface_stations", Integer.parseInt(line2[6]));
        event.put("surface_channels", Integer.parseInt(line2[7]));
        event.put("surface_components", Integer.parseInt(line2[8]));
        event.put("mantle_stations", Integer.parseInt(line2[10]));
        event.put("mantle_channels", Integer.parseInt(line2[11]));
        event.put("mantle_components", Integer.parseInt(line2[12]));
        event.put("cmt_type", Integer.parseInt(line2[14]));
        event.put("inv_type", line2.length > 15 ? line2[15] : "");

        // Parse line 3
        event.put("centroid_time_offset", 0.0);
        event.put("centroid_time_offset_err", 0.0);
        event.put("centroid_lat", 0.0);
        event.put("centroid_lat_err", 0.0);
        event.put("centroid_lon", 0.0);
        event.put("centroid_lon_err", 0.0);
        event.put("centroid_depth", 0.0);
        event.put("centroid_depth_err", 0.0);
        event.put("depth_type", "");
        event.put("body_depth", 0.0);
        if (line3[0].equals("CENTROID:")) {
            event.put("centroid_time_offset", Double.parseDouble(line3[1]));
            event.put("centroid_time_offset_err", Double.parseDouble(line3[2]));
            event.put("centroid_lat", Double.parseDouble(line3[3]));
            event.put("centroid_lat_err", Double.parseDouble(line3[4]));
            event.put("centroid_lon", Double.parseDouble(line3[5]));
            event.put("centroid_lon_err", Double.parseDouble(line3[6]));
            event.put("centroid_depth", Double.parseDouble(line3[7]));
            int idx = 8;
            if (line3[idx].equals("FIXED") || line3[idx].equals("FREE")) {
                event.put("depth_type", line3[idx]);
                idx += 1;
            } else {
                event.put("centroid_depth_err", Double.parseDouble(line3[idx]));
                idx += 1;
                event.put("depth_type", line3[idx]);
                idx += 1;
            }
            if (idx < line3.length && line3[idx].equals("BD:")) {
                event.put("body_depth", Double.parseDouble(line3[idx+1]));
            }
        }

        // Parse line 4
        event.put("exponent", Integer.parseInt(line4[0]));
        event.put("mrr", Double.parseDouble(line4[1]));
        event.put("mrr_err", Double.parseDouble(line4[2]));
        event.put("mtt", Double.parseDouble(line4[3]));
        event.put("mtt_err", Double.parseDouble(line4[4]));
        event.put("mpp", Double.parseDouble(line4[5]));
        event.put("mpp_err", Double.parseDouble(line4[6]));
        event.put("mrt", Double.parseDouble(line4[7]));
        event.put("mrt_err", Double.parseDouble(line4[8]));
        event.put("mrp", Double.parseDouble(line4[9]));
        event.put("mrp_err", Double.parseDouble(line4[10]));
        event.put("mtp", Double.parseDouble(line4[11]));
        event.put("mtp_err", Double.parseDouble(line4[12]));

        // Parse line 5
        event.put("version", line5[0]);
        event.put("principal_axis1_eigenvalue", Double.parseDouble(line5[1]));
        event.put("principal_axis1_plunge", Integer.parseInt(line5[2]));
        event.put("principal_axis1_azimuth", Integer.parseInt(line5[3]));
        event.put("principal_axis2_eigenvalue", Double.parseDouble(line5[4]));
        event.put("principal_axis2_plunge", Integer.parseInt(line5[5]));
        event.put("principal_axis2_azimuth", Integer.parseInt(line5[6]));
        event.put("principal_axis3_eigenvalue", Double.parseDouble(line5[7]));
        event.put("principal_axis3_plunge", Integer.parseInt(line5[8]));
        event.put("principal_axis3_azimuth", Integer.parseInt(line5[9]));
        event.put("scalar_moment", Double.parseDouble(line5[10]));
        event.put("nodal_plane1_strike", Integer.parseInt(line5[11]));
        event.put("nodal_plane1_dip", Integer.parseInt(line5[12]));
        event.put("nodal_plane1_rake", Integer.parseInt(line5[13]));
        event.put("nodal_plane2_strike", Integer.parseInt(line5[14]));
        event.put("nodal_plane2_dip", Integer.parseInt(line5[15]));
        event.put("nodal_plane2_rake", Integer.parseInt(line5[16]));

        return event;
    }

    public void printProperties() {
        for (Map<String, Object> event : events) {
            System.out.println("Event: " + event.get("event_name"));
            for (Map.Entry<String, Object> entry : event.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
            System.out.println();
        }
    }

    public void write(String filename) throws Exception {
        PrintWriter pw = new PrintWriter(filename);
        for (Map<String, Object> event : events) {
            // Line 1: fixed width
            String line1 = String.format("%-4s %4d %2d %2d %2d %2d %5.1f %7.2f %8.2f %5.1f %3.1f %3.1f %s", 
                event.get("hyp_cat"), event.get("year"), event.get("month"), event.get("day"), event.get("hour"), event.get("minute"), event.get("second"), 
                event.get("lat"), event.get("lon"), event.get("depth"), event.get("mb"), event.get("ms"), event.get("region"));
            pw.println(line1);
            // Line 2
            String line2 = String.format("%s BW: %d %d %d SW: %d %d %d MW: %d %d %d TMTC: %d %s", 
                event.get("event_name"), event.get("body_stations"), event.get("body_channels"), event.get("body_components"), 
                event.get("surface_stations"), event.get("surface_channels"), event.get("surface_components"), 
                event.get("mantle_stations"), event.get("mantle_channels"), event.get("mantle_components"), 
                event.get("cmt_type"), event.get("inv_type"));
            pw.println(line2);
            // Line 3
            String line3 = "CENTROID: ";
            line3 += String.format("%4.1f %3.1f %6.2f %4.2f %7.2f %4.2f %4.1f", 
                event.get("centroid_time_offset"), event.get("centroid_time_offset_err"), event.get("centroid_lat"), event.get("centroid_lat_err"), 
                event.get("centroid_lon"), event.get("centroid_lon_err"), event.get("centroid_depth"));
            if ((double) event.get("centroid_depth_err") > 0) {
                line3 += String.format(" %3.1f", event.get("centroid_depth_err"));
            }
            line3 += String.format(" %s BD: %4.1f", event.get("depth_type"), event.get("body_depth"));
            pw.println(line3);
            // Line 4
            String line4 = String.format("%d %5.2f %4.2f %5.2f %4.2f %5.2f %4.2f %5.2f %4.2f %5.2f %4.2f %5.2f %4.2f", 
                event.get("exponent"), event.get("mrr"), event.get("mrr_err"), event.get("mtt"), event.get("mtt_err"), 
                event.get("mpp"), event.get("mpp_err"), event.get("mrt"), event.get("mrt_err"), event.get("mrp"), event.get("mrp_err"), 
                event.get("mtp"), event.get("mtp_err"));
            pw.println(line4);
            // Line 5
            String line5 = String.format("%s %5.2f %2d %3d %5.2f %2d %3d %5.2f %2d %3d %5.2f %3d %2d %4d %3d %2d %4d", 
                event.get("version"), event.get("principal_axis1_eigenvalue"), event.get("principal_axis1_plunge"), event.get("principal_axis1_azimuth"), 
                event.get("principal_axis2_eigenvalue"), event.get("principal_axis2_plunge"), event.get("principal_axis2_azimuth"), 
                event.get("principal_axis3_eigenvalue"), event.get("principal_axis3_plunge"), event.get("principal_axis3_azimuth"), 
                event.get("scalar_moment"), event.get("nodal_plane1_strike"), event.get("nodal_plane1_dip"), event.get("nodal_plane1_rake"), 
                event.get("nodal_plane2_strike"), event.get("nodal_plane2_dip"), event.get("nodal_plane2_rake"));
            pw.println(line5);
        }
        pw.close();
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     NDKFile ndk = new NDKFile();
    //     ndk.read("example.ndk");
    //     ndk.printProperties();
    //     ndk.write("output.ndk");
    // }
}
  1. Javascript class that can open any file of format .NDK and decode read and write and print to console all the properties from the above list:
class NDKFile {
    constructor() {
        this.events = [];
    }

    async read(filename) {
        // For browser, use fetch for local file or server; for Node.js, use fs
        // Assuming Node.js with fs
        const fs = require('fs');
        const text = fs.readFileSync(filename, 'utf8');
        const lines = text.split('\n').filter(line => line.trim());
        for (let i = 0; i < lines.length; i += 5) {
            if (i + 4 >= lines.length) break;
            const line1 = lines[i].trim();
            const line2 = lines[i+1].split(/\s+/).filter(Boolean);
            const line3 = lines[i+2].split(/\s+/).filter(Boolean);
            const line4 = lines[i+3].split(/\s+/).filter(Boolean);
            const line5 = lines[i+4].split(/\s+/).filter(Boolean);
            const event = this.parseEvent(line1, line2, line3, line4, line5);
            this.events.push(event);
        }
    }

    parseEvent(line1, line2, line3, line4, line5) {
        const event = {};
        // Parse line 1 (fixed width)
        event.hyp_cat = line1.substring(0,4).trim();
        event.year = parseInt(line1.substring(5,9));
        event.month = parseInt(line1.substring(10,12));
        event.day = parseInt(line1.substring(13,15));
        event.hour = parseInt(line1.substring(16,18));
        event.minute = parseInt(line1.substring(19,21));
        event.second = parseFloat(line1.substring(22,27));
        event.lat = parseFloat(line1.substring(27,34));
        event.lon = parseFloat(line1.substring(34,42));
        event.depth = parseFloat(line1.substring(42,48));
        event.mb = parseFloat(line1.substring(48,52));
        event.ms = parseFloat(line1.substring(52,56));
        event.region = line1.substring(56).trim();

        // Parse line 2
        event.event_name = line2[0];
        event.body_stations = parseInt(line2[2]);
        event.body_channels = parseInt(line2[3]);
        event.body_components = parseInt(line2[4]);
        event.surface_stations = parseInt(line2[6]);
        event.surface_channels = parseInt(line2[7]);
        event.surface_components = parseInt(line2[8]);
        event.mantle_stations = parseInt(line2[10]);
        event.mantle_channels = parseInt(line2[11]);
        event.mantle_components = parseInt(line2[12]);
        event.cmt_type = parseInt(line2[14]);
        event.inv_type = line2[15] || '';

        // Parse line 3
        event.centroid_time_offset = 0;
        event.centroid_time_offset_err = 0;
        event.centroid_lat = 0;
        event.centroid_lat_err = 0;
        event.centroid_lon = 0;
        event.centroid_lon_err = 0;
        event.centroid_depth = 0;
        event.centroid_depth_err = 0;
        event.depth_type = '';
        event.body_depth = 0;
        if (line3[0] === 'CENTROID:') {
            event.centroid_time_offset = parseFloat(line3[1]);
            event.centroid_time_offset_err = parseFloat(line3[2]);
            event.centroid_lat = parseFloat(line3[3]);
            event.centroid_lat_err = parseFloat(line3[4]);
            event.centroid_lon = parseFloat(line3[5]);
            event.centroid_lon_err = parseFloat(line3[6]);
            event.centroid_depth = parseFloat(line3[7]);
            let idx = 8;
            if (line3[idx] === 'FIXED' || line3[idx] === 'FREE') {
                event.depth_type = line3[idx];
                idx += 1;
            } else {
                event.centroid_depth_err = parseFloat(line3[idx]);
                idx += 1;
                event.depth_type = line3[idx];
                idx += 1;
            }
            if (idx < line3.length && line3[idx] === 'BD:') {
                event.body_depth = parseFloat(line3[idx+1]);
            }
        }

        // Parse line 4
        event.exponent = parseInt(line4[0]);
        event.mrr = parseFloat(line4[1]);
        event.mrr_err = parseFloat(line4[2]);
        event.mtt = parseFloat(line4[3]);
        event.mtt_err = parseFloat(line4[4]);
        event.mpp = parseFloat(line4[5]);
        event.mpp_err = parseFloat(line4[6]);
        event.mrt = parseFloat(line4[7]);
        event.mrt_err = parseFloat(line4[8]);
        event.mrp = parseFloat(line4[9]);
        event.mrp_err = parseFloat(line4[10]);
        event.mtp = parseFloat(line4[11]);
        event.mtp_err = parseFloat(line4[12]);

        // Parse line 5
        event.version = line5[0];
        event.principal_axis1_eigenvalue = parseFloat(line5[1]);
        event.principal_axis1_plunge = parseInt(line5[2]);
        event.principal_axis1_azimuth = parseInt(line5[3]);
        event.principal_axis2_eigenvalue = parseFloat(line5[4]);
        event.principal_axis2_plunge = parseInt(line5[5]);
        event.principal_axis2_azimuth = parseInt(line5[6]);
        event.principal_axis3_eigenvalue = parseFloat(line5[7]);
        event.principal_axis3_plunge = parseInt(line5[8]);
        event.principal_axis3_azimuth = parseInt(line5[9]);
        event.scalar_moment = parseFloat(line5[10]);
        event.nodal_plane1_strike = parseInt(line5[11]);
        event.nodal_plane1_dip = parseInt(line5[12]);
        event.nodal_plane1_rake = parseInt(line5[13]);
        event.nodal_plane2_strike = parseInt(line5[14]);
        event.nodal_plane2_dip = parseInt(line5[15]);
        event.nodal_plane2_rake = parseInt(line5[16]);

        return event;
    }

    printProperties() {
        this.events.forEach(event => {
            console.log(`Event: ${event.event_name}`);
            for (const key in event) {
                console.log(`${key}: ${event[key]}`);
            }
            console.log('');
        });
    }

    write(filename) {
        const fs = require('fs');
        let content = '';
        this.events.forEach(event => {
            // Line 1
            content += `${event.hyp_cat.padEnd(4)} ${event.year.toString().padStart(4)} ${event.month.toString().padStart(2)} ${event.day.toString().padStart(2)} ${event.hour.toString().padStart(2)} ${event.minute.toString().padStart(2)} ${event.second.toFixed(1).padStart(5)} ${event.lat.toFixed(2).padStart(7)} ${event.lon.toFixed(2).padStart(8)} ${event.depth.toFixed(1).padStart(5)} ${event.mb.toFixed(1).padStart(3)} ${event.ms.toFixed(1).padStart(3)} ${event.region}\n`;
            // Line 2
            content += `${event.event_name} BW: ${event.body_stations} ${event.body_channels} ${event.body_components} SW: ${event.surface_stations} ${event.surface_channels} ${event.surface_components} MW: ${event.mantle_stations} ${event.mantle_channels} ${event.mantle_components} TMTC: ${event.cmt_type} ${event.inv_type}\n`;
            // Line 3
            content += 'CENTROID: ';
            content += `${event.centroid_time_offset.toFixed(1).padStart(4)} ${event.centroid_time_offset_err.toFixed(1).padStart(3)} ${event.centroid_lat.toFixed(2).padStart(6)} ${event.centroid_lat_err.toFixed(2).padStart(4)} ${event.centroid_lon.toFixed(2).padStart(7)} ${event.centroid_lon_err.toFixed(2).padStart(4)} ${event.centroid_depth.toFixed(1).padStart(4)}`;
            if (event.centroid_depth_err > 0) {
                content += ` ${event.centroid_depth_err.toFixed(1).padStart(3)}`;
            }
            content += ` ${event.depth_type} BD: ${event.body_depth.toFixed(1).padStart(4)}\n`;
            // Line 4
            content += `${event.exponent} ${event.mrr.toFixed(2).padStart(5)} ${event.mrr_err.toFixed(2).padStart(4)} ${event.mtt.toFixed(2).padStart(5)} ${event.mtt_err.toFixed(2).padStart(4)} ${event.mpp.toFixed(2).padStart(5)} ${event.mpp_err.toFixed(2).padStart(4)} ${event.mrt.toFixed(2).padStart(5)} ${event.mrt_err.toFixed(2).padStart(4)} ${event.mrp.toFixed(2).padStart(5)} ${event.mrp_err.toFixed(2).padStart(4)} ${event.mtp.toFixed(2).padStart(5)} ${event.mtp_err.toFixed(2).padStart(4)}\n`;
            // Line 5
            content += `${event.version} ${event.principal_axis1_eigenvalue.toFixed(2).padStart(5)} ${event.principal_axis1_plunge.toString().padStart(2)} ${event.principal_axis1_azimuth.toString().padStart(3)} ${event.principal_axis2_eigenvalue.toFixed(2).padStart(5)} ${event.principal_axis2_plunge.toString().padStart(2)} ${event.principal_axis2_azimuth.toString().padStart(3)} ${event.principal_axis3_eigenvalue.toFixed(2).padStart(5)} ${event.principal_axis3_plunge.toString().padStart(2)} ${event.principal_axis3_azimuth.toString().padStart(3)} ${event.scalar_moment.toFixed(2).padStart(5)} ${event.nodal_plane1_strike.toString().padStart(3)} ${event.nodal_plane1_dip.toString().padStart(2)} ${event.nodal_plane1_rake.toString().padStart(4)} ${event.nodal_plane2_strike.toString().padStart(3)} ${event.nodal_plane2_dip.toString().padStart(2)} ${event.nodal_plane2_rake.toString().padStart(4)}\n`;
        });
        fs.writeFileSync(filename, content);
    }
}

// Example usage (Node.js):
// const ndk = new NDKFile();
// await ndk.read('example.ndk');
// ndk.printProperties();
// ndk.write('output.ndk');
  1. C class that can open any file of format .NDK and decode read and write and print to console all the properties from the above list:

Since C does not have built-in classes, this is implemented in C++ as a class.

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include <iomanip>

class NDKFile {
private:
    std::vector<std::map<std::string, std::string>> events; // Using string for simplicity

public:
    void read(const std::string& filename) {
        std::ifstream file(filename);
        std::string line;
        std::vector<std::string> lines;
        while (std::getline(file, line)) {
            lines.push_back(line);
        }
        for (size_t i = 0; i < lines.size(); i += 5) {
            if (i + 4 >= lines.size()) break;
            std::string line1 = lines[i];
            std::istringstream iss2(lines[i+1]);
            std::vector<std::string> line2;
            std::string token;
            while (iss2 >> token) line2.push_back(token);
            std::istringstream iss3(lines[i+2]);
            std::vector<std::string> line3;
            while (iss3 >> token) line3.push_back(token);
            std::istringstream iss4(lines[i+3]);
            std::vector<std::string> line4;
            while (iss4 >> token) line4.push_back(token);
            std::istringstream iss5(lines[i+4]);
            std::vector<std::string> line5;
            while (iss5 >> token) line5.push_back(token);
            auto event = parseEvent(line1, line2, line3, line4, line5);
            events.push_back(event);
        }
    }

    std::map<std::string, std::string> parseEvent(const std::string& line1, const std::vector<std::string>& line2, const std::vector<std::string>& line3, const std::vector<std::string>& line4, const std::vector<std::string>& line5) {
        std::map<std::string, std::string> event;
        // Parse line 1
        event["hyp_cat"] = line1.substr(0,4);
        event["year"] = line1.substr(5,4);
        event["month"] = line1.substr(10,2);
        event["day"] = line1.substr(13,2);
        event["hour"] = line1.substr(16,2);
        event["minute"] = line1.substr(19,2);
        event["second"] = line1.substr(22,5);
        event["lat"] = line1.substr(27,7);
        event["lon"] = line1.substr(34,8);
        event["depth"] = line1.substr(42,5);
        event["mb"] = line1.substr(48,3);
        event["ms"] = line1.substr(52,3);
        event["region"] = line1.substr(56);

        // Parse line 2
        event["event_name"] = line2[0];
        event["body_stations"] = line2[2];
        event["body_channels"] = line2[3];
        event["body_components"] = line2[4];
        event["surface_stations"] = line2[6];
        event["surface_channels"] = line2[7];
        event["surface_components"] = line2[8];
        event["mantle_stations"] = line2[10];
        event["mantle_channels"] = line2[11];
        event["mantle_components"] = line2[12];
        event["cmt_type"] = line2[14];
        event["inv_type"] = (line2.size() > 15) ? line2[15] : "";

        // Parse line 3
        if (line3[0] == "CENTROID:") {
            event["centroid_time_offset"] = line3[1];
            event["centroid_time_offset_err"] = line3[2];
            event["centroid_lat"] = line3[3];
            event["centroid_lat_err"] = line3[4];
            event["centroid_lon"] = line3[5];
            event["centroid_lon_err"] = line3[6];
            event["centroid_depth"] = line3[7];
            size_t idx = 8;
            if (line3[idx] == "FIXED" || line3[idx] == "FREE") {
                event["depth_type"] = line3[idx];
                idx += 1;
            } else {
                event["centroid_depth_err"] = line3[idx];
                idx += 1;
                event["depth_type"] = line3[idx];
                idx += 1;
            }
            if (idx < line3.size() && line3[idx] == "BD:") {
                event["body_depth"] = line3[idx+1];
            }
        } else {
            event["centroid_depth_err"] = "0";
            event["body_depth"] = "0";
        }

        // Parse line 4
        event["exponent"] = line4[0];
        event["mrr"] = line4[1];
        event["mrr_err"] = line4[2];
        event["mtt"] = line4[3];
        event["mtt_err"] = line4[4];
        event["mpp"] = line4[5];
        event["mpp_err"] = line4[6];
        event["mrt"] = line4[7];
        event["mrt_err"] = line4[8];
        event["mrp"] = line4[9];
        event["mrp_err"] = line4[10];
        event["mtp"] = line4[11];
        event["mtp_err"] = line4[12];

        // Parse line 5
        event["version"] = line5[0];
        event["principal_axis1_eigenvalue"] = line5[1];
        event["principal_axis1_plunge"] = line5[2];
        event["principal_axis1_azimuth"] = line5[3];
        event["principal_axis2_eigenvalue"] = line5[4];
        event["principal_axis2_plunge"] = line5[5];
        event["principal_axis2_azimuth"] = line5[6];
        event["principal_axis3_eigenvalue"] = line5[7];
        event["principal_axis3_plunge"] = line5[8];
        event["principal_axis3_azimuth"] = line5[9];
        event["scalar_moment"] = line5[10];
        event["nodal_plane1_strike"] = line5[11];
        event["nodal_plane1_dip"] = line5[12];
        event["nodal_plane1_rake"] = line5[13];
        event["nodal_plane2_strike"] = line5[14];
        event["nodal_plane2_dip"] = line5[15];
        event["nodal_plane2_rake"] = line5[16];

        return event;
    }

    void printProperties() {
        for (const auto& event : events) {
            std::cout << "Event: " << event.at("event_name") << std::endl;
            for (const auto& pair : event) {
                std::cout << pair.first << ": " << pair.second << std::endl;
            }
            std::cout << std::endl;
        }
    }

    void write(const std::string& filename) {
        std::ofstream file(filename);
        for (const auto& event : events) {
            // Line 1 (approximate formatting)
            file << std::left << std::setw(4) << event.at("hyp_cat") 
                 << std::setw(5) << event.at("year") 
                 << std::setw(3) << event.at("month") 
                 << std::setw(3) << event.at("day") 
                 << std::setw(3) << event.at("hour") 
                 << std::setw(3) << event.at("minute") 
                 << std::fixed << std::setprecision(1) << std::setw(6) << std::stof(event.at("second")) 
                 << std::setprecision(2) << std::setw(8) << std::stof(event.at("lat")) 
                 << std::setw(9) << std::stof(event.at("lon")) 
                 << std::setprecision(1) << std::setw(6) << std::stof(event.at("depth")) 
                 << std::setw(4) << std::stof(event.at("mb")) 
                 << std::setw(4) << std::stof(event.at("ms")) 
                 << " " << event.at("region") << std::endl;
            // Line 2
            file << event.at("event_name") << " BW: " << event.at("body_stations") << " " << event.at("body_channels") << " " << event.at("body_components") 
                 << " SW: " << event.at("surface_stations") << " " << event.at("surface_channels") << " " << event.at("surface_components") 
                 << " MW: " << event.at("mantle_stations") << " " << event.at("mantle_channels") << " " << event.at("mantle_components") 
                 << " TMTC: " << event.at("cmt_type") << " " << event.at("inv_type") << std::endl;
            // Line 3
            file << "CENTROID: " << std::setw(4) << std::setprecision(1) << std::stof(event.at("centroid_time_offset")) 
                 << " " << std::setw(3) << std::stof(event.at("centroid_time_offset_err")) 
                 << " " << std::setw(6) << std::setprecision(2) << std::stof(event.at("centroid_lat")) 
                 << " " << std::setw(4) << std::stof(event.at("centroid_lat_err")) 
                 << " " << std::setw(7) << std::stof(event.at("centroid_lon")) 
                 << " " << std::setw(4) << std::stof(event.at("centroid_lon_err")) 
                 << " " << std::setw(4) << std::setprecision(1) << std::stof(event.at("centroid_depth"));
            if (std::stof(event.at("centroid_depth_err")) > 0) {
                file << " " << std::setw(3) << std::stof(event.at("centroid_depth_err"));
            }
            file << " " << event.at("depth_type") << " BD: " << std::setw(4) << std::stof(event.at("body_depth")) << std::endl;
            // Line 4
            file << event.at("exponent") << " " << std::setw(5) << std::setprecision(2) << std::stof(event.at("mrr")) 
                 << " " << std::setw(4) << std::stof(event.at("mrr_err")) 
                 << " " << std::setw(5) << std::stof(event.at("mtt")) 
                 << " " << std::setw(4) << std::stof(event.at("mtt_err")) 
                 << " " << std::setw(5) << std::stof(event.at("mpp")) 
                 << " " << std::setw(4) << std::stof(event.at("mpp_err")) 
                 << " " << std::setw(5) << std::stof(event.at("mrt")) 
                 << " " << std::setw(4) << std::stof(event.at("mrt_err")) 
                 << " " << std::setw(5) << std::stof(event.at("mrp")) 
                 << " " << std::setw(4) << std::stof(event.at("mrp_err")) 
                 << " " << std::setw(5) << std::stof(event.at("mtp")) 
                 << " " << std::setw(4) << std::stof(event.at("mtp_err")) << std::endl;
            // Line 5
            file << event.at("version") << " " << std::setw(5) << std::stof(event.at("principal_axis1_eigenvalue")) 
                 << " " << std::setw(2) << event.at("principal_axis1_plunge") 
                 << " " << std::setw(3) << event.at("principal_axis1_azimuth") 
                 << " " << std::setw(5) << std::stof(event.at("principal_axis2_eigenvalue")) 
                 << " " << std::setw(2) << event.at("principal_axis2_plunge") 
                 << " " << std::setw(3) << event.at("principal_axis2_azimuth") 
                 << " " << std::setw(5) << std::stof(event.at("principal_axis3_eigenvalue")) 
                 << " " << std::setw(2) << event.at("principal_axis3_plunge") 
                 << " " << std::setw(3) << event.at("principal_axis3_azimuth") 
                 << " " << std::setw(5) << std::stof(event.at("scalar_moment")) 
                 << " " << std::setw(3) << event.at("nodal_plane1_strike") 
                 << " " << std::setw(2) << event.at("nodal_plane1_dip") 
                 << " " << std::setw(4) << event.at("nodal_plane1_rake") 
                 << " " << std::setw(3) << event.at("nodal_plane2_strike") 
                 << " " << std::setw(2) << event.at("nodal_plane2_dip") 
                 << " " << std::setw(4) << event.at("nodal_plane2_rake") << std::endl;
        }
    }
};

// Example usage:
// int main() {
//     NDKFile ndk;
//     ndk.read("example.ndk");
//     ndk.printProperties();
//     ndk.write("output.ndk");
//     return 0;
// }