Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_Declination/generate/generate.py
Views: 1799
#!/usr/bin/env python31'''2generate field tables from IGRF13. Note that this requires python33'''45import igrf6import numpy as np7import datetime8from pathlib import Path9from pymavlink.rotmat import Vector3, Matrix310import math1112import argparse13parser = argparse.ArgumentParser(description='generate mag tables')14parser.add_argument('--sampling-res', type=int, default=10, help='sampling resolution, degrees')15parser.add_argument('--check-error', action='store_true', help='check max error')16parser.add_argument('--filename', type=str, default='tables.cpp', help='tables file')1718args = parser.parse_args()1920if not Path("AP_Declination.h").is_file():21raise OSError("Please run this tool from the AP_Declination directory")222324def write_table(f,name, table):25'''write one table'''26f.write("const float AP_Declination::%s[%u][%u] = {\n" %27(name, NUM_LAT, NUM_LON))28for i in range(NUM_LAT):29f.write(" {")30for j in range(NUM_LON):31f.write("%.5ff" % table[i][j])32if j != NUM_LON-1:33f.write(",")34f.write("}")35if i != NUM_LAT-1:36f.write(",")37f.write("\n")38f.write("};\n\n")3940date = datetime.datetime.now()4142SAMPLING_RES = args.sampling_res43SAMPLING_MIN_LAT = -9044SAMPLING_MAX_LAT = 9045SAMPLING_MIN_LON = -18046SAMPLING_MAX_LON = 1804748lats = np.arange(SAMPLING_MIN_LAT, SAMPLING_MAX_LAT+SAMPLING_RES, SAMPLING_RES)49lons = np.arange(SAMPLING_MIN_LON, SAMPLING_MAX_LON+SAMPLING_RES, SAMPLING_RES)5051NUM_LAT = lats.size52NUM_LON = lons.size5354intensity_table = np.empty((NUM_LAT, NUM_LON))55inclination_table = np.empty((NUM_LAT, NUM_LON))56declination_table = np.empty((NUM_LAT, NUM_LON))5758max_error = 059max_error_pos = None60max_error_field = None6162def get_igrf(lat, lon):63'''return field as [declination_deg, inclination_deg, intensity_gauss]'''64mag = igrf.igrf(date, glat=lat, glon=lon, alt_km=0., isv=0, itype=1)65intensity = float(mag.total/1e5)66inclination = float(mag.incl)67declination = float(mag.decl)68return [declination, inclination, intensity]6970def interpolate_table(table, latitude_deg, longitude_deg):71'''interpolate inside a table for a given lat/lon in degrees'''72# round down to nearest sampling resolution73min_lat = int(math.floor(latitude_deg / SAMPLING_RES) * SAMPLING_RES)74min_lon = int(math.floor(longitude_deg / SAMPLING_RES) * SAMPLING_RES)7576# find index of nearest low sampling point77min_lat_index = int(math.floor(-(SAMPLING_MIN_LAT) + min_lat) / SAMPLING_RES)78min_lon_index = int(math.floor(-(SAMPLING_MIN_LON) + min_lon) / SAMPLING_RES)7980# calculate intensity81data_sw = table[min_lat_index][min_lon_index]82data_se = table[min_lat_index][min_lon_index + 1]83data_ne = table[min_lat_index + 1][min_lon_index + 1]84data_nw = table[min_lat_index + 1][min_lon_index]8586# perform bilinear interpolation on the four grid corners87data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw88data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw8990value = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min91return value929394'''95calculate magnetic field intensity and orientation, interpolating in tables9697returns array [declination_deg, inclination_deg, intensity] or None98'''99def interpolate_field(latitude_deg, longitude_deg):100# limit to table bounds101if latitude_deg < SAMPLING_MIN_LAT:102return None103if latitude_deg >= SAMPLING_MAX_LAT:104return None105if longitude_deg < SAMPLING_MIN_LON:106return None107if longitude_deg >= SAMPLING_MAX_LON:108return None109110intensity_gauss = interpolate_table(intensity_table, latitude_deg, longitude_deg)111declination_deg = interpolate_table(declination_table, latitude_deg, longitude_deg)112inclination_deg = interpolate_table(inclination_table, latitude_deg, longitude_deg)113114return [declination_deg, inclination_deg, intensity_gauss]115116def field_to_Vector3(mag):117'''return mGauss field from dec, inc and intensity'''118R = Matrix3()119mag_ef = Vector3(mag[2]*1000.0, 0.0, 0.0)120R.from_euler(0.0, -math.radians(mag[1]), math.radians(mag[0]))121return R * mag_ef122123def test_error(lat, lon):124'''check for error from lat,lon'''125global max_error, max_error_pos, max_error_field126mag1 = get_igrf(lat, lon)127mag2 = interpolate_field(lat, lon)128ef1 = field_to_Vector3(mag1)129ef2 = field_to_Vector3(mag2)130err = (ef1 - ef2).length()131if err > max_error or err > 100:132print(lat, lon, err, ef1, ef2)133max_error = err134max_error_pos = (lat, lon)135max_error_field = ef1 - ef2136137def test_max_error(lat, lon):138'''check for maximum error from lat,lon over SAMPLING_RES range'''139steps = 3140delta = SAMPLING_RES/steps141for i in range(steps):142for j in range(steps):143lat2 = lat + i * delta144lon2 = lon + j * delta145if lat2 >= SAMPLING_MAX_LAT or lon2 >= SAMPLING_MAX_LON:146continue147if lat2 <= SAMPLING_MIN_LAT or lon2 <= SAMPLING_MIN_LON:148continue149test_error(lat2, lon2)150151for i,lat in enumerate(lats):152for j,lon in enumerate(lons):153mag = get_igrf(lat, lon)154declination_table[i][j] = mag[0]155inclination_table[i][j] = mag[1]156intensity_table[i][j] = mag[2]157158with open(args.filename, 'w') as f:159f.write('''// this is an auto-generated file from the IGRF tables. Do not edit160// To re-generate run generate/generate.py161162#include "AP_Declination.h"163164''')165166f.write('''const float AP_Declination::SAMPLING_RES = %u;167const float AP_Declination::SAMPLING_MIN_LAT = %u;168const float AP_Declination::SAMPLING_MAX_LAT = %u;169const float AP_Declination::SAMPLING_MIN_LON = %u;170const float AP_Declination::SAMPLING_MAX_LON = %u;171172''' % (SAMPLING_RES,173SAMPLING_MIN_LAT,174SAMPLING_MAX_LAT,175SAMPLING_MIN_LON,176SAMPLING_MAX_LON))177178179write_table(f,'declination_table', declination_table)180write_table(f,'inclination_table', inclination_table)181write_table(f,'intensity_table', intensity_table)182183if args.check_error:184print("Checking for maximum error")185for lat in range(-60,60,1):186for lon in range(-180,180,1):187test_max_error(lat, lon)188print("Generated with max error %.2f %s at (%.2f,%.2f)" % (189max_error, max_error_field, max_error_pos[0], max_error_pos[1]))190191print("Table generated in %s" % args.filename)192193194