Skip to content

Conversation

@ofir-frd
Copy link
Collaborator

@ofir-frd ofir-frd commented Jun 11, 2025

PR Type

Bug fix


Description

• Fixed time constant selection logic in LockIn driver
• Corrected auto time constant calculation formula
• Fixed wait completion status check condition
• Removed debugging comments for cleaner code


Changes walkthrough 📝

Relevant files
Bug fix
main.py
Fix time constant logic and status checks                               

src/LockIn-SignalRecovery_7265DSP/main.py

• Changed comparison operator from < to > in time constant selection

Fixed auto time constant formula from factor*period to factor/period

Corrected status byte check condition from stb & 1 == 1 to stb & 1 ==
0
• Removed debugging comments and improved code formatting

+8/-11   

Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @qodo-merge-demo
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Possible Issue

    The comparison operator in time constant selection was changed from < to >, and the status check condition was inverted from stb & 1 == 1 to stb & 1 == 0. These changes fundamentally alter the logic and could break existing functionality. The correctness of these changes needs validation against the device specifications.

        tc_index = sum(np.array(self.timeconstants_numbers) > time_constant)  # sum over boolean entries
    
        if tc_index < len(self.timeconstants):
            new_tc_key = list(self.timeconstants.keys())[tc_index]
        else:
            new_tc_key = "100k"
    
        return new_tc_key
    
    def auto_time_constant(self, factor=10.0):
        """
        checks the current frequency and adapts the time constant
    
        Args:
            factor: float, factor by which the time constant is at least higher than the period of the signal
    
        """
    
        frq = self.get_frequency()
    
        period = 1.0 / frq
        new_tc = factor / period
    
        new_tc_key = self.find_best_time_constant_key(new_tc)
    
        # sending the new time constant command
        self.port.write(self.timeconstants[new_tc_key])
        self.wait_for_complete()
    
    def wait_for_complete(self):
    
        starttime = time.time()
        while True:
            stb = self.port.port.read_stb()
    
            if stb & 1 == 0:
    📚 Focus areas based on broader codebase context

    Formula Inconsistency

    The auto time constant formula has been changed from factor*period to factor/period, which appears to invert the relationship between time constant and signal period. This should be validated against the device specification and existing working implementations to ensure the time constant is correctly calculated relative to the signal frequency. (Ref 7)

    new_tc = factor / period

    Reference reasoning: The RAG content shows a similar device (7280DSP) using time_constant = 10.0*period in its adapt method, suggesting multiplication is the standard approach for calculating time constants based on signal periods in lock-in amplifiers.

    📄 References
    1. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [406-444]
    2. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [115-146]
    3. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [446-453]
    4. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [286-314]
    5. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [680-689]
    6. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7265DSP/main.py [691-697]
    7. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7280DSP/main.py [337-384]
    8. Codium-ai/instrument-drivers/src/LockIn-SignalRecovery_7280DSP/main.py [118-142]

    @qodo-merge-demo
    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix reversed comparison operator logic

    The comparison operator change from < to > appears to reverse the intended logic
    for finding the appropriate time constant index. This could result in selecting
    an incorrect time constant that's too short for the given value.

    src/LockIn-SignalRecovery_7265DSP/main.py [508]

    -tc_index = sum(np.array(self.timeconstants_numbers) > time_constant)  # sum over boolean entries
    +tc_index = sum(np.array(self.timeconstants_numbers) < time_constant)  # sum over boolean entries
    • Apply / Chat
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion correctly identifies that changing the comparison operator from < to > inverts the logic for finding the time constant index. The original logic correctly found the index of the first time constant greater than the target value, while the new logic is flawed. This is a critical bug fix.

    High
    Correct time constant calculation formula

    The formula factor / period calculates factor * frequency, which is inverse to
    the typical time constant calculation. For proper time constant selection, it
    should be factor * period to ensure the time constant is proportionally longer
    than the signal period.

    src/LockIn-SignalRecovery_7265DSP/main.py [528-529]

     period = 1.0 / frq
    -new_tc = factor / period
    +new_tc = factor * period
    • Apply / Chat
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion correctly points out a fundamental error in the time constant calculation. The time constant should be proportional to the signal's period (factor * period), not its frequency (factor / period). The PR introduces a bug that this suggestion fixes, which is critical for the function's correctness.

    High
    Fix inverted status byte condition

    The status byte check logic has been inverted from checking if bit 0 is set
    (command processing) to checking if it's clear. This change may cause the
    function to exit prematurely before the command is actually complete.

    src/LockIn-SignalRecovery_7265DSP/main.py [543-544]

    -if stb & 1 == 0:
    +if stb & 1 == 1:
         break
    • Apply / Chat
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion correctly identifies that the PR inverts the logic for checking the operation completion status. The function wait_for_complete should wait until a command is finished. The original code if stb & 1 == 1: break likely corresponds to breaking when a "complete" or "ready" bit is set, which is a standard hardware communication pattern. The PR's change introduces a likely bug.

    High
    • More

    @ofir-frd
    Copy link
    Collaborator Author

    /ask How is the wait_for_complete method implemented elsewhere?

    @qodo-merge-demo
    Copy link

    Ask

    How is the wait_for_complete method implemented elsewhere?

    Answer:

    The wait_for_complete method implementations across the codebase follow several common patterns:

    1. Polling with timeout: Most implementations use a while loop that continuously checks a status condition with a timeout mechanism. They typically use time.time() to track elapsed time and break the loop when a timeout is exceeded.

    2. Sleep intervals: Implementations include small sleep intervals (usually 0.01-0.1 seconds) between status checks to avoid excessive CPU usage during polling.

    3. Status checking patterns: Different devices use various status indicators:

      • Boolean completion flags (like sweeper.finished())
      • Status integers where 0 typically indicates completion
      • Bitwise operations on status bytes
    4. Error handling: Most implementations raise exceptions or print warnings when timeouts occur, often providing context about what operation failed.

    5. Progress reporting: Some implementations include progress reporting during the wait period.

    The PR's changes to the wait_for_complete method align with these patterns by:

    • Maintaining the timeout mechanism with a 20-second limit
    • Using 0.01-second sleep intervals
    • Correcting the status byte check condition from stb & 1 == 1 to stb & 1 == 0, which follows the common pattern where 0 indicates completion
    • Raising an exception on timeout with a descriptive message

    This correction suggests that the device signals completion when the first bit is 0 rather than 1, which is consistent with how other status checking implementations work where 0 typically represents a "ready" or "complete" state.

    📄 References

    Comment on lines 541 to 548
    stb = self.port.port.read_stb()
    if stb & 1 == 1: # first byte indicates whether command is processed or not

    if stb & 1 == 0:
    break

    time.sleep(0.01)
    if time.time() - starttime > 20:
    raise Exception("Timeout during wait for completion.")
    Copy link
    Collaborator Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    /implement Make this wait_for_complete method work like it's commonly done in the repo.

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Code Implementation 🛠️

    Implementation: Refactor the wait_for_complete method to follow common patterns in the repository by using a timeout parameter and implementing a more structured polling loop with proper timeout handling.

    Suggested change
    stb = self.port.port.read_stb()
    if stb & 1 == 1: # first byte indicates whether command is processed or not
    if stb & 1 == 0:
    break
    time.sleep(0.01)
    if time.time() - starttime > 20:
    raise Exception("Timeout during wait for completion.")
    def wait_for_complete(self, timeout=20.0):
    """
    Wait for the device to complete the current operation.
    Args:
    timeout: float, maximum time to wait in seconds
    """
    start_time = time.time()
    while (time.time() - start_time) < timeout:
    stb = self.port.port.read_stb()
    if stb & 1 == 0:
    break
    time.sleep(0.01)
    else:
    raise Exception("Timeout during wait for completion.")
    📄 References

    See review comment here

    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @ofir-frd
    Copy link
    Collaborator Author

    /ask How is the wait_for_complete method implemented elsewhere?

    @Codium-ai Codium-ai deleted a comment from qodo-merge-demo bot Jun 12, 2025
    @qodo-merge-demo
    Copy link

    Ask

    How is the wait_for_complete method implemented elsewhere?

    Answer:

    The wait_for_complete method implementations across the codebase follow similar patterns but with different approaches:

    1. Polling with timeout pattern: Most implementations use a while loop that continuously checks a status condition with a timeout mechanism. They typically sleep for short intervals (0.01-0.2 seconds) between checks to avoid busy waiting.

    2. Status checking variations: Different devices use different status indicators:

      • Some check module status values (status == 0 for completion)
      • Others use progress indicators or finished() methods
      • The LockIn driver checks status byte bits using bitwise operations
    3. Timeout handling: All implementations include timeout protection, typically:

      • Recording start time with time.time()
      • Checking elapsed time in each loop iteration
      • Raising exceptions or breaking loops when timeout is exceeded
      • Timeout values range from 2-20 seconds depending on the operation
    4. Error handling: Most implementations include proper error handling for timeout scenarios, with descriptive error messages indicating what operation failed to complete.

    The PR's changes to the wait_for_complete method align with these patterns - it maintains the polling approach with timeout protection, but fixes the status byte check condition from stb & 1 == 1 to stb & 1 == 0, which appears to correct the logic for detecting when a command has completed processing.

    📄 References

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants