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.
- 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)
- Two direct download links for files of format .NDK:
- https://www.ldeo.columbia.edu/~gcmt/projects/CMT/catalog/jan76_dec20.ndk
- https://www.ldeo.columbia.edu/~gcmt/projects/CMT/catalog/NEW_QUICK/qcmt.ndk
- 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:
- 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')
- 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");
// }
}
- 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');
- 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;
// }