1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
//
// GENERATED FILE
//
use super::*;
use crate::SpiceContext;
use f2rust_std::*;
pub const BUFSIZ: i32 = 200;
const WDSIZE: i32 = 32;
const NITEMS: i32 = 14;
const NVARS: i32 = 14;
const NDPS: i32 = 9;
const DBUFSZ: i32 = (NDPS * BUFSIZ);
const NINTS: i32 = 1;
const IBUFSZ: i32 = (NINTS * BUFSIZ);
const LBPOOL: i32 = -5;
struct SaveVars {
AGENT: Vec<u8>,
ALT: ActualCharArray,
ALTNAT: Vec<u8>,
FRNAME: Vec<u8>,
IDSTR: Vec<u8>,
ITEM: ActualCharArray,
OLDAGT: Vec<u8>,
NAME: Vec<u8>,
SPEC: Vec<u8>,
TYPE: Vec<u8>,
UNITS: Vec<u8>,
ANGLES: StackArray<f64, 3>,
BUFFD: ActualArray2D<f64>,
MATRIX: StackArray2D<f64, 9>,
QUATRN: StackArray<f64, 4>,
QTMP: StackArray<f64, 4>,
TEMPD: f64,
AR: i32,
AT: i32,
AXES: StackArray<i32, 3>,
BUFFI: StackArray2D<i32, 200>,
IDENTS: StackArray2D<i32, 200>,
IDNT: StackArray<i32, 1>,
N: i32,
OLDID: i32,
POOL: ActualArray2D<i32>,
R: i32,
TAIL: i32,
BUFFRD: bool,
FIRST: bool,
FOUND1: bool,
FOUND2: bool,
FND: bool,
FULL: bool,
UPDATE: bool,
}
impl SaveInit for SaveVars {
fn new() -> Self {
let mut AGENT = vec![b' '; WDSIZE as usize];
let mut ALT = ActualCharArray::new(WDSIZE, 1..=NITEMS);
let mut ALTNAT = vec![b' '; WDSIZE as usize];
let mut FRNAME = vec![b' '; WDSIZE as usize];
let mut IDSTR = vec![b' '; WDSIZE as usize];
let mut ITEM = ActualCharArray::new(WDSIZE, 1..=NVARS);
let mut OLDAGT = vec![b' '; WDSIZE as usize];
let mut NAME = vec![b' '; WDSIZE as usize];
let mut SPEC = vec![b' '; WDSIZE as usize];
let mut TYPE = vec![b' '; 1 as usize];
let mut UNITS = vec![b' '; WDSIZE as usize];
let mut ANGLES = StackArray::<f64, 3>::new(1..=3);
let mut BUFFD = ActualArray2D::<f64>::new(1..=NDPS, 1..=BUFSIZ);
let mut MATRIX = StackArray2D::<f64, 9>::new(1..=3, 1..=3);
let mut QUATRN = StackArray::<f64, 4>::new(0..=3);
let mut QTMP = StackArray::<f64, 4>::new(0..=3);
let mut TEMPD: f64 = 0.0;
let mut AR: i32 = 0;
let mut AT: i32 = 0;
let mut AXES = StackArray::<i32, 3>::new(1..=3);
let mut BUFFI = StackArray2D::<i32, 200>::new(1..=NINTS, 1..=BUFSIZ);
let mut IDENTS = StackArray2D::<i32, 200>::new(1..=1, 1..=BUFSIZ);
let mut IDNT = StackArray::<i32, 1>::new(1..=1);
let mut N: i32 = 0;
let mut OLDID: i32 = 0;
let mut POOL = ActualArray2D::<i32>::new(1..=2, LBPOOL..=BUFSIZ);
let mut R: i32 = 0;
let mut TAIL: i32 = 0;
let mut BUFFRD: bool = false;
let mut FIRST: bool = false;
let mut FOUND1: bool = false;
let mut FOUND2: bool = false;
let mut FND: bool = false;
let mut FULL: bool = false;
let mut UPDATE: bool = false;
AT = 0;
{
use f2rust_std::data::Val;
let mut clist = []
.into_iter()
.chain(std::iter::repeat_n(Val::D(0.0), DBUFSZ as usize))
.chain([]);
BUFFD
.iter_mut()
.for_each(|n| *n = clist.next().unwrap().into_f64());
debug_assert!(clist.next().is_none(), "DATA not fully initialised");
}
{
use f2rust_std::data::Val;
let mut clist = []
.into_iter()
.chain(std::iter::repeat_n(Val::I(0), IBUFSZ as usize))
.chain([]);
BUFFI
.iter_mut()
.for_each(|n| *n = clist.next().unwrap().into_i32());
debug_assert!(clist.next().is_none(), "DATA not fully initialised");
}
FIRST = true;
{
use f2rust_std::data::Val;
let mut clist = []
.into_iter()
.chain(std::iter::repeat_n(Val::I(0), BUFSIZ as usize))
.chain([]);
IDENTS
.iter_mut()
.for_each(|n| *n = clist.next().unwrap().into_i32());
debug_assert!(clist.next().is_none(), "DATA not fully initialised");
}
Self {
AGENT,
ALT,
ALTNAT,
FRNAME,
IDSTR,
ITEM,
OLDAGT,
NAME,
SPEC,
TYPE,
UNITS,
ANGLES,
BUFFD,
MATRIX,
QUATRN,
QTMP,
TEMPD,
AR,
AT,
AXES,
BUFFI,
IDENTS,
IDNT,
N,
OLDID,
POOL,
R,
TAIL,
BUFFRD,
FIRST,
FOUND1,
FOUND2,
FND,
FULL,
UPDATE,
}
}
}
/// TK frame, find position rotation
///
/// Find the position rotation matrix from a Text Kernel (TK) frame
/// with the specified frame class ID to its base frame.
///
/// # Required Reading
///
/// * [FRAMES](crate::required_reading::frames)
///
/// # Brief I/O
///
/// ```text
/// VARIABLE I/O DESCRIPTION
/// -------- --- ----------------------------------------------
/// FRCODE I Frame class ID of a TK frame.
/// ROT O Rotation matrix from TK frame to frame FRAME.
/// FRAME O Frame ID of the base reference.
/// FOUND O .TRUE. if the rotation could be determined.
/// ```
///
/// # Detailed Input
///
/// ```text
/// FRCODE is the unique frame class ID of the TK frame for which
/// data is being requested. For TK frames the frame class
/// ID is always equal to the frame ID.
/// ```
///
/// # Detailed Output
///
/// ```text
/// ROT is a position rotation matrix that converts positions
/// relative to the TK frame given by its frame class ID,
/// FRCODE, to positions relative to the base frame given by
/// its frame ID, FRAME.
///
/// Thus, if a position S has components x,y,z in the TK
/// frame, then S has components x', y', z' in the base
/// frame.
///
/// .- -. .- -. .- -.
/// | x' | | | | x |
/// | y' | = | ROT | | y |
/// | z' | | | | z |
/// `- -' `- -' `- -'
///
///
/// FRAME is the ID code of the base reference frame to which ROT
/// will transform positions.
///
/// FOUND is a logical indicating whether or not a frame definition
/// for the TK frame with the frame class ID, FRCODE, was
/// constructed from kernel pool data. If ROT and FRAME were
/// constructed, FOUND will be returned with the value .TRUE.
/// Otherwise it will be returned with the value .FALSE.
/// ```
///
/// # Parameters
///
/// ```text
/// BUFSIZ is the number of rotation, frame class ID pairs that can
/// have their instance data buffered for the sake of
/// improving run-time performance. This value MUST be
/// positive and should probably be at least 10.
/// ```
///
/// # Exceptions
///
/// ```text
/// 1) If some kernel variable associated with this frame is not
/// present in the kernel pool, or does not have the proper type
/// or dimension, an error is signaled by a routine in the call
/// tree of this routine. In such a case FOUND will be set to
/// .FALSE.
///
/// 2) If the input FRCODE has the value 0, the error
/// SPICE(ZEROFRAMEID) is signaled. FOUND will be set to .FALSE.
///
/// 3) If the name of the frame corresponding to FRCODE cannot be
/// determined, the error SPICE(INCOMPLETEFRAME) is signaled.
///
/// 4) If the frame given by FRCODE is defined relative to a frame
/// that is unrecognized, the error SPICE(BADFRAMESPEC) is
/// signaled. FOUND will be set to .FALSE.
///
/// 5) If the kernel pool specification for the frame given by
/// FRCODE is not one of 'MATRIX', 'ANGLES' or 'QUATERNION',
/// the error SPICE(UNKNOWNFRAMESPEC) is signaled. FOUND will be
/// set to .FALSE.
///
/// 6) If the frame FRCODE is equal to the relative frame ID (i.e.
/// the frame is defined relative to itself), the error
/// SPICE(BADFRAMESPEC2) is signaled. FOUND will be set to .FALSE.
///
/// 7) If name-based and ID-based forms of any TKFRAME_ keyword
/// are detected in the kernel pool at the same time, the error
/// SPICE(COMPETINGFRAMESPEC) is signaled. FOUND will be set to
/// .FALSE.
/// ```
///
/// # Files
///
/// ```text
/// This routine makes use of the loaded text kernels to determine
/// the rotation from a constant offset TK frame to its base frame.
/// ```
///
/// # Particulars
///
/// ```text
/// This routine is used to construct the rotation from some frame
/// that is a constant rotation offset from some other reference
/// frame. This rotation is derived from data stored in the kernel
/// pool.
///
/// This routine is intended to be used as a low level routine by the
/// frame system software. However, you could use this routine to
/// directly retrieve the rotation from an fixed offset TK frame to
/// its base frame.
/// ```
///
/// # Examples
///
/// ```text
/// The numerical results shown for this example may differ across
/// platforms. The results depend on the SPICE kernels used as
/// input, the compiler and supporting libraries, and the machine
/// specific arithmetic implementation.
///
/// 1) Compute the rotation from the DSS-34 topocentric frame to
/// its base Earth body-fixed frame and use it to determine the
/// geodetic latitude and longitude of the DSS-34 site.
///
///
/// Use the FK kernel below to load the required topocentric
/// reference frame definition for the DSS-34 site.
///
/// earth_topo_050714.tf
///
///
/// Example code begins here.
///
///
/// PROGRAM TKFRAM_EX1
/// IMPLICIT NONE
///
/// C
/// C SPICELIB functions.
/// C
/// DOUBLE PRECISION DPR
///
/// C
/// C Local parameters
/// C
/// CHARACTER*(*) MYTOPO
/// PARAMETER ( MYTOPO = 'DSS-34_TOPO' )
///
/// INTEGER MXFRLN
/// PARAMETER ( MXFRLN = 26 )
///
/// C
/// C Local variables
/// C
/// CHARACTER*(MXFRLN) FRNAME
///
/// DOUBLE PRECISION LAT
/// DOUBLE PRECISION LON
/// DOUBLE PRECISION RAD
/// DOUBLE PRECISION ROT ( 3, 3 )
/// DOUBLE PRECISION Z ( 3 )
///
/// INTEGER FRAME
/// INTEGER FRCODE
///
/// LOGICAL FOUND
///
/// C
/// C Load the FK that contains the topocentric reference
/// C frame definition for DSS-34.
/// C
/// CALL FURNSH ( 'earth_topo_050714.tf' )
///
/// C
/// C The name of the topocentric frame is MYTOPO.
/// C First we get the ID code of the topocentric frame.
/// C
/// CALL NAMFRM ( MYTOPO, FRCODE )
///
/// C
/// C Next get the rotation from the topocentric frame to
/// C the body-fixed frame. We can use the TK frame ID in
/// C place of the TK frame class ID in this call because
/// C for TK frames these IDs are identical.
/// C
/// CALL TKFRAM ( FRCODE, ROT, FRAME, FOUND )
///
/// C
/// C Make sure the topocentric frame is relative to one of
/// C the Earth fixed frames.
/// C
/// CALL FRMNAM( FRAME, FRNAME )
///
/// IF ( FRNAME .NE. 'IAU_EARTH'
/// . .AND. FRNAME .NE. 'EARTH_FIXED'
/// . .AND. FRNAME .NE. 'ITRF93' ) THEN
///
/// WRITE (*,*) 'The frame ', MYTOPO,
/// . ' does not appear to be '
/// WRITE (*,*) 'defined relative to an '
/// . // 'Earth fixed frame.'
/// STOP
///
/// END IF
///
/// C
/// C Things look ok. Get the location of the Z-axis in the
/// C topocentric frame.
/// C
/// Z(1) = ROT(1,3)
/// Z(2) = ROT(2,3)
/// Z(3) = ROT(3,3)
///
/// C
/// C Convert the Z vector to latitude, longitude and radius.
/// C
/// CALL RECLAT ( Z, RAD, LAT, LON )
///
/// WRITE (*,'(A)') 'The geodetic coordinates of the center'
/// WRITE (*,'(A)') 'of the topographic frame are:'
/// WRITE (*,*)
/// WRITE (*,'(A,F20.13)') ' Latitude (deg): ', LAT*DPR()
/// WRITE (*,'(A,F20.13)') ' Longitude (deg): ', LON*DPR()
///
/// END
///
///
/// When this program was executed on a Mac/Intel/gfortran/64-bit
/// platform, the output was:
///
///
/// The geodetic coordinates of the center
/// of the topographic frame are:
///
/// Latitude (deg): 148.9819650021110
/// Longitude (deg): -35.3984778756552
/// ```
///
/// # Author and Institution
///
/// ```text
/// N.J. Bachman (JPL)
/// J. Diaz del Rio (ODC Space)
/// B.V. Semenov (JPL)
/// W.L. Taber (JPL)
/// F.S. Turner (JPL)
/// ```
///
/// # Version
///
/// ```text
/// - SPICELIB Version 2.3.0, 20-AUG-2021 (JDR) (BVS) (NJB)
///
/// BUG FIX: the routine now signals an error if it detects
/// name-based and ID-based forms of any TKFRAME_ keyword present
/// in the POOL at the same time. This prevents name-based
/// keywords from frame definitions loaded with lower priority
/// from being used instead of ID-based keywords from frame
/// definitions loaded with higher priority.
///
/// BUG FIX: when failing to fetch any frame keywords from the
/// POOL or for any other reason, the routine now always returns
/// FOUND = .FALSE. Previously FOUND could be set to .TRUE. by a
/// DTPOOL call preceding the failure.
///
/// BUG FIX: when failing due to a frame defined relative to
/// itself or due to an unrecognized _SPEC, the routine now always
/// returns FRAME = 0. Previously FRAME was set to the _RELATIVE
/// keyword.
///
/// BUG FIX: the misspelled short error message
/// SPICE(INCOMPLETEFRAME) was corrected. The message had been
/// spelled correctly in header comments but not in the code.
///
/// Changed to return ROT as identity for all failures; previously
/// it was returned this way only for some failures.
///
/// Changed the input argument name ID to FRCODE for consistency
/// with other routines.
///
/// Fixed minor typo on the UNKNOWNFRAMESPEC long error message.
///
/// Edited the header to comply with NAIF standard and modern
/// SPICE CK and frames terminology.
///
/// Added complete code example based on existing fragments.
///
/// Construction of kernel variable names now uses trimmed
/// strings in order to suppress gfortran compile warnings.
///
/// Added DATA statements to initialize BUFFI, BUFFD, and IDENTS.
/// This change suppresses ftnchek warnings for variables possibly
/// not initialized before use. It is not a bug fix.
///
/// Minor inline comment typos were corrected.
///
/// - SPICELIB Version 2.2.0, 08-JAN-2014 (BVS)
///
/// Added an error check for frames defined relative to
/// themselves.
///
/// Increased BUFSIZ from 20 to 200.
///
/// - SPICELIB Version 2.1.0, 23-APR-2009 (NJB)
///
/// Bug fix: watch is deleted only for frames
/// that are deleted from the buffer.
///
/// - SPICELIB Version 2.0.0, 19-MAR-2009 (NJB)
///
/// Bug fix: this routine now deletes watches set on
/// kernel variables of frames that are discarded from
/// the local buffering system.
///
/// - SPICELIB Version 1.2.0, 09-SEP-2005 (NJB)
///
/// Updated to remove non-standard use of duplicate arguments
/// in CONVRT, UCRSS, VHATG and VSCL calls.
///
/// - SPICELIB Version 1.1.0, 21-NOV-2001 (FST)
///
/// Updated this routine to dump the buffer of frame ID codes
/// it saves when it or one of the modules in its call tree
/// signals an error. This fixes a bug where a frame's ID code is
/// buffered, but the matrix and kernel pool watcher were not set
/// properly.
///
/// - SPICELIB Version 1.0.0, 18-NOV-1996 (WLT)
/// ```
pub fn tkfram(
ctx: &mut SpiceContext,
frcode: i32,
rot: &mut [[f64; 3]; 3],
frame: &mut i32,
found: &mut bool,
) -> crate::Result<()> {
TKFRAM(
frcode,
rot.as_flattened_mut(),
frame,
found,
ctx.raw_context(),
)?;
ctx.handle_errors()?;
Ok(())
}
//$Procedure TKFRAM ( TK frame, find position rotation )
pub fn TKFRAM(
FRCODE: i32,
ROT: &mut [f64],
FRAME: &mut i32,
FOUND: &mut bool,
ctx: &mut Context,
) -> f2rust_std::Result<()> {
let save = ctx.get_vars::<SaveVars>();
let save = &mut *save.borrow_mut();
let mut ROT = DummyArrayMut2D::new(ROT, 1..=3, 1..=3);
//
// Spicelib Functions
//
//
// Local Parameters
//
//
// Local Variables
//
//
// Saved variables
//
//
// Initial values
//
//
// Programmer's note: this routine makes use of the *implementation*
// of LOCATI. If that routine is changed, the logic this routine
// uses to locate buffered, old frame IDs may need to change as well.
//
//
// Before we even check in, if N is less than 1 we can
// just return.
//
//
// Perform any initializations that might be needed for this
// routine.
//
if save.FIRST {
save.FIRST = false;
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
}
//
// Now do the standard SPICE error handling. Sure this is
// a bit unconventional, but nothing will be hurt by doing
// the stuff above first.
//
if RETURN(ctx) {
return Ok(());
}
CHKIN(b"TKFRAM", ctx)?;
//
// So far, we've not FOUND the rotation to the specified frame.
//
*FOUND = false;
//
// Check the ID to make sure it is non-zero.
//
if (FRCODE == 0) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
SETMSG(b"Frame identification codes are required to be non-zero. You\'ve specified a frame with ID value zero. ", ctx);
SIGERR(b"SPICE(ZEROFRAMEID)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// Find out whether our linked list pool is already full.
// We'll use this information later to decide whether we're
// going to have to delete a watcher.
//
save.FULL = (LNKNFN(save.POOL.as_slice()) == 0);
if save.FULL {
//
// If the input frame ID is not buffered, we'll need to
// overwrite an existing buffer entry. In this case
// the call to LOCATI we're about to make will overwrite
// the ID code in the slot we're about to use. We need
// this ID code, so extract it now while we have the
// opportunity. The old ID sits at the tail of the list
// whose head node is AT.
//
save.TAIL = LNKTL(save.AT, save.POOL.as_slice(), ctx)?;
save.OLDID = save.IDENTS[[1, save.TAIL]];
//
// Create the name of the agent associated with the old
// frame.
//
fstr::assign(&mut save.OLDAGT, b"TKFRAME_#");
REPMI(
&save.OLDAGT.to_vec(),
b"#",
save.OLDID,
&mut save.OLDAGT,
ctx,
);
}
//
// Look up the address of the instance data.
//
save.IDNT[1] = FRCODE;
LOCATI(
save.IDNT.as_slice(),
1,
save.IDENTS.as_slice_mut(),
save.POOL.as_slice_mut(),
&mut save.AT,
&mut save.BUFFRD,
ctx,
)?;
if (save.FULL && !save.BUFFRD) {
//
// Since the buffer is already full, we'll delete the watcher for
// the kernel variables associated with OLDID, since there's no
// longer a need for that watcher.
//
// First clear the update status of the old agent; DWPOOL won't
// delete an agent with a unchecked update.
//
CVPOOL(&save.OLDAGT, &mut save.UPDATE, ctx)?;
DWPOOL(&save.OLDAGT, ctx)?;
}
//
// Until we have better information we put the identity matrix
// into the output rotation and set FRAME to zero.
//
IDENT(ROT.as_slice_mut());
*FRAME = 0;
//
// If we have to look up the data for our frame, we do
// it now and perform any conversions and computations that
// will be needed when it's time to convert coordinates to
// directions.
//
// Construct the name of the agent associated with the
// requested frame. (Each frame has its own agent).
//
INTSTR(FRCODE, &mut save.IDSTR, ctx);
FRMNAM(FRCODE, &mut save.FRNAME, ctx)?;
if fstr::eq(&save.FRNAME, b" ") {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
SETMSG(
b"The Text Kernel (TK) frame with ID code # does not have a recognized name. ",
ctx,
);
ERRINT(b"#", FRCODE, ctx);
SIGERR(b"SPICE(INCOMPLETEFRAME)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
fstr::assign(
&mut save.AGENT,
&fstr::concat(
b"TKFRAME_",
fstr::substr(&save.IDSTR, 1..=RTRIM(&save.IDSTR)),
),
);
save.R = RTRIM(&save.AGENT);
fstr::assign(
&mut save.ALTNAT,
&fstr::concat(
b"TKFRAME_",
fstr::substr(&save.FRNAME, 1..=RTRIM(&save.FRNAME)),
),
);
save.AR = RTRIM(&save.ALTNAT);
//
// If the frame is buffered, we check the kernel pool to
// see if there has been an update to this frame.
//
if save.BUFFRD {
CVPOOL(fstr::substr(&save.AGENT, 1..=save.R), &mut save.UPDATE, ctx)?;
} else {
//
// If the frame is not buffered we definitely need to update
// things.
save.UPDATE = true;
}
if !save.UPDATE {
//
// Just look up the rotation matrix and relative-to
// information from the local buffer.
//
ROT[[1, 1]] = save.BUFFD[[1, save.AT]];
ROT[[2, 1]] = save.BUFFD[[2, save.AT]];
ROT[[3, 1]] = save.BUFFD[[3, save.AT]];
ROT[[1, 2]] = save.BUFFD[[4, save.AT]];
ROT[[2, 2]] = save.BUFFD[[5, save.AT]];
ROT[[3, 2]] = save.BUFFD[[6, save.AT]];
ROT[[1, 3]] = save.BUFFD[[7, save.AT]];
ROT[[2, 3]] = save.BUFFD[[8, save.AT]];
ROT[[3, 3]] = save.BUFFD[[9, save.AT]];
*FRAME = save.BUFFI[[1, save.AT]];
} else {
//
// Determine how the frame is specified and what it
// is relative to. The variables that specify
// how the frame is represented and what it is relative to
// are TKFRAME_#_SPEC and TKFRAME_#_RELATIVE where # is
// replaced by the text value of ID or the frame name.
//
fstr::assign(
save.ITEM.get_mut(1),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_SPEC"),
);
fstr::assign(
save.ITEM.get_mut(2),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_RELATIVE"),
);
fstr::assign(
save.ALT.get_mut(1),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_SPEC"),
);
fstr::assign(
save.ALT.get_mut(2),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_RELATIVE"),
);
//
// See if the friendlier version of the kernel pool variables
// are available.
//
// If both forms are present, we signal an error.
//
for I in 1..=2 {
DTPOOL(
&save.ITEM[I],
&mut save.FOUND1,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
DTPOOL(
&save.ALT[I],
&mut save.FOUND2,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
if (save.FOUND1 && save.FOUND2) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"Frame name-based and frame ID-based text kernel (fixed-offset) frame definition keywords \'#\' and \'#\' are both present in the POOL. Most likely this is because loaded text kernels contain competing definitions of the \'#\' frame using different keyword styles, which is not allowed. ", ctx);
ERRCH(b"#", &save.ITEM[I], ctx);
ERRCH(b"#", &save.ALT[I], ctx);
ERRCH(b"#", &save.FRNAME, ctx);
SIGERR(b"SPICE(COMPETINGFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
if save.FOUND2 {
fstr::assign(save.ITEM.get_mut(I), save.ALT.get(I));
}
}
//
// If either the SPEC or RELATIVE frame are missing from
// the kernel pool, we simply return.
//
if (BADKPV(b"TKFRAM", &save.ITEM[1], b"=", 1, 1, b"C", ctx)?
|| BADKPV(b"TKFRAM", &save.ITEM[2], b"=", 1, 1, b"C", ctx)?)
{
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// If we make it this far, look up the SPEC and RELATIVE frame.
//
GCPOOL(
&save.ITEM[1],
1,
1,
&mut save.N,
CharArrayMut::from_mut(&mut save.SPEC),
&mut save.FND,
ctx,
)?;
GCPOOL(
&save.ITEM[2],
1,
1,
&mut save.N,
CharArrayMut::from_mut(&mut save.NAME),
&mut save.FND,
ctx,
)?;
//
// Look up the ID code for this frame.
//
NAMFRM(&save.NAME, FRAME, ctx)?;
if (*FRAME == 0) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
IDENT(ROT.as_slice_mut());
SETMSG(b"The frame to which frame # is relatively defined is not recognized. The kernel pool specification of the relative frame is \'#\'. This is not a recognized frame. ", ctx);
ERRINT(b"#", FRCODE, ctx);
ERRCH(b"#", &save.NAME, ctx);
SIGERR(b"SPICE(BADFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// Make sure that the RELATIVE frame ID is distinct from the
// frame ID. If they are the same, SPICE will go into an
// indefinite loop.
//
if (*FRAME == FRCODE) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"Bad fixed offset frame specification: the frame \'#\' (frame ID #) is defined relative to itself. SPICE cannot work with such frames. ", ctx);
ERRCH(b"#", &save.NAME, ctx);
ERRINT(b"#", FRCODE, ctx);
SIGERR(b"SPICE(BADFRAMESPEC2)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// Convert SPEC to upper case so that we can easily check
// to see if this is one of the expected specification types.
//
UCASE(&save.SPEC.to_vec(), &mut save.SPEC, ctx);
if fstr::eq(&save.SPEC, b"MATRIX") {
//
// This is the easiest case. Just grab the matrix
// from the kernel pool (and polish it up a bit just
// to make sure we have a rotation matrix).
//
// We give preference to the kernel pool variable
// TKFRAME_<name>_MATRIX if it is available.
//
// If both forms are present, we signal an error.
//
fstr::assign(
save.ITEM.get_mut(3),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_MATRIX"),
);
fstr::assign(
save.ALT.get_mut(3),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_MATRIX"),
);
DTPOOL(
&save.ITEM[3],
&mut save.FOUND1,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
DTPOOL(
&save.ALT[3],
&mut save.FOUND2,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
if (save.FOUND1 && save.FOUND2) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"Frame name-based and frame ID-based text kernel (fixed-offset) frame definition keywords \'#\' and \'#\' are both present in the POOL. Most likely this is because loaded text kernels contain competing definitions of the \'#\' frame using different keyword styles, which is not allowed. ", ctx);
ERRCH(b"#", &save.ITEM[3], ctx);
ERRCH(b"#", &save.ALT[3], ctx);
ERRCH(b"#", &save.FRNAME, ctx);
SIGERR(b"SPICE(COMPETINGFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
if save.FOUND2 {
fstr::assign(save.ITEM.get_mut(3), save.ALT.get(3));
}
if BADKPV(b"TKFRAM", &save.ITEM[3], b"=", 9, 1, b"N", ctx)? {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// The variable meets current expectations, look it up
// from the kernel pool.
//
GDPOOL(
&save.ITEM[3],
1,
9,
&mut save.N,
save.MATRIX.as_slice_mut(),
&mut save.FND,
ctx,
)?;
//
// In this case the full transformation matrix has been
// specified. We simply polish it up a bit.
//
MOVED(save.MATRIX.as_slice(), 9, ROT.as_slice_mut());
SHARPR(ROT.as_slice_mut());
//
// The matrix might not be right-handed, so correct
// the sense of the second and third columns if necessary.
//
if (VDOT(ROT.subarray([1, 2]), save.MATRIX.subarray([1, 2])) < 0.0) {
VSCLIP(-1.0, ROT.subarray_mut([1, 2]));
}
if (VDOT(ROT.subarray([1, 3]), save.MATRIX.subarray([1, 3])) < 0.0) {
VSCLIP(-1.0, ROT.subarray_mut([1, 3]));
}
} else if fstr::eq(&save.SPEC, b"ANGLES") {
//
// Look up the angles, their units and axes for the
// frame specified by ID. (Note that UNITS are optional).
// As in the previous case we give preference to the
// form TKFRAME_<name>_<item> over TKFRAME_<id>_<item>.
//
fstr::assign(
save.ITEM.get_mut(3),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_ANGLES"),
);
fstr::assign(
save.ITEM.get_mut(4),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_AXES"),
);
fstr::assign(
save.ITEM.get_mut(5),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_UNITS"),
);
fstr::assign(
save.ALT.get_mut(3),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_ANGLES"),
);
fstr::assign(
save.ALT.get_mut(4),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_AXES"),
);
fstr::assign(
save.ALT.get_mut(5),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_UNITS"),
);
//
// Again, we give preference to the more friendly form
// of TKFRAME specification.
//
// If both forms are present, we signal an error.
//
for I in 3..=5 {
DTPOOL(
&save.ITEM[I],
&mut save.FOUND1,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
DTPOOL(
&save.ALT[I],
&mut save.FOUND2,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
if (save.FOUND1 && save.FOUND2) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"Frame name-based and frame ID-based text kernel (fixed-offset) frame definition keywords \'#\' and \'#\' are both present in the POOL. Most likely this is because loaded text kernels contain competing definitions of the \'#\' frame using different keyword styles, which is not allowed. ", ctx);
ERRCH(b"#", &save.ITEM[I], ctx);
ERRCH(b"#", &save.ALT[I], ctx);
ERRCH(b"#", &save.FRNAME, ctx);
SIGERR(b"SPICE(COMPETINGFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
if save.FOUND2 {
fstr::assign(save.ITEM.get_mut(I), save.ALT.get(I));
}
}
if (BADKPV(b"TKFRAM", &save.ITEM[3], b"=", 3, 1, b"N", ctx)?
|| BADKPV(b"TKFRAM", &save.ITEM[4], b"=", 3, 1, b"N", ctx)?)
{
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
fstr::assign(&mut save.UNITS, b"RADIANS");
GDPOOL(
&save.ITEM[3],
1,
3,
&mut save.N,
save.ANGLES.as_slice_mut(),
&mut save.FND,
ctx,
)?;
GIPOOL(
&save.ITEM[4],
1,
3,
&mut save.N,
save.AXES.as_slice_mut(),
&mut save.FND,
ctx,
)?;
GCPOOL(
&save.ITEM[5],
1,
1,
&mut save.N,
CharArrayMut::from_mut(&mut save.UNITS),
&mut save.FND,
ctx,
)?;
//
// Convert angles to radians.
//
for I in 1..=3 {
CONVRT(
save.ANGLES[I],
&save.UNITS,
b"RADIANS",
&mut save.TEMPD,
ctx,
)?;
save.ANGLES[I] = save.TEMPD;
}
//
// Compute the rotation from instrument frame to CK frame.
//
EUL2M(
save.ANGLES[1],
save.ANGLES[2],
save.ANGLES[3],
save.AXES[1],
save.AXES[2],
save.AXES[3],
ROT.as_slice_mut(),
ctx,
)?;
if FAILED(ctx) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
} else if fstr::eq(&save.SPEC, b"QUATERNION") {
//
// Look up the quaternion and convert it to a rotation
// matrix. Again there are two possible variables that
// may point to the quaternion. We give preference to
// the form TKFRAME_<name>_Q over the form TKFRAME_<id>_Q.
//
// If both forms are present, we signal an error.
//
fstr::assign(
save.ITEM.get_mut(3),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_Q"),
);
fstr::assign(
save.ALT.get_mut(3),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_Q"),
);
DTPOOL(
&save.ITEM[3],
&mut save.FOUND1,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
DTPOOL(
&save.ALT[3],
&mut save.FOUND2,
&mut save.N,
&mut save.TYPE,
ctx,
)?;
if (save.FOUND1 && save.FOUND2) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"Frame name-based and frame ID-based text kernel (fixed-offset) frame definition keywords \'#\' and \'#\' are both present in the POOL. Most likely this is because loaded text kernels contain competing definitions of the \'#\' frame using different keyword styles, which is not allowed. ", ctx);
ERRCH(b"#", &save.ITEM[3], ctx);
ERRCH(b"#", &save.ALT[3], ctx);
ERRCH(b"#", &save.FRNAME, ctx);
SIGERR(b"SPICE(COMPETINGFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
if save.FOUND2 {
fstr::assign(save.ITEM.get_mut(3), save.ALT.get(3));
}
if BADKPV(b"TKFRAM", &save.ITEM[3], b"=", 4, 1, b"N", ctx)? {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// In this case we have the quaternion representation.
// Again, we do a small amount of polishing of the input.
//
GDPOOL(
&save.ITEM[3],
1,
4,
&mut save.N,
save.QUATRN.as_slice_mut(),
&mut save.FND,
ctx,
)?;
VHATG(save.QUATRN.as_slice(), 4, save.QTMP.as_slice_mut());
Q2M(save.QTMP.as_slice(), ROT.as_slice_mut());
} else {
//
// We don't recognize the SPEC for this frame. Say
// so. Also note that perhaps the user needs to upgrade
// the toolkit.
//
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
SETMSG(b"The frame specification \"# = \'#\'\" is not one of the recognized means of specifying a text-kernel constant offset frame. This may reflect a typographical error or may indicate that you need to consider updating your version of the SPICE toolkit. ", ctx);
ERRCH(b"#", &save.ITEM[1], ctx);
ERRCH(b"#", &save.SPEC, ctx);
SIGERR(b"SPICE(UNKNOWNFRAMESPEC)", ctx)?;
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// Buffer the identifier, relative frame and rotation matrix.
//
save.BUFFD[[1, save.AT]] = ROT[[1, 1]];
save.BUFFD[[2, save.AT]] = ROT[[2, 1]];
save.BUFFD[[3, save.AT]] = ROT[[3, 1]];
save.BUFFD[[4, save.AT]] = ROT[[1, 2]];
save.BUFFD[[5, save.AT]] = ROT[[2, 2]];
save.BUFFD[[6, save.AT]] = ROT[[3, 2]];
save.BUFFD[[7, save.AT]] = ROT[[1, 3]];
save.BUFFD[[8, save.AT]] = ROT[[2, 3]];
save.BUFFD[[9, save.AT]] = ROT[[3, 3]];
save.BUFFI[[1, save.AT]] = *FRAME;
//
// If these were not previously buffered, we need to set
// a watch on the various items that might be used to define
// this frame.
//
if !save.BUFFRD {
//
// Immediately check for an update so that we will
// not redundantly look for this item the next time this
// routine is called.
//
fstr::assign(
save.ITEM.get_mut(1),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_RELATIVE"),
);
fstr::assign(
save.ITEM.get_mut(2),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_SPEC"),
);
fstr::assign(
save.ITEM.get_mut(3),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_AXES"),
);
fstr::assign(
save.ITEM.get_mut(4),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_MATRIX"),
);
fstr::assign(
save.ITEM.get_mut(5),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_Q"),
);
fstr::assign(
save.ITEM.get_mut(6),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_ANGLES"),
);
fstr::assign(
save.ITEM.get_mut(7),
&fstr::concat(fstr::substr(&save.AGENT, 1..=save.R), b"_UNITS"),
);
fstr::assign(
save.ITEM.get_mut(8),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_RELATIVE"),
);
fstr::assign(
save.ITEM.get_mut(9),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_SPEC"),
);
fstr::assign(
save.ITEM.get_mut(10),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_AXES"),
);
fstr::assign(
save.ITEM.get_mut(11),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_MATRIX"),
);
fstr::assign(
save.ITEM.get_mut(12),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_Q"),
);
fstr::assign(
save.ITEM.get_mut(13),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_ANGLES"),
);
fstr::assign(
save.ITEM.get_mut(14),
&fstr::concat(fstr::substr(&save.ALTNAT, 1..=save.AR), b"_UNITS"),
);
SWPOOL(&save.AGENT, 14, save.ITEM.as_arg(), ctx)?;
CVPOOL(&save.AGENT, &mut save.UPDATE, ctx)?;
}
}
if FAILED(ctx) {
LNKINI(BUFSIZ, save.POOL.as_slice_mut(), ctx)?;
*FRAME = 0;
IDENT(ROT.as_slice_mut());
CHKOUT(b"TKFRAM", ctx)?;
return Ok(());
}
//
// All errors cause the routine to exit before we get to this
// point. If we reach this point we didn't have an error and
// hence did find the rotation from ID to FRAME.
//
*FOUND = true;
//
// That's it
//
CHKOUT(b"TKFRAM", ctx)?;
Ok(())
}