Index: trunk/psphot/src/psphotAddNoise.c
===================================================================
--- trunk/psphot/src/psphotAddNoise.c	(revision 34492)
+++ trunk/psphot/src/psphotAddNoise.c	(revision 35769)
@@ -1,4 +1,5 @@
 # include "psphotInternal.h"
 
+bool psphotAddOrSubNoise_Threaded (psThreadJob *job);
 bool psphotMaskSource(pmSource *source, bool add, psImageMaskType maskVal);
 
@@ -32,10 +33,10 @@
 }
 
-static int Nmasked = 0;
-
 // the return state indicates if any sources were actually replaced
 bool psphotAddOrSubNoiseReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, bool add) {
 
     bool status = false;
+
+    psTimerStart ("psphot.noise");
 
     // find the currently selected readout
@@ -55,5 +56,9 @@
     if (!sources) return false;
 
-    psTimerStart ("psphot.noise");
+    // determine the number of allowed threads
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
+    if (!status) {
+        nThreads = 0;
+    }
 
     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
@@ -84,4 +89,79 @@
 
     psphotVisualShowImage (readout);
+
+    // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
+    int Cx = 1, Cy = 1;
+    psphotChooseCellSizes (&Cx, &Cy, readout, nThreads);
+
+    psArray *cellGroups = psphotAssignSources (Cx, Cy, sources);
+
+    for (int i = 0; i < cellGroups->n; i++) {
+
+        psArray *cells = cellGroups->data[i];
+
+        for (int j = 0; j < cells->n; j++) {
+
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_ADD_NOISE");
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, markVal,  PS_TYPE_IMAGE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, FACTOR,   PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, SIZE,     PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, add,      PS_TYPE_U8);
+
+# if (1)
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to add/sub noise.");
+                return false;
+            }
+# else
+            if (!psphotAddOrSubNoise_Threaded(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to add/sub noise.");
+                return false;
+            }
+	    psFree(job);
+# endif
+        }
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false, true)) {
+            psFree(cellGroups);
+            psError(PS_ERR_UNKNOWN, false, "Unable to add/sub noise.");
+            return false;
+        }
+
+        // we have only supplied one type of job, so we can assume the types here
+        psThreadJob *job = NULL;
+        while ((job = psThreadJobGetDone()) != NULL) {
+            // we have no returned data from this operation
+            if (job->args->n < 1) {
+                fprintf (stderr, "error with job\n");
+            }
+            psFree(job);
+        }
+    }
+
+    if (add) {
+        psLogMsg ("psphot.noise", PS_LOG_WARN, "add noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
+    } else {
+        psLogMsg ("psphot.noise", PS_LOG_WARN, "sub noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
+    }
+
+    psFree (cellGroups);
+
+    psphotVisualShowImage (readout);
+
+    return true;
+}
+
+bool psphotAddOrSubNoise_Threaded (psThreadJob *job) {
+
+    psArray *sources = job->args->data[0];
+    psImageMaskType maskVal = PS_SCALAR_VALUE(job->args->data[1], PS_TYPE_IMAGE_MASK_DATA);
+    psImageMaskType markVal = PS_SCALAR_VALUE(job->args->data[2], PS_TYPE_IMAGE_MASK_DATA);
+    psF32 FACTOR = PS_SCALAR_VALUE(job->args->data[3], F32);
+    psF32 SIZE = PS_SCALAR_VALUE(job->args->data[4], F32);
+    psBool add  = PS_SCALAR_VALUE(job->args->data[5], U8);
 
     // loop over all source
@@ -104,13 +184,4 @@
 	psphotMaskSource (source, add, markVal);
     }
-    if (add) {
-        psLogMsg ("psphot.noise", PS_LOG_WARN, "add noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
-    } else {
-        psLogMsg ("psphot.noise", PS_LOG_WARN, "sub noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
-    }
-    fprintf (stderr, "masked %d objects\n", Nmasked);
-
-    psphotVisualShowImage (readout);
-
     return true;
 }
@@ -142,7 +213,5 @@
 	}
     }
-    Nmasked ++;
-
-    return true;
-}
-
+    return true;
+}
+
