#!/usr/bin/env python
usage="""\
Normally this is imported by ProcessResults.py, but you can also
run it stand-alone.  Stand-alone usage:
 ./ProcessResultsRange.py /mnt/data_drive/SpirentTest/DataDir/RX22-3518/xyz.T04 out_dir
This will produce several plots under out_dir/, e.g.:
  out_dir/All_std_resid.png
  out_dir/All_rms_resid.png
  out_dir/Freeways_std_resid.png
  out_dir/Freeways_rms_resid.png
For each satellite type, analyze StingerNavPC's residuals and show:
 - RMS of pseudorange residual
 - std. dev. of pseudorange residuals broken into 5-minute blocks
   (because StingerNavPC will have residual orbit/atmosphere biases)
"""

from mutils import *
import pandas as pd
from collections import defaultdict
import shutil

# Convert Stinger sat_type to a string
class StingerSatType(object):
    def __init__(self):
        self.sat_type = {}
        for sat_type,desc in get_sat_type.sat_type_dict.values():
            self.sat_type[sat_type] = desc
    def get_desc(self, sat_type):
        return self.sat_type[sat_type]

def analyze_range_resids( config_span, resid_path, out_path ):
    """Create raw data and plots of StingerNavPC's residual analysis.
    config_span = 'span' element from ProcessResults.py : parse_sampleConfig()
    resid_path = directory name where residuals.txt.gz file is
    out_path = directory name where output goes
    """
    d=doload(resid_path+'/residuals.txt.gz')
    k=snpc_res_fields()
    d=VdCls(d,k,[])

    # Overwrite output directory
    if os.path.isdir( out_path ):
        shutil.rmtree( out_path )
    os.makedirs( out_path )

    # combine timespans:
    #   spans[timespan_desc] = [(start1,end1), (start2,end2), ...]
    spans = defaultdict(list)
    for desc,start_stops in config_span:
        for start,stop in start_stops:
            spans[desc].append( (start,stop) )

    for span_desc in spans.keys():
        i_d = zeros(len(d),dtype=bool)
        for start,stop in spans[span_desc]:
            i_d[ find( (d.TIME>=start)
                       &(d.TIME<=stop)
                       &(d.pos_err_est>0.)
                       &(d.pr_sig>0.)) ] = True
        d0 = d[i_d]
        raw_rms = {}
        smooth_rms = {}
        len_std = defaultdict(list)
        raw_std = defaultdict(list)
        smooth_std = defaultdict(list)
        for sat_type in pd.unique(d.SAT_TYPE):
            d1 = d0[(d0.SAT_TYPE==sat_type)]
            if len(d1) > 0:
                raw_rms[sat_type] = float(rms( d1.pr_res ))
                smooth_rms[sat_type] = float(rms( d1.smooth_pr_res ))
            for sv in pd.unique(d1.SV):
                d2 = d1[d1.SV==sv]
                t_step = 60*5.
                t0 = d2.TIME[0]
                for t1 in r_[t0+t_step : d2.TIME[-1]+t_step : t_step]:
                    i = find( (d2.TIME>=t0) & (d2.TIME<t1) )
                    if len(i) < 2:
                        continue
                    len_std[sat_type].append( len(i) )
                    raw_std[sat_type].append( std(d2.pr_res[i]) )
                    smooth_std[sat_type].append( std(d2.smooth_pr_res[i]) )
        if len(len_std) == 0:
            continue

        sum_std = pd.DataFrame(None,
                               columns=['system',
                                        'raw',
                                        'smooth'])
        stinger_sats = StingerSatType()
        for sat_type in sorted(len_std.keys()):
            curr_len = r_[len_std[sat_type]]
            curr_raw = sum(curr_len*r_[raw_std[sat_type]])/sum(curr_len)
            curr_smooth = sum(curr_len*r_[smooth_std[sat_type]])/sum(curr_len)
            data = [
                stinger_sats.get_desc(sat_type),
                curr_raw,
                curr_smooth]
            sum_std.loc[len(sum_std)] = data
        ax = sum_std.plot.bar( x = 'system' )
        if sum_std['raw'].max() >= 50.:
            ax.set_yscale('log')
        ax.legend(loc='center left',bbox_to_anchor=(1.0, 0.5))
        ax.set_title('Segmented PR residuals : %s'%span_desc)
        ax.xaxis.set_label_text('')
        ax.grid()
        ax.set_axisbelow(True)
        ax.set_ylabel('Pseudorange std.dev [m]')
        tight_layout()
        savefig('{}/{}_std_resid.png'.format(out_path,span_desc))
        close()

        sum_rms = pd.DataFrame(None,
                               columns=['system',
                                        'raw',
                                        'smooth'])
        for sat_type in sorted(raw_rms.keys()):
            data = [
                stinger_sats.get_desc(sat_type),
                raw_rms[sat_type],
                smooth_rms[sat_type]]
            sum_rms.loc[len(sum_rms)] = data
        ax = sum_rms.plot.bar( x = 'system' )
        if sum_rms['raw'].max() >= 50.:
            ax.set_yscale('log')
        ax.legend(loc='center left',bbox_to_anchor=(1.0, 0.5))
        ax.set_title('RMS PR residuals : %s'%span_desc)
        ax.xaxis.set_label_text('')
        ax.grid()
        ax.set_axisbelow(True)
        ax.set_ylabel('Pseudorange RMS [m]')
        tight_layout()
        savefig('{}/{}_rms_resid.png'.format(out_path,span_desc))
        close()

def main():
    import argparse
    from glob import glob
    from ProcessResults import parse_sampleConfig, fill_in_auto_config_spans
    from ProcessResultsSlips import run_SNPC_slip_check

    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
                                     description=usage)
    parser.add_argument('t04_filename',
                        help='T04 file (or file pattern)')
    parser.add_argument('out_dir',
                        help='Output directory for plots.  Will get overwritten')
    args = parser.parse_args()

    dir_name = os.path.dirname(args.t04_filename) + '/'
    for tmp in glob(dir_name + '*.xml'):
        if tmp.endswith('ALM.xml') or tmp.endswith('config.xml'):
            continue
        config_filename = tmp
        break
    config = parse_sampleConfig( config_filename )
    if config.truth_format not in ["POSPAC_ASCII","STATIC"]:
        raise RuntimeError("no truth")
    if config.truth_format == "POSPAC_ASCII":
        fill_in_auto_config_spans( config, doload(config.truth_file) )

    run_SNPC_slip_check( config.truth, config.truth_file, args.t04_filename, args.out_dir,
                         do_resid=True )
    analyze_range_resids(config.span, args.out_dir, args.out_dir )

if __name__ == '__main__':
    main()
