# Path: mlat/geodesy.py
# WGS84_A = 6378137.0
# WGS84_F = 1.0/298.257223563
# WGS84_B = WGS84_A * (1 - WGS84_F)
# WGS84_ECC_SQ = 1 - WGS84_B * WGS84_B / (WGS84_A * WGS84_A)
# WGS84_ECC = math.sqrt(WGS84_ECC_SQ)
# SPHERICAL_R = 6371e3
#     N = WGS84_A / math.sqrt(1 - WGS84_ECC_SQ * math.sin(lat)**2)
# def llh2ecef(llh):
# def ecef2llh(ecef):
# def greatcircle(p0, p1):
# def ecef_distance(p0, p1):
# 
# Path: mlat/profile.py
# def trackcpu(f, **kwargs):
# def dump_cpu_profiles(tofile=None):
# def trackcpu(f, name=None, **kwargs):
#     def cpu_measurement_wrapper(*args, **kwargs):
# def dump_cpu_profiles(tofile=sys.stderr):
# 
# Path: mlat/server/clocknorm.py
# class _Predictor(object):
#     def __init__(self, predict, variance):
# def _identity_predict(x):
# def _make_predictors(clocktracker, station0, station1):
# def _label_heights(g, node, heights):
# def _tallest_branch(g, node, heights, ignore=None):
# def _convert_timestamps(g, timestamp_map, predictor_map, node, results, conversion_chain, variance):
# def normalize(clocktracker, timestamp_map):
# 
# Path: mlat/server/solver.py
# def _residuals(x_guess, pseudorange_data, altitude, altitude_error):
# def solve(measurements, altitude, altitude_error, initial_guess):
# 
# Path: mlat/server/config.py
# MIN_NUC = 6
# MAX_RANGE = 500e3
# MAX_INTERMESSAGE_RANGE = 10e3
# MAX_ALT = 50000 * constants.FTOM
# MLAT_DELAY = 2.5
# SOLVER_MAXFEV = 50


import json
import asyncio
import logging
import operator
import numpy
import modes.message
from contextlib import closing
from mlat import geodesy, constants, profile
from mlat.server import clocknorm, solver, config
        components = clocknorm.normalize(clocktracker=self.clock_tracker,
                                         timestamp_map=timestamp_map)

        # cluster timestamps into clusters that are probably copies of the
        # same transmission.
        clusters = []
        min_component_size = 4 - altitude_dof
        for component in components:
            if len(component) >= min_component_size:  # don't bother with orphan components at all
                clusters.extend(_cluster_timestamps(component, min_component_size))

        if not clusters:
            return

        # start from the most recent, largest, cluster
        result = None
        clusters.sort(key=lambda x: (x[0], x[1]))
        while clusters and not result:
            distinct, cluster_utc, cluster = clusters.pop()

            # accept fewer receivers after 10s
            # accept the same number of receivers after MLAT_DELAY - 0.5s
            # accept more receivers immediately

            elapsed = cluster_utc - last_result_time
            dof = distinct + altitude_dof - 4

            if elapsed < 10.0 and dof < last_result_dof:
                break


# Path: mlat/geodesy.py
# WGS84_A = 6378137.0
# WGS84_F = 1.0/298.257223563
# WGS84_B = WGS84_A * (1 - WGS84_F)
# WGS84_ECC_SQ = 1 - WGS84_B * WGS84_B / (WGS84_A * WGS84_A)
# WGS84_ECC = math.sqrt(WGS84_ECC_SQ)
# SPHERICAL_R = 6371e3
#     N = WGS84_A / math.sqrt(1 - WGS84_ECC_SQ * math.sin(lat)**2)
# def llh2ecef(llh):
# def ecef2llh(ecef):
# def greatcircle(p0, p1):
# def ecef_distance(p0, p1):
# 
# Path: mlat/profile.py
# def trackcpu(f, **kwargs):
# def dump_cpu_profiles(tofile=None):
# def trackcpu(f, name=None, **kwargs):
#     def cpu_measurement_wrapper(*args, **kwargs):
# def dump_cpu_profiles(tofile=sys.stderr):
# 
# Path: mlat/server/clocknorm.py
# class _Predictor(object):
#     def __init__(self, predict, variance):
# def _identity_predict(x):
# def _make_predictors(clocktracker, station0, station1):
# def _label_heights(g, node, heights):
# def _tallest_branch(g, node, heights, ignore=None):
# def _convert_timestamps(g, timestamp_map, predictor_map, node, results, conversion_chain, variance):
# def normalize(clocktracker, timestamp_map):
# 
# Path: mlat/server/solver.py
# def _residuals(x_guess, pseudorange_data, altitude, altitude_error):
# def solve(measurements, altitude, altitude_error, initial_guess):
# 
# Path: mlat/server/config.py
# MIN_NUC = 6
# MAX_RANGE = 500e3
# MAX_INTERMESSAGE_RANGE = 10e3
# MAX_ALT = 50000 * constants.FTOM
# MLAT_DELAY = 2.5
# SOLVER_MAXFEV = 50


# Path: mlat/server/mlattrack.py
import json
import asyncio
import logging
import operator
import numpy
import modes.message
from contextlib import closing
from mlat import geodesy, constants, profile
from mlat.server import clocknorm, solver, config

        components = clocknorm.normalize(clocktracker=self.clock_tracker,
                                         timestamp_map=timestamp_map)

        # cluster timestamps into clusters that are probably copies of the
        # same transmission.
        clusters = []
        min_component_size = 4 - altitude_dof
        for component in components:
            if len(component) >= min_component_size:  # don't bother with orphan components at all
                clusters.extend(_cluster_timestamps(component, min_component_size))

        if not clusters:
            return

        # start from the most recent, largest, cluster
        result = None
        clusters.sort(key=lambda x: (x[0], x[1]))
        while clusters and not result:
            distinct, cluster_utc, cluster = clusters.pop()

            # accept fewer receivers after 10s
            # accept the same number of receivers after MLAT_DELAY - 0.5s
            # accept more receivers immediately

            elapsed = cluster_utc - last_result_time
            dof = distinct + altitude_dof - 4

            if elapsed < 10.0 and dof < last_result_dof:
                break


            if elapsed < (config.MLAT_DELAY - 0.5) and dof == last_result_dof: