diff --git a/nexus/obspyImproved.py b/nexus/obspyImproved.py
index f1c6bd62caec3b63fb91fa94d65bad13d17582b7..fa136b5e5dbcf54152bdfe84bfc56e0e9f15652a 100644
--- a/nexus/obspyImproved.py
+++ b/nexus/obspyImproved.py
@@ -6,9 +6,14 @@ Lloyd Carothers
 """
 from collections import defaultdict
 import os
+import io
+from urllib.parse import urlparse
+import operator
 
+import obspy
 from obspy import read as obspy_read
 from obspy import Inventory
+from obspy.clients.nrl.client import NRLPath, NRL, RemoteNRL, LocalNRL
 from obspy import UTCDateTime
 from obspy.core.inventory import (Network, Station, Channel,
                                   Site, Equipment)
@@ -17,6 +22,7 @@ from obspy.io.mseed.core import _is_mseed
 from obspy.io.mseed import util
 
 from . import hardware
+from .hardware import NRL_ROOT
 
 MODULE = 'Nexus.2023.4.0.0'
 MODULE_URI = 'www.passcal.nmt.edu'
@@ -423,8 +429,7 @@ class InventoryIm(Inventory):
                 sr = '{:g}'.format(chan.sample_rate)
                 dl_keys = dl.get_nrl_keys(gain, sr)
                 try:
-                    response = hardware.get_nrl_response(dl_keys,
-                                                         sensor_keys)
+                    response = get_nrl_response(dl_keys, sensor_keys)
                     chan.response = response
                     print('Response computed.')
                 except Exception as e:
@@ -739,8 +744,180 @@ def scan_ms(dir_name, status_message=print):
     print(f'Scan took: {time.time() - start}')
     return streams, mseed_files
 
+
 def quick_scan_ms(dir_name, status_message=print):
     import time
     start = time.time()
 
     print(f'Scan took: {time.time() - start}')
+
+
+class NRLIm(NRL):
+    """
+    Improve NRL object to create instance of RemoteNRLIm and LocalNRLIm
+    """
+    def __new__(cls, root=None):
+        # root provided and it's no web URL
+        if root:
+            scheme = urlparse(root).scheme
+            if scheme in ('http', 'https'):
+                instance = super(NRL, cls).__new__(RemoteNRLIm)
+            else:
+                # Check if it's really a folder on the file-system.
+                if not os.path.isdir(root):
+                    msg = ("Provided path '{}' seems to be a local file path "
+                           "but the directory does not exist.").format(root)
+                    raise ValueError(msg)
+                instance = super(NRL, cls).__new__(LocalNRLIm)
+        else:
+            # Otherwise delegate to the remote NRL client to deal with all
+            # kinds of remote resources (currently only HTTP).
+            instance = super(NRL, cls).__new__(RemoteNRLIm)
+        instance.root = root
+        cls.__init__(instance)
+
+        return instance
+
+
+class RemoteNRLIm(RemoteNRL):
+    """
+    Improve RemoteNRLIm to use new _get_response()
+    """
+    def _get_response(self, base, keys):
+        """
+        Improve _get_response to use _get_value() function instead of
+        NRLDict's __getitem__()
+
+        Internal helper method to fetch a response
+
+        This circumvents the warning message that is shown for NRL v2 when a
+        datalogger-only response is fetched
+
+        :type base: str
+        :param base: either "sensors" or "dataloggers"
+        :type keys: list of str
+        :param keys: list of lookup keys
+        """
+        node = getattr(self, base)
+        for key in keys:
+            node = _get_value(node, key)
+
+        # Parse to an inventory object and return a response object.
+        description, path, resp_type = node
+        with io.BytesIO(self._read_resp(path).encode()) as buf:
+            buf.seek(0, 0)
+            return obspy.read_inventory(
+                buf, format=resp_type)[0][0][0].response, resp_type
+
+
+class LocalNRLIm(LocalNRL):
+    """
+    Improve LocalNRLIm to use new _get_response()
+    """
+    def _get_response(self, base, keys):
+        """
+        Improve _get_response to use _get_value() function instead of
+        NRLDict's __getitem__()
+
+        Internal helper method to fetch a response
+
+        This circumvents the warning message that is shown for NRL v2 when a
+        datalogger-only response is fetched
+
+        :type base: str
+        :param base: either "sensors" or "dataloggers"
+        :type keys: list of str
+        :param keys: list of lookup keys
+        """
+        node = getattr(self, base)
+        for key in keys:
+            node = _get_value(node, key)
+
+        # Parse to an inventory object and return a response object.
+        description, path, resp_type = node
+        with io.BytesIO(self._read_resp(path).encode()) as buf:
+            buf.seek(0, 0)
+            return obspy.read_inventory(
+                buf, format=resp_type)[0][0][0].response, resp_type
+
+
+def get_nrl_response(dl_keys, sensor_keys):
+    """
+    improve hardware.py's get_nrl_response()
+    """
+    nrl = NRLIm(NRL_ROOT)
+    return nrl.get_response(dl_keys, sensor_keys)
+
+
+def _get_nrl_path_from_comparison_key(nrl_dict, key, compared_value, op):
+    """
+    In case the key start with comparison operator "<=" or ">",
+    compare the given compared_value with the value in the key
+    and return the corresponding value of the key which isNRLPath.
+
+    :param nrl_dict: an NRLDict instance
+    :param key: key with comparison operator. Ex "<= 200 sps"
+    :param compared_value: the value to be compared
+    :param op: operator to be checked. Ex: "<=" or ">"
+    :return  an NRLPath
+    """
+    comparison_ops = {
+        '<=': operator.le,
+        '>': operator.gt
+    }
+    org_key = key
+    if key.strip().startswith(op):
+        standard_value_str = \
+            key.replace(op, '').replace('sps', '').strip()
+        standard_value = float(standard_value_str)
+        if comparison_ops[op](compared_value, standard_value):
+            return nrl_dict.__getitem__(org_key)
+
+
+def _get_value(nrl_dict, name):
+    """
+    Improve from obspy.client.nrl.client.NRLDict's __getitem__
+    This function along with NRLIm, RemoteNRLIm, LocalNRLIm, get_nrl_response,
+    _get_path should be removed and replace get_nrl_response with
+    hardware.get_nrl_response when obspy release an update for getting response
+    for Q8.
+
+    Getting path from nrl_dict by using name as key
+    Normally name will be one of the keys of the nrl_dict.
+    """
+    try:
+        value = nrl_dict.__getitem__(name)
+    except KeyError as e:
+        """
+        The improvement is for the case of Q8 when name won't be one of
+        the key of the nrl_dict anymore. The NRL documentation provides
+        question "Is the highest simultaneous sampling rate recorded" with
+        different keys that are comparison expression strings with a standard
+        value, e.g. "<= 200 sps", typically the maximum or minimum sample rate.
+        This ensures that the downsampling of the sample rate aligns with the
+        specified value. In such cases, the program must apply the comparison
+        using the given key to determine the correct path.
+        """
+        try:
+            name_float = float(name)
+            value = None
+            keys = nrl_dict.keys()
+            for key in keys:
+                value = _get_nrl_path_from_comparison_key(
+                    nrl_dict, key, name_float, "<=")
+                if value is not None:
+                    break
+                value = _get_nrl_path_from_comparison_key(
+                    nrl_dict, key, name_float, ">")
+                if value is not None:
+                    break
+            if value is None:
+                raise e
+        except ValueError:
+            raise e
+
+    # if encountering a not yet parsed NRL Path, expand it now
+    if isinstance(value, NRLPath):
+        value = nrl_dict._nrl._parse_ini(value)
+        nrl_dict[name] = value
+    return value