Changes to Ingest.
The basic functioning of the AWIPS radar ingest has always been
as follows:
- Decode the radar id, Nexrad message code, and any applicable tilt angle
(or layer) from the RPG message.
- Look up the entry from radarInfo.txt based on the message code, and extract
the product type (e.g. base reflectivity), resolution (gate spacing),
and number of data levels (bit depth) from the radar info.
- Match the radar id, product type, resolution, data levels and tilt angle
to entries in the data keys to identify the data key to store the
data on behalf of.
- Copy the RPG message file to the directory specified in the data key.
With the introduction of the super res velocity product, we now have
two kinds of products that would be identified as 8 bit base velocity
with gate spacing of 0.25km. From a pure data modeling approach, what
distinguishes these products is the azimuthal resolution. Thus, one
way this could have been dealt with was to add azimuthal resolution to
our ingest data model. This approach has two drawbacks; it adds more
complexity to the ingest data model, and it locks us into yet another
rigid data model that could potentially have to change again in the
future.
What we have done to solve this is to allow the option of
identifying an ingest data set with the current data model, or to
identify it solely on the basis of the Nexrad message code and
tilt angle. Here are two example generic data key entries;
each is for the 0.5 degree tilt angle. 41805 is the key for the
existing 8 bit base velocity with standard azimuth resolution; this
entry is actually unchanged. 21805 is the key for the new super res
base velocity:
41805|@@@@/@@@@Rad230km|1|0.5|0.25|256|0|radar/@@@@/V/elev0_5/res0_25/level256| | |
21805|@@@@/@@@@Rad30Okm|1|0.5|0.25|256|0,154|radar/@@@@/V/elev0_5/res0_25/az0_5/level256| | |
Note that the only differences are the key itself, the directory path,
and that the super res entry (21805) has an extra ,154 in the
seventh vertical bar delimited field. 154 is the Nexrad message code
for super res velocity, and the presence of this field tells the
software to perform the ingest data key matching based on the message
code and tilt angle. The existing entry for key 41805, not having
a Nexrad message code present, will use the same ingest data model
as it always has.
No existing directory names will change, but we will
add the az0_5/ to the directory path for any data set that is
half degree azimuth resolution. The az0_5/ is being added one
level up from the ultimate final storage directory so that none of the
storage directories get a mix of directory and data files.
Review of Code Changes.
At the end of this document is a list of all files changed against the
OB8.2 radar DCS (DCS3421). This code review will only cover changes
made in the radar ingest code. Furthermore, only changes that allow
the determination of the radar storage directory directly from the RPG
message code will be discussed; we will not discuss ingest changes
that support treating the CFC as a layer product. What follows is
a list of the files which will be discussed here:
D-2D/src/localization/scripts/genRadarDataKeys.ksh
D-2D/src/dataMgmt/DM_DataAccessInfo.H
D-2D/src/dataMgmt/DM_DataAccessInfo.C
D-2D/src/dataMgmt/dmDataAccessFns.C
D-2D/src/dataMgmt/dmDataAccessFns.H
D-2D/src/dataMgmt/RadarAccessKey.C
D-2D/src/dataMgmt/RadarAccessKey.H
In order to be able to use the RPG message code directly for
directory assignment, it is useful to be able to place the
message code directly into the data access key entry. In order
that RPG message codes placed in radarDataKeys.template
would get propagated to the specific data key entries,
genRadarDataKeys.ksh had to be changed. Also, clients of the
data access key info need a way to obtain the message code;
for this we changed the DM_DataAccessInfo class.Here are the
changed areas in these files:
genRadarDataKeys.ksh:
23: cut '-d|' -f1-6 temp2 > temp1
24: cut '-d|' -f7-11 temp2 | sed 's/^ *0 *//g' > temp3
25: $pasteUtil temp1 "-|$index" temp3 >> radarDataKeys.txt
DM_DataAccessInfo.H:
71: MessageCode messageCode();
DM_DataAccessInfo.C:
428: MessageCode DM_DataAccessInfo::messageCode()
429: {
430: return (int)_integer6_1;
431: }
The module RadarAccessKey is where clients can obtain radar data keys
on behalf of the individual items in our radar data model. A new alternate
lookup method was added that allows the RPG message code to be substituted
for the resolution, bit depth, and AWIPS product type in the data model.
For OB8.2, some logic was also changed to streamline the identification
of which radars are TDWR radars, but that will not be discussed here.
Here are the pertinent changed areas in these files:
RadarAccessKey.H:
50: DataAccessKey dataKey(MessageCode & msgCode, RadarElevation& elev,
51: RadarSite& hashSite, RadarSite& keySite);
RadarAccessKey.C:
183: if (info.messageCode()==0)
184: {
185: DM_GetRadarAccessKeyHashKey infoKey(info.getRadarProduct(),
186: info.getRadarElevation(),
187: info.getRadarLevel(),
188: info.getRadarResolution(),
189: 0);
190: dataAccessKey &= 0x4000FFFF;
191: _dict.add(infoKey, dataAccessKey);
192: }
193: else
194: {
195: DM_GetRadarAccessKeyHashKey infoKey(info.messageCode(),
196: info.getRadarElevation(),
197: 0);
198: dataAccessKey &= 0x4000FFFF;
199: _dict.add(infoKey, dataAccessKey);
200: }
:
:
241:// Use as a shortcut to verify if a key is valid.
242:static StaticTextDict * dataKeyImpl = 0;
243:
244:// -- public ----------------------------------------------------------------
245:// RadarAccessKey::dataKey(RadarProduct& prod, RadarElevation& elev,
246:// RadarLevel& level, RadarResolution& res, RadarSite& hashSite)
247://
248:// Returns a valid DataAccessKey for the specified product, or returns 0
249:// --------------------------------------------------------------------------
250:DataAccessKey RadarAccessKey::dataKey(
251: MessageCode & msgCode, RadarElevation& elev,
252: RadarSite& hashSite, RadarSite& keySite)
253: {
254: DM_GetRadarAccessKeyHashKey hashKey(msgCode, elev, hashSite);
255: DataAccessKey dataAccessKey = 0;
256:
257: if (_dict.map(hashKey, dataAccessKey))
258: {
259: if (!dataKeyImpl)
260: dataKeyImpl = DM_DataAccessInfo::allDataKeys();
261: dataAccessKey |= (keySite<<16);
262: if (!dataKeyImpl->keyValid(dataAccessKey))
263: dataAccessKey = 0;
264: }
265:
266: return dataAccessKey;
267: }
268:
269:// -- public ----------------------------------------------------------------
270:// RadarAccessKey::dataKey(RadarProduct& prod, RadarElevation& elev,
271:// RadarLevel& level, RadarResolution& res, RadarSite& hashSite)
272://
273:// Returns a valid DataAccessKey for the specified product, or returns 0
274:// --------------------------------------------------------------------------
275:DataAccessKey RadarAccessKey::dataKey(
276: RadarProduct& prod, RadarElevation& elev,
277: RadarLevel& level, RadarResolution& res,
278: RadarSite& hashSite, RadarSite& keySite)
279: {
280: DM_GetRadarAccessKeyHashKey hashKey(prod, elev, level, res, hashSite);
281: DataAccessKey dataAccessKey = 0;
282:
283: if (_dict.map(hashKey, dataAccessKey))
284: {
285: if (!dataKeyImpl)
286: dataKeyImpl = DM_DataAccessInfo::allDataKeys();
287: dataAccessKey |= (keySite<<16);
288: if (!dataKeyImpl->keyValid(dataAccessKey))
289: dataAccessKey = 0;
290: }
291:
292: return dataAccessKey;
293: }
In the module dmDataAccessFns, the method dmGetRadarAccessKey was changed
so that first a key lookup will be tried based on the new RPG message
code model, and if that failed, it would revert to the old resolution,
bit depth, product model. The parsing of the file tiltAngleGroups.txt
was moved out of dmGetRadarAccessKey, both to make it simpler and
to make the information from that table availble to outside clients;
the outside client interface logic will not be covered here as it is not
important to the radar ingest.
dmDataAccessFns.H:
93:DataAccessKey dmGetRadarAccessKey(RadarProductName, MessageCode,
94: RadarElevation, RadarLevel, RadarResolution,
95: RadarSiteName);
:
:
151: DM_GetRadarAccessKeyHashKey(MessageCode msgCode, RadarElevation elev,
152: RadarSite site)
153: : _product(-(RadarProduct)msgCode), _elev(elev), _level(0), _res(0),
154: _site(site) {}
dmDataAccessFns.C:
619:// Shared global data structures that have info parsed from tiltAngleGroups.txt
620:static float * angleLookup = 0;
621:static long * spaceOffsetLookup = 0;
622:static float * tiltOfSpaceOffset = 0;
623:static int nSpaceOffsets = 0;
624:
625:// -- static -----------------------------------------------------------------
626:// void dmParseTiltAngleGroups()
627://
628:// Parse information out of tiltAngleGroups.txt. Very hardwired to deal with
629:// a range of tilts from -0.9 to 90.0 in 0.1 degree steps.
630:// ---------------------------------------------------------------------------
631:// ---------------------------------------------------------------------------
632:static void dmParseTiltAngleGroups()
633: {
634: if (angleLookup) return;
635:
636: // Set up a default state.
637: int a, t;
638: float p, v1, v2;
639: angleLookup = new float[910];
640: angleLookup += 9;
641: // Table set up to go from -0.9 to 90.0
642: for (a=-9; a<=900; a++) angleLookup[a] = a*0.1;
643: spaceOffsetLookup = new long[910];
644: spaceOffsetLookup += 9;
645: for (a=-9; a<=900; a++) spaceOffsetLookup[a] = 0;
646:
647: // See if we have tilt angle groups available.
648: TextBuffer tbag("tiltAngleGroups.txt");
649: for (t=0; t<tbag.buffer().length(); t++)
650: {
651: a = sscanf(tbag.buffer()[t].stringPtr(),"%f %f %f",&p,&v1,&v2);
652: if (a!=3 || v1<-0.9 || v2>90 || p<v1 || p>v2) continue;
653: a = v1<0 ? -(int)(0.5-10*v1) : (int)(0.5+10*v1);
654: v2 = v2*10+0.5;
655: // Because in other parts of the radar ingest system we use
656: // zero for the null tilt case, and angle of 0.0 must ALWAYS
657: // map to 0.0.
658: if (a<=0 && v2>=0) p = 0.0;
659: nSpaceOffsets++;
660: for ( ; a<v2; a++)
661: {
662: angleLookup[a] = p;
663: spaceOffsetLookup[a] = nSpaceOffsets;
664: }
665: }
666: if (nSpaceOffsets==0)
667: {
668: logProblem << "No useable entries in tiltAngleGroups.txt" << std::endl;
669: return;
670: }
671: if (nSpaceOffsets<10)
672: logProblem << "Only " << nSpaceOffsets
673: << " entries in tiltAngleGroups.txt" << std::endl;
674: tiltOfSpaceOffset = new float[nSpaceOffsets];
675: tiltOfSpaceOffset--;
676: for (a=-9; a<=900; a++)
677: if (spaceOffsetLookup[a]!=0)
678: tiltOfSpaceOffset[spaceOffsetLookup[a]] = angleLookup[a];
679: }
680:
:
:
726:
727:// -- global -----------------------------------------------------------------
728:// dmGetRadarAccessKey()
729:// Given information identifying a radar product
730:// return the corresponding DataAccessKey (or zero if none exists).
731://
732:// ---------------------------------------------------------------------------
733:// ---------------------------------------------------------------------------
734:DataAccessKey dmGetRadarAccessKey(RadarProductName product,
735: MessageCode msgCode,
736: RadarElevation elevation,
737: RadarLevel level,
738: RadarResolution resolution,
739: RadarSiteName site)
740: {
741: static bool initialized = false;
742: static Dict dict;
743: static RadarSite knatSite = -1;
744:
745: knatSite = cvtRadarSite("knat");
746: RadarAccessKey radarKey(knatSite);
747:
748: // Initialize a dictionary, the first time through
749: if (!initialized)
750: {
751: AbsTime start = AbsTime::current();
752: dmParseTiltAngleGroups();
:
:
891: DataAccessKey dKey = radarKey.dataKey(msgCode, elevation,
892: hashSite, keySite);
893: if (dKey==0)
894: dKey = radarKey.dataKey(prod, elevation, level, resolution,
895: hashSite, keySite);
896: return dKey;
897: }
In the course of preparing the material for this review, an issue has surfaced.
As it turns out, some of the code that was modified here was previously
replicated in the file D-2D/src/dm/scan/dmGetRadarAccKey_SCAN.C and
subsequently modified. The author of dmGetRadarAccKey_SCAN.C should
be contacted so that it will be possible to discuss whether the ability
to respond to RPG message code based data key entries will have to be added
there.
List of files changed for OB8.2 radar work.
Add new RPG products, allow specification of azimuth resolution:
D-2D/src/dataMgmt/DM_RadarInfo.C
D-2D/src/dataMgmt/radarInfo.txt
New color tables for spectrum width:
D-2D/src/localization/scripts/genRadarDepictKeys.ksh
D-2D/src/dataMgmt/colorMaps.mark
Application updates:
D-2D/src/applications/radar/common/prod-mgmt.tcl
D-2D/src/applications/radar/otr/prods.conf
D-2D/src/applications/radar/rmr/prods.conf
D-2D/src/applications/radar/rps/prods.conf
D-2D/src/ipc/radar/sendOTR.cfc.sh
D-2D/src/ipc/radar/sendOtrs.C
D-2D/src/ipc/radar/sendOtrs.sh
D-2D/src/ipc/radar/ProductRequestEntry.C
Assignment of storage directory uses message code directly:
D-2D/src/localization/scripts/genRadarDataKeys.ksh
D-2D/src/dataMgmt/DM_DataAccessInfo.C
D-2D/src/dataMgmt/DM_DataAccessInfo.H
D-2D/src/dataMgmt/dmDataAccessFns.C
D-2D/src/dataMgmt/dmDataAccessFns.H
D-2D/src/dataMgmt/RadarAccessKey.C
D-2D/src/dataMgmt/RadarAccessKey.H
Treat CFC product as being layer based:
D-2D/src/dm/radar/RadarFileName.C
D-2D/src/dm/radar/RadarStorageController.C
Display changes for super-res.
D-2D/src/depict/ImageDepictable.C
D-2D/src/depict/RadarPVImageDepict.C
D-2D/src/depict/RadarPVImageDepict.H
D-2D/src/dm/radar/decodeRadar.h
Display change for CFC.
D-2D/src/dm/radar/prodDepParmsRoutines.c
Now have products with 300km coverage.
D-2D/src/dm/radar/Radar.H
D-2D/src/localization/scripts/makeRadarSups.csh
Show super-res tilts on Unit Status display
D-2D/src/dm/nexrad/graphics/Radar_fg.C
D-2D/src/dm/nexrad/nonprod/RadarGsmMsg.C
D-2D/src/dm/nexrad/nonprod/RadarGsmMsg.H
Add new data sets, displays, and menus.
D-2D/src/localization/nationalData/radarDataKeys.template
D-2D/src/localization/nationalData/radarDataMenus.template
D-2D/src/localization/nationalData/radarDepictKeys.template
D-2D/src/localization/nationalData/radarGenericImageStyle.txt
D-2D/src/localization/nationalData/radarImageStyleInfo.template
D-2D/src/localization/nationalData/radarMultiLoadInfo.template
D-2D/src/localization/nationalData/radarProductButtonInfo.template