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.
@@ -17,11 +17,15 @@ metadata that would predict the complex data characteristics
17
17
 
18
18
  import argparse
19
19
  import contextlib
20
+ import datetime
20
21
  import pathlib
21
22
 
23
+ import astropy.coordinates as apcoord
24
+ import astropy.units as apu
25
+ import astropy.utils
22
26
  import dateutil.parser
23
27
  import h5py
24
- import lxml.builder
28
+ import lxml.etree
25
29
  import numpy as np
26
30
  import numpy.linalg as npl
27
31
  import numpy.polynomial.polynomial as npp
@@ -30,9 +34,13 @@ import sarkit.verification
30
34
  import sarkit.wgs84
31
35
  import scipy.constants
32
36
  import scipy.optimize
37
+ import scipy.spatial.transform
33
38
 
39
+ from sarkit_convert import __version__
34
40
  from sarkit_convert import _utils as utils
35
41
 
42
+ astropy.utils.iers.conf.autodownload = False
43
+
36
44
  NSMAP = {
37
45
  "sicd": "urn:SICD:1.4.0",
38
46
  }
@@ -130,7 +138,6 @@ def hdf5_to_sicd(
130
138
  h5_filename,
131
139
  sicd_filename,
132
140
  classification,
133
- ostaid,
134
141
  img_str,
135
142
  chan_index,
136
143
  tx_polarizations,
@@ -141,8 +148,10 @@ def hdf5_to_sicd(
141
148
  mission_id = h5_attrs["Mission ID"]
142
149
  if mission_id == "CSG":
143
150
  dataset_str = "IMG"
151
+ burst_str = "B0001"
144
152
  else:
145
153
  dataset_str = "SBI"
154
+ burst_str = "B001"
146
155
  sample_data_h5_path = f"{img_str}/{dataset_str}"
147
156
  sample_data_shape = h5file[sample_data_h5_path].shape
148
157
  sample_data_dtype = h5file[sample_data_h5_path].dtype
@@ -171,7 +180,6 @@ def hdf5_to_sicd(
171
180
 
172
181
  # Creation Info
173
182
  creation_time = dateutil.parser.parse(h5_attrs["Product Generation UTC"])
174
- creation_site = h5_attrs["Processing Centre"]
175
183
  l0_ver = h5_attrs.get("L0 Software Version", "NONE")
176
184
  l1_ver = h5_attrs.get("L1A Software Version", "NONE")
177
185
  creation_application = f"L0: {l0_ver}, L1: {l1_ver}"
@@ -311,31 +319,34 @@ def hdf5_to_sicd(
311
319
  range_rate_per_hz = -scipy.constants.speed_of_light / (2 * center_frequency)
312
320
  range_rate = doppler_centroid * range_rate_per_hz
313
321
  range_rate_rate = doppler_rate * range_rate_per_hz
314
- doppler_centroid_poly = utils.polyfit2d(
322
+ doppler_centroid_poly = utils.polyfit2d_tol(
315
323
  grid_coords[..., 0].flatten(),
316
324
  grid_coords[..., 1].flatten(),
317
325
  doppler_centroid.flatten(),
318
326
  4,
319
327
  4,
328
+ 1e-2,
320
329
  )
321
- doppler_rate_poly = utils.polyfit2d(
330
+ doppler_rate_poly = utils.polyfit2d_tol(
322
331
  grid_coords[..., 0].flatten(),
323
332
  grid_coords[..., 1].flatten(),
324
333
  doppler_rate.flatten(),
325
334
  4,
326
335
  4,
336
+ 1e-3,
327
337
  )
328
338
  time_ca_samps = time_coords[..., 1] - start_minus_ref
329
339
  time_ca_poly = npp.polyfit(
330
340
  grid_coords[..., 1].flatten(), time_ca_samps.flatten(), 1
331
341
  )
332
342
  time_coa_samps = time_ca_samps + range_rate / range_rate_rate
333
- time_coa_poly = utils.polyfit2d(
343
+ time_coa_poly = utils.polyfit2d_tol(
334
344
  grid_coords[..., 0].flatten(),
335
345
  grid_coords[..., 1].flatten(),
336
346
  time_coa_samps.flatten(),
337
347
  4,
338
348
  4,
349
+ 1e-3,
339
350
  )
340
351
 
341
352
  range_ca = time_coords[..., 0] * scipy.constants.speed_of_light / 2
@@ -344,12 +355,13 @@ def hdf5_to_sicd(
344
355
  axis=0,
345
356
  )
346
357
  drsf = range_rate_rate * range_ca / speed_ca**2
347
- drsf_poly = utils.polyfit2d(
358
+ drsf_poly = utils.polyfit2d_tol(
348
359
  grid_coords[..., 0].flatten(),
349
360
  grid_coords[..., 1].flatten(),
350
361
  drsf.flatten(),
351
362
  4,
352
363
  4,
364
+ 1e-6,
353
365
  )
354
366
 
355
367
  llh_ddm = h5_attrs["Scene Centre Geodetic Coordinates"]
@@ -398,53 +410,198 @@ def hdf5_to_sicd(
398
410
  uspz = spz / npl.norm(spz)
399
411
  u_col = np.cross(uspz, u_row)
400
412
 
401
- # Build XML
402
- sicd = lxml.builder.ElementMaker(
403
- namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]}
404
- )
405
- collection_info = sicd.CollectionInfo(
406
- sicd.CollectorName(collector_name),
407
- sicd.CoreName(core_name),
408
- sicd.CollectType("MONOSTATIC"),
409
- sicd.RadarMode(sicd.ModeType(radar_mode_type), sicd.ModeID(radar_mode_id)),
410
- sicd.Classification(classification),
411
- )
412
- image_creation = sicd.ImageCreation(
413
- sicd.Application(creation_application),
414
- sicd.DateTime(creation_time.isoformat() + "Z"),
415
- sicd.Site(creation_site),
416
- )
417
- image_data = sicd.ImageData(
418
- sicd.PixelType(pixel_type),
419
- sicd.NumRows(str(num_rows)),
420
- sicd.NumCols(str(num_cols)),
421
- sicd.FirstRow(str(first_row)),
422
- sicd.FirstCol(str(first_col)),
423
- sicd.FullImage(sicd.NumRows(str(num_rows)), sicd.NumCols(str(num_cols))),
424
- sicd.SCPPixel(sicd.Row(str(scp_pixel[0])), sicd.Col(str(scp_pixel[1]))),
425
- )
426
-
427
- def make_xyz(arr):
428
- return [sicd.X(str(arr[0])), sicd.Y(str(arr[1])), sicd.Z(str(arr[2]))]
429
-
430
- def make_llh(arr):
431
- return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1])), sicd.HAE(str(arr[2]))]
432
-
433
- def make_ll(arr):
434
- return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1]))]
435
-
436
- # Placeholder locations
437
- geo_data = sicd.GeoData(
438
- sicd.EarthModel("WGS_84"),
439
- sicd.SCP(sicd.ECF(*make_xyz(scp_ecf)), sicd.LLH(*make_llh(scp_llh))),
440
- sicd.ImageCorners(
441
- sicd.ICP({"index": "1:FRFC"}, *make_ll([0, 0])),
442
- sicd.ICP({"index": "2:FRLC"}, *make_ll([0, 0])),
443
- sicd.ICP({"index": "3:LRLC"}, *make_ll([0, 0])),
444
- sicd.ICP({"index": "4:LRFC"}, *make_ll([0, 0])),
445
- ),
413
+ # Antenna
414
+ attitude_quaternion = np.roll(h5_attrs["Attitude Quaternions"], -1, axis=1)
415
+ attitude_times = h5_attrs["Attitude Times"]
416
+ attitude_utcs = [
417
+ ref_time + datetime.timedelta(seconds=attitude_time)
418
+ for attitude_time in attitude_times
419
+ ]
420
+
421
+ inertial_position = h5_attrs["Inertial Satellite Position"]
422
+ inertial_velocity = h5_attrs["Inertial Satellite Velocity"]
423
+ inertial_acceleration = h5_attrs["Inertial Satellite Acceleration"]
424
+ eci_apc_poly = utils.fit_state_vectors(
425
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
426
+ h5_attrs["State Vectors Times"]
427
+ - (collection_start_time - ref_time).total_seconds(),
428
+ inertial_position,
429
+ inertial_velocity,
430
+ inertial_acceleration,
431
+ order=5,
432
+ )
433
+
434
+ def get_nadir_plane_at_time(time):
435
+ obstime = collection_start_time + datetime.timedelta(seconds=time)
436
+ eci_pos = npp.polyval(time, eci_apc_poly)
437
+ eci_vel = npp.polyval(time, npp.polyder(eci_apc_poly))
438
+
439
+ # Derived from https://adsabs.harvard.edu/full/2006ESASP.606E..35C, Section 3
440
+ z_sc = -eci_pos / np.linalg.norm(eci_pos)
441
+ y_sc_dir = np.cross(z_sc, eci_vel)
442
+ y_sc = y_sc_dir / np.linalg.norm(y_sc_dir)
443
+ x_sc = np.cross(y_sc, z_sc)
444
+ eci_2_ecf = np.array(
445
+ [
446
+ apcoord.GCRS(
447
+ apcoord.CartesianRepresentation(1, 0, 0, unit=apu.m),
448
+ obstime=obstime,
449
+ )
450
+ .transform_to(apcoord.ITRS(obstime=obstime))
451
+ .data.xyz.value,
452
+ apcoord.GCRS(
453
+ apcoord.CartesianRepresentation(0, 1, 0, unit=apu.m),
454
+ obstime=obstime,
455
+ )
456
+ .transform_to(apcoord.ITRS(obstime=obstime))
457
+ .data.xyz.value,
458
+ apcoord.GCRS(
459
+ apcoord.CartesianRepresentation(0, 0, 1, unit=apu.m),
460
+ obstime=obstime,
461
+ )
462
+ .transform_to(apcoord.ITRS(obstime=obstime))
463
+ .data.xyz.value,
464
+ ]
465
+ )
466
+ x_sc = x_sc @ eci_2_ecf
467
+ y_sc = y_sc @ eci_2_ecf
468
+ z_sc = z_sc @ eci_2_ecf
469
+ return np.array([x_sc, y_sc, z_sc])
470
+
471
+ rel_att_times = np.array(
472
+ [(att_utc - collection_start_time).total_seconds() for att_utc in attitude_utcs]
473
+ )
474
+ good_indices = np.where(
475
+ np.logical_and(
476
+ np.less(-60, rel_att_times),
477
+ np.less(rel_att_times, 60 + collection_duration),
478
+ )
479
+ )
480
+ good_times = rel_att_times[good_indices]
481
+ good_att_quat = attitude_quaternion[good_indices]
482
+ nadir_planes = [get_nadir_plane_at_time(time) for time in good_times]
483
+ body_frame = np.array(
484
+ [
485
+ scipy.spatial.transform.Rotation.from_quat(att_quat)
486
+ .inv()
487
+ .apply(nadir_plane.T)
488
+ .T
489
+ for att_quat, nadir_plane in zip(good_att_quat, nadir_planes)
490
+ ]
491
+ )
492
+
493
+ ux_rot = body_frame[:, 0, :]
494
+ uy_rot = body_frame[:, 1, :]
495
+
496
+ ant_x_dir_poly = utils.fit_state_vectors(
497
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
498
+ good_times,
499
+ ux_rot,
500
+ None,
501
+ None,
502
+ order=4,
503
+ )
504
+ ant_y_dir_poly = utils.fit_state_vectors(
505
+ (0, (collection_stop_time - collection_start_time).total_seconds()),
506
+ good_times,
507
+ uy_rot,
508
+ None,
509
+ None,
510
+ order=4,
446
511
  )
447
512
 
513
+ freq_zero = h5_attrs["Radar Frequency"]
514
+ antenna_beam_elevation = h5_attrs[img_str]["Antenna Beam Elevation"]
515
+ fit_order = 4
516
+
517
+ def fit_steering(code_change_lines, dcs):
518
+ if len(code_change_lines) > 1:
519
+ times = code_change_lines / prf
520
+ return npp.polyfit(times, dcs, fit_order)
521
+ else:
522
+ return np.array(dcs).reshape((1,))
523
+
524
+ if radar_mode_type != "STRIPMAP":
525
+ azimuth_ramp_code_change_lines = h5_attrs[img_str][burst_str][
526
+ "Azimuth Ramp Code Change Lines"
527
+ ]
528
+ azimuth_steering = h5_attrs[img_str][burst_str]["Azimuth Steering"]
529
+ elevation_ramp_code_change_lines = h5_attrs[img_str][burst_str][
530
+ "Elevation Ramp Code Change Lines"
531
+ ]
532
+ elevation_steering = h5_attrs[img_str][burst_str]["Elevation Steering"]
533
+ eb_dcx = np.sin(np.deg2rad(azimuth_steering))
534
+ eb_dcx_poly = fit_steering(azimuth_ramp_code_change_lines, eb_dcx)
535
+ eb_dcy = -np.sin(np.deg2rad(antenna_beam_elevation + elevation_steering))
536
+ eb_dcy_poly = fit_steering(elevation_ramp_code_change_lines, eb_dcy)
537
+ else:
538
+ eb_dcx_poly = [0.0]
539
+ eb_dcy_poly = [-np.sin(np.deg2rad(antenna_beam_elevation))]
540
+
541
+ antenna_az_gains = h5_attrs[img_str]["Azimuth Antenna Pattern Gains"]
542
+ antenna_az_origin = h5_attrs[img_str]["Azimuth Antenna Pattern Origin"]
543
+ antenna_az_spacing = h5_attrs[img_str]["Azimuth Antenna Pattern Resolution"]
544
+
545
+ antenna_rg_gains = h5_attrs[img_str]["Range Antenna Pattern Gains"]
546
+ antenna_rg_origin = h5_attrs[img_str]["Range Antenna Pattern Origin"]
547
+ antenna_rg_spacing = h5_attrs[img_str]["Range Antenna Pattern Resolution"]
548
+
549
+ def fit_gains(origin, spacing, gains):
550
+ fit_limit = -9
551
+ array_mask = gains > fit_limit
552
+ dcs = np.sin(np.deg2rad(origin + spacing * np.arange(len(gains))))
553
+ return npp.polyfit(dcs[array_mask], gains[array_mask], fit_order)
554
+
555
+ antenna_array_gain = np.zeros((fit_order + 1, fit_order + 1), dtype=float)
556
+ antenna_array_gain[0, :] = fit_gains(
557
+ antenna_rg_origin, antenna_rg_spacing, antenna_rg_gains
558
+ )
559
+ antenna_array_gain[:, 0] = fit_gains(
560
+ antenna_az_origin, antenna_az_spacing, antenna_az_gains
561
+ )
562
+ antenna_array_gain[0, 0] = 0.0
563
+
564
+ # Build XML
565
+ sicd_xml_obj = lxml.etree.Element(
566
+ f"{{{NSMAP['sicd']}}}SICD", nsmap={None: NSMAP["sicd"]}
567
+ )
568
+ sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
569
+
570
+ sicd_ew["CollectionInfo"] = {
571
+ "CollectorName": collector_name,
572
+ "CoreName": core_name,
573
+ "CollectType": "MONOSTATIC",
574
+ "RadarMode": {
575
+ "ModeType": radar_mode_type,
576
+ "ModeID": radar_mode_id,
577
+ },
578
+ "Classification": classification,
579
+ }
580
+ sicd_ew["ImageCreation"] = {
581
+ "Application": creation_application,
582
+ "DateTime": creation_time,
583
+ }
584
+ sicd_ew["ImageData"] = {
585
+ "PixelType": pixel_type,
586
+ "NumRows": num_rows,
587
+ "NumCols": num_cols,
588
+ "FirstRow": first_row,
589
+ "FirstCol": first_col,
590
+ "FullImage": {
591
+ "NumRows": num_rows,
592
+ "NumCols": num_cols,
593
+ },
594
+ "SCPPixel": scp_pixel,
595
+ }
596
+
597
+ sicd_ew["GeoData"] = {
598
+ "EarthModel": "WGS_84",
599
+ "SCP": {
600
+ "ECF": scp_ecf,
601
+ "LLH": scp_llh,
602
+ },
603
+ }
604
+
448
605
  dc_sgn = np.sign(-doppler_rate_poly[0, 0])
449
606
  col_deltakcoa_poly = (
450
607
  -look * dc_sgn * doppler_centroid_poly * intervals[1] / spacings[1]
@@ -468,168 +625,180 @@ def hdf5_to_sicd(
468
625
  col_window_name = h5_attrs["Azimuth Focusing Weighting Function"]
469
626
  col_window_coeff = h5_attrs["Azimuth Focusing Weighting Coefficient"]
470
627
 
471
- grid = sicd.Grid(
472
- sicd.ImagePlane("SLANT"),
473
- sicd.Type("RGZERO"),
474
- sicd.TimeCOAPoly(),
475
- sicd.Row(
476
- sicd.UVectECF(*make_xyz(u_row)),
477
- sicd.SS(str(spacings[0])),
478
- sicd.ImpRespWid(str(row_wid)),
479
- sicd.Sgn("-1"),
480
- sicd.ImpRespBW(str(row_bw)),
481
- sicd.KCtr(str(center_frequency / (scipy.constants.speed_of_light / 2))),
482
- sicd.DeltaK1(str(-row_bw / 2)),
483
- sicd.DeltaK2(str(row_bw / 2)),
484
- sicd.DeltaKCOAPoly(),
485
- sicd.WgtType(
486
- sicd.WindowName(row_window_name),
487
- sicd.Parameter({"name": "COEFFICIENT"}, str(row_window_coeff)),
488
- ),
489
- ),
490
- sicd.Col(
491
- sicd.UVectECF(*make_xyz(u_col)),
492
- sicd.SS(str(spacings[1])),
493
- sicd.ImpRespWid(str(col_wid)),
494
- sicd.Sgn("-1"),
495
- sicd.ImpRespBW(str(col_bw)),
496
- sicd.KCtr("0"),
497
- sicd.DeltaK1(str(dk1)),
498
- sicd.DeltaK2(str(dk2)),
499
- sicd.DeltaKCOAPoly(),
500
- sicd.WgtType(
501
- sicd.WindowName(col_window_name),
502
- sicd.Parameter({"name": "COEFFICIENT"}, str(col_window_coeff)),
503
- ),
504
- ),
505
- )
506
- sksicd.Poly2dType().set_elem(grid.find("./{*}TimeCOAPoly"), time_coa_poly)
507
- sksicd.Poly2dType().set_elem(grid.find("./{*}Row/{*}DeltaKCOAPoly"), [[0]])
508
- sksicd.Poly2dType().set_elem(
509
- grid.find("./{*}Col/{*}DeltaKCOAPoly"), col_deltakcoa_poly
510
- )
628
+ sicd_ew["Grid"] = {
629
+ "ImagePlane": "SLANT",
630
+ "Type": "RGZERO",
631
+ "TimeCOAPoly": time_coa_poly,
632
+ "Row": {
633
+ "UVectECF": u_row,
634
+ "SS": spacings[0],
635
+ "ImpRespWid": row_wid,
636
+ "Sgn": -1,
637
+ "ImpRespBW": row_bw,
638
+ "KCtr": center_frequency / (scipy.constants.speed_of_light / 2),
639
+ "DeltaK1": -row_bw / 2,
640
+ "DeltaK2": row_bw / 2,
641
+ "DeltaKCOAPoly": [[0.0]],
642
+ "WgtType": {
643
+ "WindowName": row_window_name,
644
+ "Parameter": [("COEFFICIENT", str(row_window_coeff))],
645
+ },
646
+ },
647
+ "Col": {
648
+ "UVectECF": u_col,
649
+ "SS": spacings[1],
650
+ "ImpRespWid": col_wid,
651
+ "Sgn": -1,
652
+ "ImpRespBW": col_bw,
653
+ "KCtr": 0.0,
654
+ "DeltaK1": dk1,
655
+ "DeltaK2": dk2,
656
+ "DeltaKCOAPoly": col_deltakcoa_poly,
657
+ "WgtType": {
658
+ "WindowName": col_window_name,
659
+ "Parameter": [("COEFFICIENT", str(col_window_coeff))],
660
+ },
661
+ },
662
+ }
511
663
  rcs_row_sf = None
512
664
  rcs_col_sf = None
513
665
  if row_window_name == "HAMMING":
514
666
  wgts = scipy.signal.windows.general_hamming(512, row_window_coeff, sym=True)
515
- wgtfunc = sicd.WgtFunct()
516
- sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, wgts)
517
- grid.find("./{*}Row").append(wgtfunc)
667
+ sicd_ew["Grid"]["Row"]["WgtFunct"] = wgts
518
668
  row_broadening_factor = utils.broadening_from_amp(wgts)
519
669
  row_wid = row_broadening_factor / row_bw
520
- sksicd.DblType().set_elem(grid.find("./{*}Row/{*}ImpRespWid"), row_wid)
670
+ sicd_ew["Grid"]["Row"]["ImpRespWid"] = row_wid
521
671
  rcs_row_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
522
672
  if col_window_name == "HAMMING":
523
673
  wgts = scipy.signal.windows.general_hamming(512, col_window_coeff, sym=True)
524
- wgtfunc = sicd.WgtFunct()
525
- sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, wgts)
526
- grid.find("./{*}Col").append(wgtfunc)
674
+ sicd_ew["Grid"]["Col"]["WgtFunct"] = wgts
527
675
  col_broadening_factor = utils.broadening_from_amp(wgts)
528
676
  col_wid = col_broadening_factor / col_bw
529
- sksicd.DblType().set_elem(grid.find("./{*}Col/{*}ImpRespWid"), col_wid)
677
+ sicd_ew["Grid"]["Col"]["ImpRespWid"] = col_wid
530
678
  rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
531
679
 
532
- timeline = sicd.Timeline(
533
- sicd.CollectStart(collection_start_time.isoformat() + "Z"),
534
- sicd.CollectDuration(str(collection_duration)),
535
- sicd.IPP(
536
- {"size": "1"},
537
- sicd.Set(
538
- {"index": "1"},
539
- sicd.TStart(str(0)),
540
- sicd.TEnd(str(num_pulses / prf)),
541
- sicd.IPPStart(str(0)),
542
- sicd.IPPEnd(str(num_pulses - 1)),
543
- sicd.IPPPoly(),
544
- ),
545
- ),
546
- )
547
- sksicd.PolyType().set_elem(timeline.find("./{*}IPP/{*}Set/{*}IPPPoly"), [0, prf])
680
+ sicd_ew["Timeline"] = {
681
+ "CollectStart": collection_start_time,
682
+ "CollectDuration": collection_duration,
683
+ "IPP": {
684
+ "@size": 1,
685
+ "Set": [
686
+ {
687
+ "@index": 1,
688
+ "TStart": 0,
689
+ "TEnd": num_pulses / prf,
690
+ "IPPStart": 0,
691
+ "IPPEnd": num_pulses - 1,
692
+ "IPPPoly": [0, prf],
693
+ }
694
+ ],
695
+ },
696
+ }
548
697
 
549
- position = sicd.Position(sicd.ARPPoly())
550
- sksicd.XyzPolyType().set_elem(position.find("./{*}ARPPoly"), apc_poly)
698
+ sicd_ew["Position"]["ARPPoly"] = apc_poly
551
699
 
552
- rcv_channels = sicd.RcvChannels(
553
- {"size": str(len(tx_rcv_pols))},
554
- )
700
+ chan_parameters = []
555
701
  for ndx, tx_rcv_pol in enumerate(tx_rcv_pols):
556
- rcv_channels.append(
557
- sicd.ChanParameters(
558
- {"index": str(ndx + 1)}, sicd.TxRcvPolarization(tx_rcv_pol)
559
- )
702
+ chan_parameters.append(
703
+ {
704
+ "@index": ndx + 1,
705
+ "TxRcvPolarization": tx_rcv_pol,
706
+ }
560
707
  )
561
708
 
562
- radar_collection = sicd.RadarCollection(
563
- sicd.TxFrequency(sicd.Min(str(tx_freq_min)), sicd.Max(str(tx_freq_max))),
564
- sicd.Waveform(
565
- {"size": "1"},
566
- sicd.WFParameters(
567
- {"index": "1"},
568
- sicd.TxPulseLength(str(tx_pulse_length)),
569
- sicd.TxRFBandwidth(str(tx_rf_bw)),
570
- sicd.TxFreqStart(str(tx_freq_start)),
571
- sicd.TxFMRate(str(tx_fm_rate)),
572
- sicd.RcvWindowLength(str(rcv_window_length)),
573
- sicd.ADCSampleRate(str(adc_sample_rate)),
574
- ),
575
- ),
576
- sicd.TxPolarization(tx_polarization),
577
- rcv_channels,
578
- )
709
+ sicd_ew["RadarCollection"] = {
710
+ "TxFrequency": {
711
+ "Min": tx_freq_min,
712
+ "Max": tx_freq_max,
713
+ },
714
+ "Waveform": {
715
+ "@size": 1,
716
+ "WFParameters": [
717
+ {
718
+ "@index": 1,
719
+ "TxPulseLength": tx_pulse_length,
720
+ "TxRFBandwidth": tx_rf_bw,
721
+ "TxFreqStart": tx_freq_start,
722
+ "TxFMRate": tx_fm_rate,
723
+ "RcvWindowLength": rcv_window_length,
724
+ "ADCSampleRate": adc_sample_rate,
725
+ }
726
+ ],
727
+ },
728
+ "TxPolarization": tx_polarization,
729
+ "RcvChannels": {
730
+ "@size": len(tx_rcv_pols),
731
+ "ChanParameters": chan_parameters,
732
+ },
733
+ }
579
734
  if len(tx_polarizations) > 1:
580
- radar_collection.find("./{*}TxPolarization").text = "SEQUENCE"
581
- tx_sequence = sicd.TxSequence({"size": str(len(tx_polarizations))})
735
+ sicd_ew["RadarCollection"]["TxPolarization"] = "SEQUENCE"
736
+ tx_steps = []
582
737
  for ndx, tx_pol in enumerate(tx_polarizations):
583
- tx_sequence.append(
584
- sicd.TxStep({"index": str(ndx + 1)}, sicd.TxPolarization(tx_pol))
738
+ tx_steps.append(
739
+ {
740
+ "@index": ndx + 1,
741
+ "TxPolarization": tx_pol,
742
+ }
585
743
  )
586
- rcv_channels.addprevious(tx_sequence)
587
-
588
- image_formation = sicd.ImageFormation(
589
- sicd.RcvChanProc(sicd.NumChanProc("1"), sicd.ChanIndex(str(chan_index))),
590
- sicd.TxRcvPolarizationProc(tx_rcv_polarization),
591
- sicd.TStartProc(str(0)),
592
- sicd.TEndProc(str(collection_duration)),
593
- sicd.TxFrequencyProc(
594
- sicd.MinProc(str(tx_freq_min)), sicd.MaxProc(str(tx_freq_max))
595
- ),
596
- sicd.ImageFormAlgo("RMA"),
597
- sicd.STBeamComp("NO"),
598
- sicd.ImageBeamComp("SV"),
599
- sicd.AzAutofocus("NO"),
600
- sicd.RgAutofocus("NO"),
601
- )
602
-
603
- rma = sicd.RMA(
604
- sicd.RMAlgoType("OMEGA_K"),
605
- sicd.ImageType("INCA"),
606
- sicd.INCA(
607
- sicd.TimeCAPoly(),
608
- sicd.R_CA_SCP(str(scp_rca)),
609
- sicd.FreqZero(str(center_frequency)),
610
- sicd.DRateSFPoly(),
611
- sicd.DopCentroidPoly(),
612
- ),
613
- )
614
- sksicd.PolyType().set_elem(rma.find("./{*}INCA/{*}TimeCAPoly"), time_ca_poly)
615
- sksicd.Poly2dType().set_elem(rma.find("./{*}INCA/{*}DRateSFPoly"), drsf_poly)
616
- sksicd.Poly2dType().set_elem(
617
- rma.find("./{*}INCA/{*}DopCentroidPoly"), doppler_centroid_poly
618
- )
619
- sicd_xml_obj = sicd.SICD(
620
- collection_info,
621
- image_creation,
622
- image_data,
623
- geo_data,
624
- grid,
625
- timeline,
626
- position,
627
- radar_collection,
628
- image_formation,
629
- rma,
630
- )
744
+ sicd_ew["RadarCollection"]["TxSequence"] = {
745
+ "@size": len(tx_polarizations),
746
+ "TxStep": tx_steps,
747
+ }
748
+
749
+ now = (
750
+ datetime.datetime.now(datetime.timezone.utc)
751
+ .isoformat(timespec="microseconds")
752
+ .replace("+00:00", "Z")
753
+ )
754
+ sicd_ew["ImageFormation"] = {
755
+ "RcvChanProc": {
756
+ "NumChanProc": 1,
757
+ "ChanIndex": [chan_index],
758
+ },
759
+ "TxRcvPolarizationProc": tx_rcv_polarization,
760
+ "TStartProc": 0,
761
+ "TEndProc": collection_duration,
762
+ "TxFrequencyProc": {
763
+ "MinProc": tx_freq_min,
764
+ "MaxProc": tx_freq_max,
765
+ },
766
+ "ImageFormAlgo": "RMA",
767
+ "STBeamComp": "NO",
768
+ "ImageBeamComp": "SV",
769
+ "AzAutofocus": "NO",
770
+ "RgAutofocus": "NO",
771
+ "Processing": [
772
+ {
773
+ "Type": f"sarkit-convert {__version__} @ {now}",
774
+ "Applied": True,
775
+ },
776
+ ],
777
+ }
778
+
779
+ sicd_ew["Antenna"]["TwoWay"]["XAxisPoly"] = ant_x_dir_poly
780
+ sicd_ew["Antenna"]["TwoWay"]["YAxisPoly"] = ant_y_dir_poly
781
+ sicd_ew["Antenna"]["TwoWay"]["FreqZero"] = freq_zero
782
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCXPoly"] = eb_dcx_poly
783
+ sicd_ew["Antenna"]["TwoWay"]["EB"]["DCYPoly"] = eb_dcy_poly
784
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["GainPoly"] = antenna_array_gain
785
+ sicd_ew["Antenna"]["TwoWay"]["Array"]["PhasePoly"] = np.zeros(
786
+ dtype=float, shape=(1, 1)
787
+ )
788
+
789
+ sicd_ew["RMA"] = {
790
+ "RMAlgoType": "OMEGA_K",
791
+ "ImageType": "INCA",
792
+ "INCA": {
793
+ "TimeCAPoly": time_ca_poly,
794
+ "R_CA_SCP": scp_rca,
795
+ "FreqZero": center_frequency,
796
+ "DRateSFPoly": drsf_poly,
797
+ "DopCentroidPoly": doppler_centroid_poly,
798
+ },
799
+ }
631
800
 
632
- image_formation.addnext(sksicd.compute_scp_coa(sicd_xml_obj.getroottree()))
801
+ sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
633
802
 
634
803
  # Add Radiometric
635
804
  if mission_id == "CSK":
@@ -648,27 +817,16 @@ def hdf5_to_sicd(
648
817
  sigmazero_poly = betazero_poly * np.cos(graze) * np.cos(twist)
649
818
  gammazero_poly = betazero_poly / np.tan(graze) * np.cos(twist)
650
819
 
651
- radiometric = sicd.Radiometric(
652
- sicd.SigmaZeroSFPoly(), sicd.BetaZeroSFPoly(), sicd.GammaZeroSFPoly()
653
- )
654
- sksicd.Poly2dType().set_elem(
655
- radiometric.find("./{*}SigmaZeroSFPoly"), sigmazero_poly
656
- )
657
- sksicd.Poly2dType().set_elem(
658
- radiometric.find("./{*}BetaZeroSFPoly"), betazero_poly
659
- )
660
- sksicd.Poly2dType().set_elem(
661
- radiometric.find("./{*}GammaZeroSFPoly"), gammazero_poly
662
- )
820
+ sicd_ew["Radiometric"] = {
821
+ "SigmaZeroSFPoly": sigmazero_poly,
822
+ "BetaZeroSFPoly": betazero_poly,
823
+ "GammaZeroSFPoly": gammazero_poly,
824
+ }
663
825
  if rcs_row_sf and rcs_col_sf:
664
826
  rcssf_poly = betazero_poly * (
665
827
  rcs_row_sf * rcs_col_sf / (row_bw * col_bw)
666
828
  )
667
- radiometric.find("./{*}SigmaZeroSFPoly").addprevious(sicd.RCSSFPoly())
668
- sksicd.Poly2dType().set_elem(
669
- radiometric.find("./{*}RCSSFPoly"), rcssf_poly
670
- )
671
- sicd_xml_obj.find("./{*}RMA").addprevious(radiometric)
829
+ sicd_ew["Radiometric"]["RCSSFPoly"] = rcssf_poly
672
830
 
673
831
  # Add Geodata Corners
674
832
  sicd_xmltree = sicd_xml_obj.getroottree()
@@ -685,8 +843,7 @@ def hdf5_to_sicd(
685
843
  sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
686
844
  )
687
845
  icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
688
- xml_helper = sksicd.XmlHelper(sicd_xmltree)
689
- xml_helper.set("./{*}GeoData/{*}ImageCorners", icp_llh[:, :2])
846
+ sicd_ew["GeoData"]["ImageCorners"] = icp_llh[:, :2]
690
847
 
691
848
  # Validate XML
692
849
  sicd_con = sarkit.verification.SicdConsistency(sicd_xmltree)
@@ -708,7 +865,7 @@ def hdf5_to_sicd(
708
865
  metadata = sksicd.NitfMetadata(
709
866
  xmltree=sicd_xmltree,
710
867
  file_header_part={
711
- "ostaid": ostaid,
868
+ "ostaid": h5_attrs["Processing Centre"],
712
869
  "ftitle": core_name,
713
870
  "security": {
714
871
  "clas": classification[0].upper(),
@@ -756,11 +913,6 @@ def main(args=None):
756
913
  type=pathlib.Path,
757
914
  help='path of the output SICD file. The string "{pol}" will be replaced with polarization for multiple images',
758
915
  )
759
- parser.add_argument(
760
- "--ostaid",
761
- help="content of the originating station ID (OSTAID) field of the NITF header",
762
- default="Unknown",
763
- )
764
916
  config = parser.parse_args(args)
765
917
 
766
918
  tx_polarizations = []
@@ -810,7 +962,6 @@ def main(args=None):
810
962
  h5_filename=config.input_h5_file,
811
963
  sicd_filename=img_info["filename"],
812
964
  classification=config.classification,
813
- ostaid=config.ostaid,
814
965
  img_str=img_str,
815
966
  chan_index=img_info["chan_index"],
816
967
  tx_polarizations=tx_polarizations,