After much research it appears the best solution to this issue is to use two nested PID loops. The inner loop will control the quads angular velocity, while the outer loop will control the quads angle by setting the target angular velocity. In hindsight this is a much more intuitive approach, as the motors are used to control angular velocity, not angle, so trying to control the angle directly is a poor approach.
Tuning the angular velocity loop will require the quad to be able to rotate indefinitely on its single axis jig whilst streaming data, something the current rs232 cable restricts, so an XBee will need to be integrated. Also, my current Euler angle representation doesn't allow any rotation past 180 degrees due to the singularity at this point caused by the 180 -> -180 degrees jump, so I will need to move to a better singularity-free representation.
The two main candidates for this are quaternions, a complex but computationally efficient concept using three imaginary and one real axes, or a direction cosine matrix, a 3x3 matrix storing the cosines of the angles between the three axes of the reference rotation (the gravity vector in this case) and the three axes of the rotated system.
Due to this large code rewrite and PCB redesign I will postpone implementing this until after summer when I have more free time, hopefully using it as my third year research project. Therefore for now I will try to improve the PID loop as much as I can, and hopefully spend more time actually flying instead of designing.