Path: blob/master/libraries/AP_Declination/generate/generate.py
9640 views
#!/usr/bin/env python312# flake8: noqa3'''4generate field tables from IGRF13. Note that this requires python35'''67import igrf8import numpy as np9import datetime10from pathlib import Path11from pymavlink.rotmat import Vector3, Matrix312import math1314import argparse15parser = argparse.ArgumentParser(description='generate mag tables')16parser.add_argument('--sampling-res', type=int, default=10, help='sampling resolution, degrees')17parser.add_argument('--check-error', action='store_true', help='check max error')18parser.add_argument('--filename', type=str, default='tables.cpp', help='tables file')1920args = parser.parse_args()2122if not Path("AP_Declination.h").is_file():23raise OSError("Please run this tool from the AP_Declination directory")242526def write_table(f,name, table):27'''write one table'''28f.write("__EXTFLASHFUNC__ const float AP_Declination::%s[%u][%u] = {\n" %29(name, NUM_LAT, NUM_LON))30for i in range(NUM_LAT):31f.write(" {")32for j in range(NUM_LON):33f.write("%.5ff" % table[i][j])34if j != NUM_LON-1:35f.write(",")36f.write("}")37if i != NUM_LAT-1:38f.write(",")39f.write("\n")40f.write("};\n\n")4142date = datetime.datetime.now()4344SAMPLING_RES = args.sampling_res45SAMPLING_MIN_LAT = -9046SAMPLING_MAX_LAT = 9047SAMPLING_MIN_LON = -18048SAMPLING_MAX_LON = 1804950lats = np.arange(SAMPLING_MIN_LAT, SAMPLING_MAX_LAT+SAMPLING_RES, SAMPLING_RES)51lons = np.arange(SAMPLING_MIN_LON, SAMPLING_MAX_LON+SAMPLING_RES, SAMPLING_RES)5253NUM_LAT = lats.size54NUM_LON = lons.size5556intensity_table = np.empty((NUM_LAT, NUM_LON))57inclination_table = np.empty((NUM_LAT, NUM_LON))58declination_table = np.empty((NUM_LAT, NUM_LON))5960max_error = 061max_error_pos = None62max_error_field = None6364def get_igrf(lat, lon):65'''return field as [declination_deg, inclination_deg, intensity_gauss]'''66mag = igrf.igrf(date, glat=lat, glon=lon, alt_km=0., isv=0, itype=1)67intensity = float(mag.total/1e5)68inclination = float(mag.incl)69declination = float(mag.decl)70return [declination, inclination, intensity]7172def interpolate_table(table, latitude_deg, longitude_deg):73'''interpolate inside a table for a given lat/lon in degrees'''74# round down to nearest sampling resolution75min_lat = int(math.floor(latitude_deg / SAMPLING_RES) * SAMPLING_RES)76min_lon = int(math.floor(longitude_deg / SAMPLING_RES) * SAMPLING_RES)7778# find index of nearest low sampling point79min_lat_index = int(math.floor(-(SAMPLING_MIN_LAT) + min_lat) / SAMPLING_RES)80min_lon_index = int(math.floor(-(SAMPLING_MIN_LON) + min_lon) / SAMPLING_RES)8182# calculate intensity83data_sw = table[min_lat_index][min_lon_index]84data_se = table[min_lat_index][min_lon_index + 1]85data_ne = table[min_lat_index + 1][min_lon_index + 1]86data_nw = table[min_lat_index + 1][min_lon_index]8788# perform bilinear interpolation on the four grid corners89data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw90data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw9192value = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min93return value949596'''97calculate magnetic field intensity and orientation, interpolating in tables9899returns array [declination_deg, inclination_deg, intensity] or None100'''101def interpolate_field(latitude_deg, longitude_deg):102# limit to table bounds103if latitude_deg < SAMPLING_MIN_LAT:104return None105if latitude_deg >= SAMPLING_MAX_LAT:106return None107if longitude_deg < SAMPLING_MIN_LON:108return None109if longitude_deg >= SAMPLING_MAX_LON:110return None111112intensity_gauss = interpolate_table(intensity_table, latitude_deg, longitude_deg)113declination_deg = interpolate_table(declination_table, latitude_deg, longitude_deg)114inclination_deg = interpolate_table(inclination_table, latitude_deg, longitude_deg)115116return [declination_deg, inclination_deg, intensity_gauss]117118def field_to_Vector3(mag):119'''return mGauss field from dec, inc and intensity'''120R = Matrix3()121mag_ef = Vector3(mag[2]*1000.0, 0.0, 0.0)122R.from_euler(0.0, -math.radians(mag[1]), math.radians(mag[0]))123return R * mag_ef124125def test_error(lat, lon):126'''check for error from lat,lon'''127global max_error, max_error_pos, max_error_field128mag1 = get_igrf(lat, lon)129mag2 = interpolate_field(lat, lon)130ef1 = field_to_Vector3(mag1)131ef2 = field_to_Vector3(mag2)132err = (ef1 - ef2).length()133if err > max_error or err > 100:134print(lat, lon, err, ef1, ef2)135max_error = err136max_error_pos = (lat, lon)137max_error_field = ef1 - ef2138139def test_max_error(lat, lon):140'''check for maximum error from lat,lon over SAMPLING_RES range'''141steps = 3142delta = SAMPLING_RES/steps143for i in range(steps):144for j in range(steps):145lat2 = lat + i * delta146lon2 = lon + j * delta147if lat2 >= SAMPLING_MAX_LAT or lon2 >= SAMPLING_MAX_LON:148continue149if lat2 <= SAMPLING_MIN_LAT or lon2 <= SAMPLING_MIN_LON:150continue151test_error(lat2, lon2)152153for i,lat in enumerate(lats):154for j,lon in enumerate(lons):155mag = get_igrf(lat, lon)156declination_table[i][j] = mag[0]157inclination_table[i][j] = mag[1]158intensity_table[i][j] = mag[2]159160with open(args.filename, 'w') as f:161f.write('''// this is an auto-generated file from the IGRF tables. Do not edit162// To re-generate run generate/generate.py163164#include "AP_Declination.h"165166''')167168f.write('''const float AP_Declination::SAMPLING_RES = %u;169const float AP_Declination::SAMPLING_MIN_LAT = %u;170const float AP_Declination::SAMPLING_MAX_LAT = %u;171const float AP_Declination::SAMPLING_MIN_LON = %u;172const float AP_Declination::SAMPLING_MAX_LON = %u;173174''' % (SAMPLING_RES,175SAMPLING_MIN_LAT,176SAMPLING_MAX_LAT,177SAMPLING_MIN_LON,178SAMPLING_MAX_LON))179180181write_table(f,'declination_table', declination_table)182write_table(f,'inclination_table', inclination_table)183write_table(f,'intensity_table', intensity_table)184185if args.check_error:186print("Checking for maximum error")187for lat in range(-60,60,1):188for lon in range(-180,180,1):189test_max_error(lat, lon)190print("Generated with max error %.2f %s at (%.2f,%.2f)" % (191max_error, max_error_field, max_error_pos[0], max_error_pos[1]))192193print("Table generated in %s" % args.filename)194195196