def encode_section3(message: dict, context: dict): """Encodes section 3.""" buf = context['buf'] section3 = message['section3'] start = buf.tell() buf.seek(start + 3) buf.write(b'\x00') # Set to 0 per standard number_of_subsets = shift_uint(section3['number_of_subsets'], 16, 0, 16) buf.write(number_of_subsets) flags_byte = 0 if section3['observed_flag']: flags_byte |= 0x80 if section3['compressed_flag']: flags_byte |= 0x40 buf.write(bytes([flags_byte])) for descriptor in section3['descriptors']: f, x, y = parse_ref(descriptor) fx = bytearray(1) fx = encode_uint(fx, f, 0, 2) fx = encode_uint(fx, x, 2, 6) buf.write(fx) buf.write(bytes([y])) end = buf.tell() section_len = end - start buf.seek(start) section_len_b = shift_uint(section_len, 24, 0, 24) buf.write(section_len_b) buf.seek(end)
def get_sequence_description(fxy_str: str) -> pd.DataFrame: """Returns a sequence description used for encoding/decoding.""" summary = get_summary(fxy_str).copy(deep=True) # Get rid of the old index because it references a combination of other tables summary.reset_index(drop=True, inplace=True) summary.rename(columns={ 'BUFR_Scale': 'scale', 'BUFR_ReferenceValue': 'offset', 'FXY': 'fxy', 'Parent': 'parent', 'BUFR_DataWidth_Bits': 'bit_len', }, inplace=True) summary['type'] = np.nan summary['text'] = summary['Title'] for i, row in summary.iterrows(): f, x, y = parse_ref(row['fxy']) typename = '' title = f'{row["Title"]} ({row["BUFR_Unit"]})' if f == 2: typename = 'operator' elif row['BUFR_Unit'] == 'CCITT IA5': typename = 'string' elif row['BUFR_Unit'] == 'Replication': typename = 'replication' else: typename = 'numeric' summary.iloc[i, summary.columns.get_loc('type')] = typename summary.iloc[i, summary.columns.get_loc('text')] = title return summary
def table_d_lookup(f, x, y, parent=None): """Returns a data frame for a Table D.""" df = get_table_d(f, x, y) sub_references = [] fxy_str = f'{f}{x:02d}{y:03d}' if parent is None: title = df.iloc[0]['Title_en'] subtitle = '' sub_references.append((fxy_str, fxy_str, title, subtitle)) parent = fxy_str for index, row in df.iterrows(): ref = row['FXY2'] title = row['ElementName_en'] subtitle = row['ElementDescription_en'] ref_f, ref_x, ref_y = parse_ref(ref) if ref_f == 0: sub_references.append((parent, ref, title, subtitle)) if ref_f == 2 and ref_x == 8: sub_references.append((parent, ref, title, subtitle)) if ref_f == 1: sub_references.append((parent, ref, title, subtitle)) if ref_f == 3: sub_references.append((parent, ref, title, subtitle)) sub_refs = table_d_lookup(ref_f, ref_x, ref_y, ref) sub_references.extend(sub_refs) return sub_references
def encode_section4(message: dict, context: dict): """Encodes section 4.""" buf = context['buf'] write_buf = io.BytesIO() start = buf.tell() buf.seek(start + 3) buf.write(b'\x00') bit_offset = 0 sequence = message['section4'][:] override_bitlength = None for seq in sequence: # Deal with operators if seq['type'] == 'operator': f, x, y = parse_ref(seq['fxy']) if (f, x) == (2, 8): if y > 0: override_bitlength = y * 8 else: override_bitlength = None continue if seq['bit_len'] < 1: # Skip 0-length sections, they're for information purposes only continue if seq['type'] == 'numeric': bitlen = seq['bit_len'] if override_bitlength: bitlen = override_bitlength value = float(seq['value']) if seq['scale']: value = value * math.pow(10, seq['scale']) if seq['offset']: value = value - seq['offset'] # The value should be ROUNDED to the nearest integer value = int(np.round(value)) write_uint(write_buf, value, bit_offset, bitlen) bit_offset += seq['bit_len'] elif seq['type'] == 'string': bitlen = seq['bit_len'] if override_bitlength: bitlen = override_bitlength write_ascii(write_buf, seq['value'], bit_offset, bitlen) bit_offset += seq['bit_len'] write_buf.seek(0) buf.write(write_buf.read()) # Write section length end = buf.tell() section_len = end - start section_len_b = shift_uint(section_len, 24, 0, 24) buf.seek(start) buf.write(section_len_b) buf.seek(end)
def get_summary(fxy_str: str) -> pd.DataFrame: """Returns a summary table of the contents of the FXXYYY sequence.""" f, x, y = parse_ref(fxy_str) references = table_d_lookup(f, x, y) df = combine_references(references) columns = [ 'Parent', 'FXY', 'Title', 'Subtitle', 'BUFR_DataWidth_Bits', 'BUFR_Unit', 'BUFR_Scale', 'BUFR_ReferenceValue', ] return df[columns]
def get_code_table_figure(fxy_str: str, code_figure: int) -> dict: """Returns the code table row for the given FXXYYY string.""" f, x, y = parse_ref(fxy_str) filename = f'BUFRCREX_CodeFlag_en_{x:02d}.csv' utf8_reader = codecs.getreader('utf-8') with pkg_resources.resource_stream('bufrtools.tables', f'data/{filename}') as f: reader = csv.DictReader(utf8_reader(f)) for row in reader: if fxy_str != row['FXY']: continue if '-' in row['CodeFigure']: start, end = (int(i) for i in row['CodeFigure'].split('-')) if start <= code_figure and code_figure <= end: return row else: try: val = int(row['CodeFigure']) if val == code_figure: return row except ValueError: pass
def get_code_table(fxy_str: str) -> pd.DataFrame: """Returns the code table for the given FXXYYY string.""" f, x, y = parse_ref(fxy_str) filename = f'BUFRCREX_CodeFlag_en_{x:02d}.csv' utf8_reader = codecs.getreader('utf-8') with pkg_resources.resource_stream('bufrtools.tables', f'data/{filename}') as f: reader = csv.DictReader(utf8_reader(f)) rows = [] for row in reader: if fxy_str != row['FXY']: continue if '-' in row['CodeFigure']: start, end = (int(i) for i in row['CodeFigure'].split('-')) for i in range(start, end + 1): enumerated_row = copy.copy(row) enumerated_row['CodeFigure'] = i rows.append(enumerated_row) else: rows.append(row) df = pd.DataFrame(rows) df = df.astype({'CodeFigure': np.uint16}) return df
def combine_references(references) -> pd.DataFrame: """Returns a data frame for a generic reference, that will be flattened.""" frames = [] for parent, reference, title, subtitle in references: f, x, y = parse_ref(reference) if f == 2 and x == 8: frames.append( pd.DataFrame([{ 'Parent': parent, 'FXY': f'{f}{x:02d}{y:03d}', 'ElementName_en': f'Operator Change CCITT IA5 width to {8 * y}', 'BUFR_DataWidth_Bits': 0, 'BUFR_Unit': 'Operator', 'Title': title, 'Subtitle': subtitle, }])) elif f == 1: if y == 0: frames.append( pd.DataFrame([{ 'Parent': parent, 'FXY': f'{f}{x:02d}{y:03d}', 'ClassName_en': 'Replication', 'ElementName_en': f'Delayed Replication: {x}', 'BUFR_DataWidth_Bits': 0, 'BUFR_Unit': 'Replication', 'Title': title, 'Subtitle': subtitle, }])) else: frames.append( pd.DataFrame([{ 'Parent': parent, 'FXY': f'{f}{x:02d}{y:03d}', 'ClassName_en': 'Replication', 'ElementName_en': f'Replication {x}', 'BUFR_DataWidth_Bits': 0, 'BUFR_Unit': 'Replication', 'Title': title, 'Subtitle': subtitle, }])) elif f == 3: df = get_table_d(f, x, y) name = df.iloc[0]['Title_en'] frames.append( pd.DataFrame([{ 'Parent': parent, 'FXY': f'{f}{x:02d}{y:03d}', 'ClassName_en': 'sequence', 'ElementName_en': f'{name}', 'BUFR_DataWidth_Bits': 0, 'BUFR_Unit': 'Sequence', 'Title': title, 'Subtitle': subtitle, }])) else: df = get_table_b(f, x, y) df['Parent'] = parent df['Title'] = title df['Subtitle'] = subtitle frames.append(df) return pd.concat(frames)