sarkit-convert 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,10 +15,11 @@ metadata that would predict the complex data characteristics
15
15
  """
16
16
 
17
17
  import argparse
18
+ import datetime
18
19
  import pathlib
19
20
 
20
21
  import dateutil.parser
21
- import lxml.builder
22
+ import lxml.etree
22
23
  import numpy as np
23
24
  import numpy.linalg as npl
24
25
  import numpy.polynomial.polynomial as npp
@@ -26,8 +27,8 @@ import sarkit.sicd as sksicd
26
27
  import sarkit.verification
27
28
  import sarkit.wgs84
28
29
  import scipy.constants
29
- from lxml import etree
30
30
 
31
+ from sarkit_convert import __version__
31
32
  from sarkit_convert import _utils as utils
32
33
 
33
34
  NSMAP = {
@@ -57,10 +58,6 @@ def _parse_to_naive(timestamp_str):
57
58
  return dateutil.parser.parse(timestamp_str).replace(tzinfo=None)
58
59
 
59
60
 
60
- def _naive_to_sicd_str(timestamp):
61
- return timestamp.replace(tzinfo=None).isoformat() + "Z"
62
-
63
-
64
61
  def _boolstr_to_bool(text):
65
62
  return text in ("1", "true")
66
63
 
@@ -154,7 +151,6 @@ def cosar_to_sicd(
154
151
  cosar_file,
155
152
  sicd_file,
156
153
  classification,
157
- ostaid,
158
154
  chan_index,
159
155
  tx_polarizations,
160
156
  tx_rcv_pols,
@@ -196,7 +192,7 @@ def cosar_to_sicd(
196
192
  application = generation_system.text
197
193
  version = generation_system.attrib["version"]
198
194
  creation_application = f"{application} version {version}"
199
- creation_site = tsx_xml.findtext(
195
+ originator_facility = tsx_xml.findtext(
200
196
  "./productInfo/generationInfo/level1ProcessingFacility"
201
197
  )
202
198
 
@@ -491,43 +487,156 @@ def cosar_to_sicd(
491
487
  uspz = spz / npl.norm(spz)
492
488
  u_col = np.cross(uspz, u_row)
493
489
 
494
- # Build XML
495
- sicd = lxml.builder.ElementMaker(
496
- namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]}
490
+ # Antenna
491
+ attitude_elem = tsx_xml.find("./platform/attitude")
492
+ attitude_utcs = []
493
+ attitude_quaternions = []
494
+ for attitude_data in attitude_elem.findall("./attitudeData"):
495
+ attitude_utcs.append(_parse_to_naive(attitude_data.findtext("./timeUTC")))
496
+ quat = [
497
+ float(attitude_data.findtext("./q0")),
498
+ float(attitude_data.findtext("./q1")),
499
+ float(attitude_data.findtext("./q2")),
500
+ float(attitude_data.findtext("./q3")),
501
+ ]
502
+ attitude_quaternions.append(quat)
503
+ attitude_quaternions = np.array(attitude_quaternions)
504
+
505
+ def get_mech_frame_from_quat(att_quat):
506
+ scipy_quat = np.roll(att_quat, -1)
507
+ return scipy.spatial.transform.Rotation.from_quat(scipy_quat).inv().as_matrix()
508
+
509
+ rel_att_times = np.array(
510
+ [(att_utc - collection_start_time).total_seconds() for att_utc in attitude_utcs]
511
+ )
512
+ good_indices = np.where(
513
+ np.logical_and(
514
+ np.less(-60, rel_att_times),
515
+ np.less(rel_att_times, 60 + collection_duration),
516
+ )
497
517
  )
498
- collection_info = sicd.CollectionInfo(
499
- sicd.CollectorName(collector_name),
500
- sicd.CoreName(core_name),
501
- sicd.CollectType("MONOSTATIC"),
502
- sicd.RadarMode(sicd.ModeType(radar_mode_type), sicd.ModeID(radar_mode_id)),
503
- sicd.Classification(classification),
518
+ good_times = rel_att_times[good_indices]
519
+ good_att_quat = attitude_quaternions[good_indices]
520
+ mech_frame = np.array(
521
+ [get_mech_frame_from_quat(att_quat) for att_quat in good_att_quat]
504
522
  )
505
- image_creation = sicd.ImageCreation(
506
- sicd.Application(creation_application),
507
- sicd.DateTime(_naive_to_sicd_str(creation_time)),
508
- sicd.Site(creation_site),
523
+
524
+ ux_rot = mech_frame[:, 0, :]
525
+ uy_rot = mech_frame[:, 1, :]
526
+
527
+ ant_x_dir_poly = utils.fit_state_vectors(
528
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
529
+ good_times,
530
+ ux_rot,
531
+ None,
532
+ None,
533
+ order=4,
534
+ )
535
+ ant_y_dir_poly = utils.fit_state_vectors(
536
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
537
+ good_times,
538
+ uy_rot,
539
+ None,
540
+ None,
541
+ order=4,
542
+ )
543
+
544
+ freq_zero = center_frequency
545
+ antenna_pattern_elem = tsx_xml.xpath(
546
+ f'./calibration/calibrationData/antennaPattern[polLayer[text()="{pol_layer}"]]'
547
+ )[0]
548
+ antenna_beam_elevation = np.arctan2(
549
+ float(antenna_pattern_elem.findtext("./beamPointingVector/y")),
550
+ float(antenna_pattern_elem.findtext("./beamPointingVector/z")),
551
+ )
552
+ spotlight_elem = tsx_xml.find(
553
+ "./productInfo/acquisitionInfo/imagingModeSpecificInfo/spotLight"
554
+ )
555
+ if spotlight_elem is not None:
556
+ azimuth_steering = np.array(
557
+ [
558
+ float(spotlight_elem.findtext("./azimuthSteeringAngleFirst")),
559
+ float(spotlight_elem.findtext("./azimuthSteeringAngleLast")),
560
+ ]
561
+ )
562
+ eb_dcx_poly = npp.polyfit(
563
+ [0, collection_duration], np.sin(np.deg2rad(azimuth_steering)), 1
564
+ )
565
+ else:
566
+ eb_dcx_poly = [0.0]
567
+ eb_dcy_poly = [-look * np.sin(antenna_beam_elevation)]
568
+
569
+ def get_angles_gains(pattern_elem):
570
+ antenna_angles = []
571
+ antenna_gains = []
572
+ for gain_ext in pattern_elem.findall("./gainExt"):
573
+ antenna_angles.append(float(gain_ext.attrib["angle"]))
574
+ antenna_gains.append(float(gain_ext.text))
575
+ return np.array(antenna_angles), np.array(antenna_gains)
576
+
577
+ antenna_rg_angles, antenna_rg_gains = get_angles_gains(
578
+ antenna_pattern_elem.find("./elevationPattern")
509
579
  )
510
- image_data = sicd.ImageData(
511
- sicd.PixelType(COSAR_PIXEL_TYPE),
512
- sicd.NumRows(str(num_rows)),
513
- sicd.NumCols(str(num_cols)),
514
- sicd.FirstRow(str(first_row)),
515
- sicd.FirstCol(str(first_col)),
516
- sicd.FullImage(sicd.NumRows(str(num_rows)), sicd.NumCols(str(num_cols))),
517
- sicd.SCPPixel(sicd.Row(str(scp_pixel[0])), sicd.Col(str(scp_pixel[1]))),
580
+ antenna_az_angles, antenna_az_gains = get_angles_gains(
581
+ antenna_pattern_elem.find("./azimuthPattern")
518
582
  )
519
583
 
520
- def make_xyz(arr):
521
- return [sicd.X(str(arr[0])), sicd.Y(str(arr[1])), sicd.Z(str(arr[2]))]
584
+ fit_order = 4
522
585
 
523
- def make_llh(arr):
524
- return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1])), sicd.HAE(str(arr[2]))]
586
+ def fit_gains(angles, gains):
587
+ fit_limit = -9
588
+ array_mask = gains > fit_limit
589
+ dcs = np.sin(np.deg2rad(angles))
590
+ return npp.polyfit(dcs[array_mask], gains[array_mask], fit_order)
525
591
 
526
- # Will add ImageCorners later
527
- geo_data = sicd.GeoData(
528
- sicd.EarthModel("WGS_84"),
529
- sicd.SCP(sicd.ECF(*make_xyz(scp_ecf)), sicd.LLH(*make_llh(scp_llh))),
592
+ antenna_array_gain = np.zeros((fit_order + 1, fit_order + 1), dtype=float)
593
+ antenna_array_gain[0, :] = fit_gains(
594
+ antenna_rg_angles + look * np.rad2deg(antenna_beam_elevation),
595
+ antenna_rg_gains,
530
596
  )
597
+ antenna_array_gain[:, 0] = fit_gains(antenna_az_angles, antenna_az_gains)
598
+ antenna_array_gain[0, 0] = 0.0
599
+
600
+ # Build XML
601
+ sicd_xml_obj = lxml.etree.Element(
602
+ f"{{{NSMAP['sicd']}}}SICD", nsmap={None: NSMAP["sicd"]}
603
+ )
604
+ sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
605
+
606
+ sicd_ew["CollectionInfo"] = {
607
+ "CollectorName": collector_name,
608
+ "CoreName": core_name,
609
+ "CollectType": "MONOSTATIC",
610
+ "RadarMode": {
611
+ "ModeType": radar_mode_type,
612
+ "ModeID": radar_mode_id,
613
+ },
614
+ "Classification": classification,
615
+ }
616
+ sicd_ew["ImageCreation"] = {
617
+ "Application": creation_application,
618
+ "DateTime": creation_time,
619
+ }
620
+ sicd_ew["ImageData"] = {
621
+ "PixelType": COSAR_PIXEL_TYPE,
622
+ "NumRows": num_rows,
623
+ "NumCols": num_cols,
624
+ "FirstRow": first_row,
625
+ "FirstCol": first_col,
626
+ "FullImage": {
627
+ "NumRows": num_rows,
628
+ "NumCols": num_cols,
629
+ },
630
+ "SCPPixel": scp_pixel,
631
+ }
632
+
633
+ sicd_ew["GeoData"] = {
634
+ "EarthModel": "WGS_84",
635
+ "SCP": {
636
+ "ECF": scp_ecf,
637
+ "LLH": scp_llh,
638
+ },
639
+ }
531
640
 
532
641
  dc_sgn = np.sign(-doppler_rate_poly[0, 0])
533
642
  col_deltakcoa_poly = (
@@ -552,168 +661,180 @@ def cosar_to_sicd(
552
661
  col_window_name = proc_param.findtext("./azimuthWindowID")
553
662
  col_window_coeff = float(proc_param.findtext("./azimuthWindowCoefficient"))
554
663
 
555
- grid = sicd.Grid(
556
- sicd.ImagePlane("SLANT"),
557
- sicd.Type("RGZERO"),
558
- sicd.TimeCOAPoly(),
559
- sicd.Row(
560
- sicd.UVectECF(*make_xyz(u_row)),
561
- sicd.SS(str(spacings[0])),
562
- sicd.ImpRespWid(str(row_wid)),
563
- sicd.Sgn("-1"),
564
- sicd.ImpRespBW(str(row_bw)),
565
- sicd.KCtr(str(center_frequency / (scipy.constants.speed_of_light / 2))),
566
- sicd.DeltaK1(str(-row_bw / 2)),
567
- sicd.DeltaK2(str(row_bw / 2)),
568
- sicd.DeltaKCOAPoly(),
569
- sicd.WgtType(
570
- sicd.WindowName(row_window_name),
571
- sicd.Parameter({"name": "COEFFICIENT"}, str(row_window_coeff)),
572
- ),
573
- ),
574
- sicd.Col(
575
- sicd.UVectECF(*make_xyz(u_col)),
576
- sicd.SS(str(spacings[1])),
577
- sicd.ImpRespWid(str(col_wid)),
578
- sicd.Sgn("-1"),
579
- sicd.ImpRespBW(str(col_bw)),
580
- sicd.KCtr("0"),
581
- sicd.DeltaK1(str(dk1)),
582
- sicd.DeltaK2(str(dk2)),
583
- sicd.DeltaKCOAPoly(),
584
- sicd.WgtType(
585
- sicd.WindowName(col_window_name),
586
- sicd.Parameter({"name": "COEFFICIENT"}, str(col_window_coeff)),
587
- ),
588
- ),
589
- )
590
- sksicd.Poly2dType().set_elem(grid.find("./{*}TimeCOAPoly"), time_coa_poly)
591
- sksicd.Poly2dType().set_elem(grid.find("./{*}Row/{*}DeltaKCOAPoly"), [[0]])
592
- sksicd.Poly2dType().set_elem(
593
- grid.find("./{*}Col/{*}DeltaKCOAPoly"), col_deltakcoa_poly
594
- )
664
+ sicd_ew["Grid"] = {
665
+ "ImagePlane": "SLANT",
666
+ "Type": "RGZERO",
667
+ "TimeCOAPoly": time_coa_poly,
668
+ "Row": {
669
+ "UVectECF": u_row,
670
+ "SS": spacings[0],
671
+ "ImpRespWid": row_wid,
672
+ "Sgn": -1,
673
+ "ImpRespBW": row_bw,
674
+ "KCtr": center_frequency / (scipy.constants.speed_of_light / 2),
675
+ "DeltaK1": -row_bw / 2,
676
+ "DeltaK2": row_bw / 2,
677
+ "DeltaKCOAPoly": [[0.0]],
678
+ "WgtType": {
679
+ "WindowName": row_window_name,
680
+ "Parameter": [("COEFFICIENT", str(row_window_coeff))],
681
+ },
682
+ },
683
+ "Col": {
684
+ "UVectECF": u_col,
685
+ "SS": spacings[1],
686
+ "ImpRespWid": col_wid,
687
+ "Sgn": -1,
688
+ "ImpRespBW": col_bw,
689
+ "KCtr": 0.0,
690
+ "DeltaK1": dk1,
691
+ "DeltaK2": dk2,
692
+ "DeltaKCOAPoly": col_deltakcoa_poly,
693
+ "WgtType": {
694
+ "WindowName": col_window_name,
695
+ "Parameter": [("COEFFICIENT", str(col_window_coeff))],
696
+ },
697
+ },
698
+ }
595
699
  rcs_row_sf = None
596
700
  rcs_col_sf = None
597
701
  if row_window_name == "Hamming":
598
702
  wgts = scipy.signal.windows.general_hamming(512, row_window_coeff, sym=True)
599
- wgtfunc = sicd.WgtFunct()
600
- sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, wgts)
601
- grid.find("./{*}Row").append(wgtfunc)
703
+ sicd_ew["Grid"]["Row"]["WgtFunct"] = wgts
602
704
  row_broadening_factor = utils.broadening_from_amp(wgts)
603
705
  row_wid = row_broadening_factor / row_bw
604
- sksicd.DblType().set_elem(grid.find("./{*}Row/{*}ImpRespWid"), row_wid)
706
+ sicd_ew["Grid"]["Row"]["ImpRespWid"] = row_wid
605
707
  rcs_row_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
606
708
  if col_window_name == "Hamming":
607
709
  wgts = scipy.signal.windows.general_hamming(512, col_window_coeff, sym=True)
608
- wgtfunc = sicd.WgtFunct()
609
- sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, wgts)
610
- grid.find("./{*}Col").append(wgtfunc)
710
+ sicd_ew["Grid"]["Col"]["WgtFunct"] = wgts
611
711
  col_broadening_factor = utils.broadening_from_amp(wgts)
612
712
  col_wid = col_broadening_factor / col_bw
613
- sksicd.DblType().set_elem(grid.find("./{*}Col/{*}ImpRespWid"), col_wid)
713
+ sicd_ew["Grid"]["Col"]["ImpRespWid"] = col_wid
614
714
  rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
615
715
 
616
- timeline = sicd.Timeline(
617
- sicd.CollectStart(_naive_to_sicd_str(collection_start_time)),
618
- sicd.CollectDuration(str(collection_duration)),
619
- sicd.IPP(
620
- {"size": "1"},
621
- sicd.Set(
622
- {"index": "1"},
623
- sicd.TStart(str(0)),
624
- sicd.TEnd(str(num_pulses / prf)),
625
- sicd.IPPStart(str(0)),
626
- sicd.IPPEnd(str(num_pulses - 1)),
627
- sicd.IPPPoly(),
628
- ),
629
- ),
630
- )
631
- sksicd.PolyType().set_elem(timeline.find("./{*}IPP/{*}Set/{*}IPPPoly"), [0, prf])
716
+ sicd_ew["Timeline"] = {
717
+ "CollectStart": collection_start_time,
718
+ "CollectDuration": collection_duration,
719
+ "IPP": {
720
+ "@size": 1,
721
+ "Set": [
722
+ {
723
+ "@index": 1,
724
+ "TStart": 0,
725
+ "TEnd": num_pulses / prf,
726
+ "IPPStart": 0,
727
+ "IPPEnd": num_pulses - 1,
728
+ "IPPPoly": [0, prf],
729
+ }
730
+ ],
731
+ },
732
+ }
632
733
 
633
- position = sicd.Position(sicd.ARPPoly())
634
- sksicd.XyzPolyType().set_elem(position.find("./{*}ARPPoly"), apc_poly)
734
+ sicd_ew["Position"]["ARPPoly"] = apc_poly
635
735
 
636
- rcv_channels = sicd.RcvChannels(
637
- {"size": str(len(tx_rcv_pols))},
638
- )
736
+ chan_parameters = []
639
737
  for ndx, tx_rcv_pol in enumerate(tx_rcv_pols):
640
- rcv_channels.append(
641
- sicd.ChanParameters(
642
- {"index": str(ndx + 1)}, sicd.TxRcvPolarization(tx_rcv_pol)
643
- )
738
+ chan_parameters.append(
739
+ {
740
+ "@index": ndx + 1,
741
+ "TxRcvPolarization": tx_rcv_pol,
742
+ }
644
743
  )
645
744
 
646
- radar_collection = sicd.RadarCollection(
647
- sicd.TxFrequency(sicd.Min(str(tx_freq_min)), sicd.Max(str(tx_freq_max))),
648
- sicd.Waveform(
649
- {"size": "1"},
650
- sicd.WFParameters(
651
- {"index": "1"},
652
- sicd.TxPulseLength(str(tx_pulse_length)),
653
- sicd.TxRFBandwidth(str(tx_rf_bw)),
654
- sicd.TxFreqStart(str(tx_freq_start)),
655
- sicd.TxFMRate(str(tx_fm_rate)),
656
- sicd.RcvWindowLength(str(rcv_window_length)),
657
- sicd.ADCSampleRate(str(adc_sample_rate)),
658
- ),
659
- ),
660
- sicd.TxPolarization(tx_polarization),
661
- rcv_channels,
662
- )
745
+ sicd_ew["RadarCollection"] = {
746
+ "TxFrequency": {
747
+ "Min": tx_freq_min,
748
+ "Max": tx_freq_max,
749
+ },
750
+ "Waveform": {
751
+ "@size": 1,
752
+ "WFParameters": [
753
+ {
754
+ "@index": 1,
755
+ "TxPulseLength": tx_pulse_length,
756
+ "TxRFBandwidth": tx_rf_bw,
757
+ "TxFreqStart": tx_freq_start,
758
+ "TxFMRate": tx_fm_rate,
759
+ "RcvWindowLength": rcv_window_length,
760
+ "ADCSampleRate": adc_sample_rate,
761
+ }
762
+ ],
763
+ },
764
+ "TxPolarization": tx_polarization,
765
+ "RcvChannels": {
766
+ "@size": len(tx_rcv_pols),
767
+ "ChanParameters": chan_parameters,
768
+ },
769
+ }
663
770
  if len(tx_polarizations) > 1:
664
- radar_collection.find("./{*}TxPolarization").text = "SEQUENCE"
665
- tx_sequence = sicd.TxSequence({"size": str(len(tx_polarizations))})
771
+ sicd_ew["RadarCollection"]["TxPolarization"] = "SEQUENCE"
772
+ tx_steps = []
666
773
  for ndx, tx_pol in enumerate(tx_polarizations):
667
- tx_sequence.append(
668
- sicd.TxStep({"index": str(ndx + 1)}, sicd.TxPolarization(tx_pol))
774
+ tx_steps.append(
775
+ {
776
+ "@index": ndx + 1,
777
+ "TxPolarization": tx_pol,
778
+ }
669
779
  )
670
- rcv_channels.addprevious(tx_sequence)
671
-
672
- image_formation = sicd.ImageFormation(
673
- sicd.RcvChanProc(sicd.NumChanProc("1"), sicd.ChanIndex(str(chan_index))),
674
- sicd.TxRcvPolarizationProc(tx_rcv_polarization),
675
- sicd.TStartProc(str(0)),
676
- sicd.TEndProc(str(collection_duration)),
677
- sicd.TxFrequencyProc(
678
- sicd.MinProc(str(tx_freq_min)), sicd.MaxProc(str(tx_freq_max))
679
- ),
680
- sicd.ImageFormAlgo("RMA"),
681
- sicd.STBeamComp(st_beam_comp),
682
- sicd.ImageBeamComp("SV"),
683
- sicd.AzAutofocus("NO"),
684
- sicd.RgAutofocus("NO"),
685
- )
686
-
687
- rma = sicd.RMA(
688
- sicd.RMAlgoType("OMEGA_K"),
689
- sicd.ImageType("INCA"),
690
- sicd.INCA(
691
- sicd.TimeCAPoly(),
692
- sicd.R_CA_SCP(str(scp_rca)),
693
- sicd.FreqZero(str(center_frequency)),
694
- sicd.DRateSFPoly(),
695
- sicd.DopCentroidPoly(),
696
- ),
697
- )
698
- sksicd.PolyType().set_elem(rma.find("./{*}INCA/{*}TimeCAPoly"), time_ca_poly)
699
- sksicd.Poly2dType().set_elem(rma.find("./{*}INCA/{*}DRateSFPoly"), drsf_poly)
700
- sksicd.Poly2dType().set_elem(
701
- rma.find("./{*}INCA/{*}DopCentroidPoly"), doppler_centroid_poly
702
- )
703
- sicd_xml_obj = sicd.SICD(
704
- collection_info,
705
- image_creation,
706
- image_data,
707
- geo_data,
708
- grid,
709
- timeline,
710
- position,
711
- radar_collection,
712
- image_formation,
713
- rma,
714
- )
715
-
716
- image_formation.addnext(sksicd.compute_scp_coa(sicd_xml_obj.getroottree()))
780
+ sicd_ew["RadarCollection"]["TxSequence"] = {
781
+ "@size": len(tx_polarizations),
782
+ "TxStep": tx_steps,
783
+ }
784
+
785
+ now = (
786
+ datetime.datetime.now(datetime.timezone.utc)
787
+ .isoformat(timespec="microseconds")
788
+ .replace("+00:00", "Z")
789
+ )
790
+ sicd_ew["ImageFormation"] = {
791
+ "RcvChanProc": {
792
+ "NumChanProc": 1,
793
+ "ChanIndex": [chan_index],
794
+ },
795
+ "TxRcvPolarizationProc": tx_rcv_polarization,
796
+ "TStartProc": 0,
797
+ "TEndProc": collection_duration,
798
+ "TxFrequencyProc": {
799
+ "MinProc": tx_freq_min,
800
+ "MaxProc": tx_freq_max,
801
+ },
802
+ "ImageFormAlgo": "RMA",
803
+ "STBeamComp": st_beam_comp,
804
+ "ImageBeamComp": "SV",
805
+ "AzAutofocus": "NO",
806
+ "RgAutofocus": "NO",
807
+ "Processing": [
808
+ {
809
+ "Type": f"sarkit-convert {__version__} @ {now}",
810
+ "Applied": True,
811
+ },
812
+ ],
813
+ }
814
+
815
+ sicd_ew["Antenna"]["TwoWay"]["XAxisPoly"] = ant_x_dir_poly
816
+ sicd_ew["Antenna"]["TwoWay"]["YAxisPoly"] = ant_y_dir_poly
817
+ sicd_ew["Antenna"]["TwoWay"]["FreqZero"] = freq_zero
818
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCXPoly"] = eb_dcx_poly
819
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCYPoly"] = eb_dcy_poly
820
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["GainPoly"] = antenna_array_gain
821
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["PhasePoly"] = np.zeros(
822
+ dtype=float, shape=(1, 1)
823
+ )
824
+
825
+ sicd_ew["RMA"] = {
826
+ "RMAlgoType": "OMEGA_K",
827
+ "ImageType": "INCA",
828
+ "INCA": {
829
+ "TimeCAPoly": time_ca_poly,
830
+ "R_CA_SCP": scp_rca,
831
+ "FreqZero": center_frequency,
832
+ "DRateSFPoly": drsf_poly,
833
+ "DopCentroidPoly": doppler_centroid_poly,
834
+ },
835
+ }
836
+
837
+ sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
717
838
 
718
839
  # Add Radiometric
719
840
  cal_constant = float(cal_const_elem.findtext("./calFactor"))
@@ -738,28 +859,19 @@ def cosar_to_sicd(
738
859
  1e-2,
739
860
  )
740
861
 
741
- radiometric = sicd.Radiometric(
742
- sicd.NoiseLevel(sicd.NoiseLevelType("ABSOLUTE"), sicd.NoisePoly()),
743
- sicd.SigmaZeroSFPoly(),
744
- sicd.BetaZeroSFPoly(),
745
- sicd.GammaZeroSFPoly(),
746
- )
747
- sksicd.Poly2dType().set_elem(
748
- radiometric.find("./{*}NoiseLevel/{*}NoisePoly"), noise_poly
749
- )
750
- sksicd.Poly2dType().set_elem(
751
- radiometric.find("./{*}SigmaZeroSFPoly"), sigmazero_poly
752
- )
753
- sksicd.Poly2dType().set_elem(radiometric.find("./{*}BetaZeroSFPoly"), betazero_poly)
754
- sksicd.Poly2dType().set_elem(
755
- radiometric.find("./{*}GammaZeroSFPoly"), gammazero_poly
756
- )
862
+ sicd_ew["Radiometric"] = {
863
+ "NoiseLevel": {
864
+ "NoiseLevelType": "ABSOLUTE",
865
+ "NoisePoly": noise_poly,
866
+ },
867
+ "SigmaZeroSFPoly": sigmazero_poly,
868
+ "BetaZeroSFPoly": betazero_poly,
869
+ "GammaZeroSFPoly": gammazero_poly,
870
+ }
871
+
757
872
  if rcs_row_sf and rcs_col_sf:
758
873
  rcssf_poly = betazero_poly * (rcs_row_sf * rcs_col_sf / (row_bw * col_bw))
759
- radiometric.find("./{*}SigmaZeroSFPoly").addprevious(sicd.RCSSFPoly())
760
- sksicd.Poly2dType().set_elem(radiometric.find("./{*}RCSSFPoly"), rcssf_poly)
761
-
762
- sicd_xml_obj.find("./{*}RMA").addprevious(radiometric)
874
+ sicd_ew["Radiometric"]["RCSSFPoly"] = rcssf_poly
763
875
 
764
876
  # Add Geodata Corners
765
877
  sicd_xmltree = sicd_xml_obj.getroottree()
@@ -776,18 +888,15 @@ def cosar_to_sicd(
776
888
  sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
777
889
  )
778
890
  icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
779
- image_corners = sicd.ImageCorners()
780
- sksicd.ImageCornersType().set_elem(image_corners, icp_llh[:, :2])
781
- geo_data.append(image_corners)
891
+ sicd_ew["GeoData"]["ImageCorners"] = icp_llh[:, :2]
782
892
 
783
893
  # Add RNIIRS
784
- xml_helper = sksicd.XmlHelper(sicd_xmltree)
785
- inf_density, pred_rniirs = utils.get_rniirs_estimate(xml_helper)
786
- collection_info.append(
787
- sicd.Parameter({"name": "INFORMATION_DENSITY"}, f"{inf_density:.2g}")
894
+ inf_density, pred_rniirs = utils.get_rniirs_estimate(sicd_ew)
895
+ sicd_ew["CollectionInfo"].add(
896
+ "Parameter", ("INFORMATION_DENSITY", f"{inf_density:.2g}")
788
897
  )
789
- collection_info.append(
790
- sicd.Parameter({"name": "PREDICTED_RNIIRS"}, f"{pred_rniirs:.2g}")
898
+ sicd_ew["CollectionInfo"].add(
899
+ "Parameter", ("PREDICTED_RNIIRS", f"{pred_rniirs:.1f}")
791
900
  )
792
901
 
793
902
  # Validate XML
@@ -803,7 +912,7 @@ def cosar_to_sicd(
803
912
  metadata = sksicd.NitfMetadata(
804
913
  xmltree=sicd_xmltree,
805
914
  file_header_part={
806
- "ostaid": ostaid,
915
+ "ostaid": originator_facility,
807
916
  "ftitle": core_name,
808
917
  "security": {
809
918
  "clas": classification[0].upper(),
@@ -847,17 +956,12 @@ def main(args=None):
847
956
  type=pathlib.Path,
848
957
  help='path of the output SICD file. The string "{pol}" will be replaced with polarization for multiple images',
849
958
  )
850
- parser.add_argument(
851
- "--ostaid",
852
- help="content of the originating station ID (OSTAID) field of the NITF header",
853
- default="Unknown",
854
- )
855
959
  config = parser.parse_args(args)
856
960
 
857
961
  if not config.input_xml_file.is_file():
858
962
  raise ValueError(f"Input XML file {str(config.input_xml_file)} is not a file")
859
963
 
860
- tsx_xml = etree.parse(config.input_xml_file).getroot()
964
+ tsx_xml = lxml.etree.parse(config.input_xml_file).getroot()
861
965
 
862
966
  images = dict()
863
967
  img_ndx = 1
@@ -895,7 +999,6 @@ def main(args=None):
895
999
  cosar_file=img_info["cosar_filename"],
896
1000
  sicd_file=img_info["sicd_filename"],
897
1001
  classification=config.classification,
898
- ostaid=config.ostaid,
899
1002
  chan_index=img_info["chan_index"],
900
1003
  tx_polarizations=tx_polarizations,
901
1004
  tx_rcv_pols=tx_rcv_pols,