We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
libgcf assumes 6 character stream IDs
The GCF reader (libgcf.py) assumes the stream ID is 6 characters, and hard-codes access to element 4:
```
    header['station'] = stid[:4]
    header['channel'] = (channel_prefix[:2] + stid[4]).upper()
```

The Stream ID is made up of three parts: first is up to 4 characters for serial number, then one char for channel letter, then one channel for tap number. Rather than assume the channel letter is at index 4, it should be considered the 2nd last...
```
    header['station'] = stid[:-2]
    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()
```

This was causing some valid GCF files to be considered invalid when they had short IDs.
libgcf assumes 6 character stream IDs
The GCF reader (libgcf.py) assumes the stream ID is 6 characters, and hard-codes access to element 4:
```
    header['station'] = stid[:4]
    header['channel'] = (channel_prefix[:2] + stid[4]).upper()
```

The Stream ID is made up of three parts: first is up to 4 characters for serial number, then one char for channel letter, then one channel for tap number. Rather than assume the channel letter is at index 4, it should be considered the 2nd last...
```
    header['station'] = stid[:-2]
    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()
```

This was causing some valid GCF files to be considered invalid when they had short IDs.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `obspy/io/gcf/libgcf.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # reads Guralp Compressed Format (GCF) Files
3 # By Ran Novitsky Nof @ BSL, 2016
4 # ran.nof@gmail.com
5 # Based on Guralp's GCF reference (GCF-RFC-GCFR, Issue C, 2011-01-05)
6 # more details available from: http://www.guralp.com/apps/ok?doc=GCF_Intro
7 # last access: June, 2016
8 from __future__ import (absolute_import, division, print_function,
9                         unicode_literals)
10 from future.builtins import *  # NOQA
11 
12 import numpy as np
13 
14 from obspy import UTCDateTime
15 
16 SPS_D = {  # Table 3.1: special sample rates
17     157: 0.1,
18     161: 0.125,
19     162: 0.2,
20     164: 0.25,
21     167: 0.5,
22     171: 400,
23     174: 500,
24     176: 1000,
25     179: 2000,
26     181: 4000}
27 TIME_OFFSETS_D = {  # Table 3.1: Time fractional offset denominator
28     171: 8.,
29     174: 2.,
30     176: 4.,
31     179: 8.,
32     181: 16.}
33 COMPRESSION_D = {  # Table 3.2: format field to data type
34     1: '>i4',
35     2: '>i2',
36     4: '>i1'}
37 
38 
39 def is_gcf(f):
40     """
41     Test if file is GCF by reading at least 1 data block
42     """
43     header, data = read_data_block(f)
44 
45 
46 def decode36(data):
47     """
48     Converts an integer into a base36 string.
49     """
50     # http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/Decoding_Base_36_numbers_C.htm
51     s = ''
52     while data:
53         imed = data % 36
54         if imed > 9:
55             c = chr(imed - 10 + ord('A'))
56         else:
57             c = chr(imed + ord('0'))
58         s = c + s
59         data = data // 36
60     return s
61 
62 
63 def decode_date_time(data):
64     """
65     Decode date and time field.
66 
67     The date code is a 32 bit value specifying the start time of the block.
68     Bits 0-16 contain the number of seconds since midnight,
69     and bits 17-31 the number of days since 17th November 1989.
70     """
71     # prevent numpy array
72     days = int(data >> 17)
73     secs = int(data & 0x1FFFF)
74     starttime = UTCDateTime('1989-11-17') + days * 86400 + secs
75     return starttime
76 
77 
78 def read_data_block(f, headonly=False, channel_prefix="HH", **kwargs):
79     """
80     Read one data block from GCF file.
81 
82     more details can be found here:
83     http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/GCF_Specification.htm
84     f - file object to read from
85     if skipData is True, Only header is returned.
86     if not a data block (SPS=0) - returns None.
87     """
88     # get ID
89     sysid = f.read(4)
90     if not sysid:
91         raise EOFError  # got to EOF
92     sysid = np.frombuffer(sysid, count=1, dtype='>u4')
93     if sysid >> 31 & 0b1 > 0:
94         sysid = (sysid << 6) >> 6
95     sysid = decode36(sysid)
96     # get Stream ID
97     stid = np.frombuffer(f.read(4), count=1, dtype='>u4')
98     stid = decode36(stid)
99     # get Date & Time
100     data = np.frombuffer(f.read(4), count=1, dtype='>u4')
101     starttime = decode_date_time(data)
102     # get data format
103     # get reserved, SPS, data type compression,
104     # number of 32bit records (num_records)
105     reserved, sps, compress, num_records = np.frombuffer(f.read(4), count=4,
106                                                          dtype='>u1')
107     compression = compress & 0b00000111  # get compression code
108     t_offset = compress >> 4  # get time offset
109     if t_offset > 0:
110         starttime = starttime + t_offset / TIME_OFFSETS_D[sps]
111     if sps in SPS_D:
112         sps = SPS_D[sps]  # get special SPS value if needed
113     if not sps:
114         f.seek(num_records * 4, 1)  # skip if not a data block
115         if 1008 - num_records * 4 > 0:
116             # keep skipping to get 1008 record
117             f.seek(1008 - num_records * 4, 1)
118         return None
119     npts = num_records * compression  # number of samples
120     header = {}
121     header['starttime'] = starttime
122     header['station'] = stid[:4]
123     header['channel'] = (channel_prefix[:2] + stid[4]).upper()
124     header['sampling_rate'] = float(sps)
125     header['npts'] = npts
126     if headonly:
127         f.seek(4 * (num_records + 2), 1)  # skip data part (inc. FIC and RIC)
128         # skip to end of block if only partly filled with data
129         if 1000 - num_records * 4 > 0:
130             f.seek(1000 - num_records * 4, 1)
131         return header
132     else:
133         # get FIC
134         fic = np.frombuffer(f.read(4), count=1, dtype='>i4')
135         # get incremental data
136         data = np.frombuffer(f.read(4 * num_records), count=npts,
137                              dtype=COMPRESSION_D[compression])
138         # construct time series
139         data = (fic + np.cumsum(data)).astype('i4')
140         # get RIC
141         ric = np.frombuffer(f.read(4), count=1, dtype='>i4')
142         # skip to end of block if only partly filled with data
143         if 1000 - num_records * 4 > 0:
144             f.seek(1000 - num_records * 4, 1)
145         # verify last data sample matches RIC
146         if not data[-1] == ric:
147             raise ValueError("Last sample mismatch with RIC")
148         return header, data
149 
150 
151 def read_header(f, **kwargs):
152     """
153     Reads header only from GCF file.
154     """
155     return read_data_block(f, headonly=True, **kwargs)
156 
157 
158 def read(f, **kwargs):
159     """
160     Reads header and data from GCF file.
161     """
162     return read_data_block(f, headonly=False, **kwargs)
163
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/obspy/io/gcf/libgcf.py b/obspy/io/gcf/libgcf.py
--- a/obspy/io/gcf/libgcf.py
+++ b/obspy/io/gcf/libgcf.py
@@ -119,8 +119,8 @@
     npts = num_records * compression  # number of samples
     header = {}
     header['starttime'] = starttime
-    header['station'] = stid[:4]
-    header['channel'] = (channel_prefix[:2] + stid[4]).upper()
+    header['station'] = stid[:-2]
+    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()
     header['sampling_rate'] = float(sps)
     header['npts'] = npts
     if headonly:

{"golden_diff": "diff --git a/obspy/io/gcf/libgcf.py b/obspy/io/gcf/libgcf.py\n--- a/obspy/io/gcf/libgcf.py\n+++ b/obspy/io/gcf/libgcf.py\n@@ -119,8 +119,8 @@\n     npts = num_records * compression  # number of samples\n     header = {}\n     header['starttime'] = starttime\n-    header['station'] = stid[:4]\n-    header['channel'] = (channel_prefix[:2] + stid[4]).upper()\n+    header['station'] = stid[:-2]\n+    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()\n     header['sampling_rate'] = float(sps)\n     header['npts'] = npts\n     if headonly:\n", "issue": "libgcf assumes 6 character stream IDs\nThe GCF reader (libgcf.py) assumes the stream ID is 6 characters, and hard-codes access to element 4:\r\n```\r\n    header['station'] = stid[:4]\r\n    header['channel'] = (channel_prefix[:2] + stid[4]).upper()\r\n```\r\n\r\nThe Stream ID is made up of three parts: first is up to 4 characters for serial number, then one char for channel letter, then one channel for tap number. Rather than assume the channel letter is at index 4, it should be considered the 2nd last...\r\n```\r\n    header['station'] = stid[:-2]\r\n    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()\r\n```\r\n\r\nThis was causing some valid GCF files to be considered invalid when they had short IDs.\nlibgcf assumes 6 character stream IDs\nThe GCF reader (libgcf.py) assumes the stream ID is 6 characters, and hard-codes access to element 4:\r\n```\r\n    header['station'] = stid[:4]\r\n    header['channel'] = (channel_prefix[:2] + stid[4]).upper()\r\n```\r\n\r\nThe Stream ID is made up of three parts: first is up to 4 characters for serial number, then one char for channel letter, then one channel for tap number. Rather than assume the channel letter is at index 4, it should be considered the 2nd last...\r\n```\r\n    header['station'] = stid[:-2]\r\n    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()\r\n```\r\n\r\nThis was causing some valid GCF files to be considered invalid when they had short IDs.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# reads Guralp Compressed Format (GCF) Files\n# By Ran Novitsky Nof @ BSL, 2016\n# ran.nof@gmail.com\n# Based on Guralp's GCF reference (GCF-RFC-GCFR, Issue C, 2011-01-05)\n# more details available from: http://www.guralp.com/apps/ok?doc=GCF_Intro\n# last access: June, 2016\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\nfrom future.builtins import *  # NOQA\n\nimport numpy as np\n\nfrom obspy import UTCDateTime\n\nSPS_D = {  # Table 3.1: special sample rates\n    157: 0.1,\n    161: 0.125,\n    162: 0.2,\n    164: 0.25,\n    167: 0.5,\n    171: 400,\n    174: 500,\n    176: 1000,\n    179: 2000,\n    181: 4000}\nTIME_OFFSETS_D = {  # Table 3.1: Time fractional offset denominator\n    171: 8.,\n    174: 2.,\n    176: 4.,\n    179: 8.,\n    181: 16.}\nCOMPRESSION_D = {  # Table 3.2: format field to data type\n    1: '>i4',\n    2: '>i2',\n    4: '>i1'}\n\n\ndef is_gcf(f):\n    \"\"\"\n    Test if file is GCF by reading at least 1 data block\n    \"\"\"\n    header, data = read_data_block(f)\n\n\ndef decode36(data):\n    \"\"\"\n    Converts an integer into a base36 string.\n    \"\"\"\n    # http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/Decoding_Base_36_numbers_C.htm\n    s = ''\n    while data:\n        imed = data % 36\n        if imed > 9:\n            c = chr(imed - 10 + ord('A'))\n        else:\n            c = chr(imed + ord('0'))\n        s = c + s\n        data = data // 36\n    return s\n\n\ndef decode_date_time(data):\n    \"\"\"\n    Decode date and time field.\n\n    The date code is a 32 bit value specifying the start time of the block.\n    Bits 0-16 contain the number of seconds since midnight,\n    and bits 17-31 the number of days since 17th November 1989.\n    \"\"\"\n    # prevent numpy array\n    days = int(data >> 17)\n    secs = int(data & 0x1FFFF)\n    starttime = UTCDateTime('1989-11-17') + days * 86400 + secs\n    return starttime\n\n\ndef read_data_block(f, headonly=False, channel_prefix=\"HH\", **kwargs):\n    \"\"\"\n    Read one data block from GCF file.\n\n    more details can be found here:\n    http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/GCF_Specification.htm\n    f - file object to read from\n    if skipData is True, Only header is returned.\n    if not a data block (SPS=0) - returns None.\n    \"\"\"\n    # get ID\n    sysid = f.read(4)\n    if not sysid:\n        raise EOFError  # got to EOF\n    sysid = np.frombuffer(sysid, count=1, dtype='>u4')\n    if sysid >> 31 & 0b1 > 0:\n        sysid = (sysid << 6) >> 6\n    sysid = decode36(sysid)\n    # get Stream ID\n    stid = np.frombuffer(f.read(4), count=1, dtype='>u4')\n    stid = decode36(stid)\n    # get Date & Time\n    data = np.frombuffer(f.read(4), count=1, dtype='>u4')\n    starttime = decode_date_time(data)\n    # get data format\n    # get reserved, SPS, data type compression,\n    # number of 32bit records (num_records)\n    reserved, sps, compress, num_records = np.frombuffer(f.read(4), count=4,\n                                                         dtype='>u1')\n    compression = compress & 0b00000111  # get compression code\n    t_offset = compress >> 4  # get time offset\n    if t_offset > 0:\n        starttime = starttime + t_offset / TIME_OFFSETS_D[sps]\n    if sps in SPS_D:\n        sps = SPS_D[sps]  # get special SPS value if needed\n    if not sps:\n        f.seek(num_records * 4, 1)  # skip if not a data block\n        if 1008 - num_records * 4 > 0:\n            # keep skipping to get 1008 record\n            f.seek(1008 - num_records * 4, 1)\n        return None\n    npts = num_records * compression  # number of samples\n    header = {}\n    header['starttime'] = starttime\n    header['station'] = stid[:4]\n    header['channel'] = (channel_prefix[:2] + stid[4]).upper()\n    header['sampling_rate'] = float(sps)\n    header['npts'] = npts\n    if headonly:\n        f.seek(4 * (num_records + 2), 1)  # skip data part (inc. FIC and RIC)\n        # skip to end of block if only partly filled with data\n        if 1000 - num_records * 4 > 0:\n            f.seek(1000 - num_records * 4, 1)\n        return header\n    else:\n        # get FIC\n        fic = np.frombuffer(f.read(4), count=1, dtype='>i4')\n        # get incremental data\n        data = np.frombuffer(f.read(4 * num_records), count=npts,\n                             dtype=COMPRESSION_D[compression])\n        # construct time series\n        data = (fic + np.cumsum(data)).astype('i4')\n        # get RIC\n        ric = np.frombuffer(f.read(4), count=1, dtype='>i4')\n        # skip to end of block if only partly filled with data\n        if 1000 - num_records * 4 > 0:\n            f.seek(1000 - num_records * 4, 1)\n        # verify last data sample matches RIC\n        if not data[-1] == ric:\n            raise ValueError(\"Last sample mismatch with RIC\")\n        return header, data\n\n\ndef read_header(f, **kwargs):\n    \"\"\"\n    Reads header only from GCF file.\n    \"\"\"\n    return read_data_block(f, headonly=True, **kwargs)\n\n\ndef read(f, **kwargs):\n    \"\"\"\n    Reads header and data from GCF file.\n    \"\"\"\n    return read_data_block(f, headonly=False, **kwargs)\n", "path": "obspy/io/gcf/libgcf.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# reads Guralp Compressed Format (GCF) Files\n# By Ran Novitsky Nof @ BSL, 2016\n# ran.nof@gmail.com\n# Based on Guralp's GCF reference (GCF-RFC-GCFR, Issue C, 2011-01-05)\n# more details available from: http://www.guralp.com/apps/ok?doc=GCF_Intro\n# last access: June, 2016\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\nfrom future.builtins import *  # NOQA\n\nimport numpy as np\n\nfrom obspy import UTCDateTime\n\nSPS_D = {  # Table 3.1: special sample rates\n    157: 0.1,\n    161: 0.125,\n    162: 0.2,\n    164: 0.25,\n    167: 0.5,\n    171: 400,\n    174: 500,\n    176: 1000,\n    179: 2000,\n    181: 4000}\nTIME_OFFSETS_D = {  # Table 3.1: Time fractional offset denominator\n    171: 8.,\n    174: 2.,\n    176: 4.,\n    179: 8.,\n    181: 16.}\nCOMPRESSION_D = {  # Table 3.2: format field to data type\n    1: '>i4',\n    2: '>i2',\n    4: '>i1'}\n\n\ndef is_gcf(f):\n    \"\"\"\n    Test if file is GCF by reading at least 1 data block\n    \"\"\"\n    header, data = read_data_block(f)\n\n\ndef decode36(data):\n    \"\"\"\n    Converts an integer into a base36 string.\n    \"\"\"\n    # http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/Decoding_Base_36_numbers_C.htm\n    s = ''\n    while data:\n        imed = data % 36\n        if imed > 9:\n            c = chr(imed - 10 + ord('A'))\n        else:\n            c = chr(imed + ord('0'))\n        s = c + s\n        data = data // 36\n    return s\n\n\ndef decode_date_time(data):\n    \"\"\"\n    Decode date and time field.\n\n    The date code is a 32 bit value specifying the start time of the block.\n    Bits 0-16 contain the number of seconds since midnight,\n    and bits 17-31 the number of days since 17th November 1989.\n    \"\"\"\n    # prevent numpy array\n    days = int(data >> 17)\n    secs = int(data & 0x1FFFF)\n    starttime = UTCDateTime('1989-11-17') + days * 86400 + secs\n    return starttime\n\n\ndef read_data_block(f, headonly=False, channel_prefix=\"HH\", **kwargs):\n    \"\"\"\n    Read one data block from GCF file.\n\n    more details can be found here:\n    http://geophysics.eas.gatech.edu/GTEQ/Scream4.4/GCF_Specification.htm\n    f - file object to read from\n    if skipData is True, Only header is returned.\n    if not a data block (SPS=0) - returns None.\n    \"\"\"\n    # get ID\n    sysid = f.read(4)\n    if not sysid:\n        raise EOFError  # got to EOF\n    sysid = np.frombuffer(sysid, count=1, dtype='>u4')\n    if sysid >> 31 & 0b1 > 0:\n        sysid = (sysid << 6) >> 6\n    sysid = decode36(sysid)\n    # get Stream ID\n    stid = np.frombuffer(f.read(4), count=1, dtype='>u4')\n    stid = decode36(stid)\n    # get Date & Time\n    data = np.frombuffer(f.read(4), count=1, dtype='>u4')\n    starttime = decode_date_time(data)\n    # get data format\n    # get reserved, SPS, data type compression,\n    # number of 32bit records (num_records)\n    reserved, sps, compress, num_records = np.frombuffer(f.read(4), count=4,\n                                                         dtype='>u1')\n    compression = compress & 0b00000111  # get compression code\n    t_offset = compress >> 4  # get time offset\n    if t_offset > 0:\n        starttime = starttime + t_offset / TIME_OFFSETS_D[sps]\n    if sps in SPS_D:\n        sps = SPS_D[sps]  # get special SPS value if needed\n    if not sps:\n        f.seek(num_records * 4, 1)  # skip if not a data block\n        if 1008 - num_records * 4 > 0:\n            # keep skipping to get 1008 record\n            f.seek(1008 - num_records * 4, 1)\n        return None\n    npts = num_records * compression  # number of samples\n    header = {}\n    header['starttime'] = starttime\n    header['station'] = stid[:-2]\n    header['channel'] = (channel_prefix[:2] + stid[-2]).upper()\n    header['sampling_rate'] = float(sps)\n    header['npts'] = npts\n    if headonly:\n        f.seek(4 * (num_records + 2), 1)  # skip data part (inc. FIC and RIC)\n        # skip to end of block if only partly filled with data\n        if 1000 - num_records * 4 > 0:\n            f.seek(1000 - num_records * 4, 1)\n        return header\n    else:\n        # get FIC\n        fic = np.frombuffer(f.read(4), count=1, dtype='>i4')\n        # get incremental data\n        data = np.frombuffer(f.read(4 * num_records), count=npts,\n                             dtype=COMPRESSION_D[compression])\n        # construct time series\n        data = (fic + np.cumsum(data)).astype('i4')\n        # get RIC\n        ric = np.frombuffer(f.read(4), count=1, dtype='>i4')\n        # skip to end of block if only partly filled with data\n        if 1000 - num_records * 4 > 0:\n            f.seek(1000 - num_records * 4, 1)\n        # verify last data sample matches RIC\n        if not data[-1] == ric:\n            raise ValueError(\"Last sample mismatch with RIC\")\n        return header, data\n\n\ndef read_header(f, **kwargs):\n    \"\"\"\n    Reads header only from GCF file.\n    \"\"\"\n    return read_data_block(f, headonly=True, **kwargs)\n\n\ndef read(f, **kwargs):\n    \"\"\"\n    Reads header and data from GCF file.\n    \"\"\"\n    return read_data_block(f, headonly=False, **kwargs)\n", "path": "obspy/io/gcf/libgcf.py"}]}